From acc44aaf6c49717652c1b0d2e7bdef5684c61a00 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 15:22:43 -0700 Subject: [PATCH 001/290] 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 60b4cde4708..11b6f3e9865 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -41,6 +41,7 @@ ATTR_WEBHOOK_ENCRYPTED = 'encrypted' 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 61188b50e1b..9efd1fcd9f8 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -20,15 +20,16 @@ from homeassistant.helpers.discovery import load_platform 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 a935110754c..bbdfcde93e7 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 5ef602bc2efc2e28702b5647d7ef43a28975b0eb Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 15:33:37 -0700 Subject: [PATCH 002/290] 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 11b6f3e9865..3f1a6bc988c 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -43,7 +43,6 @@ ATTR_WEBHOOK_TYPE = 'type' 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 @@ FIRE_EVENT_SCHEMA = vol.Schema({ }) 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 9efd1fcd9f8..e8372c8648d 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -15,7 +15,7 @@ from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, 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 @@ from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_NAME, ATTR_EVENT_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_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 919a2a6e1fb..6dfe050191b 100644 --- a/tests/components/mobile_app/const.py +++ b/tests/components/mobile_app/const.py @@ -49,7 +49,9 @@ REGISTER_CLEARTEXT = { 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 195d33e15b2..7861e63459a 100644 --- a/tests/components/mobile_app/test_http_api.py +++ b/tests/components/mobile_app/test_http_api.py @@ -5,7 +5,7 @@ import pytest 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 bbdfcde93e7..75e8903c494 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 673c8907e399eeb0961d3ebb33c23f66d3867627 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 15:38:53 -0700 Subject: [PATCH 003/290] 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 3f1a6bc988c..7a497d76454 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_DATA = 'app_data' 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_EVENT_TYPE = 'event_type' 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 @@ RENDER_TEMPLATE_SCHEMA = vol.Schema({ } }) +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 30083cc86b1..4948407b63b 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.storage import Store 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 @@ class RegistrationsView(HomeAssistantView): 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 e8372c8648d..4d3e0aef4c6 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -6,7 +6,9 @@ from typing import Dict 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.discovery import load_platform 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 2787671de5a6a4960b5462d875c1a2681c3eae37 Mon Sep 17 00:00:00 2001 From: Phil Hawthorne Date: Thu, 14 Mar 2019 13:08:23 +1100 Subject: [PATCH 004/290] 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 83b4f3ad934..96a4c747293 100644 --- a/homeassistant/components/sensor/waze_travel_time.py +++ b/homeassistant/components/sensor/waze_travel_time.py @@ -218,7 +218,6 @@ class WazeTravelTime(Entity): 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 3fcfba0a1e41db4fa5806316352e6e72dd81d3df Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 21:55:30 -0700 Subject: [PATCH 005/290] 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 2f94cf48f4d..6a561e570c5 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -21,7 +21,7 @@ from homeassistant.helpers.event import async_track_point_in_utc_time 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_HUE = 'philips_hue' 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 @@ CONFIG_ENTRY_HANDLERS = { } 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 c930723d00b..65e2479bad8 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 95da6d41f9fe7f2c5d5d30783ba69bf52b75b80c Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 22:05:56 -0700 Subject: [PATCH 006/290] 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 badc403c7c8..888a4d51c95 100644 --- a/homeassistant/components/default_config/__init__.py +++ b/homeassistant/components/default_config/__init__.py @@ -6,7 +6,6 @@ DEPENDENCIES = ( 'cloud', 'config', 'conversation', - 'discovery', 'frontend', 'history', 'logbook', @@ -17,6 +16,7 @@ DEPENDENCIES = ( 'sun', 'system_health', 'updater', + 'zeroconf', ) diff --git a/homeassistant/config.py b/homeassistant/config.py index db59e2c2744..19b8087e538 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -75,6 +75,9 @@ introduction: # http: # base_url: example.duckdns.org:8123 +# Discover some devices automatically +discovery: + # Sensors sensor: # Weather prediction From e75b12b92aacbb1bbd9752a18d84f5fe069b070e Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 00:34:52 -0700 Subject: [PATCH 007/290] Add myself to CODEOWNERS --- CODEOWNERS | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index a795c4c3151..96de36290b4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -66,15 +66,17 @@ homeassistant/components/device_tracker/automatic.py @armills homeassistant/components/device_tracker/bt_smarthub.py @jxwolstenholme homeassistant/components/device_tracker/huawei_router.py @abmantis homeassistant/components/device_tracker/quantum_gateway.py @cisasteelersfan +homeassistant/components/device_tracker/synology_srm.py @aerialls homeassistant/components/device_tracker/tile.py @bachya homeassistant/components/device_tracker/traccar.py @ludeeus -homeassistant/components/device_tracker/synology_srm.py @aerialls homeassistant/components/device_tracker/xfinity.py @cisasteelersfan +homeassistant/components/lametric/notify.py @robbiet480 homeassistant/components/light/lifx_legacy.py @amelchio homeassistant/components/light/yeelight.py @rytilahti homeassistant/components/light/yeelightsunflower.py @lindsaymarkward homeassistant/components/lock/nello.py @pschmitt homeassistant/components/lock/nuki.py @pschmitt +homeassistant/components/media_player/braviatv.py @robbiet480 homeassistant/components/media_player/emby.py @mezz64 homeassistant/components/media_player/kodi.py @armills homeassistant/components/media_player/liveboxplaytv.py @pschmitt @@ -83,11 +85,18 @@ homeassistant/components/media_player/monoprice.py @etsinko homeassistant/components/media_player/mpd.py @fabaff homeassistant/components/media_player/xiaomi_tv.py @fattdev homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth +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/scene/lifx_cloud.py @amelchio @@ -100,12 +109,15 @@ homeassistant/components/sensor/darksky.py @fabaff homeassistant/components/sensor/discogs.py @thibmaek homeassistant/components/sensor/file.py @fabaff homeassistant/components/sensor/filter.py @dgomes +homeassistant/components/sensor/fitbit.py @robbiet480 homeassistant/components/sensor/fixer.py @fabaff homeassistant/components/sensor/flunearyou.py @bachya homeassistant/components/sensor/gearbest.py @HerrHofrat homeassistant/components/sensor/gitter.py @fabaff homeassistant/components/sensor/glances.py @fabaff +homeassistant/components/sensor/google_travel_time.py @robbiet480 homeassistant/components/sensor/gpsd.py @fabaff +homeassistant/components/sensor/gtfs.py @robbiet480 homeassistant/components/sensor/integration.py @dgomes homeassistant/components/sensor/irish_rail_transport.py @ttroy50 homeassistant/components/sensor/jewish_calendar.py @tsvi @@ -117,6 +129,7 @@ homeassistant/components/sensor/moon.py @fabaff homeassistant/components/sensor/netdata.py @fabaff homeassistant/components/sensor/nmbs.py @thibmaek homeassistant/components/sensor/nsw_fuel_station.py @nickw444 +homeassistant/components/sensor/ohmconnect.py @robbiet480 homeassistant/components/sensor/pi_hole.py @fabaff homeassistant/components/sensor/pollen.py @bachya homeassistant/components/sensor/pvoutput.py @fabaff @@ -133,11 +146,13 @@ homeassistant/components/sensor/swiss*.py @fabaff homeassistant/components/sensor/sytadin.py @gautric homeassistant/components/sensor/tautulli.py @ludeeus homeassistant/components/sensor/time_date.py @fabaff +homeassistant/components/sensor/uber.py @robbiet480 homeassistant/components/sensor/version.py @fabaff homeassistant/components/sensor/waqi.py @andrey-git homeassistant/components/sensor/worldclock.py @fabaff homeassistant/components/switch/switchbot.py @danielhiversen homeassistant/components/switch/switchmate.py @danielhiversen +homeassistant/components/tts/amazon_polly.py @robbiet480 homeassistant/components/vacuum/roomba.py @pschmitt homeassistant/components/weather/__init__.py @fabaff homeassistant/components/weather/darksky.py @fabaff @@ -175,6 +190,7 @@ homeassistant/components/esphome/*.py @OttoWinter # F homeassistant/components/freebox/*.py @snoof85 +homeassistant/components/foursquare/* @robbiet480 # G homeassistant/components/googlehome/* @ludeeus @@ -188,6 +204,7 @@ homeassistant/components/huawei_lte/* @scop # I homeassistant/components/influx/* @fabaff +homeassistant/components/ios/* @robbiet480 homeassistant/components/ipma/* @dgomes # K @@ -201,6 +218,7 @@ homeassistant/components/luftdaten/* @fabaff # M homeassistant/components/matrix/* @tinloaf homeassistant/components/melissa/* @kennedyshead +homeassistant/components/mobile_app/* @robbiet480 homeassistant/components/*/melissa.py @kennedyshead homeassistant/components/*/mystrom.py @fabaff @@ -246,6 +264,7 @@ homeassistant/components/toon/* @frenck # U homeassistant/components/unifi/* @kane610 homeassistant/components/upcloud/* @scop +homeassistant/components/upnp/* @robbiet480 homeassistant/components/utility_meter/* @dgomes # V @@ -259,6 +278,7 @@ homeassistant/components/xiaomi_aqara/* @danielhiversen @syssi homeassistant/components/xiaomi_miio/* @rytilahti @syssi # Z +homeassistant/components/zeroconf/* @robbiet480 homeassistant/components/zoneminder/* @rohankapoorcom # Other code From 6381242ecaf91299fb644aea8a6146ea15d9f111 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Thu, 14 Mar 2019 02:25:07 -0700 Subject: [PATCH 008/290] 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 ab43dc8c6ea..458fdff87fd 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv 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 65e2479bad8..525d726e73c 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 abf2e763b124f03304e1d6b4cc0008bf3e4ad8e2 Mon Sep 17 00:00:00 2001 From: kbickar Date: Thu, 14 Mar 2019 06:46:44 -0400 Subject: [PATCH 009/290] Add sense guard clause (#22014) --- homeassistant/components/sense/__init__.py | 4 ++-- homeassistant/components/sense/binary_sensor.py | 2 ++ homeassistant/components/sense/sensor.py | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sense/__init__.py b/homeassistant/components/sense/__init__.py index 97771200bcd..be3ab75b555 100644 --- a/homeassistant/components/sense/__init__.py +++ b/homeassistant/components/sense/__init__.py @@ -44,7 +44,7 @@ async def async_setup(hass, config): _LOGGER.error("Could not authenticate with sense server") return False hass.async_create_task( - async_load_platform(hass, 'sensor', DOMAIN, None, config)) + async_load_platform(hass, 'sensor', DOMAIN, {}, config)) hass.async_create_task( - async_load_platform(hass, 'binary_sensor', DOMAIN, None, config)) + async_load_platform(hass, 'binary_sensor', DOMAIN, {}, config)) return True diff --git a/homeassistant/components/sense/binary_sensor.py b/homeassistant/components/sense/binary_sensor.py index 545aaa8ae7b..0341f65e963 100644 --- a/homeassistant/components/sense/binary_sensor.py +++ b/homeassistant/components/sense/binary_sensor.py @@ -52,6 +52,8 @@ MDI_ICONS = { async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Sense binary sensor.""" + if discovery_info is None: + return data = hass.data[SENSE_DATA] sense_devices = await data.get_discovered_device_data() diff --git a/homeassistant/components/sense/sensor.py b/homeassistant/components/sense/sensor.py index 4810ebf1958..ffde584c0ae 100644 --- a/homeassistant/components/sense/sensor.py +++ b/homeassistant/components/sense/sensor.py @@ -48,6 +48,8 @@ SENSOR_VARIANTS = [PRODUCTION_NAME.lower(), CONSUMPTION_NAME.lower()] async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Sense sensor.""" + if discovery_info is None: + return data = hass.data[SENSE_DATA] @Throttle(MIN_TIME_BETWEEN_DAILY_UPDATES) From 4ff8a46acfd5080ed5aac61596f4b44a8e89cdd5 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 14 Mar 2019 05:09:32 -0700 Subject: [PATCH 010/290] Upgrade prefetched tox env to py37 (#22029) --- 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 03d6ab47c24..90c9eee3485 100644 --- a/virtualization/Docker/Dockerfile.dev +++ b/virtualization/Docker/Dockerfile.dev @@ -51,7 +51,7 @@ COPY homeassistant/const.py homeassistant/const.py # Prefetch dependencies for tox COPY homeassistant/package_constraints.txt homeassistant/package_constraints.txt -RUN tox -e py36 --notest +RUN tox -e py37 --notest # END: Development additions From 2b25c25ca8bdb9891b98d13a2b62bda1d9d6ac56 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Thu, 14 Mar 2019 13:11:04 +0100 Subject: [PATCH 011/290] 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 7676806cfdf..d66059c55a0 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -15,7 +15,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send 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 525d726e73c..1bcba2f3ab3 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 b022428cb6d59526c155c64d2ba40d582e075ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Thu, 14 Mar 2019 15:00:19 +0100 Subject: [PATCH 012/290] change logging to debug for nmap (#22036) --- .../components/device_tracker/nmap_tracker.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/device_tracker/nmap_tracker.py b/homeassistant/components/device_tracker/nmap_tracker.py index 3c090e8cd3b..e553d323b72 100644 --- a/homeassistant/components/device_tracker/nmap_tracker.py +++ b/homeassistant/components/device_tracker/nmap_tracker.py @@ -1,14 +1,9 @@ -""" -Support for scanning a network with nmap. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.nmap_tracker/ -""" -from datetime import timedelta +"""Support for scanning a network with nmap.""" import logging import re import subprocess from collections import namedtuple +from datetime import timedelta import voluptuous as vol @@ -74,7 +69,7 @@ class NmapDeviceScanner(DeviceScanner): self._options = config[CONF_OPTIONS] self.home_interval = timedelta(minutes=minutes) - _LOGGER.info("Scanner initialized") + _LOGGER.debug("Scanner initialized") def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" @@ -105,7 +100,7 @@ class NmapDeviceScanner(DeviceScanner): Returns boolean if scanning successful. """ - _LOGGER.info("Scanning...") + _LOGGER.debug("Scanning...") from nmap import PortScanner, PortScannerError scanner = PortScanner() @@ -146,5 +141,5 @@ class NmapDeviceScanner(DeviceScanner): self.last_results = last_results - _LOGGER.info("nmap scan successful") + _LOGGER.debug("nmap scan successful") return True From 300384410fbd230a1d25c881a5f5795011b0b2d7 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 14 Mar 2019 10:20:25 -0400 Subject: [PATCH 013/290] Refactor ZHA gateway into modules and add admin protections to API (#22023) * refactor * cleanup * fix tests * admin all the things --- homeassistant/components/zha/__init__.py | 95 +--- homeassistant/components/zha/api.py | 44 +- homeassistant/components/zha/core/const.py | 7 + .../components/zha/core/discovery.py | 265 +++++++++ homeassistant/components/zha/core/gateway.py | 531 ++---------------- homeassistant/components/zha/core/patches.py | 41 ++ .../components/zha/core/registries.py | 271 +++++++++ tests/components/zha/conftest.py | 7 +- tests/components/zha/test_api.py | 3 +- 9 files changed, 697 insertions(+), 567 deletions(-) create mode 100644 homeassistant/components/zha/core/discovery.py create mode 100644 homeassistant/components/zha/core/patches.py create mode 100644 homeassistant/components/zha/core/registries.py diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 82efd564742..adc092dcbe1 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -4,10 +4,7 @@ Support for Zigbee Home Automation devices. For more details about this component, please refer to the documentation at https://home-assistant.io/components/zha/ """ -import asyncio import logging -import os -import types import voluptuous as vol @@ -21,13 +18,13 @@ from . import api from .core import ZHAGateway from .core.const import ( COMPONENTS, CONF_BAUDRATE, CONF_DATABASE, CONF_DEVICE_CONFIG, - CONF_RADIO_TYPE, CONF_USB_PATH, DATA_ZHA, DATA_ZHA_BRIDGE_ID, + CONF_RADIO_TYPE, CONF_USB_PATH, DATA_ZHA, DATA_ZHA_CONFIG, DATA_ZHA_CORE_COMPONENT, DATA_ZHA_DISPATCHERS, - DATA_ZHA_RADIO, DEFAULT_BAUDRATE, DEFAULT_DATABASE_NAME, DATA_ZHA_GATEWAY, + DATA_ZHA_RADIO, DEFAULT_BAUDRATE, DATA_ZHA_GATEWAY, DEFAULT_RADIO_TYPE, DOMAIN, RadioType, DATA_ZHA_CORE_EVENTS, ENABLE_QUIRKS) -from .core.gateway import establish_device_mappings +from .core.registries import establish_device_mappings from .core.channels.registry import populate_channel_registry -from .core.store import async_get_registry +from .core.patches import apply_cluster_listener_patch REQUIREMENTS = [ 'bellows-homeassistant==0.7.1', @@ -108,82 +105,32 @@ async def async_setup_entry(hass, config_entry): # pylint: disable=W0611, W0612 import zhaquirks # noqa - usb_path = config_entry.data.get(CONF_USB_PATH) - baudrate = config.get(CONF_BAUDRATE, DEFAULT_BAUDRATE) - radio_type = config_entry.data.get(CONF_RADIO_TYPE) - if radio_type == RadioType.ezsp.name: - import bellows.ezsp - from bellows.zigbee.application import ControllerApplication - radio = bellows.ezsp.EZSP() - radio_description = "EZSP" - elif radio_type == RadioType.xbee.name: - import zigpy_xbee.api - from zigpy_xbee.zigbee.application import ControllerApplication - radio = zigpy_xbee.api.XBee() - radio_description = "XBee" - elif radio_type == RadioType.deconz.name: - import zigpy_deconz.api - from zigpy_deconz.zigbee.application import ControllerApplication - radio = zigpy_deconz.api.Deconz() - radio_description = "Deconz" - - await radio.connect(usb_path, baudrate) - hass.data[DATA_ZHA][DATA_ZHA_RADIO] = radio - - if CONF_DATABASE in config: - database = config[CONF_DATABASE] - else: - database = os.path.join(hass.config.config_dir, DEFAULT_DATABASE_NAME) - # patch zigpy listener to prevent flooding logs with warnings due to # how zigpy implemented its listeners - from zigpy.appdb import ClusterPersistingListener + apply_cluster_listener_patch() - def zha_send_event(self, cluster, command, args): - pass - - ClusterPersistingListener.zha_send_event = types.MethodType( - zha_send_event, - ClusterPersistingListener - ) - - zha_storage = await async_get_registry(hass) - zha_gateway = ZHAGateway(hass, config, zha_storage) - - # Patch handle_message until zigpy can provide an event here - def handle_message(sender, is_reply, profile, cluster, - src_ep, dst_ep, tsn, command_id, args): - """Handle message from a device.""" - if not sender.initializing and sender.ieee in zha_gateway.devices and \ - not zha_gateway.devices[sender.ieee].available: - zha_gateway.async_device_became_available( - sender, is_reply, profile, cluster, src_ep, dst_ep, tsn, - command_id, args - ) - return sender.handle_message( - is_reply, profile, cluster, src_ep, dst_ep, tsn, command_id, args) - - application_controller = ControllerApplication(radio, database) - application_controller.handle_message = handle_message - application_controller.add_listener(zha_gateway) - await application_controller.startup(auto_form=True) - - hass.data[DATA_ZHA][DATA_ZHA_BRIDGE_ID] = str(application_controller.ieee) - - init_tasks = [] - for device in application_controller.devices.values(): - init_tasks.append(zha_gateway.async_device_initialized(device, False)) - await asyncio.gather(*init_tasks) + zha_gateway = ZHAGateway(hass, config) + await zha_gateway.async_initialize(config_entry) 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_ZIGBEE, str(application_controller.ieee))}, - identifiers={(DOMAIN, str(application_controller.ieee))}, + connections={ + ( + CONNECTION_ZIGBEE, + str(zha_gateway.application_controller.ieee) + ) + }, + identifiers={ + ( + DOMAIN, + str(zha_gateway.application_controller.ieee) + ) + }, name="Zigbee Coordinator", manufacturer="ZHA", - model=radio_description, + model=zha_gateway.radio_description, ) for component in COMPONENTS: @@ -192,7 +139,7 @@ async def async_setup_entry(hass, config_entry): config_entry, component) ) - api.async_load_api(hass, application_controller, zha_gateway) + api.async_load_api(hass) async def async_zha_shutdown(event): """Handle shutdown tasks.""" diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 6d79f3b3320..544e354ba2f 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -74,6 +74,7 @@ SERVICE_SCHEMAS = { } +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices' @@ -103,6 +104,7 @@ async def websocket_get_devices(hass, connection, msg): connection.send_result(msg[ID], devices) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/reconfigure', @@ -117,6 +119,7 @@ async def websocket_reconfigure_node(hass, connection, msg): hass.async_create_task(device.async_configure()) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters', @@ -149,6 +152,7 @@ async def websocket_device_clusters(hass, connection, msg): connection.send_result(msg[ID], response_clusters) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters/attributes', @@ -190,6 +194,7 @@ async def websocket_device_cluster_attributes(hass, connection, msg): connection.send_result(msg[ID], cluster_attributes) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters/commands', @@ -241,6 +246,7 @@ async def websocket_device_cluster_commands(hass, connection, msg): connection.send_result(msg[ID], cluster_commands) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters/attributes/value', @@ -283,6 +289,7 @@ async def websocket_read_zigbee_cluster_attributes(hass, connection, msg): connection.send_result(msg[ID], str(success.get(attribute))) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/bindable', @@ -311,6 +318,7 @@ async def websocket_get_bindable_devices(hass, connection, msg): )) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/bind', @@ -330,6 +338,7 @@ async def websocket_bind_devices(hass, connection, msg): ) +@websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/unbind', @@ -386,16 +395,19 @@ async def async_binding_operation(zha_gateway, source_ieee, target_ieee, await asyncio.gather(*bind_tasks) -def async_load_api(hass, application_controller, zha_gateway): +def async_load_api(hass): """Set up the web socket API.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + application_controller = zha_gateway.application_controller + 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) - hass.services.async_register(DOMAIN, SERVICE_PERMIT, permit, - schema=SERVICE_SCHEMAS[SERVICE_PERMIT]) + 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.""" @@ -405,8 +417,8 @@ def async_load_api(hass, application_controller, zha_gateway): _LOGGER.info("Removing node %s", ieee) await application_controller.remove(ieee) - hass.services.async_register(DOMAIN, SERVICE_REMOVE, remove, - schema=SERVICE_SCHEMAS[IEEE_SERVICE]) + hass.helpers.service.async_register_admin_service( + DOMAIN, SERVICE_REMOVE, remove, schema=SERVICE_SCHEMAS[IEEE_SERVICE]) async def set_zigbee_cluster_attributes(service): """Set zigbee attribute for cluster on zha entity.""" @@ -438,11 +450,12 @@ def async_load_api(hass, application_controller, zha_gateway): "{}: [{}]".format(RESPONSE, response) ) - hass.services.async_register(DOMAIN, SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE, - set_zigbee_cluster_attributes, - schema=SERVICE_SCHEMAS[ - SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE - ]) + hass.helpers.service.async_register_admin_service( + DOMAIN, SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE, + set_zigbee_cluster_attributes, + schema=SERVICE_SCHEMAS[ + SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE + ]) async def issue_zigbee_cluster_command(service): """Issue command on zigbee cluster on zha entity.""" @@ -477,11 +490,12 @@ def async_load_api(hass, application_controller, zha_gateway): "{}: [{}]".format(RESPONSE, response) ) - hass.services.async_register(DOMAIN, SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND, - issue_zigbee_cluster_command, - schema=SERVICE_SCHEMAS[ - SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND - ]) + hass.helpers.service.async_register_admin_service( + DOMAIN, SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND, + issue_zigbee_cluster_command, + schema=SERVICE_SCHEMAS[ + SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND + ]) websocket_api.async_register_command(hass, websocket_get_devices) websocket_api.async_register_command(hass, websocket_reconfigure_node) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 33376b056c6..1e41cbbbec5 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -33,6 +33,10 @@ CONF_USB_PATH = 'usb_path' DATA_DEVICE_CONFIG = 'zha_device_config' ENABLE_QUIRKS = 'enable_quirks' +RADIO = 'radio' +RADIO_DESCRIPTION = 'radio_description' +CONTROLLER = 'controller' + DEFAULT_RADIO_TYPE = 'ezsp' DEFAULT_BAUDRATE = 57600 DEFAULT_DATABASE_NAME = 'zigbee.db' @@ -114,6 +118,9 @@ DISCOVERY_KEY = 'zha_discovery_info' DEVICE_CLASS = {} SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {} SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {} +SENSOR_TYPES = {} +RADIO_TYPES = {} +BINARY_SENSOR_TYPES = {} CLUSTER_REPORT_CONFIGS = {} CUSTOM_CLUSTER_MAPPINGS = {} COMPONENT_CLUSTERS = {} diff --git a/homeassistant/components/zha/core/discovery.py b/homeassistant/components/zha/core/discovery.py new file mode 100644 index 00000000000..6d35fa24615 --- /dev/null +++ b/homeassistant/components/zha/core/discovery.py @@ -0,0 +1,265 @@ +""" +Device discovery functions for Zigbee Home Automation. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/zha/ +""" + +import logging + +from homeassistant import const as ha_const +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_send +from . import const as zha_const +from .channels import ( + AttributeListeningChannel, EventRelayChannel, ZDOChannel +) +from .channels.registry import ZIGBEE_CHANNEL_REGISTRY +from .const import ( + CONF_DEVICE_CONFIG, COMPONENTS, ZHA_DISCOVERY_NEW, DATA_ZHA, + SENSOR_TYPE, UNKNOWN, BINARY_SENSOR_TYPES, NO_SENSOR_CLUSTERS, + EVENT_RELAY_CLUSTERS, SENSOR_TYPES, GENERIC, + POWER_CONFIGURATION_CHANNEL +) +from ..device_entity import ZhaDeviceEntity + +_LOGGER = logging.getLogger(__name__) + + +@callback +def async_process_endpoint( + hass, config, endpoint_id, endpoint, discovery_infos, device, + zha_device, is_new_join): + """Process an endpoint on a zigpy device.""" + import zigpy.profiles + + if endpoint_id == 0: # ZDO + _async_create_cluster_channel( + endpoint, + zha_device, + is_new_join, + channel_class=ZDOChannel + ) + return + + component = None + profile_clusters = ([], []) + device_key = "{}-{}".format(device.ieee, endpoint_id) + node_config = {} + if CONF_DEVICE_CONFIG in config: + node_config = config[CONF_DEVICE_CONFIG].get( + device_key, {} + ) + + if endpoint.profile_id in zigpy.profiles.PROFILES: + profile = zigpy.profiles.PROFILES[endpoint.profile_id] + if zha_const.DEVICE_CLASS.get(endpoint.profile_id, + {}).get(endpoint.device_type, + None): + profile_clusters = profile.CLUSTERS[endpoint.device_type] + profile_info = zha_const.DEVICE_CLASS[endpoint.profile_id] + component = profile_info[endpoint.device_type] + + if ha_const.CONF_TYPE in node_config: + component = node_config[ha_const.CONF_TYPE] + profile_clusters = zha_const.COMPONENT_CLUSTERS[component] + + if component and component in COMPONENTS: + profile_match = _async_handle_profile_match( + hass, endpoint, profile_clusters, zha_device, + component, device_key, is_new_join) + discovery_infos.append(profile_match) + + discovery_infos.extend(_async_handle_single_cluster_matches( + hass, + endpoint, + zha_device, + profile_clusters, + device_key, + is_new_join + )) + + +@callback +def _async_create_cluster_channel(cluster, zha_device, is_new_join, + channels=None, channel_class=None): + """Create a cluster channel and attach it to a device.""" + if channel_class is None: + channel_class = ZIGBEE_CHANNEL_REGISTRY.get(cluster.cluster_id, + AttributeListeningChannel) + channel = channel_class(cluster, zha_device) + zha_device.add_cluster_channel(channel) + if channels is not None: + channels.append(channel) + + +@callback +def async_dispatch_discovery_info(hass, is_new_join, discovery_info): + """Dispatch or store discovery information.""" + if not discovery_info['channels']: + _LOGGER.warning( + "there are no channels in the discovery info: %s", discovery_info) + return + component = discovery_info['component'] + if is_new_join: + async_dispatcher_send( + hass, + ZHA_DISCOVERY_NEW.format(component), + discovery_info + ) + else: + hass.data[DATA_ZHA][component][discovery_info['unique_id']] = \ + discovery_info + + +@callback +def _async_handle_profile_match(hass, endpoint, profile_clusters, zha_device, + component, device_key, is_new_join): + """Dispatch a profile match to the appropriate HA component.""" + in_clusters = [endpoint.in_clusters[c] + for c in profile_clusters[0] + if c in endpoint.in_clusters] + out_clusters = [endpoint.out_clusters[c] + for c in profile_clusters[1] + if c in endpoint.out_clusters] + + channels = [] + + for cluster in in_clusters: + _async_create_cluster_channel( + cluster, zha_device, is_new_join, channels=channels) + + for cluster in out_clusters: + _async_create_cluster_channel( + cluster, zha_device, is_new_join, channels=channels) + + discovery_info = { + 'unique_id': device_key, + 'zha_device': zha_device, + 'channels': channels, + 'component': component + } + + if component == 'binary_sensor': + discovery_info.update({SENSOR_TYPE: UNKNOWN}) + cluster_ids = [] + cluster_ids.extend(profile_clusters[0]) + cluster_ids.extend(profile_clusters[1]) + for cluster_id in cluster_ids: + if cluster_id in BINARY_SENSOR_TYPES: + discovery_info.update({ + SENSOR_TYPE: BINARY_SENSOR_TYPES.get( + cluster_id, UNKNOWN) + }) + break + + return discovery_info + + +@callback +def _async_handle_single_cluster_matches(hass, endpoint, zha_device, + profile_clusters, device_key, + is_new_join): + """Dispatch single cluster matches to HA components.""" + cluster_matches = [] + cluster_match_results = [] + for cluster in endpoint.in_clusters.values(): + # don't let profiles prevent these channels from being created + if cluster.cluster_id in NO_SENSOR_CLUSTERS: + cluster_match_results.append( + _async_handle_channel_only_cluster_match( + zha_device, + cluster, + is_new_join, + )) + + if cluster.cluster_id not in profile_clusters[0]: + cluster_match_results.append(_async_handle_single_cluster_match( + hass, + zha_device, + cluster, + device_key, + zha_const.SINGLE_INPUT_CLUSTER_DEVICE_CLASS, + is_new_join, + )) + + for cluster in endpoint.out_clusters.values(): + if cluster.cluster_id not in profile_clusters[1]: + cluster_match_results.append(_async_handle_single_cluster_match( + hass, + zha_device, + cluster, + device_key, + zha_const.SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, + is_new_join, + )) + + if cluster.cluster_id in EVENT_RELAY_CLUSTERS: + _async_create_cluster_channel( + cluster, + zha_device, + is_new_join, + channel_class=EventRelayChannel + ) + + for cluster_match in cluster_match_results: + if cluster_match is not None: + cluster_matches.append(cluster_match) + return cluster_matches + + +@callback +def _async_handle_channel_only_cluster_match( + zha_device, cluster, is_new_join): + """Handle a channel only cluster match.""" + _async_create_cluster_channel(cluster, zha_device, is_new_join) + + +@callback +def _async_handle_single_cluster_match(hass, zha_device, cluster, device_key, + device_classes, is_new_join): + """Dispatch a single cluster match to a HA component.""" + component = None # sub_component = None + for cluster_type, candidate_component in device_classes.items(): + if isinstance(cluster_type, int): + if cluster.cluster_id == cluster_type: + component = candidate_component + elif isinstance(cluster, cluster_type): + component = candidate_component + break + + if component is None or component not in COMPONENTS: + return + channels = [] + _async_create_cluster_channel(cluster, zha_device, is_new_join, + channels=channels) + + cluster_key = "{}-{}".format(device_key, cluster.cluster_id) + discovery_info = { + 'unique_id': cluster_key, + 'zha_device': zha_device, + 'channels': channels, + 'entity_suffix': '_{}'.format(cluster.cluster_id), + 'component': component + } + + if component == 'sensor': + discovery_info.update({ + SENSOR_TYPE: SENSOR_TYPES.get(cluster.cluster_id, GENERIC) + }) + if component == 'binary_sensor': + discovery_info.update({ + SENSOR_TYPE: BINARY_SENSOR_TYPES.get(cluster.cluster_id, UNKNOWN) + }) + + return discovery_info + + +@callback +def async_create_device_entity(zha_device): + """Create ZHADeviceEntity.""" + device_entity_channels = [] + if POWER_CONFIGURATION_CHANNEL in zha_device.cluster_channels: + channel = zha_device.cluster_channels.get(POWER_CONFIGURATION_CHANNEL) + device_entity_channels.append(channel) + return ZhaDeviceEntity(zha_device, device_entity_channels) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 8a925ddfda4..89b2d9b77a6 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -5,39 +5,35 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/zha/ """ +import asyncio import collections import itertools import logging -from homeassistant import const as ha_const +import os + from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity_component import EntityComponent -from . import const as zha_const from .const import ( - COMPONENTS, CONF_DEVICE_CONFIG, DATA_ZHA, DATA_ZHA_CORE_COMPONENT, DOMAIN, - ZHA_DISCOVERY_NEW, DEVICE_CLASS, SINGLE_INPUT_CLUSTER_DEVICE_CLASS, - SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, COMPONENT_CLUSTERS, HUMIDITY, - TEMPERATURE, ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT, - GENERIC, SENSOR_TYPE, EVENT_RELAY_CLUSTERS, UNKNOWN, OPENING, ZONE, - OCCUPANCY, CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_IMMEDIATE, - REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT, - REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, SIGNAL_REMOVE, - NO_SENSOR_CLUSTERS, POWER_CONFIGURATION_CHANNEL, BINDABLE_CLUSTERS, - DATA_ZHA_GATEWAY, ACCELERATION) + 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_TYPES, + RADIO, CONTROLLER, RADIO_DESCRIPTION) from .device import ZHADevice, DeviceStatus -from ..device_entity import ZhaDeviceEntity from .channels import ( - AttributeListeningChannel, EventRelayChannel, ZDOChannel, MAINS_POWERED + ZDOChannel, MAINS_POWERED ) -from .channels.registry import ZIGBEE_CHANNEL_REGISTRY from .helpers import convert_ieee +from .discovery import ( + async_process_endpoint, async_dispatch_discovery_info, + async_create_device_entity +) +from .store import async_get_registry +from .patches import apply_application_controller_patch _LOGGER = logging.getLogger(__name__) -SENSOR_TYPES = {} -BINARY_SENSOR_TYPES = {} -SMARTTHINGS_HUMIDITY_CLUSTER = 64581 -SMARTTHINGS_ACCELERATION_CLUSTER = 64514 EntityReference = collections.namedtuple( 'EntityReference', 'reference_id zha_device cluster_channels device_info') @@ -45,17 +41,52 @@ EntityReference = collections.namedtuple( class ZHAGateway: """Gateway that handles events that happen on the ZHA Zigbee network.""" - def __init__(self, hass, config, zha_storage): + def __init__(self, hass, config): """Initialize the gateway.""" self._hass = hass self._config = config self._component = EntityComponent(_LOGGER, DOMAIN, hass) self._devices = {} self._device_registry = collections.defaultdict(list) - self.zha_storage = zha_storage + self.zha_storage = None + self.application_controller = None + self.radio_description = None hass.data[DATA_ZHA][DATA_ZHA_CORE_COMPONENT] = self._component hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] = self + async def async_initialize(self, config_entry): + """Initialize controller and connect radio.""" + self.zha_storage = await async_get_registry(self._hass) + + usb_path = config_entry.data.get(CONF_USB_PATH) + baudrate = self._config.get(CONF_BAUDRATE, DEFAULT_BAUDRATE) + radio_type = config_entry.data.get(CONF_RADIO_TYPE) + + radio_details = RADIO_TYPES[radio_type][RADIO]() + 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] + else: + database = os.path.join( + self._hass.config.config_dir, DEFAULT_DATABASE_NAME) + + self.application_controller = radio_details[CONTROLLER]( + radio, database) + apply_application_controller_patch(self) + self.application_controller.add_listener(self) + await self.application_controller.startup(auto_form=True) + self._hass.data[DATA_ZHA][DATA_ZHA_BRIDGE_ID] = str( + self.application_controller.ieee) + + init_tasks = [] + for device in self.application_controller.devices.values(): + init_tasks.append(self.async_device_initialized(device, False)) + await asyncio.gather(*init_tasks) + def device_joined(self, device): """Handle device joined. @@ -166,9 +197,9 @@ class ZHAGateway: discovery_infos = [] for endpoint_id, endpoint in device.endpoints.items(): - self._async_process_endpoint( - endpoint_id, endpoint, discovery_infos, device, zha_device, - is_new_join + async_process_endpoint( + self._hass, self._config, endpoint_id, endpoint, + discovery_infos, device, zha_device, is_new_join ) if is_new_join: @@ -191,459 +222,11 @@ class ZHAGateway: await zha_device.async_initialize(from_cache=True) for discovery_info in discovery_infos: - _async_dispatch_discovery_info( + async_dispatch_discovery_info( self._hass, is_new_join, discovery_info ) - device_entity = _async_create_device_entity(zha_device) + device_entity = async_create_device_entity(zha_device) await self._component.async_add_entities([device_entity]) - - @callback - def _async_process_endpoint( - self, endpoint_id, endpoint, discovery_infos, device, zha_device, - is_new_join): - """Process an endpoint on a zigpy device.""" - import zigpy.profiles - - if endpoint_id == 0: # ZDO - _async_create_cluster_channel( - endpoint, - zha_device, - is_new_join, - channel_class=ZDOChannel - ) - return - - component = None - profile_clusters = ([], []) - device_key = "{}-{}".format(device.ieee, endpoint_id) - node_config = {} - if CONF_DEVICE_CONFIG in self._config: - node_config = self._config[CONF_DEVICE_CONFIG].get( - device_key, {} - ) - - if endpoint.profile_id in zigpy.profiles.PROFILES: - profile = zigpy.profiles.PROFILES[endpoint.profile_id] - if zha_const.DEVICE_CLASS.get(endpoint.profile_id, - {}).get(endpoint.device_type, - None): - profile_clusters = profile.CLUSTERS[endpoint.device_type] - profile_info = zha_const.DEVICE_CLASS[endpoint.profile_id] - component = profile_info[endpoint.device_type] - - if ha_const.CONF_TYPE in node_config: - component = node_config[ha_const.CONF_TYPE] - profile_clusters = zha_const.COMPONENT_CLUSTERS[component] - - if component and component in COMPONENTS: - profile_match = _async_handle_profile_match( - self._hass, endpoint, profile_clusters, zha_device, - component, device_key, is_new_join) - discovery_infos.append(profile_match) - - discovery_infos.extend(_async_handle_single_cluster_matches( - self._hass, - endpoint, - zha_device, - profile_clusters, - device_key, - is_new_join - )) - - -@callback -def _async_create_cluster_channel(cluster, zha_device, is_new_join, - channels=None, channel_class=None): - """Create a cluster channel and attach it to a device.""" - if channel_class is None: - channel_class = ZIGBEE_CHANNEL_REGISTRY.get(cluster.cluster_id, - AttributeListeningChannel) - channel = channel_class(cluster, zha_device) - zha_device.add_cluster_channel(channel) - if channels is not None: - channels.append(channel) - - -@callback -def _async_dispatch_discovery_info(hass, is_new_join, discovery_info): - """Dispatch or store discovery information.""" - if not discovery_info['channels']: - _LOGGER.warning( - "there are no channels in the discovery info: %s", discovery_info) - return - component = discovery_info['component'] - if is_new_join: - async_dispatcher_send( - hass, - ZHA_DISCOVERY_NEW.format(component), - discovery_info - ) - else: - hass.data[DATA_ZHA][component][discovery_info['unique_id']] = \ - discovery_info - - -@callback -def _async_handle_profile_match(hass, endpoint, profile_clusters, zha_device, - component, device_key, is_new_join): - """Dispatch a profile match to the appropriate HA component.""" - in_clusters = [endpoint.in_clusters[c] - for c in profile_clusters[0] - if c in endpoint.in_clusters] - out_clusters = [endpoint.out_clusters[c] - for c in profile_clusters[1] - if c in endpoint.out_clusters] - - channels = [] - - for cluster in in_clusters: - _async_create_cluster_channel( - cluster, zha_device, is_new_join, channels=channels) - - for cluster in out_clusters: - _async_create_cluster_channel( - cluster, zha_device, is_new_join, channels=channels) - - discovery_info = { - 'unique_id': device_key, - 'zha_device': zha_device, - 'channels': channels, - 'component': component - } - - if component == 'binary_sensor': - discovery_info.update({SENSOR_TYPE: UNKNOWN}) - cluster_ids = [] - cluster_ids.extend(profile_clusters[0]) - cluster_ids.extend(profile_clusters[1]) - for cluster_id in cluster_ids: - if cluster_id in BINARY_SENSOR_TYPES: - discovery_info.update({ - SENSOR_TYPE: BINARY_SENSOR_TYPES.get( - cluster_id, UNKNOWN) - }) - break - - return discovery_info - - -@callback -def _async_handle_single_cluster_matches(hass, endpoint, zha_device, - profile_clusters, device_key, - is_new_join): - """Dispatch single cluster matches to HA components.""" - cluster_matches = [] - cluster_match_results = [] - for cluster in endpoint.in_clusters.values(): - # don't let profiles prevent these channels from being created - if cluster.cluster_id in NO_SENSOR_CLUSTERS: - cluster_match_results.append( - _async_handle_channel_only_cluster_match( - zha_device, - cluster, - is_new_join, - )) - - if cluster.cluster_id not in profile_clusters[0]: - cluster_match_results.append(_async_handle_single_cluster_match( - hass, - zha_device, - cluster, - device_key, - zha_const.SINGLE_INPUT_CLUSTER_DEVICE_CLASS, - is_new_join, - )) - - for cluster in endpoint.out_clusters.values(): - if cluster.cluster_id not in profile_clusters[1]: - cluster_match_results.append(_async_handle_single_cluster_match( - hass, - zha_device, - cluster, - device_key, - zha_const.SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, - is_new_join, - )) - - if cluster.cluster_id in EVENT_RELAY_CLUSTERS: - _async_create_cluster_channel( - cluster, - zha_device, - is_new_join, - channel_class=EventRelayChannel - ) - - for cluster_match in cluster_match_results: - if cluster_match is not None: - cluster_matches.append(cluster_match) - return cluster_matches - - -@callback -def _async_handle_channel_only_cluster_match( - zha_device, cluster, is_new_join): - """Handle a channel only cluster match.""" - _async_create_cluster_channel(cluster, zha_device, is_new_join) - - -@callback -def _async_handle_single_cluster_match(hass, zha_device, cluster, device_key, - device_classes, is_new_join): - """Dispatch a single cluster match to a HA component.""" - component = None # sub_component = None - for cluster_type, candidate_component in device_classes.items(): - if isinstance(cluster_type, int): - if cluster.cluster_id == cluster_type: - component = candidate_component - elif isinstance(cluster, cluster_type): - component = candidate_component - break - - if component is None or component not in COMPONENTS: - return - channels = [] - _async_create_cluster_channel(cluster, zha_device, is_new_join, - channels=channels) - - cluster_key = "{}-{}".format(device_key, cluster.cluster_id) - discovery_info = { - 'unique_id': cluster_key, - 'zha_device': zha_device, - 'channels': channels, - 'entity_suffix': '_{}'.format(cluster.cluster_id), - 'component': component - } - - if component == 'sensor': - discovery_info.update({ - SENSOR_TYPE: SENSOR_TYPES.get(cluster.cluster_id, GENERIC) - }) - if component == 'binary_sensor': - discovery_info.update({ - SENSOR_TYPE: BINARY_SENSOR_TYPES.get(cluster.cluster_id, UNKNOWN) - }) - - return discovery_info - - -@callback -def _async_create_device_entity(zha_device): - """Create ZHADeviceEntity.""" - device_entity_channels = [] - if POWER_CONFIGURATION_CHANNEL in zha_device.cluster_channels: - channel = zha_device.cluster_channels.get(POWER_CONFIGURATION_CHANNEL) - device_entity_channels.append(channel) - return ZhaDeviceEntity(zha_device, device_entity_channels) - - -def establish_device_mappings(): - """Establish mappings between ZCL objects and HA ZHA objects. - - These cannot be module level, as importing bellows must be done in a - in a function. - """ - from zigpy import zcl - from zigpy.profiles import PROFILES, zha, zll - - if zha.PROFILE_ID not in DEVICE_CLASS: - DEVICE_CLASS[zha.PROFILE_ID] = {} - if zll.PROFILE_ID not in DEVICE_CLASS: - DEVICE_CLASS[zll.PROFILE_ID] = {} - - EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id) - EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id) - - NO_SENSOR_CLUSTERS.append(zcl.clusters.general.Basic.cluster_id) - NO_SENSOR_CLUSTERS.append( - zcl.clusters.general.PowerConfiguration.cluster_id) - NO_SENSOR_CLUSTERS.append(zcl.clusters.lightlink.LightLink.cluster_id) - - BINDABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id) - BINDABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id) - BINDABLE_CLUSTERS.append(zcl.clusters.lighting.Color.cluster_id) - - DEVICE_CLASS[zha.PROFILE_ID].update({ - zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor', - zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor', - zha.DeviceType.REMOTE_CONTROL: 'binary_sensor', - zha.DeviceType.SMART_PLUG: 'switch', - zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT: 'light', - zha.DeviceType.ON_OFF_LIGHT: 'light', - zha.DeviceType.DIMMABLE_LIGHT: 'light', - zha.DeviceType.COLOR_DIMMABLE_LIGHT: 'light', - zha.DeviceType.ON_OFF_LIGHT_SWITCH: 'binary_sensor', - zha.DeviceType.DIMMER_SWITCH: 'binary_sensor', - zha.DeviceType.COLOR_DIMMER_SWITCH: 'binary_sensor', - }) - - DEVICE_CLASS[zll.PROFILE_ID].update({ - zll.DeviceType.ON_OFF_LIGHT: 'light', - zll.DeviceType.ON_OFF_PLUGIN_UNIT: 'switch', - zll.DeviceType.DIMMABLE_LIGHT: 'light', - zll.DeviceType.DIMMABLE_PLUGIN_UNIT: 'light', - zll.DeviceType.COLOR_LIGHT: 'light', - zll.DeviceType.EXTENDED_COLOR_LIGHT: 'light', - zll.DeviceType.COLOR_TEMPERATURE_LIGHT: 'light', - zll.DeviceType.COLOR_CONTROLLER: 'binary_sensor', - zll.DeviceType.COLOR_SCENE_CONTROLLER: 'binary_sensor', - zll.DeviceType.CONTROLLER: 'binary_sensor', - zll.DeviceType.SCENE_CONTROLLER: 'binary_sensor', - zll.DeviceType.ON_OFF_SENSOR: 'binary_sensor', - }) - - SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({ - zcl.clusters.general.OnOff: 'switch', - zcl.clusters.measurement.RelativeHumidity: 'sensor', - # this works for now but if we hit conflicts we can break it out to - # a different dict that is keyed by manufacturer - SMARTTHINGS_HUMIDITY_CLUSTER: 'sensor', - zcl.clusters.measurement.TemperatureMeasurement: 'sensor', - zcl.clusters.measurement.PressureMeasurement: 'sensor', - zcl.clusters.measurement.IlluminanceMeasurement: 'sensor', - zcl.clusters.smartenergy.Metering: 'sensor', - zcl.clusters.homeautomation.ElectricalMeasurement: 'sensor', - zcl.clusters.security.IasZone: 'binary_sensor', - zcl.clusters.measurement.OccupancySensing: 'binary_sensor', - zcl.clusters.hvac.Fan: 'fan', - SMARTTHINGS_ACCELERATION_CLUSTER: 'binary_sensor', - }) - - SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS.update({ - zcl.clusters.general.OnOff: 'binary_sensor', - }) - - SENSOR_TYPES.update({ - zcl.clusters.measurement.RelativeHumidity.cluster_id: HUMIDITY, - SMARTTHINGS_HUMIDITY_CLUSTER: HUMIDITY, - zcl.clusters.measurement.TemperatureMeasurement.cluster_id: - TEMPERATURE, - zcl.clusters.measurement.PressureMeasurement.cluster_id: PRESSURE, - zcl.clusters.measurement.IlluminanceMeasurement.cluster_id: - ILLUMINANCE, - zcl.clusters.smartenergy.Metering.cluster_id: METERING, - zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id: - ELECTRICAL_MEASUREMENT, - }) - - BINARY_SENSOR_TYPES.update({ - zcl.clusters.measurement.OccupancySensing.cluster_id: OCCUPANCY, - zcl.clusters.security.IasZone.cluster_id: ZONE, - zcl.clusters.general.OnOff.cluster_id: OPENING, - SMARTTHINGS_ACCELERATION_CLUSTER: ACCELERATION, - }) - - CLUSTER_REPORT_CONFIGS.update({ - zcl.clusters.general.Alarms.cluster_id: [], - zcl.clusters.general.Basic.cluster_id: [], - zcl.clusters.general.Commissioning.cluster_id: [], - zcl.clusters.general.Identify.cluster_id: [], - zcl.clusters.general.Groups.cluster_id: [], - zcl.clusters.general.Scenes.cluster_id: [], - zcl.clusters.general.Partition.cluster_id: [], - zcl.clusters.general.Ota.cluster_id: [], - zcl.clusters.general.PowerProfile.cluster_id: [], - zcl.clusters.general.ApplianceControl.cluster_id: [], - zcl.clusters.general.PollControl.cluster_id: [], - zcl.clusters.general.GreenPowerProxy.cluster_id: [], - zcl.clusters.general.OnOffConfiguration.cluster_id: [], - zcl.clusters.lightlink.LightLink.cluster_id: [], - zcl.clusters.general.OnOff.cluster_id: [{ - 'attr': 'on_off', - 'config': REPORT_CONFIG_IMMEDIATE - }], - zcl.clusters.general.LevelControl.cluster_id: [{ - 'attr': 'current_level', - 'config': REPORT_CONFIG_ASAP - }], - zcl.clusters.lighting.Color.cluster_id: [{ - 'attr': 'current_x', - 'config': REPORT_CONFIG_DEFAULT - }, { - 'attr': 'current_y', - 'config': REPORT_CONFIG_DEFAULT - }, { - 'attr': 'color_temperature', - 'config': REPORT_CONFIG_DEFAULT - }], - zcl.clusters.measurement.RelativeHumidity.cluster_id: [{ - 'attr': 'measured_value', - 'config': ( - REPORT_CONFIG_MIN_INT, - REPORT_CONFIG_MAX_INT, - 50 - ) - }], - zcl.clusters.measurement.TemperatureMeasurement.cluster_id: [{ - 'attr': 'measured_value', - 'config': ( - REPORT_CONFIG_MIN_INT, - REPORT_CONFIG_MAX_INT, - 50 - ) - }], - SMARTTHINGS_ACCELERATION_CLUSTER: [{ - 'attr': 'acceleration', - 'config': REPORT_CONFIG_ASAP - }, { - 'attr': 'x_axis', - 'config': REPORT_CONFIG_ASAP - }, { - 'attr': 'y_axis', - 'config': REPORT_CONFIG_ASAP - }, { - 'attr': 'z_axis', - 'config': REPORT_CONFIG_ASAP - }], - SMARTTHINGS_HUMIDITY_CLUSTER: [{ - 'attr': 'measured_value', - 'config': ( - REPORT_CONFIG_MIN_INT, - REPORT_CONFIG_MAX_INT, - 50 - ) - }], - zcl.clusters.measurement.PressureMeasurement.cluster_id: [{ - 'attr': 'measured_value', - 'config': REPORT_CONFIG_DEFAULT - }], - zcl.clusters.measurement.IlluminanceMeasurement.cluster_id: [{ - 'attr': 'measured_value', - 'config': REPORT_CONFIG_DEFAULT - }], - zcl.clusters.smartenergy.Metering.cluster_id: [{ - 'attr': 'instantaneous_demand', - 'config': REPORT_CONFIG_DEFAULT - }], - zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id: [{ - 'attr': 'active_power', - 'config': REPORT_CONFIG_DEFAULT - }], - zcl.clusters.general.PowerConfiguration.cluster_id: [{ - 'attr': 'battery_voltage', - 'config': REPORT_CONFIG_DEFAULT - }, { - 'attr': 'battery_percentage_remaining', - 'config': REPORT_CONFIG_DEFAULT - }], - zcl.clusters.measurement.OccupancySensing.cluster_id: [{ - 'attr': 'occupancy', - 'config': REPORT_CONFIG_IMMEDIATE - }], - zcl.clusters.hvac.Fan.cluster_id: [{ - 'attr': 'fan_mode', - 'config': REPORT_CONFIG_OP - }], - }) - - # A map of hass components to all Zigbee clusters it could use - for profile_id, classes in DEVICE_CLASS.items(): - profile = PROFILES[profile_id] - for device_type, component in classes.items(): - if component not in COMPONENT_CLUSTERS: - COMPONENT_CLUSTERS[component] = (set(), set()) - clusters = profile.CLUSTERS[device_type] - COMPONENT_CLUSTERS[component][0].update(clusters[0]) - COMPONENT_CLUSTERS[component][1].update(clusters[1]) diff --git a/homeassistant/components/zha/core/patches.py b/homeassistant/components/zha/core/patches.py new file mode 100644 index 00000000000..8f708a568d1 --- /dev/null +++ b/homeassistant/components/zha/core/patches.py @@ -0,0 +1,41 @@ +""" +Patch functions for Zigbee Home Automation. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/zha/ +""" + +import types + + +def apply_cluster_listener_patch(): + """Apply patches to ZHA objects.""" + # patch zigpy listener to prevent flooding logs with warnings due to + # how zigpy implemented its listeners + from zigpy.appdb import ClusterPersistingListener + + def zha_send_event(self, cluster, command, args): + pass + + ClusterPersistingListener.zha_send_event = types.MethodType( + zha_send_event, + ClusterPersistingListener + ) + + +def apply_application_controller_patch(zha_gateway): + """Apply patches to ZHA objects.""" + # Patch handle_message until zigpy can provide an event here + def handle_message(sender, is_reply, profile, cluster, + src_ep, dst_ep, tsn, command_id, args): + """Handle message from a device.""" + if not sender.initializing and sender.ieee in zha_gateway.devices and \ + not zha_gateway.devices[sender.ieee].available: + zha_gateway.async_device_became_available( + sender, is_reply, profile, cluster, src_ep, dst_ep, tsn, + command_id, args + ) + return sender.handle_message( + is_reply, profile, cluster, src_ep, dst_ep, tsn, command_id, args) + + zha_gateway.application_controller.handle_message = handle_message diff --git a/homeassistant/components/zha/core/registries.py b/homeassistant/components/zha/core/registries.py new file mode 100644 index 00000000000..4d6b1faba00 --- /dev/null +++ b/homeassistant/components/zha/core/registries.py @@ -0,0 +1,271 @@ +""" +Mapping registries for Zigbee Home Automation. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/zha/ +""" + +from .const import ( + DEVICE_CLASS, SINGLE_INPUT_CLUSTER_DEVICE_CLASS, + SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, COMPONENT_CLUSTERS, HUMIDITY, + TEMPERATURE, ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT, + EVENT_RELAY_CLUSTERS, OPENING, ZONE, + OCCUPANCY, CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_IMMEDIATE, + REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT, + REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, + NO_SENSOR_CLUSTERS, BINDABLE_CLUSTERS, ACCELERATION, SENSOR_TYPES, + BINARY_SENSOR_TYPES, RADIO_TYPES, RadioType, RADIO, RADIO_DESCRIPTION, + CONTROLLER +) + +SMARTTHINGS_HUMIDITY_CLUSTER = 64581 +SMARTTHINGS_ACCELERATION_CLUSTER = 64514 + + +def establish_device_mappings(): + """Establish mappings between ZCL objects and HA ZHA objects. + + These cannot be module level, as importing bellows must be done in a + in a function. + """ + from zigpy import zcl + from zigpy.profiles import PROFILES, zha, zll + + if zha.PROFILE_ID not in DEVICE_CLASS: + DEVICE_CLASS[zha.PROFILE_ID] = {} + if zll.PROFILE_ID not in DEVICE_CLASS: + DEVICE_CLASS[zll.PROFILE_ID] = {} + + def get_ezsp_radio(): + import bellows.ezsp + from bellows.zigbee.application import ControllerApplication + return { + RADIO: bellows.ezsp.EZSP(), + CONTROLLER: ControllerApplication + } + + RADIO_TYPES[RadioType.ezsp.name] = { + RADIO: get_ezsp_radio, + RADIO_DESCRIPTION: 'EZSP' + } + + def get_xbee_radio(): + import zigpy_xbee.api + from zigpy_xbee.zigbee.application import ControllerApplication + return { + RADIO: zigpy_xbee.api.XBee(), + CONTROLLER: ControllerApplication + } + + RADIO_TYPES[RadioType.xbee.name] = { + RADIO: get_xbee_radio, + RADIO_DESCRIPTION: 'XBee' + } + + def get_deconz_radio(): + import zigpy_deconz.api + from zigpy_deconz.zigbee.application import ControllerApplication + return { + RADIO: zigpy_deconz.api.Deconz(), + CONTROLLER: ControllerApplication + } + + RADIO_TYPES[RadioType.deconz.name] = { + RADIO: get_deconz_radio, + RADIO_DESCRIPTION: 'Deconz' + } + + EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id) + EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id) + + NO_SENSOR_CLUSTERS.append(zcl.clusters.general.Basic.cluster_id) + NO_SENSOR_CLUSTERS.append( + zcl.clusters.general.PowerConfiguration.cluster_id) + NO_SENSOR_CLUSTERS.append(zcl.clusters.lightlink.LightLink.cluster_id) + + BINDABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id) + BINDABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id) + BINDABLE_CLUSTERS.append(zcl.clusters.lighting.Color.cluster_id) + + DEVICE_CLASS[zha.PROFILE_ID].update({ + zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor', + zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor', + zha.DeviceType.REMOTE_CONTROL: 'binary_sensor', + zha.DeviceType.SMART_PLUG: 'switch', + zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT: 'light', + zha.DeviceType.ON_OFF_LIGHT: 'light', + zha.DeviceType.DIMMABLE_LIGHT: 'light', + zha.DeviceType.COLOR_DIMMABLE_LIGHT: 'light', + zha.DeviceType.ON_OFF_LIGHT_SWITCH: 'binary_sensor', + zha.DeviceType.DIMMER_SWITCH: 'binary_sensor', + zha.DeviceType.COLOR_DIMMER_SWITCH: 'binary_sensor', + }) + + DEVICE_CLASS[zll.PROFILE_ID].update({ + zll.DeviceType.ON_OFF_LIGHT: 'light', + zll.DeviceType.ON_OFF_PLUGIN_UNIT: 'switch', + zll.DeviceType.DIMMABLE_LIGHT: 'light', + zll.DeviceType.DIMMABLE_PLUGIN_UNIT: 'light', + zll.DeviceType.COLOR_LIGHT: 'light', + zll.DeviceType.EXTENDED_COLOR_LIGHT: 'light', + zll.DeviceType.COLOR_TEMPERATURE_LIGHT: 'light', + zll.DeviceType.COLOR_CONTROLLER: 'binary_sensor', + zll.DeviceType.COLOR_SCENE_CONTROLLER: 'binary_sensor', + zll.DeviceType.CONTROLLER: 'binary_sensor', + zll.DeviceType.SCENE_CONTROLLER: 'binary_sensor', + zll.DeviceType.ON_OFF_SENSOR: 'binary_sensor', + }) + + SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({ + zcl.clusters.general.OnOff: 'switch', + zcl.clusters.measurement.RelativeHumidity: 'sensor', + # this works for now but if we hit conflicts we can break it out to + # a different dict that is keyed by manufacturer + SMARTTHINGS_HUMIDITY_CLUSTER: 'sensor', + zcl.clusters.measurement.TemperatureMeasurement: 'sensor', + zcl.clusters.measurement.PressureMeasurement: 'sensor', + zcl.clusters.measurement.IlluminanceMeasurement: 'sensor', + zcl.clusters.smartenergy.Metering: 'sensor', + zcl.clusters.homeautomation.ElectricalMeasurement: 'sensor', + zcl.clusters.security.IasZone: 'binary_sensor', + zcl.clusters.measurement.OccupancySensing: 'binary_sensor', + zcl.clusters.hvac.Fan: 'fan', + SMARTTHINGS_ACCELERATION_CLUSTER: 'binary_sensor', + }) + + SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS.update({ + zcl.clusters.general.OnOff: 'binary_sensor', + }) + + SENSOR_TYPES.update({ + zcl.clusters.measurement.RelativeHumidity.cluster_id: HUMIDITY, + SMARTTHINGS_HUMIDITY_CLUSTER: HUMIDITY, + zcl.clusters.measurement.TemperatureMeasurement.cluster_id: + TEMPERATURE, + zcl.clusters.measurement.PressureMeasurement.cluster_id: PRESSURE, + zcl.clusters.measurement.IlluminanceMeasurement.cluster_id: + ILLUMINANCE, + zcl.clusters.smartenergy.Metering.cluster_id: METERING, + zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id: + ELECTRICAL_MEASUREMENT, + }) + + BINARY_SENSOR_TYPES.update({ + zcl.clusters.measurement.OccupancySensing.cluster_id: OCCUPANCY, + zcl.clusters.security.IasZone.cluster_id: ZONE, + zcl.clusters.general.OnOff.cluster_id: OPENING, + SMARTTHINGS_ACCELERATION_CLUSTER: ACCELERATION, + }) + + CLUSTER_REPORT_CONFIGS.update({ + zcl.clusters.general.Alarms.cluster_id: [], + zcl.clusters.general.Basic.cluster_id: [], + zcl.clusters.general.Commissioning.cluster_id: [], + zcl.clusters.general.Identify.cluster_id: [], + zcl.clusters.general.Groups.cluster_id: [], + zcl.clusters.general.Scenes.cluster_id: [], + zcl.clusters.general.Partition.cluster_id: [], + zcl.clusters.general.Ota.cluster_id: [], + zcl.clusters.general.PowerProfile.cluster_id: [], + zcl.clusters.general.ApplianceControl.cluster_id: [], + zcl.clusters.general.PollControl.cluster_id: [], + zcl.clusters.general.GreenPowerProxy.cluster_id: [], + zcl.clusters.general.OnOffConfiguration.cluster_id: [], + zcl.clusters.lightlink.LightLink.cluster_id: [], + zcl.clusters.general.OnOff.cluster_id: [{ + 'attr': 'on_off', + 'config': REPORT_CONFIG_IMMEDIATE + }], + zcl.clusters.general.LevelControl.cluster_id: [{ + 'attr': 'current_level', + 'config': REPORT_CONFIG_ASAP + }], + zcl.clusters.lighting.Color.cluster_id: [{ + 'attr': 'current_x', + 'config': REPORT_CONFIG_DEFAULT + }, { + 'attr': 'current_y', + 'config': REPORT_CONFIG_DEFAULT + }, { + 'attr': 'color_temperature', + 'config': REPORT_CONFIG_DEFAULT + }], + zcl.clusters.measurement.RelativeHumidity.cluster_id: [{ + 'attr': 'measured_value', + 'config': ( + REPORT_CONFIG_MIN_INT, + REPORT_CONFIG_MAX_INT, + 50 + ) + }], + zcl.clusters.measurement.TemperatureMeasurement.cluster_id: [{ + 'attr': 'measured_value', + 'config': ( + REPORT_CONFIG_MIN_INT, + REPORT_CONFIG_MAX_INT, + 50 + ) + }], + SMARTTHINGS_ACCELERATION_CLUSTER: [{ + 'attr': 'acceleration', + 'config': REPORT_CONFIG_ASAP + }, { + 'attr': 'x_axis', + 'config': REPORT_CONFIG_ASAP + }, { + 'attr': 'y_axis', + 'config': REPORT_CONFIG_ASAP + }, { + 'attr': 'z_axis', + 'config': REPORT_CONFIG_ASAP + }], + SMARTTHINGS_HUMIDITY_CLUSTER: [{ + 'attr': 'measured_value', + 'config': ( + REPORT_CONFIG_MIN_INT, + REPORT_CONFIG_MAX_INT, + 50 + ) + }], + zcl.clusters.measurement.PressureMeasurement.cluster_id: [{ + 'attr': 'measured_value', + 'config': REPORT_CONFIG_DEFAULT + }], + zcl.clusters.measurement.IlluminanceMeasurement.cluster_id: [{ + 'attr': 'measured_value', + 'config': REPORT_CONFIG_DEFAULT + }], + zcl.clusters.smartenergy.Metering.cluster_id: [{ + 'attr': 'instantaneous_demand', + 'config': REPORT_CONFIG_DEFAULT + }], + zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id: [{ + 'attr': 'active_power', + 'config': REPORT_CONFIG_DEFAULT + }], + zcl.clusters.general.PowerConfiguration.cluster_id: [{ + 'attr': 'battery_voltage', + 'config': REPORT_CONFIG_DEFAULT + }, { + 'attr': 'battery_percentage_remaining', + 'config': REPORT_CONFIG_DEFAULT + }], + zcl.clusters.measurement.OccupancySensing.cluster_id: [{ + 'attr': 'occupancy', + 'config': REPORT_CONFIG_IMMEDIATE + }], + zcl.clusters.hvac.Fan.cluster_id: [{ + 'attr': 'fan_mode', + 'config': REPORT_CONFIG_OP + }], + }) + + # A map of hass components to all Zigbee clusters it could use + for profile_id, classes in DEVICE_CLASS.items(): + profile = PROFILES[profile_id] + for device_type, component in classes.items(): + if component not in COMPONENT_CLUSTERS: + COMPONENT_CLUSTERS[component] = (set(), set()) + clusters = profile.CLUSTERS[device_type] + COMPONENT_CLUSTERS[component][0].update(clusters[0]) + COMPONENT_CLUSTERS[component][1].update(clusters[1]) diff --git a/tests/components/zha/conftest.py b/tests/components/zha/conftest.py index de05c89bbb0..cd0f615973d 100644 --- a/tests/components/zha/conftest.py +++ b/tests/components/zha/conftest.py @@ -6,7 +6,8 @@ from homeassistant.components.zha.core.const import ( DOMAIN, DATA_ZHA, COMPONENTS ) from homeassistant.components.zha.core.gateway import ZHAGateway -from homeassistant.components.zha.core.gateway import establish_device_mappings +from homeassistant.components.zha.core.registries import \ + establish_device_mappings from homeassistant.components.zha.core.channels.registry \ import populate_channel_registry from .common import async_setup_entry @@ -36,7 +37,9 @@ async def zha_gateway_fixture(hass): hass.data[DATA_ZHA].get(component, {}) ) zha_storage = await async_get_registry(hass) - return ZHAGateway(hass, {}, zha_storage) + gateway = ZHAGateway(hass, {}) + gateway.zha_storage = zha_storage + return gateway @pytest.fixture(autouse=True) diff --git a/tests/components/zha/test_api.py b/tests/components/zha/test_api.py index 5858c7560d9..3a30405f22e 100644 --- a/tests/components/zha/test_api.py +++ b/tests/components/zha/test_api.py @@ -1,5 +1,4 @@ """Test ZHA API.""" -from unittest.mock import Mock import pytest from homeassistant.components.switch import DOMAIN from homeassistant.components.zha.api import ( @@ -18,7 +17,7 @@ async def zha_client(hass, config_entry, zha_gateway, hass_ws_client): from zigpy.zcl.clusters.general import OnOff, Basic # load the ZHA API - async_load_api(hass, Mock(), zha_gateway) + async_load_api(hass) # create zigpy device await async_init_zigpy_device( From e480f75d6d65135efb04cdb6426e14aabd1f7a14 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 14 Mar 2019 09:10:36 -0700 Subject: [PATCH 014/290] 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 c0b6158f186..19a9f7583ec 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -24,7 +24,7 @@ from homeassistant.core import callback 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 @@ class LIFXManager: 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 @@ class LIFXManager: """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 @@ class LIFXManager: 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 ef2e3f607ad057b8a72618b889d1053be2c5bc89 Mon Sep 17 00:00:00 2001 From: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> Date: Thu, 14 Mar 2019 09:49:57 -0700 Subject: [PATCH 015/290] Ps4 remove throttling (#21961) * Remove throttling * Correct docstring --- homeassistant/components/ps4/media_player.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index 74dce515d9d..e2b8e97f425 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -2,16 +2,14 @@ Support for PlayStation 4 consoles. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.ps4/ +https://home-assistant.io/components/ps4/ """ -from datetime import timedelta import logging import socket import voluptuous as vol import homeassistant.helpers.config_validation as cv -import homeassistant.util as util from homeassistant.components.media_player import ( MediaPlayerDevice, ENTITY_IMAGE_URL) from homeassistant.components.media_player.const import ( @@ -38,9 +36,6 @@ ICON = 'mdi:playstation' GAMES_FILE = '.ps4-games.json' MEDIA_IMAGE_DEFAULT = None -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) -MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=10) - COMMANDS = ( 'up', 'down', @@ -139,7 +134,6 @@ class PS4Device(MediaPlayerDevice): """Subscribe PS4 events.""" self.hass.data[PS4_DATA].devices.append(self) - @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update(self): """Retrieve the latest data.""" try: From bd930b6e96086df951a08bf219b4ac600823e04f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 14 Mar 2019 18:14:27 +0100 Subject: [PATCH 016/290] Upgrade youtube_dl to 2019.03.09 (#22041) --- homeassistant/components/media_extractor/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_extractor/__init__.py b/homeassistant/components/media_extractor/__init__.py index ff6277242ca..b343c13ef9a 100644 --- a/homeassistant/components/media_extractor/__init__.py +++ b/homeassistant/components/media_extractor/__init__.py @@ -12,7 +12,7 @@ from homeassistant.const import ( ATTR_ENTITY_ID) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['youtube_dl==2019.03.01'] +REQUIREMENTS = ['youtube_dl==2019.03.09'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 1bcba2f3ab3..4b82eed9f81 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1823,7 +1823,7 @@ yeelight==0.4.3 yeelightsunflower==0.0.10 # homeassistant.components.media_extractor -youtube_dl==2019.03.01 +youtube_dl==2019.03.09 # homeassistant.components.light.zengge zengge==0.2 From 018a5d5c1fd4971de2190e59f14d335f258d4545 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 14 Mar 2019 18:18:25 +0100 Subject: [PATCH 017/290] 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 409358c2f04..2d8b06dd466 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -187,7 +187,7 @@ class NetatmoThermostat(ClimateDevice): "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 4e84e8a15e18d24c84f4f4465c082f217c3d1afa Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 14 Mar 2019 18:33:43 +0100 Subject: [PATCH 018/290] Async support for Daikin (#21638) * asyncio support for Daikin * version bump pydaikin * pass session to pydaikin.Appliance * all entities should have update --- homeassistant/components/daikin/__init__.py | 25 ++++++++++++++------- homeassistant/components/daikin/climate.py | 24 ++++++++++---------- homeassistant/components/daikin/sensor.py | 4 ++-- requirements_all.txt | 2 +- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index ce4a58162c7..04cf8a584bf 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -17,7 +17,7 @@ from homeassistant.util import Throttle from . import config_flow # noqa pylint_disable=unused-import from .const import KEY_HOST -REQUIREMENTS = ['pydaikin==0.9'] +REQUIREMENTS = ['pydaikin==1.1.0'] _LOGGER = logging.getLogger(__name__) @@ -87,9 +87,11 @@ async def async_unload_entry(hass, config_entry): async def daikin_api_setup(hass, host): """Create a Daikin instance only once.""" from pydaikin.appliance import Appliance + session = hass.helpers.aiohttp_client.async_get_clientsession() try: with async_timeout.timeout(10): - device = await hass.async_add_executor_job(Appliance, host) + device = Appliance(host, session) + await device.init() except asyncio.TimeoutError: _LOGGER.error("Connection to Daikin could not be established") return None @@ -97,8 +99,7 @@ async def daikin_api_setup(hass, host): _LOGGER.error("Unexpected error creating device") return None - name = device.values['name'] - api = DaikinApi(device, name) + api = DaikinApi(device) return api @@ -106,21 +107,29 @@ async def daikin_api_setup(hass, host): class DaikinApi: """Keep the Daikin instance in one place and centralize the update.""" - def __init__(self, device, name): + def __init__(self, device): """Initialize the Daikin Handle.""" self.device = device - self.name = name + self.name = device.values['name'] self.ip_address = device.ip + self._available = True @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self, **kwargs): + async def async_update(self, **kwargs): """Pull the latest data from Daikin.""" try: - self.device.update_status() + await self.device.update_status() + self._available = True except timeout: _LOGGER.warning( "Connection failed for %s", self.ip_address ) + self._available = False + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self._available @property def mac(self): diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index 775e4a216e5..869c38869cb 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -146,7 +146,7 @@ class DaikinClimate(ClimateDevice): return value - def set(self, settings): + async def _set(self, settings): """Set device settings using API.""" values = {} @@ -173,7 +173,7 @@ class DaikinClimate(ClimateDevice): _LOGGER.error("Invalid temperature %s", value) if values: - self._api.device.set(values) + await self._api.device.set(values) @property def supported_features(self): @@ -210,9 +210,9 @@ class DaikinClimate(ClimateDevice): """Return the supported step of target temperature.""" return 1 - def set_temperature(self, **kwargs): + async def async_set_temperature(self, **kwargs): """Set new target temperature.""" - self.set(kwargs) + await self._set(kwargs) @property def current_operation(self): @@ -224,18 +224,18 @@ class DaikinClimate(ClimateDevice): """Return the list of available operation modes.""" return self._list.get(ATTR_OPERATION_MODE) - def set_operation_mode(self, operation_mode): + async def async_set_operation_mode(self, operation_mode): """Set HVAC mode.""" - self.set({ATTR_OPERATION_MODE: operation_mode}) + await self._set({ATTR_OPERATION_MODE: operation_mode}) @property def current_fan_mode(self): """Return the fan setting.""" return self.get(ATTR_FAN_MODE) - def set_fan_mode(self, fan_mode): + async def async_set_fan_mode(self, fan_mode): """Set fan mode.""" - self.set({ATTR_FAN_MODE: fan_mode}) + await self._set({ATTR_FAN_MODE: fan_mode}) @property def fan_list(self): @@ -247,18 +247,18 @@ class DaikinClimate(ClimateDevice): """Return the fan setting.""" return self.get(ATTR_SWING_MODE) - def set_swing_mode(self, swing_mode): + async def async_set_swing_mode(self, swing_mode): """Set new target temperature.""" - self.set({ATTR_SWING_MODE: swing_mode}) + await self._set({ATTR_SWING_MODE: swing_mode}) @property def swing_list(self): """List of available swing modes.""" return self._list.get(ATTR_SWING_MODE) - def update(self): + async def async_update(self): """Retrieve latest state.""" - self._api.update() + await self._api.async_update() @property def device_info(self): diff --git a/homeassistant/components/daikin/sensor.py b/homeassistant/components/daikin/sensor.py index 6065a182274..3669dfac280 100644 --- a/homeassistant/components/daikin/sensor.py +++ b/homeassistant/components/daikin/sensor.py @@ -96,9 +96,9 @@ class DaikinClimateSensor(Entity): """Return the unit of measurement.""" return self._unit_of_measurement - def update(self): + async def async_update(self): """Retrieve latest state.""" - self._api.update() + await self._api.async_update() @property def device_info(self): diff --git a/requirements_all.txt b/requirements_all.txt index 4b82eed9f81..a4967357d51 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==0.9 +pydaikin==1.1.0 # homeassistant.components.danfoss_air pydanfossair==0.0.7 From 8d2d71c16a20362b81d8d58759fa678ced899bc5 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Thu, 14 Mar 2019 12:56:33 -0500 Subject: [PATCH 019/290] Bump amcrest to 1.2.6 & use new exceptions (#22040) * Bump amcrest to 1.2.6 & use new exceptions * Fix lint failure * Use AmcrestError instead of individual ones --- homeassistant/components/amcrest/__init__.py | 9 ++++----- homeassistant/components/amcrest/camera.py | 8 ++++---- requirements_all.txt | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index b976c1bd9d3..b11c263c56b 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -4,8 +4,6 @@ from datetime import timedelta import aiohttp import voluptuous as vol -from requests.exceptions import HTTPError, ConnectTimeout -from requests.exceptions import ConnectionError as ConnectError from homeassistant.const import ( CONF_NAME, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD, @@ -13,7 +11,8 @@ from homeassistant.const import ( from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['amcrest==1.2.5'] + +REQUIREMENTS = ['amcrest==1.2.6'] DEPENDENCIES = ['ffmpeg'] _LOGGER = logging.getLogger(__name__) @@ -91,7 +90,7 @@ CONFIG_SCHEMA = vol.Schema({ def setup(hass, config): """Set up the Amcrest IP Camera component.""" - from amcrest import AmcrestCamera + from amcrest import AmcrestCamera, AmcrestError hass.data[DATA_AMCREST] = {} amcrest_cams = config[DOMAIN] @@ -105,7 +104,7 @@ def setup(hass, config): # pylint: disable=pointless-statement camera.current_time - except (ConnectError, ConnectTimeout, HTTPError) as ex: + except AmcrestError as ex: _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex)) hass.components.persistent_notification.create( 'Error: {}
' diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index f6c507e73f4..32885c2a83c 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -2,9 +2,6 @@ import asyncio import logging -from requests import RequestException -from urllib3.exceptions import ReadTimeoutError - from homeassistant.components.amcrest import ( DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT) from homeassistant.components.camera import Camera @@ -14,6 +11,7 @@ from homeassistant.helpers.aiohttp_client import ( async_get_clientsession, async_aiohttp_proxy_web, async_aiohttp_proxy_stream) + DEPENDENCIES = ['amcrest', 'ffmpeg'] _LOGGER = logging.getLogger(__name__) @@ -51,13 +49,15 @@ class AmcrestCam(Camera): async def async_camera_image(self): """Return a still image response from the camera.""" + from amcrest import AmcrestError + async with self._snapshot_lock: try: # Send the request to snap a picture and return raw jpg data response = await self.hass.async_add_executor_job( self._camera.snapshot, self._resolution) return response.data - except (RequestException, ReadTimeoutError, ValueError) as error: + except AmcrestError as error: _LOGGER.error( 'Could not get camera image due to error %s', error) return None diff --git a/requirements_all.txt b/requirements_all.txt index a4967357d51..ca8193045fa 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.5 +amcrest==1.2.6 # homeassistant.components.androidtv.media_player androidtv==0.0.11 From b25e951dccc9a125d01a87ebc6599d98b5ddc488 Mon Sep 17 00:00:00 2001 From: emontnemery Date: Thu, 14 Mar 2019 18:58:32 +0100 Subject: [PATCH 020/290] 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 693c15fa424..9bee2b81d61 100644 --- a/homeassistant/components/alarm_control_panel/manual_mqtt.py +++ b/homeassistant/components/alarm_control_panel/manual_mqtt.py @@ -342,18 +342,18 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel): ) @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 5f52da745ee..ff89cd47024 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 3a820d189f4..0a1b327dca9 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 6e545d19fe2..fb6a94f1870 100644 --- a/homeassistant/components/mqtt_eventstream/__init__.py +++ b/homeassistant/components/mqtt_eventstream/__init__.py @@ -74,9 +74,9 @@ def async_setup(hass, config): # 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 c0d3d152270..df6b815e4c5 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 2b79e4c3a9a..95825f4ca13 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 @@ async def async_setup_platform(hass, config, async_add_entities, 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 b52f039281c..36f99719da4 100644 --- a/homeassistant/components/sensor/mqtt_room.py +++ b/homeassistant/components/sensor/mqtt_room.py @@ -90,16 +90,16 @@ class MQTTRoomSensor(Entity): 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 20cc7137ef8..0cc96d66b1a 100644 --- a/homeassistant/components/snips/__init__.py +++ b/homeassistant/components/snips/__init__.py @@ -95,14 +95,14 @@ async def async_setup(hass, config): 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 62f12d242ab00a799d27d156aa6422e1c6d1b194 Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Thu, 14 Mar 2019 15:29:21 -0400 Subject: [PATCH 021/290] Z-Wave usb_path in configuration.yaml overrides config entry usb_path (#22038) * usb_path in configuration.yaml overrides config entry * Minor text update * Update __init__.py --- homeassistant/components/zwave/__init__.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index be76eca4efd..604f03387bd 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -169,8 +169,7 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_DEBUG, default=DEFAULT_DEBUG): cv.boolean, vol.Optional(CONF_POLLING_INTERVAL, default=DEFAULT_POLLING_INTERVAL): cv.positive_int, - vol.Optional(CONF_USB_STICK_PATH, default=DEFAULT_CONF_USB_STICK_PATH): - cv.string, + vol.Optional(CONF_USB_STICK_PATH): cv.string, }), }, extra=vol.ALLOW_EXTRA) @@ -239,7 +238,8 @@ async def async_setup(hass, config): hass.async_create_task(hass.config_entries.flow.async_init( DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, data={ - CONF_USB_STICK_PATH: conf[CONF_USB_STICK_PATH], + CONF_USB_STICK_PATH: conf.get( + CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH), CONF_NETWORK_KEY: conf.get(CONF_NETWORK_KEY), } )) @@ -271,9 +271,14 @@ async def async_setup_entry(hass, config_entry): config.get(CONF_DEVICE_CONFIG_DOMAIN), config.get(CONF_DEVICE_CONFIG_GLOB)) + usb_path = config.get( + CONF_USB_STICK_PATH, config_entry.data[CONF_USB_STICK_PATH]) + + _LOGGER.info('Z-Wave USB path is %s', usb_path) + # Setup options options = ZWaveOption( - config_entry.data[CONF_USB_STICK_PATH], + usb_path, user_path=hass.config.config_dir, config_path=config.get(CONF_CONFIG_PATH)) @@ -374,7 +379,7 @@ async def async_setup_entry(hass, config_entry): def network_ready(): """Handle the query of all awake nodes.""" - _LOGGER.info("Zwave network is ready for use. All awake nodes " + _LOGGER.info("Z-Wave network is ready for use. All awake nodes " "have been queried. Sleeping nodes will be " "queried when they awake.") hass.bus.fire(const.EVENT_NETWORK_READY) From 3769f5893acaa33e1e87a436f0bb8924be560426 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 12:57:50 -0700 Subject: [PATCH 022/290] 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 | 73 +++++-------- .../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, 254 insertions(+), 212 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 00000000000..646151a5229 --- /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 0d95bfe6832..1c348ea0782 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 @@ REQUIREMENTS = ['PyNaCl==1.3.0'] 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: {}} + app_config = { + DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, + } - if hass.data.get(DOMAIN) is None: - hass.data[DOMAIN] = {DATA_DELETED_IDS: [], DATA_REGISTRATIONS: {}} - - 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 7a497d76454..3ba029fec0e 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -17,8 +17,9 @@ CONF_CLOUDHOOK_URL = 'cloudhook_url' 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_DATA = 'app_data' 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 @@ ATTR_WEBHOOK_TYPE = 'type' 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 1f67170a72c..5ec3b99b291 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -11,8 +11,7 @@ from homeassistant.helpers.typing import HomeAssistantType 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 4948407b63b..8076d217cac 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -8,29 +8,16 @@ from homeassistant.auth.util import generate_secret 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 @@ class RegistrationsView(HomeAssistantView): 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 00000000000..646151a5229 --- /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 4d3e0aef4c6..1fab29160b7 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 @@ 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 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 @@ from .const import (ATTR_ALTITUDE, ATTR_APP_COMPONENT, ATTR_BATTERY, 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 + device_registry = await dr.async_get_registry(hass) - 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.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 5f6a25cbcec..7bc1e59d623 100644 --- a/homeassistant/components/mobile_app/websocket_api.py +++ b/homeassistant/components/mobile_app/websocket_api.py @@ -17,16 +17,14 @@ from homeassistant.exceptions import HomeAssistantError 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 1036c02fd0d..e00d7204a79 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -161,6 +161,7 @@ FLOWS = [ 'locative', 'luftdaten', 'mailgun', + 'mobile_app', 'mqtt', 'nest', 'openuv', diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 57265cf696d..acd0befda4e 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -170,11 +170,13 @@ class FlowHandler: } @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 87ed83d9a7e..d5e4331f7b9 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -226,6 +226,7 @@ def test_abort(hass, client): 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 1f91eb7e442..bed275a534d 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 7861e63459a..eb9d1f54d93 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 75e8903c494..a70e8ba1275 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 homeassistant.core import callback 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 @@ async def test_webhook_handle_fire_event(hass, webhook_client): # noqa: F811 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 614fd33974b..ee656159d2e 100644 --- a/tests/components/mobile_app/test_websocket_api.py +++ b/tests/components/mobile_app/test_websocket_api.py @@ -9,33 +9,6 @@ from . import authed_api_client, setup_ws, webhook_client # noqa: F401 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 6a80ffa8ccfd6a3de9388f5637d51636e8f18d57 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 15 Mar 2019 00:18:31 +0100 Subject: [PATCH 023/290] 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 c854fe69be9..2e324f06738 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -3,6 +3,7 @@ import logging 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 063a9daf00a..f73c16b1904 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -136,12 +136,16 @@ class CloudClient(Interface): 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 65e026389f0..fdedacd6dbb 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -7,6 +7,7 @@ PREF_ENABLE_GOOGLE = 'google_enabled' 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 263c17935cb..16ff8f0c213 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 @@ class CloudPreferences: 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 @@ class CloudPreferences: (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 @@ class CloudPreferences: 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 d3e2e50f3a7..0de395c8bbc 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 dcaced19663ae0dedc0f4a017ebc327e264c4ee8 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 17:24:53 -0700 Subject: [PATCH 024/290] 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 1c348ea0782..ecbe8d70847 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -3,13 +3,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.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 @@ REQUIREMENTS = ['PyNaCl==1.3.0'] 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 00000000000..289a50584c9 --- /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 3ba029fec0e..d38df31b214 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_CLOUDHOOK_URL = 'cloudhook_url' 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 @@ 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' 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 @@ UPDATE_REGISTRATION_SCHEMA = vol.Schema({ 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 @@ UPDATE_LOCATION_SCHEMA = vol.Schema({ 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 00000000000..05736b3a689 --- /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 5ec3b99b291..60bd8b4e1d6 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -11,7 +11,8 @@ from homeassistant.helpers.typing import HomeAssistantType 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 00000000000..c6a53ce57ec --- /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 1fab29160b7..aafa6046d11 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -10,30 +10,38 @@ from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES, 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 bed275a534d..cf617ff0528 100644 --- a/tests/components/mobile_app/__init__.py +++ b/tests/components/mobile_app/__init__.py @@ -6,9 +6,9 @@ from tests.common import mock_device_registry 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 00000000000..d8cb91a8bc6 --- /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 9575c20b7c1360a57274ec0aaa7d89e92a03abd4 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Thu, 14 Mar 2019 20:29:12 -0400 Subject: [PATCH 025/290] Minor ZHA consts fixes (#22049) * Use consts for zha.core.consts * Fix ZHA battery reporting config. --- homeassistant/components/zha/core/const.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 1e41cbbbec5..521bd529e30 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -1,6 +1,12 @@ """All constants related to the ZHA component.""" import enum +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR +from homeassistant.components.fan import DOMAIN as FAN +from homeassistant.components.light import DOMAIN as LIGHT +from homeassistant.components.sensor import DOMAIN as SENSOR +from homeassistant.components.switch import DOMAIN as SWITCH + DOMAIN = 'zha' BAUD_RATES = [ @@ -17,13 +23,13 @@ DATA_ZHA_CORE_EVENTS = 'zha_core_events' DATA_ZHA_GATEWAY = 'zha_gateway' ZHA_DISCOVERY_NEW = 'zha_discovery_new_{}' -COMPONENTS = [ - 'binary_sensor', - 'fan', - 'light', - 'sensor', - 'switch', -] +COMPONENTS = ( + BINARY_SENSOR, + FAN, + LIGHT, + SENSOR, + SWITCH, +) CONF_BAUDRATE = 'baudrate' CONF_DATABASE = 'database_path' @@ -141,7 +147,7 @@ REPORT_CONFIG_DEFAULT = (REPORT_CONFIG_MIN_INT, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_ASAP = (REPORT_CONFIG_MIN_INT_ASAP, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_RPT_CHANGE) REPORT_CONFIG_BATTERY_SAVE = (REPORT_CONFIG_MIN_INT_BATTERY_SAVE, - REPORT_CONFIG_MAX_INT, + REPORT_CONFIG_MAX_INT_BATTERY_SAVE, REPORT_CONFIG_RPT_CHANGE) REPORT_CONFIG_IMMEDIATE = (REPORT_CONFIG_MIN_INT_IMMEDIATE, REPORT_CONFIG_MAX_INT, From 851378739fad254d10c0873b245df676117f2042 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 17:48:38 -0700 Subject: [PATCH 026/290] Fix cloud services.yaml --- homeassistant/components/cloud/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/cloud/services.yaml b/homeassistant/components/cloud/services.yaml index 9ef814e0087..20c25225ce2 100644 --- a/homeassistant/components/cloud/services.yaml +++ b/homeassistant/components/cloud/services.yaml @@ -1,4 +1,4 @@ -# Describes the format for available light services +# Describes the format for available cloud services remote_connect: description: Make instance UI available outside over NabuCasa cloud. From 89c96279ce54950b8078775535c32266cc3cf5b9 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 14 Mar 2019 21:00:49 -0400 Subject: [PATCH 027/290] Cleanup remaining constants stuff in ZHA (#22050) * clean up constants * fix quirks until it can be upgradded --- homeassistant/components/zha/const.py | 1 + .../components/zha/core/channels/__init__.py | 5 ++-- .../components/zha/core/channels/general.py | 6 ++--- homeassistant/components/zha/core/const.py | 12 ---------- .../components/zha/core/discovery.py | 23 ++++++++++--------- homeassistant/components/zha/core/gateway.py | 11 +++++---- homeassistant/components/zha/core/helpers.py | 4 +++- .../components/zha/core/registries.py | 23 +++++++++++++------ 8 files changed, 44 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/zha/const.py b/homeassistant/components/zha/const.py index abcd17a0461..e7cf424990b 100644 --- a/homeassistant/components/zha/const.py +++ b/homeassistant/components/zha/const.py @@ -6,3 +6,4 @@ at https://home-assistant.io/components/fan.zha/ """ # pylint: disable=W0614,W0401 from .core.const import * # noqa: F401,F403 +from .core.registries import * # noqa: F401,F403 diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index 92518bd33ff..ef3ef71477f 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -17,9 +17,10 @@ from ..helpers import ( bind_configure_reporting, construct_unique_id, safe_read, get_attr_id_by_name) from ..const import ( - CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED, - ATTRIBUTE_CHANNEL, EVENT_RELAY_CHANNEL, ZDO_CHANNEL + REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED, ATTRIBUTE_CHANNEL, + EVENT_RELAY_CHANNEL, ZDO_CHANNEL ) +from ..registries import CLUSTER_REPORT_CONFIGS NODE_DESCRIPTOR_REQUEST = 0x0002 MAINS_POWERED = 1 diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index cd16fe5d22e..c0b0367be99 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -10,9 +10,9 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from . import ZigbeeChannel, parse_and_log_command 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_ATTR_UPDATED, SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL, + SIGNAL_STATE_ATTR, BASIC_CHANNEL, ON_OFF_CHANNEL, LEVEL_CHANNEL, + POWER_CONFIGURATION_CHANNEL ) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 521bd529e30..c5837cc33e7 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -121,18 +121,6 @@ class RadioType(enum.Enum): DISCOVERY_KEY = 'zha_discovery_info' -DEVICE_CLASS = {} -SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {} -SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {} -SENSOR_TYPES = {} -RADIO_TYPES = {} -BINARY_SENSOR_TYPES = {} -CLUSTER_REPORT_CONFIGS = {} -CUSTOM_CLUSTER_MAPPINGS = {} -COMPONENT_CLUSTERS = {} -EVENT_RELAY_CLUSTERS = [] -NO_SENSOR_CLUSTERS = [] -BINDABLE_CLUSTERS = [] REPORT_CONFIG_MAX_INT = 900 REPORT_CONFIG_MAX_INT_BATTERY_SAVE = 10800 diff --git a/homeassistant/components/zha/core/discovery.py b/homeassistant/components/zha/core/discovery.py index 6d35fa24615..f5bd6ee99f2 100644 --- a/homeassistant/components/zha/core/discovery.py +++ b/homeassistant/components/zha/core/discovery.py @@ -10,16 +10,18 @@ import logging from homeassistant import const as ha_const from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send -from . import const as zha_const from .channels import ( AttributeListeningChannel, EventRelayChannel, ZDOChannel ) from .channels.registry import ZIGBEE_CHANNEL_REGISTRY from .const import ( CONF_DEVICE_CONFIG, COMPONENTS, ZHA_DISCOVERY_NEW, DATA_ZHA, - SENSOR_TYPE, UNKNOWN, BINARY_SENSOR_TYPES, NO_SENSOR_CLUSTERS, - EVENT_RELAY_CLUSTERS, SENSOR_TYPES, GENERIC, - POWER_CONFIGURATION_CHANNEL + SENSOR_TYPE, UNKNOWN, GENERIC, POWER_CONFIGURATION_CHANNEL +) +from .registries import ( + BINARY_SENSOR_TYPES, NO_SENSOR_CLUSTERS, EVENT_RELAY_CLUSTERS, + SENSOR_TYPES, DEVICE_CLASS, COMPONENT_CLUSTERS, + SINGLE_INPUT_CLUSTER_DEVICE_CLASS, SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS ) from ..device_entity import ZhaDeviceEntity @@ -53,16 +55,15 @@ def async_process_endpoint( if endpoint.profile_id in zigpy.profiles.PROFILES: profile = zigpy.profiles.PROFILES[endpoint.profile_id] - if zha_const.DEVICE_CLASS.get(endpoint.profile_id, - {}).get(endpoint.device_type, - None): + if DEVICE_CLASS.get(endpoint.profile_id, {}).get( + endpoint.device_type, None): profile_clusters = profile.CLUSTERS[endpoint.device_type] - profile_info = zha_const.DEVICE_CLASS[endpoint.profile_id] + profile_info = DEVICE_CLASS[endpoint.profile_id] component = profile_info[endpoint.device_type] if ha_const.CONF_TYPE in node_config: component = node_config[ha_const.CONF_TYPE] - profile_clusters = zha_const.COMPONENT_CLUSTERS[component] + profile_clusters = COMPONENT_CLUSTERS[component] if component and component in COMPONENTS: profile_match = _async_handle_profile_match( @@ -179,7 +180,7 @@ def _async_handle_single_cluster_matches(hass, endpoint, zha_device, zha_device, cluster, device_key, - zha_const.SINGLE_INPUT_CLUSTER_DEVICE_CLASS, + SINGLE_INPUT_CLUSTER_DEVICE_CLASS, is_new_join, )) @@ -190,7 +191,7 @@ def _async_handle_single_cluster_matches(hass, endpoint, zha_device, zha_device, cluster, device_key, - zha_const.SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, + SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, is_new_join, )) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 89b2d9b77a6..adaab0e6616 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -15,11 +15,11 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity_component import EntityComponent 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_TYPES, - RADIO, CONTROLLER, RADIO_DESCRIPTION) + 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 +) from .device import ZHADevice, DeviceStatus from .channels import ( ZDOChannel, MAINS_POWERED @@ -31,6 +31,7 @@ from .discovery import ( ) from .store import async_get_registry from .patches import apply_application_controller_patch +from .registries import RADIO_TYPES _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index d6e9cc32338..b00626031ed 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -11,7 +11,9 @@ from concurrent.futures import TimeoutError as Timeout from homeassistant.core import callback from .const import ( DEFAULT_BAUDRATE, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_MIN_INT, - REPORT_CONFIG_RPT_CHANGE, RadioType, IN, OUT, BINDABLE_CLUSTERS) + REPORT_CONFIG_RPT_CHANGE, RadioType, IN, OUT +) +from .registries import BINDABLE_CLUSTERS _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zha/core/registries.py b/homeassistant/components/zha/core/registries.py index 4d6b1faba00..3cbd31aa304 100644 --- a/homeassistant/components/zha/core/registries.py +++ b/homeassistant/components/zha/core/registries.py @@ -6,21 +6,30 @@ https://home-assistant.io/components/zha/ """ from .const import ( - DEVICE_CLASS, SINGLE_INPUT_CLUSTER_DEVICE_CLASS, - SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, COMPONENT_CLUSTERS, HUMIDITY, + HUMIDITY, TEMPERATURE, ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT, - EVENT_RELAY_CLUSTERS, OPENING, ZONE, - OCCUPANCY, CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_IMMEDIATE, + OCCUPANCY, REPORT_CONFIG_IMMEDIATE, OPENING, ZONE, RADIO_DESCRIPTION, REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT, - REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, - NO_SENSOR_CLUSTERS, BINDABLE_CLUSTERS, ACCELERATION, SENSOR_TYPES, - BINARY_SENSOR_TYPES, RADIO_TYPES, RadioType, RADIO, RADIO_DESCRIPTION, + REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, ACCELERATION, RadioType, RADIO, CONTROLLER ) SMARTTHINGS_HUMIDITY_CLUSTER = 64581 SMARTTHINGS_ACCELERATION_CLUSTER = 64514 +DEVICE_CLASS = {} +SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {} +SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {} +SENSOR_TYPES = {} +RADIO_TYPES = {} +BINARY_SENSOR_TYPES = {} +CLUSTER_REPORT_CONFIGS = {} +CUSTOM_CLUSTER_MAPPINGS = {} +COMPONENT_CLUSTERS = {} +EVENT_RELAY_CLUSTERS = [] +NO_SENSOR_CLUSTERS = [] +BINDABLE_CLUSTERS = [] + def establish_device_mappings(): """Establish mappings between ZCL objects and HA ZHA objects. From 5a9e8b2d3e17bc2086915e576c0642520f969aee Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 19:46:59 -0700 Subject: [PATCH 028/290] 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 2e324f06738..3e3d6f975e9 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 d38df31b214..3aa4626da29 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -17,6 +17,7 @@ STORAGE_KEY = DOMAIN 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 8076d217cac..2ae8f441e52 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -5,7 +5,9 @@ from typing import Dict 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.const import (HTTP_CREATED, CONF_WEBHOOK_ID) 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 @@ class RegistrationsView(HomeAssistantView): 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 3b34594aa3097bfe39070151d06f622160ea9754 Mon Sep 17 00:00:00 2001 From: escoand Date: Fri, 15 Mar 2019 08:43:54 +0100 Subject: [PATCH 029/290] Add HTTP auth and SSL verification to REST notify (#22016) * add HTTP auth and SSL verification * use internal import * fix long line * avoid extra import --- homeassistant/components/notify/rest.py | 43 ++++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/notify/rest.py b/homeassistant/components/notify/rest.py index df25045c6ec..bc4341d7b47 100644 --- a/homeassistant/components/notify/rest.py +++ b/homeassistant/components/notify/rest.py @@ -12,8 +12,11 @@ import voluptuous as vol from homeassistant.components.notify import ( ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService, PLATFORM_SCHEMA) -from homeassistant.const import (CONF_RESOURCE, CONF_METHOD, CONF_NAME, - CONF_HEADERS) +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 CONF_DATA = 'data' @@ -23,6 +26,7 @@ CONF_TARGET_PARAMETER_NAME = 'target_param_name' CONF_TITLE_PARAMETER_NAME = 'title_param_name' DEFAULT_MESSAGE_PARAM_NAME = 'message' DEFAULT_METHOD = 'GET' +DEFAULT_VERIFY_SSL = True PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_RESOURCE): cv.url, @@ -35,7 +39,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_TARGET_PARAMETER_NAME): cv.string, vol.Optional(CONF_TITLE_PARAMETER_NAME): cv.string, vol.Optional(CONF_DATA): dict, - vol.Optional(CONF_DATA_TEMPLATE): {cv.match_all: cv.template_complex} + vol.Optional(CONF_DATA_TEMPLATE): {cv.match_all: cv.template_complex}, + vol.Optional(CONF_AUTHENTICATION): + vol.In([HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]), + vol.Optional(CONF_PASSWORD): cv.string, + vol.Optional(CONF_USERNAME): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, }) _LOGGER = logging.getLogger(__name__) @@ -51,17 +60,30 @@ def get_service(hass, config, discovery_info=None): target_param_name = config.get(CONF_TARGET_PARAMETER_NAME) data = config.get(CONF_DATA) data_template = config.get(CONF_DATA_TEMPLATE) + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + verify_ssl = config.get(CONF_VERIFY_SSL) + + if username and password: + if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION: + auth = requests.auth.HTTPDigestAuth(username, password) + else: + auth = requests.auth.HTTPBasicAuth(username, password) + else: + auth = None return RestNotificationService( hass, resource, method, headers, message_param_name, - title_param_name, target_param_name, data, data_template) + title_param_name, target_param_name, data, data_template, auth, + verify_ssl) class RestNotificationService(BaseNotificationService): """Implementation of a notification service for REST.""" def __init__(self, hass, resource, method, headers, message_param_name, - title_param_name, target_param_name, data, data_template): + title_param_name, target_param_name, data, data_template, + auth, verify_ssl): """Initialize the service.""" self._resource = resource self._hass = hass @@ -72,6 +94,8 @@ class RestNotificationService(BaseNotificationService): self._target_param_name = target_param_name self._data = data self._data_template = data_template + self._auth = auth + self._verify_ssl = verify_ssl def send_message(self, message="", **kwargs): """Send a message to a user.""" @@ -104,13 +128,16 @@ class RestNotificationService(BaseNotificationService): if self._method == 'POST': response = requests.post(self._resource, headers=self._headers, - data=data, timeout=10) + data=data, timeout=10, auth=self._auth, + verify=self._verify_ssl) elif self._method == 'POST_JSON': response = requests.post(self._resource, headers=self._headers, - json=data, timeout=10) + json=data, timeout=10, auth=self._auth, + verify=self._verify_ssl) else: # default GET response = requests.get(self._resource, headers=self._headers, - params=data, timeout=10) + params=data, timeout=10, auth=self._auth, + verify=self._verify_ssl) success_codes = (200, 201, 202, 203, 204, 205, 206, 207, 208, 226) if response.status_code not in success_codes: From 941f9b29dc656e0036aeca98cb30d9b0913ebd16 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 07:41:34 -0700 Subject: [PATCH 030/290] 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 65f65cbcec5..8865ff39cea 100644 --- a/homeassistant/components/config/config_entries.py +++ b/homeassistant/components/config/config_entries.py @@ -118,6 +118,16 @@ class ConfigManagerFlowIndexView(FlowManagerIndexView): # 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 @@ class ConfigManagerFlowResourceView(FlowManagerResourceView): # 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 @@ class OptionManagerFlowIndexView(FlowManagerIndexView): 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 d5e4331f7b9..852a5adf6a2 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -255,6 +255,10 @@ def test_create_account(hass, client): 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 test_create_account(hass, client): 'title': 'Test Entry', 'type': 'create_entry', 'version': 1, + 'result': entries[0].entry_id, 'description': None, 'description_placeholders': None, } @@ -317,6 +322,10 @@ def test_two_step_flow(hass, client): '/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 test_two_step_flow(hass, client): 'type': 'create_entry', 'title': 'user-title', 'version': 1, + 'result': entries[0].entry_id, 'description': None, 'description_placeholders': None, } From 3ce50b0a6a66bf7caeec5d91e32d2b246c846954 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 07:47:13 -0700 Subject: [PATCH 031/290] 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 d8cb91a8bc6..f399f842745 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 e581d9e249f2d543dece08176ba1fb194fbb5fa4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 09:14:20 -0700 Subject: [PATCH 032/290] 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 f399f842745..5dc285cfe9e 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 101225749b51194c1fc326b12f11bb5cbb760425 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 15 Mar 2019 17:22:00 +0100 Subject: [PATCH 033/290] Upgrade ruamel.yaml to 0.15.89 (#22064) --- 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 fa1fe5a959d..04704a00484 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.88 +ruamel.yaml==0.15.89 voluptuous==0.11.5 voluptuous-serialize==2.1.0 diff --git a/requirements_all.txt b/requirements_all.txt index ca8193045fa..b97d2ac588b 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.88 +ruamel.yaml==0.15.89 voluptuous==0.11.5 voluptuous-serialize==2.1.0 diff --git a/setup.py b/setup.py index c4c9d0e53ed..8f7e84dd8d8 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ REQUIRES = [ 'pytz>=2018.07', 'pyyaml>=3.13,<4', 'requests==2.21.0', - 'ruamel.yaml==0.15.88', + 'ruamel.yaml==0.15.89', 'voluptuous==0.11.5', 'voluptuous-serialize==2.1.0', ] From cf69f25354ca00bbad929877c7777a6f2f2aa610 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 15 Mar 2019 10:01:15 -0700 Subject: [PATCH 034/290] 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 a856a3d8e82..c87b2c3f624 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 @@ from homeassistant.const import ( 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 @@ class SmartHomeView(http.HomeAssistantView): 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 9520d3828881bc2191da6abd88c707fab58a3857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isabella=20Gross=20Alstr=C3=B6m?= Date: Fri, 15 Mar 2019 18:14:22 +0100 Subject: [PATCH 035/290] Update rest.py (#22077) Added specific error logs for 5xx and 4xx responses, debug log for 2xx and other statuses. --- homeassistant/components/notify/rest.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/notify/rest.py b/homeassistant/components/notify/rest.py index bc4341d7b47..469319a0f3e 100644 --- a/homeassistant/components/notify/rest.py +++ b/homeassistant/components/notify/rest.py @@ -139,8 +139,19 @@ class RestNotificationService(BaseNotificationService): params=data, timeout=10, auth=self._auth, verify=self._verify_ssl) - success_codes = (200, 201, 202, 203, 204, 205, 206, 207, 208, 226) - if response.status_code not in success_codes: + if response.status_code >= 500 and response.status_code < 600: _LOGGER.exception( - "Error sending message. Response %d: %s:", + "Server error. Response %d: %s:", + response.status_code, response.reason) + elif response.status_code >= 400 and response.status_code < 500: + _LOGGER.exception( + "Client error. Response %d: %s:", + response.status_code, response.reason) + elif response.status_code >= 200 and response.status_code < 300: + _LOGGER.debug( + "Success. Response %d: %s:", + response.status_code, response.reason) + else: + _LOGGER.debug( + "Response %d: %s:", response.status_code, response.reason) From 17ba33004c89cc3e1e96a0c32cd8593f63b2dc7f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 15 Mar 2019 18:39:53 +0100 Subject: [PATCH 036/290] 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 3e3d6f975e9..9f6e678e417 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ from .const import ( 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 async_setup(hass, config): 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 00000000000..874c3420c58 --- /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 f73c16b1904..7fdfc786515 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -6,15 +6,18 @@ from typing import Any, Dict 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 @@ class CloudClient(Interface): 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 fdedacd6dbb..2816e3f6dc9 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -25,3 +25,5 @@ CONF_ACME_DIRECTORY_SERVER = 'acme_directory_server' MODE_DEV = "development" MODE_PROD = "production" + +DISPATCHER_REMOTE_UPDATE = 'cloud_remote_update' diff --git a/requirements_all.txt b/requirements_all.txt index b97d2ac588b..24f850833a6 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 f7f1b77559a..31e540c7242 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 00000000000..938829b809b --- /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 087748b5ffa36424174fd120a99299e35dc0aa0d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 10:43:32 -0700 Subject: [PATCH 037/290] 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 70c2bab0829..a8d2cbc35b9 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 24f850833a6..10c423aaa82 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 31e540c7242..65993daefa7 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 dc656205c476c7e71864ecbc17a04160c75c0de7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 11:11:59 -0700 Subject: [PATCH 038/290] 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 7fdfc786515..da89f8331a9 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -128,7 +128,7 @@ class CloudClient(Interface): @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 53b204347d6b2f83186d24c31c04956c3de0a87e Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Fri, 15 Mar 2019 12:25:09 -0700 Subject: [PATCH 039/290] 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 458fdff87fd..1282a40cac5 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv 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 10c423aaa82..bed0bad7032 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -158,7 +158,7 @@ alpha_vantage==2.1.0 amcrest==1.2.6 # 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 42265036ff31753b8c167c792d7acb918f00f9a6 Mon Sep 17 00:00:00 2001 From: Nacho Barrientos Date: Sat, 16 Mar 2019 02:18:10 +0100 Subject: [PATCH 040/290] Telegram_bot: Allow fetching data from unverified SSL endpoints (#22067) (#22069) * Telegram_bot: Allow fetching data from unverified SSL endpoints (#22067) This patch adds an extra option to the payload that describes the resource to send to disable the SSL server verification when polling data from URLs. * Use the boolean interpretation of the variable directly Co-Authored-By: nbarrientos --- homeassistant/components/telegram_bot/__init__.py | 7 ++++++- homeassistant/components/telegram_bot/services.yaml | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index 78d45535c48..e8b19ec2b2c 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -48,6 +48,7 @@ ATTR_TEXT = 'text' ATTR_URL = 'url' ATTR_USER_ID = 'user_id' ATTR_USERNAME = 'username' +ATTR_VERIFY_SSL = 'verify_ssl' CONF_ALLOWED_CHAT_IDS = 'allowed_chat_ids' CONF_PROXY_URL = 'proxy_url' @@ -108,6 +109,7 @@ SERVICE_SCHEMA_SEND_FILE = BASE_SERVICE_SCHEMA.extend({ vol.Optional(ATTR_USERNAME): cv.string, vol.Optional(ATTR_PASSWORD): cv.string, vol.Optional(ATTR_AUTHENTICATION): cv.string, + vol.Optional(ATTR_VERIFY_SSL): cv.boolean, }) SERVICE_SCHEMA_SEND_LOCATION = BASE_SERVICE_SCHEMA.extend({ @@ -164,7 +166,7 @@ SERVICE_MAP = { def load_data(hass, url=None, filepath=None, username=None, password=None, - authentication=None, num_retries=5): + authentication=None, num_retries=5, verify_ssl=None): """Load data into ByteIO/File container from a source.""" try: if url is not None: @@ -175,6 +177,8 @@ def load_data(hass, url=None, filepath=None, username=None, password=None, params["auth"] = HTTPDigestAuth(username, password) else: params["auth"] = HTTPBasicAuth(username, password) + if verify_ssl: + params["verify"] = verify_ssl retry_num = 0 while retry_num < num_retries: req = requests.get(url, **params) @@ -536,6 +540,7 @@ class TelegramNotificationService: username=kwargs.get(ATTR_USERNAME), password=kwargs.get(ATTR_PASSWORD), authentication=kwargs.get(ATTR_AUTHENTICATION), + verify_ssl=kwargs.get(ATTR_VERIFY_SSL), ) if file_content: for chat_id in self._get_target_chat_ids(target): diff --git a/homeassistant/components/telegram_bot/services.yaml b/homeassistant/components/telegram_bot/services.yaml index d8039c0b384..206898bfda2 100644 --- a/homeassistant/components/telegram_bot/services.yaml +++ b/homeassistant/components/telegram_bot/services.yaml @@ -52,6 +52,9 @@ send_photo: disable_notification: description: Sends the message silently. iOS users and Web users will not receive a notification, Android users will receive a notification with no sound. example: true + verify_ssl: + description: Enable or disable SSL certificate verification. Set to false if you're downloading the file from a URL and you don't want to validate the SSL certificate of the server. + example: false keyboard: description: List of rows of commands, comma-separated, to make a custom keyboard. example: '["/command1, /command2", "/command3"]' @@ -80,6 +83,9 @@ send_sticker: disable_notification: description: Sends the message silently. iOS users and Web users will not receive a notification, Android users will receive a notification with no sound. example: true + verify_ssl: + description: Enable or disable SSL certificate verification. Set to false if you're downloading the file from a URL and you don't want to validate the SSL certificate of the server. + example: false keyboard: description: List of rows of commands, comma-separated, to make a custom keyboard. example: '["/command1, /command2", "/command3"]' @@ -111,6 +117,9 @@ send_video: disable_notification: description: Sends the message silently. iOS users and Web users will not receive a notification, Android users will receive a notification with no sound. example: true + verify_ssl: + description: Enable or disable SSL certificate verification. Set to false if you're downloading the file from a URL and you don't want to validate the SSL certificate of the server. + example: false keyboard: description: List of rows of commands, comma-separated, to make a custom keyboard. example: '["/command1, /command2", "/command3"]' @@ -142,6 +151,9 @@ send_document: disable_notification: description: Sends the message silently. iOS users and Web users will not receive a notification, Android users will receive a notification with no sound. example: true + verify_ssl: + description: Enable or disable SSL certificate verification. Set to false if you're downloading the file from a URL and you don't want to validate the SSL certificate of the server. + example: false keyboard: description: List of rows of commands, comma-separated, to make a custom keyboard. example: '["/command1, /command2", "/command3"]' From dbdf5558e64fa3b29b40db550eee15db812bd7b7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 19:26:10 -0700 Subject: [PATCH 041/290] 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 2816e3f6dc9..1286832c0c7 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -27,3 +27,7 @@ MODE_DEV = "development" 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 61b3b8576ec..212bdfb4bf8 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -18,7 +18,7 @@ from homeassistant.components.google_assistant import smart_home as google_sh 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 @@ SCHEMA_WS_HOOK_DELETE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ }) -_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 @@ def _handle_cloud_errors(handler): 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 @@ def _handle_cloud_errors(handler): 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 _require_cloud_login(handler): 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 16ff8f0c213..b0244f6b1fb 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 @@ class CloudPreferences: 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 @@ class CloudPreferences: @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 @@ class CloudPreferences: 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 3ab4b1030fa..6c50a158cad 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -7,6 +7,7 @@ from jose import jwt 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 d0365f59116b9106bfd735c721c10cd6700dbf51 Mon Sep 17 00:00:00 2001 From: Andre Lengwenus Date: Sat, 16 Mar 2019 04:10:56 +0100 Subject: [PATCH 042/290] Add LCN sensor platform (#21440) --- homeassistant/components/lcn/__init__.py | 23 ++++- homeassistant/components/lcn/const.py | 36 +++++++ homeassistant/components/lcn/sensor.py | 117 +++++++++++++++++++++++ 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100755 homeassistant/components/lcn/sensor.py diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py index c7c180737f0..6b995643443 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -5,11 +5,14 @@ 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_TRANSITION, DATA_LCN, DEFAULT_NAME, DIM_MODES, - DOMAIN, MOTOR_PORTS, OUTPUT_PORTS, PATTERN_ADDRESS, RELAY_PORTS) + 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_SWITCHES, CONF_USERNAME) + 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 @@ -78,6 +81,17 @@ LIGHTS_SCHEMA = vol.Schema({ lambda value: value * 1000), }) +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(VARIABLES + SETPOINTS + + THRESHOLDS + S0_INPUTS + + LED_PORTS + LOGICOP_PORTS)), + vol.Optional(CONF_UNIT_OF_MEASUREMENT, default='native'): + vol.All(vol.Upper, vol.In(VAR_UNITS)) +}) + SWITCHES_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_ADDRESS): is_address, @@ -104,6 +118,8 @@ CONFIG_SCHEMA = vol.Schema({ cv.ensure_list, [COVERS_SCHEMA]), vol.Optional(CONF_LIGHTS): vol.All( cv.ensure_list, [LIGHTS_SCHEMA]), + vol.Optional(CONF_SENSORS): vol.All( + cv.ensure_list, [SENSORS_SCHEMA]), vol.Optional(CONF_SWITCHES): vol.All( cv.ensure_list, [SWITCHES_SCHEMA]) }) @@ -162,6 +178,7 @@ async def async_setup(hass, config): # load platforms for component, conf_key in (('cover', CONF_COVERS), ('light', CONF_LIGHTS), + ('sensor', CONF_SENSORS), ('switch', CONF_SWITCHES)): if conf_key in config[DOMAIN]: hass.async_create_task( diff --git a/homeassistant/components/lcn/const.py b/homeassistant/components/lcn/const.py index 02b35b06797..c9ee92fb175 100644 --- a/homeassistant/components/lcn/const.py +++ b/homeassistant/components/lcn/const.py @@ -16,11 +16,47 @@ CONF_DIM_MODE = 'dim_mode' CONF_DIMMABLE = 'dimmable' CONF_TRANSITION = 'transition' CONF_MOTOR = 'motor' +CONF_SOURCE = 'source' DIM_MODES = ['STEPS50', 'STEPS200'] + OUTPUT_PORTS = ['OUTPUT1', 'OUTPUT2', 'OUTPUT3', 'OUTPUT4'] + RELAY_PORTS = ['RELAY1', 'RELAY2', 'RELAY3', 'RELAY4', 'RELAY5', 'RELAY6', 'RELAY7', 'RELAY8', 'MOTORONOFF1', 'MOTORUPDOWN1', 'MOTORONOFF2', 'MOTORUPDOWN2', 'MOTORONOFF3', 'MOTORUPDOWN3', 'MOTORONOFF4', 'MOTORUPDOWN4'] + MOTOR_PORTS = ['MOTOR1', 'MOTOR2', 'MOTOR3', 'MOTOR4'] + +LED_PORTS = ['LED1', 'LED2', 'LED3', 'LED4', 'LED5', 'LED6', + 'LED7', 'LED8', 'LED9', 'LED10', 'LED11', 'LED12'] + +LOGICOP_PORTS = ['LOGICOP1', 'LOGICOP2', 'LOGICOP3', 'LOGICOP4'] + +VARIABLES = ['VAR1ORTVAR', 'VAR2ORR1VAR', 'VAR3ORR2VAR', + 'TVAR', 'R1VAR', 'R2VAR', + 'VAR1', 'VAR2', 'VAR3', 'VAR4', 'VAR5', 'VAR6', + 'VAR7', 'VAR8', 'VAR9', 'VAR10', 'VAR11', 'VAR12'] + +SETPOINTS = ['R1VARSETPOINT', 'R2VARSETPOINT'] + +THRESHOLDS = ['THRS1', 'THRS2', 'THRS3', 'THRS4', 'THRS5', + 'THRS2_1', 'THRS2_2', 'THRS2_3', 'THRS2_4', + 'THRS3_1', 'THRS3_2', 'THRS3_3', 'THRS3_4', + 'THRS4_1', 'THRS4_2', 'THRS4_3', 'THRS4_4'] + +S0_INPUTS = ['S0INPUT1', 'S0INPUT2', 'S0INPUT3', 'S0INPUT4'] + +VAR_UNITS = ['', 'LCN', 'NATIVE', + 'CELSIUS', '\u00b0CELSIUS', '\u00b0C', + 'KELVIN', '\u00b0KELVIN', '\u00b0K', + 'FAHRENHEIT', '\u00b0FAHRENHEIT', '\u00b0F' + 'LUX_T', 'LX_T', + 'LUX_I', 'LUX', 'LX', + 'M/S', 'METERPERSECOND', + '%', 'PERCENT', + 'PPM', + 'VOLT', 'V', + 'AMPERE', 'AMP', 'A', + 'DEGREE', '\u00b0'] diff --git a/homeassistant/components/lcn/sensor.py b/homeassistant/components/lcn/sensor.py new file mode 100755 index 00000000000..6b0a3aa3e4e --- /dev/null +++ b/homeassistant/components/lcn/sensor.py @@ -0,0 +1,117 @@ +"""Support for LCN sensors.""" +from homeassistant.components.lcn import LcnDevice, get_connection +from homeassistant.components.lcn.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'] + + +async def async_setup_platform(hass, hass_config, async_add_entities, + discovery_info=None): + """Set up the LCN 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 VARIABLES + SETPOINTS + THRESHOLDS + \ + S0_INPUTS: + device = LcnVariableSensor(config, address_connection) + else: # in LED_PORTS + LOGICOP_PORTS + device = LcnLedLogicSensor(config, address_connection) + + devices.append(device) + + async_add_entities(devices) + + +class LcnVariableSensor(LcnDevice): + """Representation of a LCN sensor for variables.""" + + def __init__(self, config, address_connection): + """Initialize the LCN sensor.""" + super().__init__(config, address_connection) + + self.variable = self.pypck.lcn_defs.Var[config[CONF_SOURCE]] + self.unit = self.pypck.lcn_defs.VarUnit[ + config[CONF_UNIT_OF_MEASUREMENT]] + + 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.variable)) + + @property + def state(self): + """Return the state of the entity.""" + return self._value + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, if any.""" + return self.unit.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.variable: + return + + self._value = (input_obj.get_value().to_var_unit(self.unit)) + self.async_schedule_update_ha_state() + + +class LcnLedLogicSensor(LcnDevice): + """Representation of a LCN sensor for leds and logicops.""" + + def __init__(self, config, address_connection): + """Initialize the LCN sensor.""" + super().__init__(config, address_connection) + + if config[CONF_SOURCE] in LED_PORTS: + self.source = self.pypck.lcn_defs.LedPort[config[CONF_SOURCE]] + else: + self.source = self.pypck.lcn_defs.LogicOpPort[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 state(self): + """Return the state of the entity.""" + 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.ModStatusLedsAndLogicOps): + return + + if self.source in self.pypck.lcn_defs.LedPort: + self._value = input_obj.get_led_state( + self.source.value).name.lower() + elif self.source in self.pypck.lcn_defs.LogicOpPort: + self._value = input_obj.get_logic_op_state( + self.source.value).name.lower() + + self.async_schedule_update_ha_state() From f396de623b355b4e9b05b54afe0d616e1664a18c Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sat, 16 Mar 2019 02:19:32 -0400 Subject: [PATCH 043/290] 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 83ffdd499e9..db9e73f3e1b 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -76,8 +76,3 @@ class FFmpegCamera(Camera): 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 ae7e849c234..c8d6721ac18 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -28,12 +28,14 @@ _LOGGER = logging.getLogger(__name__) 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 @@ class GenericCamera(Camera): 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 @@ class GenericCamera(Camera): 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 b0bd029a80c..da0bae7c50b 100644 --- a/homeassistant/components/camera/onvif.py +++ b/homeassistant/components/camera/onvif.py @@ -230,8 +230,3 @@ class ONVIFHassCamera(Camera): 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 179c2315bec470038f81768da3bf76f449ded70c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 23:17:41 -0700 Subject: [PATCH 044/290] 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 a8d2cbc35b9..5d5585ddd23 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 bed0bad7032..1ef8f651f39 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 65993daefa7..752ee21d614 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 ade86b9b8d105367e62f5f7515110b9ab6f4090f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 23:25:48 -0700 Subject: [PATCH 045/290] Version bump to 0.91.0dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 85097dcb652..f825e066f76 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 = 91 PATCH_VERSION = '0.dev0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) From b4f8d157d690f22d8961647b24155e88c2501c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Sat, 16 Mar 2019 09:13:45 +0200 Subject: [PATCH 046/290] Upgrade pytest to 4.3.1 (#22088) --- 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 9aa5d7d5c91..bf96353144c 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -13,5 +13,5 @@ pytest-aiohttp==0.3.0 pytest-cov==2.6.1 pytest-sugar==0.9.2 pytest-timeout==1.3.3 -pytest==4.3.0 +pytest==4.3.1 requests_mock==1.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 752ee21d614..32aec10fd15 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -14,7 +14,7 @@ pytest-aiohttp==0.3.0 pytest-cov==2.6.1 pytest-sugar==0.9.2 pytest-timeout==1.3.3 -pytest==4.3.0 +pytest==4.3.1 requests_mock==1.5.2 From 9e4bd88a0600f5d0a19ba03f5a80823f2ddb3b63 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sat, 16 Mar 2019 02:20:49 -0500 Subject: [PATCH 047/290] Bump amcrest to 1.2.7 for correct RTSP port (#22099) amcrest 1.2.7 now includes camera's configured RTSP port in generated URL. Necessary to make new stream component work correctly if camera does not use default RTSP port. --- homeassistant/components/amcrest/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index b11c263c56b..84dc0b5bb01 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -12,7 +12,7 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['amcrest==1.2.6'] +REQUIREMENTS = ['amcrest==1.2.7'] DEPENDENCIES = ['ffmpeg'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 1ef8f651f39..d5a4cde09ac 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.6 +amcrest==1.2.7 # homeassistant.components.androidtv.media_player androidtv==0.0.12 From 7807b409258864a2aa35e25f2f6aacc416429319 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sat, 16 Mar 2019 04:33:22 -0600 Subject: [PATCH 048/290] Add available property to Ambient PWS (#22092) * Add available property to Ambient PWS * Linting * remove blank line --- homeassistant/components/ambient_station/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 545415f9d5d..9ef2b3a5263 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -411,6 +411,13 @@ class AmbientWeatherEntity(Entity): self._state = None self._station_name = station_name + @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)) + @property def device_info(self): """Return device registry information for this entity.""" From 0466e43478f2bdf481122264c3240bb35fd2c563 Mon Sep 17 00:00:00 2001 From: Andre Lengwenus Date: Sat, 16 Mar 2019 16:31:58 +0100 Subject: [PATCH 049/290] Restricted temperature units for Celsius and Fahrenheit to the standardized units (#22108) --- homeassistant/components/lcn/const.py | 11 +++++++---- homeassistant/components/lcn/sensor.py | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/lcn/const.py b/homeassistant/components/lcn/const.py index c9ee92fb175..ee7a3a79cde 100644 --- a/homeassistant/components/lcn/const.py +++ b/homeassistant/components/lcn/const.py @@ -1,6 +1,9 @@ +# coding: utf-8 """Constants for the LCN component.""" import re +from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT + DOMAIN = 'lcn' DATA_LCN = 'lcn' DEFAULT_NAME = 'pchk' @@ -49,9 +52,9 @@ THRESHOLDS = ['THRS1', 'THRS2', 'THRS3', 'THRS4', 'THRS5', S0_INPUTS = ['S0INPUT1', 'S0INPUT2', 'S0INPUT3', 'S0INPUT4'] VAR_UNITS = ['', 'LCN', 'NATIVE', - 'CELSIUS', '\u00b0CELSIUS', '\u00b0C', - 'KELVIN', '\u00b0KELVIN', '\u00b0K', - 'FAHRENHEIT', '\u00b0FAHRENHEIT', '\u00b0F' + TEMP_CELSIUS, + '°K', + TEMP_FAHRENHEIT, 'LUX_T', 'LX_T', 'LUX_I', 'LUX', 'LX', 'M/S', 'METERPERSECOND', @@ -59,4 +62,4 @@ VAR_UNITS = ['', 'LCN', 'NATIVE', 'PPM', 'VOLT', 'V', 'AMPERE', 'AMP', 'A', - 'DEGREE', '\u00b0'] + 'DEGREE', '°'] diff --git a/homeassistant/components/lcn/sensor.py b/homeassistant/components/lcn/sensor.py index 6b0a3aa3e4e..e56c42de058 100755 --- a/homeassistant/components/lcn/sensor.py +++ b/homeassistant/components/lcn/sensor.py @@ -43,8 +43,8 @@ class LcnVariableSensor(LcnDevice): super().__init__(config, address_connection) self.variable = self.pypck.lcn_defs.Var[config[CONF_SOURCE]] - self.unit = self.pypck.lcn_defs.VarUnit[ - config[CONF_UNIT_OF_MEASUREMENT]] + self.unit = self.pypck.lcn_defs.VarUnit.parse( + config[CONF_UNIT_OF_MEASUREMENT]) self._value = None From c90f0d5bd66ef857962d5839fb2476738c9706b0 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 16 Mar 2019 16:32:51 +0100 Subject: [PATCH 050/290] 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 d66059c55a0..4fa24604edc 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -8,8 +8,9 @@ from homeassistant.components.binary_sensor import DOMAIN as COMP_BINARY_SENSOR 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 4423572682a24f8de7a832ab6ae46590594b7e1e Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 16 Mar 2019 16:34:31 +0100 Subject: [PATCH 051/290] 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 584be4c0c64..1a7c031638f 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -37,8 +37,8 @@ class OpenThermGateway(ClimateDevice): 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 @@ class OpenThermGateway(ClimateDevice): @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 d33cad0b2454824bdb42ee24640bacd3e5c87ded Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 16 Mar 2019 23:51:50 +0100 Subject: [PATCH 052/290] 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 4fa24604edc..acb277c0ef5 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -8,9 +8,8 @@ from homeassistant.components.binary_sensor import DOMAIN as COMP_BINARY_SENSOR 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 f5076188ef8831fd055882d2fc43c08060edcfb2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 16 Mar 2019 20:44:05 -0700 Subject: [PATCH 053/290] Consolidate all platforms that have no tests (#22096) * Consolidate * Fix tests * Update imports * Fix import * Use importlib because integration and package share name * Fix more tests * Update .coveragerc and CODEOWNERS --- .coveragerc | 732 +++++++++--------- CODEOWNERS | 144 ++-- .../components/acer_projector/__init__.py | 1 + .../switch.py} | 0 .../components/actiontec/__init__.py | 1 + .../device_tracker.py} | 0 .../components/aftership/__init__.py | 1 + .../aftership.py => aftership/sensor.py} | 0 .../components/airvisual/__init__.py | 1 + .../airvisual.py => airvisual/sensor.py} | 0 .../components/aladdin_connect/__init__.py | 1 + .../cover.py} | 0 .../components/alarmdotcom/__init__.py | 1 + .../alarm_control_panel.py} | 0 .../components/alpha_vantage/__init__.py | 1 + .../sensor.py} | 0 .../components/android_ip_webcam/__init__.py | 2 +- .../components/anel_pwrctrl/__init__.py | 1 + .../switch.py} | 0 homeassistant/components/anthemav/__init__.py | 1 + .../anthemav.py => anthemav/media_player.py} | 0 homeassistant/components/aquostv/__init__.py | 1 + .../aquostv.py => aquostv/media_player.py} | 0 homeassistant/components/arest/__init__.py | 1 + .../arest.py => arest/binary_sensor.py} | 0 .../{sensor/arest.py => arest/sensor.py} | 0 .../{switch/arest.py => arest/switch.py} | 0 homeassistant/components/aruba/__init__.py | 1 + .../aruba.py => aruba/device_tracker.py} | 0 homeassistant/components/arwn/__init__.py | 1 + .../{sensor/arwn.py => arwn/sensor.py} | 0 .../components/asterisk_cdr/__init__.py | 1 + .../mailbox.py} | 0 .../{sensor/asuswrt.py => asuswrt/sensor.py} | 0 homeassistant/components/avion/__init__.py | 1 + .../{light/avion.py => avion/light.py} | 5 +- homeassistant/components/axis/camera.py | 2 +- homeassistant/components/bbox/__init__.py | 1 + .../bbox.py => bbox/device_tracker.py} | 0 .../{sensor/bbox.py => bbox/sensor.py} | 0 homeassistant/components/bh1750/__init__.py | 1 + .../{sensor/bh1750.py => bh1750/sensor.py} | 0 homeassistant/components/bitcoin/__init__.py | 1 + .../{sensor/bitcoin.py => bitcoin/sensor.py} | 0 .../components/blinksticklight/__init__.py | 1 + .../light.py} | 0 homeassistant/components/blinkt/__init__.py | 1 + .../{light/blinkt.py => blinkt/light.py} | 3 +- .../components/blockchain/__init__.py | 1 + .../blockchain.py => blockchain/sensor.py} | 0 .../components/bluesound/__init__.py | 1 + .../media_player.py} | 0 .../bluetooth_le_tracker/__init__.py | 1 + .../device_tracker.py} | 0 .../components/bluetooth_tracker/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/bme280/__init__.py | 1 + .../{sensor/bme280.py => bme280/sensor.py} | 0 homeassistant/components/bme680/__init__.py | 1 + .../{sensor/bme680.py => bme680/sensor.py} | 3 +- homeassistant/components/bom/__init__.py | 1 + .../{weather/bom.py => bom/weather.py} | 0 homeassistant/components/braviatv/__init__.py | 1 + .../braviatv.py => braviatv/media_player.py} | 0 .../components/broadlink/__init__.py | 1 + .../broadlink.py => broadlink/sensor.py} | 0 .../broadlink.py => broadlink/switch.py} | 0 .../components/brottsplatskartan/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/brunt/__init__.py | 1 + .../{cover/brunt.py => brunt/cover.py} | 0 .../components/bt_home_hub_5/__init__.py | 1 + .../device_tracker.py} | 0 .../components/bt_smarthub/__init__.py | 1 + .../device_tracker.py} | 0 .../components/buienradar/__init__.py | 1 + .../buienradar.py => buienradar/sensor.py} | 2 +- .../buienradar.py => buienradar/weather.py} | 2 +- .../alarm_control_panel.py} | 0 .../{camera/canary.py => canary/camera.py} | 0 .../components/cert_expiry/__init__.py | 1 + .../cert_expiry.py => cert_expiry/sensor.py} | 0 homeassistant/components/channels/__init__.py | 1 + .../channels.py => channels/media_player.py} | 0 .../components/cisco_ios/__init__.py | 1 + .../device_tracker.py} | 0 .../components/citybikes/__init__.py | 1 + .../citybikes.py => citybikes/sensor.py} | 0 .../components/clementine/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/cmus/__init__.py | 1 + .../cmus.py => cmus/media_player.py} | 0 .../components/co2signal/__init__.py | 1 + .../co2signal.py => co2signal/sensor.py} | 0 .../coinbase.py => coinbase/sensor.py} | 0 .../comed_hourly_pricing/__init__.py | 1 + .../sensor.py} | 0 .../components/concord232/__init__.py | 1 + .../alarm_control_panel.py} | 0 .../binary_sensor.py} | 0 .../components/coolmaster/__init__.py | 1 + .../coolmaster.py => coolmaster/climate.py} | 0 homeassistant/components/cpuspeed/__init__.py | 1 + .../cpuspeed.py => cpuspeed/sensor.py} | 0 .../components/crimereports/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/cups/__init__.py | 1 + .../{sensor/cups.py => cups/sensor.py} | 5 +- .../components/currencylayer/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/ddwrt/__init__.py | 1 + .../ddwrt.py => ddwrt/device_tracker.py} | 0 homeassistant/components/decora/__init__.py | 1 + .../{light/decora.py => decora/light.py} | 3 +- .../components/decora_wifi/__init__.py | 1 + .../decora_wifi.py => decora_wifi/light.py} | 0 homeassistant/components/deluge/__init__.py | 1 + .../{sensor/deluge.py => deluge/sensor.py} | 0 .../{switch/deluge.py => deluge/switch.py} | 0 .../demo.py => demo/air_quality.py} | 0 .../demo.py => demo/alarm_control_panel.py} | 0 .../demo.py => demo/binary_sensor.py} | 0 .../demo.py => demo/device_tracker.py} | 0 .../demo.py => demo/image_processing.py} | 0 .../{mailbox/demo.py => demo/mailbox.py} | 0 .../{sensor/demo.py => demo/sensor.py} | 0 .../{switch/demo.py => demo/switch.py} | 0 .../{weather/demo.py => demo/weather.py} | 0 homeassistant/components/denon/__init__.py | 1 + .../denon.py => denon/media_player.py} | 0 homeassistant/components/denonavr/__init__.py | 1 + .../denonavr.py => denonavr/media_player.py} | 0 .../components/deutsche_bahn/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/dht/__init__.py | 1 + .../{sensor/dht.py => dht/sensor.py} | 0 .../components/digitalloggers/__init__.py | 1 + .../switch.py} | 0 homeassistant/components/discogs/__init__.py | 1 + .../{sensor/discogs.py => discogs/sensor.py} | 0 .../components/dlib_face_detect/__init__.py | 1 + .../image_processing.py} | 0 .../components/dlib_face_identify/__init__.py | 1 + .../image_processing.py} | 0 homeassistant/components/dlink/__init__.py | 1 + .../{switch/dlink.py => dlink/switch.py} | 0 homeassistant/components/dlna_dmr/__init__.py | 1 + .../dlna_dmr.py => dlna_dmr/media_player.py} | 0 homeassistant/components/dnsip/__init__.py | 1 + .../{sensor/dnsip.py => dnsip/sensor.py} | 0 .../dublin_bus_transport/__init__.py | 1 + .../sensor.py} | 0 .../components/duke_energy/__init__.py | 1 + .../duke_energy.py => duke_energy/sensor.py} | 0 homeassistant/components/dunehd/__init__.py | 1 + .../dunehd.py => dunehd/media_player.py} | 0 .../dwd_weather_warnings/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/ebox/__init__.py | 1 + .../{sensor/ebox.py => ebox/sensor.py} | 0 homeassistant/components/econet/__init__.py | 1 + .../econet.py => econet/water_heater.py} | 0 .../eddystone_temperature/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/edimax/__init__.py | 1 + .../{switch/edimax.py => edimax/switch.py} | 0 .../components/eliqonline/__init__.py | 1 + .../eliqonline.py => eliqonline/sensor.py} | 0 homeassistant/components/emby/__init__.py | 1 + .../emby.py => emby/media_player.py} | 0 homeassistant/components/emoncms/__init__.py | 1 + .../{sensor/emoncms.py => emoncms/sensor.py} | 0 .../components/enphase_envoy/__init__.py | 1 + .../sensor.py} | 0 .../components/envirophat/__init__.py | 1 + .../envirophat.py => envirophat/sensor.py} | 3 +- homeassistant/components/ephember/__init__.py | 1 + .../ephember.py => ephember/climate.py} | 0 homeassistant/components/epson/__init__.py | 1 + .../epson.py => epson/media_player.py} | 0 .../components/eq3btsmart/__init__.py | 1 + .../eq3btsmart.py => eq3btsmart/climate.py} | 0 .../components/etherscan/__init__.py | 1 + .../etherscan.py => etherscan/sensor.py} | 0 .../components/familyhub/__init__.py | 1 + .../familyhub.py => familyhub/camera.py} | 0 homeassistant/components/fedex/__init__.py | 1 + .../{sensor/fedex.py => fedex/sensor.py} | 0 .../{camera/ffmpeg.py => ffmpeg/camera.py} | 0 .../components/ffmpeg_motion/__init__.py | 1 + .../binary_sensor.py} | 0 .../components/ffmpeg_noise/__init__.py | 1 + .../binary_sensor.py} | 2 +- homeassistant/components/fints/__init__.py | 1 + .../{sensor/fints.py => fints/sensor.py} | 0 homeassistant/components/fitbit/__init__.py | 1 + .../{sensor/fitbit.py => fitbit/sensor.py} | 0 homeassistant/components/fixer/__init__.py | 1 + .../{sensor/fixer.py => fixer/sensor.py} | 0 homeassistant/components/flexit/__init__.py | 1 + .../{climate/flexit.py => flexit/climate.py} | 0 homeassistant/components/flic/__init__.py | 1 + .../flic.py => flic/binary_sensor.py} | 0 .../components/flunearyou/__init__.py | 1 + .../flunearyou.py => flunearyou/sensor.py} | 0 homeassistant/components/flux_led/__init__.py | 1 + .../{light/flux_led.py => flux_led/light.py} | 0 homeassistant/components/foscam/__init__.py | 1 + .../{camera/foscam.py => foscam/camera.py} | 0 homeassistant/components/fritz/__init__.py | 1 + .../fritz.py => fritz/device_tracker.py} | 0 .../fritzbox_callmonitor/__init__.py | 1 + .../sensor.py} | 0 .../fritzbox_netmonitor/__init__.py | 1 + .../sensor.py} | 0 .../components/fritzdect/__init__.py | 1 + .../fritzdect.py => fritzdect/switch.py} | 0 .../components/frontier_silicon/__init__.py | 1 + .../media_player.py} | 0 .../components/futurenow/__init__.py | 1 + .../futurenow.py => futurenow/light.py} | 0 homeassistant/components/garadget/__init__.py | 1 + .../{cover/garadget.py => garadget/cover.py} | 0 homeassistant/components/gearbest/__init__.py | 1 + .../gearbest.py => gearbest/sensor.py} | 0 homeassistant/components/geizhals/__init__.py | 1 + .../geizhals.py => geizhals/sensor.py} | 0 homeassistant/components/github/__init__.py | 1 + .../{sensor/github.py => github/sensor.py} | 0 .../components/gitlab_ci/__init__.py | 1 + .../gitlab_ci.py => gitlab_ci/sensor.py} | 0 homeassistant/components/gitter/__init__.py | 1 + .../{sensor/gitter.py => gitter/sensor.py} | 0 homeassistant/components/glances/__init__.py | 1 + .../{sensor/glances.py => glances/sensor.py} | 0 .../components/gogogate2/__init__.py | 1 + .../gogogate2.py => gogogate2/cover.py} | 0 .../components/google_maps/__init__.py | 1 + .../device_tracker.py} | 0 .../components/google_travel_time/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/gpmdp/__init__.py | 1 + .../gpmdp.py => gpmdp/media_player.py} | 0 homeassistant/components/gpsd/__init__.py | 1 + .../{sensor/gpsd.py => gpsd/sensor.py} | 0 .../components/gpslogger/__init__.py | 2 +- .../sensor.py} | 0 .../components/greenwave/__init__.py | 1 + .../greenwave.py => greenwave/light.py} | 0 .../components/gstreamer/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/gtfs/__init__.py | 1 + .../{sensor/gtfs.py => gtfs/sensor.py} | 0 homeassistant/components/gtt/__init__.py | 1 + .../{sensor/gtt.py => gtt/sensor.py} | 0 .../components/harman_kardon_avr/__init__.py | 1 + .../media_player.py} | 0 .../components/haveibeenpwned/__init__.py | 1 + .../sensor.py} | 0 .../components/heatmiser/__init__.py | 1 + .../heatmiser.py => heatmiser/climate.py} | 0 .../components/hikvision/__init__.py | 1 + .../binary_sensor.py} | 0 .../components/hikvisioncam/__init__.py | 1 + .../switch.py} | 0 .../components/hitron_coda/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/hook/__init__.py | 1 + .../{switch/hook.py => hook/switch.py} | 0 homeassistant/components/horizon/__init__.py | 1 + .../horizon.py => horizon/media_player.py} | 0 homeassistant/components/hp_ilo/__init__.py | 1 + .../{sensor/hp_ilo.py => hp_ilo/sensor.py} | 0 homeassistant/components/htu21d/__init__.py | 1 + .../{sensor/htu21d.py => htu21d/sensor.py} | 0 .../components/huawei_router/__init__.py | 1 + .../device_tracker.py} | 0 .../hunterdouglas_powerview/__init__.py | 1 + .../scene.py} | 0 homeassistant/components/hyperion/__init__.py | 1 + .../{light/hyperion.py => hyperion/light.py} | 0 homeassistant/components/ialarm/__init__.py | 1 + .../alarm_control_panel.py} | 0 homeassistant/components/icloud/__init__.py | 1 + .../icloud.py => icloud/device_tracker.py} | 0 homeassistant/components/iglo/__init__.py | 1 + .../{light/iglo.py => iglo/light.py} | 0 .../components/iliad_italy/__init__.py | 1 + .../iliad_italy.py => iliad_italy/sensor.py} | 0 homeassistant/components/imap/__init__.py | 1 + .../{sensor/imap.py => imap/sensor.py} | 0 .../influxdb.py => influxdb/sensor.py} | 0 .../irish_rail_transport/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/iss/__init__.py | 1 + .../iss.py => iss/binary_sensor.py} | 0 homeassistant/components/itunes/__init__.py | 1 + .../itunes.py => itunes/media_player.py} | 0 homeassistant/components/kankun/__init__.py | 1 + .../{switch/kankun.py => kankun/switch.py} | 0 .../components/keenetic_ndms2/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/kiwi/__init__.py | 1 + .../components/{lock/kiwi.py => kiwi/lock.py} | 0 homeassistant/components/kodi/__init__.py | 1 + .../kodi.py => kodi/media_player.py} | 0 homeassistant/components/kwb/__init__.py | 1 + .../{sensor/kwb.py => kwb/sensor.py} | 0 homeassistant/components/lacrosse/__init__.py | 1 + .../lacrosse.py => lacrosse/sensor.py} | 0 homeassistant/components/lastfm/__init__.py | 1 + .../{sensor/lastfm.py => lastfm/sensor.py} | 0 .../components/launch_library/__init__.py | 1 + .../sensor.py} | 0 .../components/lg_netcast/__init__.py | 1 + .../media_player.py} | 0 .../components/lg_soundbar/__init__.py | 1 + .../media_player.py} | 0 .../components/lifx_cloud/__init__.py | 1 + .../lifx_cloud.py => lifx_cloud/scene.py} | 0 .../components/lifx_legacy/__init__.py | 1 + .../lifx_legacy.py => lifx_legacy/light.py} | 0 .../components/limitlessled/__init__.py | 1 + .../limitlessled.py => limitlessled/light.py} | 0 .../components/linksys_ap/__init__.py | 1 + .../device_tracker.py} | 0 .../components/linksys_smart/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/linky/__init__.py | 1 + .../{sensor/linky.py => linky/sensor.py} | 0 .../components/linux_battery/__init__.py | 1 + .../sensor.py} | 0 .../components/liveboxplaytv/__init__.py | 1 + .../media_player.py} | 0 .../components/lockitron/__init__.py | 1 + .../{lock/lockitron.py => lockitron/lock.py} | 0 .../components/london_underground/__init__.py | 1 + .../sensor.py} | 0 .../components/loopenergy/__init__.py | 1 + .../loopenergy.py => loopenergy/sensor.py} | 0 homeassistant/components/luci/__init__.py | 1 + .../luci.py => luci/device_tracker.py} | 0 homeassistant/components/lw12wifi/__init__.py | 1 + .../{light/lw12wifi.py => lw12wifi/light.py} | 0 homeassistant/components/lyft/__init__.py | 1 + .../{sensor/lyft.py => lyft/sensor.py} | 0 .../components/magicseaweed/__init__.py | 1 + .../sensor.py} | 0 .../components/mediaroom/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/met/__init__.py | 1 + .../{weather/met.py => met/weather.py} | 0 .../components/metoffice/__init__.py | 1 + .../metoffice.py => metoffice/sensor.py} | 0 .../metoffice.py => metoffice/weather.py} | 2 +- homeassistant/components/miflora/__init__.py | 1 + .../{sensor/miflora.py => miflora/sensor.py} | 0 homeassistant/components/mikrotik/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/mill/__init__.py | 1 + .../{climate/mill.py => mill/climate.py} | 0 .../components/mitemp_bt/__init__.py | 1 + .../mitemp_bt.py => mitemp_bt/sensor.py} | 0 homeassistant/components/mjpeg/__init__.py | 1 + .../{camera/mjpeg.py => mjpeg/camera.py} | 0 .../components/modem_callerid/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/mopar/__init__.py | 1 + .../{sensor/mopar.py => mopar/sensor.py} | 0 homeassistant/components/mpchc/__init__.py | 1 + .../mpchc.py => mpchc/media_player.py} | 0 homeassistant/components/mpd/__init__.py | 1 + .../mpd.py => mpd/media_player.py} | 0 homeassistant/components/mvglive/__init__.py | 1 + .../{sensor/mvglive.py => mvglive/sensor.py} | 0 homeassistant/components/myq/__init__.py | 1 + .../components/{cover/myq.py => myq/cover.py} | 0 homeassistant/components/mystrom/__init__.py | 1 + .../mystrom.py => mystrom/binary_sensor.py} | 0 .../{light/mystrom.py => mystrom/light.py} | 0 .../{switch/mystrom.py => mystrom/switch.py} | 0 homeassistant/components/nad/__init__.py | 1 + .../nad.py => nad/media_player.py} | 0 homeassistant/components/nanoleaf/__init__.py | 1 + .../{light/nanoleaf.py => nanoleaf/light.py} | 0 .../nederlandse_spoorwegen/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/nello/__init__.py | 1 + .../{lock/nello.py => nello/lock.py} | 0 .../alarm_control_panel.py} | 0 .../binary_sensor.py} | 0 .../components/netatmo_public/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/netdata/__init__.py | 1 + .../{sensor/netdata.py => netdata/sensor.py} | 0 homeassistant/components/netgear/__init__.py | 1 + .../netgear.py => netgear/device_tracker.py} | 0 homeassistant/components/netio/__init__.py | 1 + .../{switch/netio.py => netio/switch.py} | 0 .../components/neurio_energy/__init__.py | 1 + .../sensor.py} | 0 .../components/niko_home_control/__init__.py | 1 + .../light.py} | 0 homeassistant/components/nilu/__init__.py | 1 + .../nilu.py => nilu/air_quality.py} | 0 .../components/nmap_tracker/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/nmbs/__init__.py | 1 + .../{sensor/nmbs.py => nmbs/sensor.py} | 0 .../components/noaa_tides/__init__.py | 1 + .../noaa_tides.py => noaa_tides/sensor.py} | 0 .../components/norway_air/__init__.py | 1 + .../air_quality.py} | 0 homeassistant/components/nuki/__init__.py | 1 + .../components/{lock/nuki.py => nuki/lock.py} | 0 homeassistant/components/nut/__init__.py | 1 + .../{sensor/nut.py => nut/sensor.py} | 0 .../nx584.py => nx584/alarm_control_panel.py} | 0 homeassistant/components/nzbget/__init__.py | 1 + .../{sensor/nzbget.py => nzbget/sensor.py} | 0 homeassistant/components/oem/__init__.py | 1 + .../{climate/oem.py => oem/climate.py} | 0 .../components/ohmconnect/__init__.py | 1 + .../ohmconnect.py => ohmconnect/sensor.py} | 0 homeassistant/components/onewire/__init__.py | 1 + .../{sensor/onewire.py => onewire/sensor.py} | 0 homeassistant/components/onkyo/__init__.py | 1 + .../onkyo.py => onkyo/media_player.py} | 0 homeassistant/components/onvif/__init__.py | 1 + .../{camera/onvif.py => onvif/camera.py} | 0 homeassistant/components/opencv/__init__.py | 1 + .../opencv.py => opencv/image_processing.py} | 0 homeassistant/components/openevse/__init__.py | 1 + .../openevse.py => openevse/sensor.py} | 0 .../components/openexchangerates/__init__.py | 1 + .../sensor.py} | 0 .../components/opengarage/__init__.py | 1 + .../opengarage.py => opengarage/cover.py} | 0 homeassistant/components/openhome/__init__.py | 1 + .../openhome.py => openhome/media_player.py} | 0 .../components/opensensemap/__init__.py | 1 + .../air_quality.py} | 0 homeassistant/components/opensky/__init__.py | 1 + .../{sensor/opensky.py => opensky/sensor.py} | 0 .../components/openweathermap/__init__.py | 1 + .../sensor.py} | 0 .../weather.py} | 0 homeassistant/components/opple/__init__.py | 1 + .../{light/opple.py => opple/light.py} | 0 homeassistant/components/orvibo/__init__.py | 1 + .../{switch/orvibo.py => orvibo/switch.py} | 0 .../components/osramlightify/__init__.py | 1 + .../light.py} | 0 homeassistant/components/otp/__init__.py | 1 + .../{sensor/otp.py => otp/sensor.py} | 0 .../components/panasonic_bluray/__init__.py | 1 + .../media_player.py} | 0 .../components/panasonic_viera/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/pandora/__init__.py | 1 + .../pandora.py => pandora/media_player.py} | 0 homeassistant/components/pencom/__init__.py | 1 + .../{switch/pencom.py => pencom/switch.py} | 0 .../components/philips_js/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/pi_hole/__init__.py | 1 + .../{sensor/pi_hole.py => pi_hole/sensor.py} | 0 homeassistant/components/piglow/__init__.py | 1 + .../{light/piglow.py => piglow/light.py} | 0 homeassistant/components/ping/__init__.py | 1 + .../ping.py => ping/binary_sensor.py} | 0 .../ping.py => ping/device_tracker.py} | 0 homeassistant/components/pioneer/__init__.py | 1 + .../pioneer.py => pioneer/media_player.py} | 0 homeassistant/components/pjlink/__init__.py | 1 + .../pjlink.py => pjlink/media_player.py} | 0 homeassistant/components/plex/__init__.py | 1 + .../plex.py => plex/media_player.py} | 0 .../{sensor/plex.py => plex/sensor.py} | 0 .../components/pocketcasts/__init__.py | 1 + .../pocketcasts.py => pocketcasts/sensor.py} | 0 homeassistant/components/pollen/__init__.py | 1 + .../{sensor/pollen.py => pollen/sensor.py} | 0 homeassistant/components/postnl/__init__.py | 1 + .../{sensor/postnl.py => postnl/sensor.py} | 0 .../components/prezzibenzina/__init__.py | 1 + .../sensor.py} | 0 .../components/proliphix/__init__.py | 1 + .../proliphix.py => proliphix/climate.py} | 0 homeassistant/components/proxy/__init__.py | 1 + .../{camera/proxy.py => proxy/camera.py} | 0 .../pulseaudio_loopback/__init__.py | 1 + .../switch.py} | 0 .../components/pushbullet/__init__.py | 1 + .../pushbullet.py => pushbullet/sensor.py} | 0 homeassistant/components/pvoutput/__init__.py | 1 + .../pvoutput.py => pvoutput/sensor.py} | 0 homeassistant/components/pyload/__init__.py | 1 + .../{sensor/pyload.py => pyload/sensor.py} | 0 .../components/qbittorrent/__init__.py | 1 + .../qbittorrent.py => qbittorrent/sensor.py} | 0 homeassistant/components/qnap/__init__.py | 1 + .../{sensor/qnap.py => qnap/sensor.py} | 0 homeassistant/components/qrcode/__init__.py | 1 + .../qrcode.py => qrcode/image_processing.py} | 0 .../components/quantum_gateway/__init__.py | 1 + .../device_tracker.py} | 0 .../components/radiotherm/__init__.py | 1 + .../radiotherm.py => radiotherm/climate.py} | 0 .../rainbird.py => rainbird/sensor.py} | 0 .../rainbird.py => rainbird/switch.py} | 0 homeassistant/components/raspyrfm/__init__.py | 1 + .../raspyrfm.py => raspyrfm/switch.py} | 0 .../components/recollect_waste/__init__.py | 1 + .../sensor.py} | 0 .../components/recswitch/__init__.py | 1 + .../recswitch.py => recswitch/switch.py} | 0 .../components/rejseplanen/__init__.py | 1 + .../rejseplanen.py => rejseplanen/sensor.py} | 0 .../{camera/ring.py => ring/camera.py} | 0 homeassistant/components/ripple/__init__.py | 1 + .../{sensor/ripple.py => ripple/sensor.py} | 0 .../components/ritassist/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/roomba/__init__.py | 1 + .../{vacuum/roomba.py => roomba/vacuum.py} | 0 homeassistant/components/rova/__init__.py | 1 + .../{sensor/rova.py => rova/sensor.py} | 0 .../components/rpi_camera/__init__.py | 1 + .../rpi_camera.py => rpi_camera/camera.py} | 0 .../components/rpi_gpio_pwm/__init__.py | 1 + .../rpi_gpio_pwm.py => rpi_gpio_pwm/light.py} | 0 homeassistant/components/rpi_rf/__init__.py | 1 + .../{switch/rpi_rf.py => rpi_rf/switch.py} | 3 +- homeassistant/components/rtorrent/__init__.py | 1 + .../rtorrent.py => rtorrent/sensor.py} | 0 .../components/russound_rio/__init__.py | 1 + .../media_player.py} | 0 .../components/russound_rnet/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/ruter/__init__.py | 1 + .../{sensor/ruter.py => ruter/sensor.py} | 0 homeassistant/components/scrape/__init__.py | 1 + .../{sensor/scrape.py => scrape/sensor.py} | 0 homeassistant/components/sensehat/__init__.py | 1 + .../{light/sensehat.py => sensehat/light.py} | 0 .../sensehat.py => sensehat/sensor.py} | 0 homeassistant/components/sensibo/__init__.py | 1 + .../sensibo.py => sensibo/climate.py} | 0 homeassistant/components/serial/__init__.py | 1 + .../{sensor/serial.py => serial/sensor.py} | 0 .../components/serial_pm/__init__.py | 1 + .../serial_pm.py => serial_pm/sensor.py} | 0 homeassistant/components/sesame/__init__.py | 1 + .../{lock/sesame.py => sesame/lock.py} | 0 .../components/seven_segments/__init__.py | 1 + .../image_processing.py} | 0 .../components/seventeentrack/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/shodan/__init__.py | 1 + .../{sensor/shodan.py => shodan/sensor.py} | 0 homeassistant/components/sht31/__init__.py | 1 + .../{sensor/sht31.py => sht31/sensor.py} | 0 homeassistant/components/sky_hub/__init__.py | 1 + .../sky_hub.py => sky_hub/device_tracker.py} | 0 .../components/skybeacon/__init__.py | 1 + .../skybeacon.py => skybeacon/sensor.py} | 0 homeassistant/components/sma/__init__.py | 1 + .../{sensor/sma.py => sma/sensor.py} | 0 homeassistant/components/snapcast/__init__.py | 1 + .../snapcast.py => snapcast/media_player.py} | 0 homeassistant/components/snmp/__init__.py | 1 + .../snmp.py => snmp/device_tracker.py} | 0 .../{sensor/snmp.py => snmp/sensor.py} | 0 .../{switch/snmp.py => snmp/switch.py} | 0 homeassistant/components/sochain/__init__.py | 1 + .../{sensor/sochain.py => sochain/sensor.py} | 0 .../components/socialblade/__init__.py | 1 + .../socialblade.py => socialblade/sensor.py} | 0 .../components/solaredge/__init__.py | 1 + .../solaredge.py => solaredge/sensor.py} | 0 homeassistant/components/songpal/__init__.py | 1 + .../songpal.py => songpal/media_player.py} | 0 .../components/sony_projector/__init__.py | 1 + .../switch.py} | 0 .../spc.py => spc/alarm_control_panel.py} | 0 .../spc.py => spc/binary_sensor.py} | 0 .../components/spotcrime/__init__.py | 1 + .../spotcrime.py => spotcrime/sensor.py} | 0 homeassistant/components/spotify/__init__.py | 1 + .../spotify.py => spotify/media_player.py} | 0 .../components/squeezebox/__init__.py | 1 + .../media_player.py} | 0 .../components/starlingbank/__init__.py | 1 + .../sensor.py} | 0 .../components/steam_online/__init__.py | 1 + .../sensor.py} | 0 .../components/supervisord/__init__.py | 1 + .../supervisord.py => supervisord/sensor.py} | 0 .../swiss_hydrological_data/__init__.py | 1 + .../sensor.py} | 0 .../swiss_public_transport/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/swisscom/__init__.py | 1 + .../device_tracker.py} | 0 .../components/switchbot/__init__.py | 1 + .../switchbot.py => switchbot/switch.py} | 0 .../components/switchmate/__init__.py | 1 + .../switchmate.py => switchmate/switch.py} | 0 homeassistant/components/syncthru/__init__.py | 1 + .../syncthru.py => syncthru/sensor.py} | 0 homeassistant/components/synology/__init__.py | 1 + .../synology.py => synology/camera.py} | 0 .../components/synology_srm/__init__.py | 1 + .../device_tracker.py} | 0 .../components/synologydsm/__init__.py | 1 + .../synologydsm.py => synologydsm/sensor.py} | 0 .../components/systemmonitor/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/sytadin/__init__.py | 1 + .../{sensor/sytadin.py => sytadin/sensor.py} | 0 .../components/tank_utility/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/tapsaff/__init__.py | 1 + .../tapsaff.py => tapsaff/binary_sensor.py} | 0 homeassistant/components/tautulli/__init__.py | 1 + .../tautulli.py => tautulli/sensor.py} | 0 homeassistant/components/ted5000/__init__.py | 1 + .../{sensor/ted5000.py => ted5000/sensor.py} | 0 homeassistant/components/telnet/__init__.py | 1 + .../{switch/telnet.py => telnet/switch.py} | 0 homeassistant/components/temper/__init__.py | 1 + .../{sensor/temper.py => temper/sensor.py} | 0 .../components/tensorflow/__init__.py | 1 + .../image_processing.py} | 0 .../components/thermoworks_smoke/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/thomson/__init__.py | 1 + .../thomson.py => thomson/device_tracker.py} | 0 homeassistant/components/tikteck/__init__.py | 1 + .../{light/tikteck.py => tikteck/light.py} | 0 homeassistant/components/tile/__init__.py | 1 + .../tile.py => tile/device_tracker.py} | 0 homeassistant/components/todoist/__init__.py | 1 + .../todoist.py => todoist/calendar.py} | 0 homeassistant/components/torque/__init__.py | 1 + .../{sensor/torque.py => torque/sensor.py} | 0 .../components/totalconnect/__init__.py | 1 + .../alarm_control_panel.py} | 0 .../components/touchline/__init__.py | 1 + .../touchline.py => touchline/climate.py} | 0 homeassistant/components/traccar/__init__.py | 1 + .../traccar.py => traccar/device_tracker.py} | 0 homeassistant/components/trackr/__init__.py | 1 + .../trackr.py => trackr/device_tracker.py} | 0 .../trafikverket_weatherstation/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/travisci/__init__.py | 1 + .../travisci.py => travisci/sensor.py} | 0 homeassistant/components/twitch/__init__.py | 1 + .../{sensor/twitch.py => twitch/sensor.py} | 0 homeassistant/components/ubee/__init__.py | 1 + .../ubee.py => ubee/device_tracker.py} | 0 homeassistant/components/uber/__init__.py | 1 + .../{sensor/uber.py => uber/sensor.py} | 0 homeassistant/components/ubus/__init__.py | 1 + .../ubus.py => ubus/device_tracker.py} | 0 .../components/ue_smart_radio/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/ups/__init__.py | 1 + .../{sensor/ups.py => ups/sensor.py} | 0 .../components/uptimerobot/__init__.py | 1 + .../binary_sensor.py} | 0 homeassistant/components/uscis/__init__.py | 1 + .../{sensor/uscis.py => uscis/sensor.py} | 0 .../components/vasttrafik/__init__.py | 1 + .../vasttrafik.py => vasttrafik/sensor.py} | 0 homeassistant/components/venstar/__init__.py | 1 + .../venstar.py => venstar/climate.py} | 0 homeassistant/components/vesync/__init__.py | 1 + .../{switch/vesync.py => vesync/switch.py} | 0 .../components/viaggiatreno/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/vizio/__init__.py | 1 + .../vizio.py => vizio/media_player.py} | 0 homeassistant/components/vlc/__init__.py | 1 + .../vlc.py => vlc/media_player.py} | 0 .../components/volkszaehler/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/volumio/__init__.py | 1 + .../volumio.py => volumio/media_player.py} | 0 homeassistant/components/waqi/__init__.py | 1 + .../{sensor/waqi.py => waqi/sensor.py} | 0 .../components/waze_travel_time/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/whois/__init__.py | 1 + .../{sensor/whois.py => whois/sensor.py} | 0 .../components/worldtidesinfo/__init__.py | 1 + .../sensor.py} | 0 .../components/worxlandroid/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/x10/__init__.py | 1 + .../components/{light/x10.py => x10/light.py} | 0 .../components/xbox_live/__init__.py | 1 + .../xbox_live.py => xbox_live/sensor.py} | 0 homeassistant/components/xeoma/__init__.py | 1 + .../{camera/xeoma.py => xeoma/camera.py} | 0 homeassistant/components/xfinity/__init__.py | 1 + .../xfinity.py => xfinity/device_tracker.py} | 0 homeassistant/components/xiaomi/__init__.py | 1 + .../{camera/xiaomi.py => xiaomi/camera.py} | 0 .../components/xiaomi_tv/__init__.py | 1 + .../media_player.py} | 0 .../components/yale_smart_alarm/__init__.py | 1 + .../alarm_control_panel.py} | 0 .../components/yamaha_musiccast/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/yeelight/__init__.py | 1 + .../{light/yeelight.py => yeelight/light.py} | 0 .../components/yeelightsunflower/__init__.py | 1 + .../light.py} | 0 homeassistant/components/yi/__init__.py | 1 + .../components/{camera/yi.py => yi/camera.py} | 0 homeassistant/components/zamg/__init__.py | 1 + .../{sensor/zamg.py => zamg/sensor.py} | 0 .../{weather/zamg.py => zamg/weather.py} | 2 +- homeassistant/components/zengge/__init__.py | 1 + .../{light/zengge.py => zengge/light.py} | 0 .../components/zestimate/__init__.py | 1 + .../zestimate.py => zestimate/sensor.py} | 0 .../components/zhong_hong/__init__.py | 1 + .../zhong_hong.py => zhong_hong/climate.py} | 0 .../components/ziggo_mediabox_xl/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/zoneminder/camera.py | 2 +- requirements_all.txt | 637 ++++++++------- requirements_test_all.txt | 25 +- script/gen_requirements_all.py | 3 +- .../alarm_control_panel/test_manual.py | 2 +- tests/components/device_tracker/test_init.py | 2 +- .../components/image_processing/test_init.py | 8 +- ...oldindicator.py => test_mold_indicator.py} | 0 742 files changed, 1145 insertions(+), 795 deletions(-) create mode 100644 homeassistant/components/acer_projector/__init__.py rename homeassistant/components/{switch/acer_projector.py => acer_projector/switch.py} (100%) create mode 100644 homeassistant/components/actiontec/__init__.py rename homeassistant/components/{device_tracker/actiontec.py => actiontec/device_tracker.py} (100%) create mode 100644 homeassistant/components/aftership/__init__.py rename homeassistant/components/{sensor/aftership.py => aftership/sensor.py} (100%) create mode 100644 homeassistant/components/airvisual/__init__.py rename homeassistant/components/{sensor/airvisual.py => airvisual/sensor.py} (100%) create mode 100644 homeassistant/components/aladdin_connect/__init__.py rename homeassistant/components/{cover/aladdin_connect.py => aladdin_connect/cover.py} (100%) create mode 100644 homeassistant/components/alarmdotcom/__init__.py rename homeassistant/components/{alarm_control_panel/alarmdotcom.py => alarmdotcom/alarm_control_panel.py} (100%) create mode 100644 homeassistant/components/alpha_vantage/__init__.py rename homeassistant/components/{sensor/alpha_vantage.py => alpha_vantage/sensor.py} (100%) create mode 100644 homeassistant/components/anel_pwrctrl/__init__.py rename homeassistant/components/{switch/anel_pwrctrl.py => anel_pwrctrl/switch.py} (100%) create mode 100644 homeassistant/components/anthemav/__init__.py rename homeassistant/components/{media_player/anthemav.py => anthemav/media_player.py} (100%) create mode 100644 homeassistant/components/aquostv/__init__.py rename homeassistant/components/{media_player/aquostv.py => aquostv/media_player.py} (100%) create mode 100644 homeassistant/components/arest/__init__.py rename homeassistant/components/{binary_sensor/arest.py => arest/binary_sensor.py} (100%) rename homeassistant/components/{sensor/arest.py => arest/sensor.py} (100%) rename homeassistant/components/{switch/arest.py => arest/switch.py} (100%) create mode 100644 homeassistant/components/aruba/__init__.py rename homeassistant/components/{device_tracker/aruba.py => aruba/device_tracker.py} (100%) create mode 100644 homeassistant/components/arwn/__init__.py rename homeassistant/components/{sensor/arwn.py => arwn/sensor.py} (100%) create mode 100644 homeassistant/components/asterisk_cdr/__init__.py rename homeassistant/components/{mailbox/asterisk_cdr.py => asterisk_cdr/mailbox.py} (100%) rename homeassistant/components/{sensor/asuswrt.py => asuswrt/sensor.py} (100%) create mode 100644 homeassistant/components/avion/__init__.py rename homeassistant/components/{light/avion.py => avion/light.py} (97%) create mode 100644 homeassistant/components/bbox/__init__.py rename homeassistant/components/{device_tracker/bbox.py => bbox/device_tracker.py} (100%) rename homeassistant/components/{sensor/bbox.py => bbox/sensor.py} (100%) create mode 100644 homeassistant/components/bh1750/__init__.py rename homeassistant/components/{sensor/bh1750.py => bh1750/sensor.py} (100%) create mode 100644 homeassistant/components/bitcoin/__init__.py rename homeassistant/components/{sensor/bitcoin.py => bitcoin/sensor.py} (100%) create mode 100644 homeassistant/components/blinksticklight/__init__.py rename homeassistant/components/{light/blinksticklight.py => blinksticklight/light.py} (100%) create mode 100644 homeassistant/components/blinkt/__init__.py rename homeassistant/components/{light/blinkt.py => blinkt/light.py} (98%) create mode 100644 homeassistant/components/blockchain/__init__.py rename homeassistant/components/{sensor/blockchain.py => blockchain/sensor.py} (100%) create mode 100644 homeassistant/components/bluesound/__init__.py rename homeassistant/components/{media_player/bluesound.py => bluesound/media_player.py} (100%) create mode 100644 homeassistant/components/bluetooth_le_tracker/__init__.py rename homeassistant/components/{device_tracker/bluetooth_le_tracker.py => bluetooth_le_tracker/device_tracker.py} (100%) create mode 100644 homeassistant/components/bluetooth_tracker/__init__.py rename homeassistant/components/{device_tracker/bluetooth_tracker.py => bluetooth_tracker/device_tracker.py} (100%) create mode 100644 homeassistant/components/bme280/__init__.py rename homeassistant/components/{sensor/bme280.py => bme280/sensor.py} (100%) create mode 100644 homeassistant/components/bme680/__init__.py rename homeassistant/components/{sensor/bme680.py => bme680/sensor.py} (99%) create mode 100644 homeassistant/components/bom/__init__.py rename homeassistant/components/{weather/bom.py => bom/weather.py} (100%) create mode 100644 homeassistant/components/braviatv/__init__.py rename homeassistant/components/{media_player/braviatv.py => braviatv/media_player.py} (100%) create mode 100644 homeassistant/components/broadlink/__init__.py rename homeassistant/components/{sensor/broadlink.py => broadlink/sensor.py} (100%) rename homeassistant/components/{switch/broadlink.py => broadlink/switch.py} (100%) create mode 100644 homeassistant/components/brottsplatskartan/__init__.py rename homeassistant/components/{sensor/brottsplatskartan.py => brottsplatskartan/sensor.py} (100%) create mode 100644 homeassistant/components/brunt/__init__.py rename homeassistant/components/{cover/brunt.py => brunt/cover.py} (100%) create mode 100644 homeassistant/components/bt_home_hub_5/__init__.py rename homeassistant/components/{device_tracker/bt_home_hub_5.py => bt_home_hub_5/device_tracker.py} (100%) create mode 100644 homeassistant/components/bt_smarthub/__init__.py rename homeassistant/components/{device_tracker/bt_smarthub.py => bt_smarthub/device_tracker.py} (100%) create mode 100644 homeassistant/components/buienradar/__init__.py rename homeassistant/components/{sensor/buienradar.py => buienradar/sensor.py} (99%) rename homeassistant/components/{weather/buienradar.py => buienradar/weather.py} (98%) rename homeassistant/components/{alarm_control_panel/canary.py => canary/alarm_control_panel.py} (100%) rename homeassistant/components/{camera/canary.py => canary/camera.py} (100%) create mode 100644 homeassistant/components/cert_expiry/__init__.py rename homeassistant/components/{sensor/cert_expiry.py => cert_expiry/sensor.py} (100%) create mode 100644 homeassistant/components/channels/__init__.py rename homeassistant/components/{media_player/channels.py => channels/media_player.py} (100%) create mode 100644 homeassistant/components/cisco_ios/__init__.py rename homeassistant/components/{device_tracker/cisco_ios.py => cisco_ios/device_tracker.py} (100%) create mode 100644 homeassistant/components/citybikes/__init__.py rename homeassistant/components/{sensor/citybikes.py => citybikes/sensor.py} (100%) create mode 100644 homeassistant/components/clementine/__init__.py rename homeassistant/components/{media_player/clementine.py => clementine/media_player.py} (100%) create mode 100644 homeassistant/components/cmus/__init__.py rename homeassistant/components/{media_player/cmus.py => cmus/media_player.py} (100%) create mode 100644 homeassistant/components/co2signal/__init__.py rename homeassistant/components/{sensor/co2signal.py => co2signal/sensor.py} (100%) rename homeassistant/components/{sensor/coinbase.py => coinbase/sensor.py} (100%) create mode 100644 homeassistant/components/comed_hourly_pricing/__init__.py rename homeassistant/components/{sensor/comed_hourly_pricing.py => comed_hourly_pricing/sensor.py} (100%) create mode 100644 homeassistant/components/concord232/__init__.py rename homeassistant/components/{alarm_control_panel/concord232.py => concord232/alarm_control_panel.py} (100%) rename homeassistant/components/{binary_sensor/concord232.py => concord232/binary_sensor.py} (100%) create mode 100644 homeassistant/components/coolmaster/__init__.py rename homeassistant/components/{climate/coolmaster.py => coolmaster/climate.py} (100%) create mode 100644 homeassistant/components/cpuspeed/__init__.py rename homeassistant/components/{sensor/cpuspeed.py => cpuspeed/sensor.py} (100%) create mode 100644 homeassistant/components/crimereports/__init__.py rename homeassistant/components/{sensor/crimereports.py => crimereports/sensor.py} (100%) create mode 100644 homeassistant/components/cups/__init__.py rename homeassistant/components/{sensor/cups.py => cups/sensor.py} (97%) create mode 100644 homeassistant/components/currencylayer/__init__.py rename homeassistant/components/{sensor/currencylayer.py => currencylayer/sensor.py} (100%) create mode 100644 homeassistant/components/ddwrt/__init__.py rename homeassistant/components/{device_tracker/ddwrt.py => ddwrt/device_tracker.py} (100%) create mode 100644 homeassistant/components/decora/__init__.py rename homeassistant/components/{light/decora.py => decora/light.py} (98%) create mode 100644 homeassistant/components/decora_wifi/__init__.py rename homeassistant/components/{light/decora_wifi.py => decora_wifi/light.py} (100%) create mode 100644 homeassistant/components/deluge/__init__.py rename homeassistant/components/{sensor/deluge.py => deluge/sensor.py} (100%) rename homeassistant/components/{switch/deluge.py => deluge/switch.py} (100%) rename homeassistant/components/{air_quality/demo.py => demo/air_quality.py} (100%) rename homeassistant/components/{alarm_control_panel/demo.py => demo/alarm_control_panel.py} (100%) rename homeassistant/components/{binary_sensor/demo.py => demo/binary_sensor.py} (100%) rename homeassistant/components/{device_tracker/demo.py => demo/device_tracker.py} (100%) rename homeassistant/components/{image_processing/demo.py => demo/image_processing.py} (100%) rename homeassistant/components/{mailbox/demo.py => demo/mailbox.py} (100%) rename homeassistant/components/{sensor/demo.py => demo/sensor.py} (100%) rename homeassistant/components/{switch/demo.py => demo/switch.py} (100%) rename homeassistant/components/{weather/demo.py => demo/weather.py} (100%) create mode 100644 homeassistant/components/denon/__init__.py rename homeassistant/components/{media_player/denon.py => denon/media_player.py} (100%) create mode 100644 homeassistant/components/denonavr/__init__.py rename homeassistant/components/{media_player/denonavr.py => denonavr/media_player.py} (100%) create mode 100644 homeassistant/components/deutsche_bahn/__init__.py rename homeassistant/components/{sensor/deutsche_bahn.py => deutsche_bahn/sensor.py} (100%) create mode 100644 homeassistant/components/dht/__init__.py rename homeassistant/components/{sensor/dht.py => dht/sensor.py} (100%) create mode 100644 homeassistant/components/digitalloggers/__init__.py rename homeassistant/components/{switch/digitalloggers.py => digitalloggers/switch.py} (100%) create mode 100644 homeassistant/components/discogs/__init__.py rename homeassistant/components/{sensor/discogs.py => discogs/sensor.py} (100%) create mode 100644 homeassistant/components/dlib_face_detect/__init__.py rename homeassistant/components/{image_processing/dlib_face_detect.py => dlib_face_detect/image_processing.py} (100%) create mode 100644 homeassistant/components/dlib_face_identify/__init__.py rename homeassistant/components/{image_processing/dlib_face_identify.py => dlib_face_identify/image_processing.py} (100%) create mode 100644 homeassistant/components/dlink/__init__.py rename homeassistant/components/{switch/dlink.py => dlink/switch.py} (100%) create mode 100644 homeassistant/components/dlna_dmr/__init__.py rename homeassistant/components/{media_player/dlna_dmr.py => dlna_dmr/media_player.py} (100%) create mode 100644 homeassistant/components/dnsip/__init__.py rename homeassistant/components/{sensor/dnsip.py => dnsip/sensor.py} (100%) create mode 100644 homeassistant/components/dublin_bus_transport/__init__.py rename homeassistant/components/{sensor/dublin_bus_transport.py => dublin_bus_transport/sensor.py} (100%) create mode 100644 homeassistant/components/duke_energy/__init__.py rename homeassistant/components/{sensor/duke_energy.py => duke_energy/sensor.py} (100%) create mode 100644 homeassistant/components/dunehd/__init__.py rename homeassistant/components/{media_player/dunehd.py => dunehd/media_player.py} (100%) create mode 100644 homeassistant/components/dwd_weather_warnings/__init__.py rename homeassistant/components/{sensor/dwd_weather_warnings.py => dwd_weather_warnings/sensor.py} (100%) create mode 100644 homeassistant/components/ebox/__init__.py rename homeassistant/components/{sensor/ebox.py => ebox/sensor.py} (100%) create mode 100644 homeassistant/components/econet/__init__.py rename homeassistant/components/{water_heater/econet.py => econet/water_heater.py} (100%) create mode 100644 homeassistant/components/eddystone_temperature/__init__.py rename homeassistant/components/{sensor/eddystone_temperature.py => eddystone_temperature/sensor.py} (100%) create mode 100644 homeassistant/components/edimax/__init__.py rename homeassistant/components/{switch/edimax.py => edimax/switch.py} (100%) create mode 100644 homeassistant/components/eliqonline/__init__.py rename homeassistant/components/{sensor/eliqonline.py => eliqonline/sensor.py} (100%) create mode 100644 homeassistant/components/emby/__init__.py rename homeassistant/components/{media_player/emby.py => emby/media_player.py} (100%) create mode 100644 homeassistant/components/emoncms/__init__.py rename homeassistant/components/{sensor/emoncms.py => emoncms/sensor.py} (100%) create mode 100644 homeassistant/components/enphase_envoy/__init__.py rename homeassistant/components/{sensor/enphase_envoy.py => enphase_envoy/sensor.py} (100%) create mode 100644 homeassistant/components/envirophat/__init__.py rename homeassistant/components/{sensor/envirophat.py => envirophat/sensor.py} (98%) create mode 100644 homeassistant/components/ephember/__init__.py rename homeassistant/components/{climate/ephember.py => ephember/climate.py} (100%) create mode 100644 homeassistant/components/epson/__init__.py rename homeassistant/components/{media_player/epson.py => epson/media_player.py} (100%) create mode 100644 homeassistant/components/eq3btsmart/__init__.py rename homeassistant/components/{climate/eq3btsmart.py => eq3btsmart/climate.py} (100%) create mode 100644 homeassistant/components/etherscan/__init__.py rename homeassistant/components/{sensor/etherscan.py => etherscan/sensor.py} (100%) create mode 100644 homeassistant/components/familyhub/__init__.py rename homeassistant/components/{camera/familyhub.py => familyhub/camera.py} (100%) create mode 100644 homeassistant/components/fedex/__init__.py rename homeassistant/components/{sensor/fedex.py => fedex/sensor.py} (100%) rename homeassistant/components/{camera/ffmpeg.py => ffmpeg/camera.py} (100%) create mode 100644 homeassistant/components/ffmpeg_motion/__init__.py rename homeassistant/components/{binary_sensor/ffmpeg_motion.py => ffmpeg_motion/binary_sensor.py} (100%) create mode 100644 homeassistant/components/ffmpeg_noise/__init__.py rename homeassistant/components/{binary_sensor/ffmpeg_noise.py => ffmpeg_noise/binary_sensor.py} (97%) create mode 100644 homeassistant/components/fints/__init__.py rename homeassistant/components/{sensor/fints.py => fints/sensor.py} (100%) create mode 100644 homeassistant/components/fitbit/__init__.py rename homeassistant/components/{sensor/fitbit.py => fitbit/sensor.py} (100%) create mode 100644 homeassistant/components/fixer/__init__.py rename homeassistant/components/{sensor/fixer.py => fixer/sensor.py} (100%) create mode 100644 homeassistant/components/flexit/__init__.py rename homeassistant/components/{climate/flexit.py => flexit/climate.py} (100%) create mode 100644 homeassistant/components/flic/__init__.py rename homeassistant/components/{binary_sensor/flic.py => flic/binary_sensor.py} (100%) create mode 100644 homeassistant/components/flunearyou/__init__.py rename homeassistant/components/{sensor/flunearyou.py => flunearyou/sensor.py} (100%) create mode 100644 homeassistant/components/flux_led/__init__.py rename homeassistant/components/{light/flux_led.py => flux_led/light.py} (100%) create mode 100644 homeassistant/components/foscam/__init__.py rename homeassistant/components/{camera/foscam.py => foscam/camera.py} (100%) create mode 100644 homeassistant/components/fritz/__init__.py rename homeassistant/components/{device_tracker/fritz.py => fritz/device_tracker.py} (100%) create mode 100644 homeassistant/components/fritzbox_callmonitor/__init__.py rename homeassistant/components/{sensor/fritzbox_callmonitor.py => fritzbox_callmonitor/sensor.py} (100%) create mode 100644 homeassistant/components/fritzbox_netmonitor/__init__.py rename homeassistant/components/{sensor/fritzbox_netmonitor.py => fritzbox_netmonitor/sensor.py} (100%) create mode 100644 homeassistant/components/fritzdect/__init__.py rename homeassistant/components/{switch/fritzdect.py => fritzdect/switch.py} (100%) create mode 100644 homeassistant/components/frontier_silicon/__init__.py rename homeassistant/components/{media_player/frontier_silicon.py => frontier_silicon/media_player.py} (100%) create mode 100644 homeassistant/components/futurenow/__init__.py rename homeassistant/components/{light/futurenow.py => futurenow/light.py} (100%) create mode 100644 homeassistant/components/garadget/__init__.py rename homeassistant/components/{cover/garadget.py => garadget/cover.py} (100%) create mode 100644 homeassistant/components/gearbest/__init__.py rename homeassistant/components/{sensor/gearbest.py => gearbest/sensor.py} (100%) create mode 100644 homeassistant/components/geizhals/__init__.py rename homeassistant/components/{sensor/geizhals.py => geizhals/sensor.py} (100%) create mode 100644 homeassistant/components/github/__init__.py rename homeassistant/components/{sensor/github.py => github/sensor.py} (100%) create mode 100644 homeassistant/components/gitlab_ci/__init__.py rename homeassistant/components/{sensor/gitlab_ci.py => gitlab_ci/sensor.py} (100%) create mode 100644 homeassistant/components/gitter/__init__.py rename homeassistant/components/{sensor/gitter.py => gitter/sensor.py} (100%) create mode 100644 homeassistant/components/glances/__init__.py rename homeassistant/components/{sensor/glances.py => glances/sensor.py} (100%) create mode 100644 homeassistant/components/gogogate2/__init__.py rename homeassistant/components/{cover/gogogate2.py => gogogate2/cover.py} (100%) create mode 100644 homeassistant/components/google_maps/__init__.py rename homeassistant/components/{device_tracker/google_maps.py => google_maps/device_tracker.py} (100%) create mode 100644 homeassistant/components/google_travel_time/__init__.py rename homeassistant/components/{sensor/google_travel_time.py => google_travel_time/sensor.py} (100%) create mode 100644 homeassistant/components/gpmdp/__init__.py rename homeassistant/components/{media_player/gpmdp.py => gpmdp/media_player.py} (100%) create mode 100644 homeassistant/components/gpsd/__init__.py rename homeassistant/components/{sensor/gpsd.py => gpsd/sensor.py} (100%) rename homeassistant/components/{sensor/greeneye_monitor.py => greeneye_monitor/sensor.py} (100%) create mode 100644 homeassistant/components/greenwave/__init__.py rename homeassistant/components/{light/greenwave.py => greenwave/light.py} (100%) create mode 100644 homeassistant/components/gstreamer/__init__.py rename homeassistant/components/{media_player/gstreamer.py => gstreamer/media_player.py} (100%) create mode 100644 homeassistant/components/gtfs/__init__.py rename homeassistant/components/{sensor/gtfs.py => gtfs/sensor.py} (100%) create mode 100644 homeassistant/components/gtt/__init__.py rename homeassistant/components/{sensor/gtt.py => gtt/sensor.py} (100%) create mode 100644 homeassistant/components/harman_kardon_avr/__init__.py rename homeassistant/components/{media_player/harman_kardon_avr.py => harman_kardon_avr/media_player.py} (100%) create mode 100644 homeassistant/components/haveibeenpwned/__init__.py rename homeassistant/components/{sensor/haveibeenpwned.py => haveibeenpwned/sensor.py} (100%) create mode 100644 homeassistant/components/heatmiser/__init__.py rename homeassistant/components/{climate/heatmiser.py => heatmiser/climate.py} (100%) create mode 100644 homeassistant/components/hikvision/__init__.py rename homeassistant/components/{binary_sensor/hikvision.py => hikvision/binary_sensor.py} (100%) create mode 100644 homeassistant/components/hikvisioncam/__init__.py rename homeassistant/components/{switch/hikvisioncam.py => hikvisioncam/switch.py} (100%) create mode 100644 homeassistant/components/hitron_coda/__init__.py rename homeassistant/components/{device_tracker/hitron_coda.py => hitron_coda/device_tracker.py} (100%) create mode 100644 homeassistant/components/hook/__init__.py rename homeassistant/components/{switch/hook.py => hook/switch.py} (100%) create mode 100644 homeassistant/components/horizon/__init__.py rename homeassistant/components/{media_player/horizon.py => horizon/media_player.py} (100%) create mode 100644 homeassistant/components/hp_ilo/__init__.py rename homeassistant/components/{sensor/hp_ilo.py => hp_ilo/sensor.py} (100%) create mode 100644 homeassistant/components/htu21d/__init__.py rename homeassistant/components/{sensor/htu21d.py => htu21d/sensor.py} (100%) create mode 100644 homeassistant/components/huawei_router/__init__.py rename homeassistant/components/{device_tracker/huawei_router.py => huawei_router/device_tracker.py} (100%) create mode 100644 homeassistant/components/hunterdouglas_powerview/__init__.py rename homeassistant/components/{scene/hunterdouglas_powerview.py => hunterdouglas_powerview/scene.py} (100%) create mode 100644 homeassistant/components/hyperion/__init__.py rename homeassistant/components/{light/hyperion.py => hyperion/light.py} (100%) create mode 100644 homeassistant/components/ialarm/__init__.py rename homeassistant/components/{alarm_control_panel/ialarm.py => ialarm/alarm_control_panel.py} (100%) create mode 100644 homeassistant/components/icloud/__init__.py rename homeassistant/components/{device_tracker/icloud.py => icloud/device_tracker.py} (100%) create mode 100644 homeassistant/components/iglo/__init__.py rename homeassistant/components/{light/iglo.py => iglo/light.py} (100%) create mode 100644 homeassistant/components/iliad_italy/__init__.py rename homeassistant/components/{sensor/iliad_italy.py => iliad_italy/sensor.py} (100%) create mode 100644 homeassistant/components/imap/__init__.py rename homeassistant/components/{sensor/imap.py => imap/sensor.py} (100%) rename homeassistant/components/{sensor/influxdb.py => influxdb/sensor.py} (100%) create mode 100644 homeassistant/components/irish_rail_transport/__init__.py rename homeassistant/components/{sensor/irish_rail_transport.py => irish_rail_transport/sensor.py} (100%) create mode 100644 homeassistant/components/iss/__init__.py rename homeassistant/components/{binary_sensor/iss.py => iss/binary_sensor.py} (100%) create mode 100644 homeassistant/components/itunes/__init__.py rename homeassistant/components/{media_player/itunes.py => itunes/media_player.py} (100%) create mode 100644 homeassistant/components/kankun/__init__.py rename homeassistant/components/{switch/kankun.py => kankun/switch.py} (100%) create mode 100644 homeassistant/components/keenetic_ndms2/__init__.py rename homeassistant/components/{device_tracker/keenetic_ndms2.py => keenetic_ndms2/device_tracker.py} (100%) create mode 100644 homeassistant/components/kiwi/__init__.py rename homeassistant/components/{lock/kiwi.py => kiwi/lock.py} (100%) create mode 100644 homeassistant/components/kodi/__init__.py rename homeassistant/components/{media_player/kodi.py => kodi/media_player.py} (100%) create mode 100644 homeassistant/components/kwb/__init__.py rename homeassistant/components/{sensor/kwb.py => kwb/sensor.py} (100%) create mode 100644 homeassistant/components/lacrosse/__init__.py rename homeassistant/components/{sensor/lacrosse.py => lacrosse/sensor.py} (100%) create mode 100644 homeassistant/components/lastfm/__init__.py rename homeassistant/components/{sensor/lastfm.py => lastfm/sensor.py} (100%) create mode 100644 homeassistant/components/launch_library/__init__.py rename homeassistant/components/{sensor/launch_library.py => launch_library/sensor.py} (100%) create mode 100644 homeassistant/components/lg_netcast/__init__.py rename homeassistant/components/{media_player/lg_netcast.py => lg_netcast/media_player.py} (100%) create mode 100644 homeassistant/components/lg_soundbar/__init__.py rename homeassistant/components/{media_player/lg_soundbar.py => lg_soundbar/media_player.py} (100%) create mode 100644 homeassistant/components/lifx_cloud/__init__.py rename homeassistant/components/{scene/lifx_cloud.py => lifx_cloud/scene.py} (100%) create mode 100644 homeassistant/components/lifx_legacy/__init__.py rename homeassistant/components/{light/lifx_legacy.py => lifx_legacy/light.py} (100%) create mode 100644 homeassistant/components/limitlessled/__init__.py rename homeassistant/components/{light/limitlessled.py => limitlessled/light.py} (100%) create mode 100644 homeassistant/components/linksys_ap/__init__.py rename homeassistant/components/{device_tracker/linksys_ap.py => linksys_ap/device_tracker.py} (100%) create mode 100644 homeassistant/components/linksys_smart/__init__.py rename homeassistant/components/{device_tracker/linksys_smart.py => linksys_smart/device_tracker.py} (100%) create mode 100644 homeassistant/components/linky/__init__.py rename homeassistant/components/{sensor/linky.py => linky/sensor.py} (100%) create mode 100644 homeassistant/components/linux_battery/__init__.py rename homeassistant/components/{sensor/linux_battery.py => linux_battery/sensor.py} (100%) create mode 100644 homeassistant/components/liveboxplaytv/__init__.py rename homeassistant/components/{media_player/liveboxplaytv.py => liveboxplaytv/media_player.py} (100%) create mode 100644 homeassistant/components/lockitron/__init__.py rename homeassistant/components/{lock/lockitron.py => lockitron/lock.py} (100%) create mode 100644 homeassistant/components/london_underground/__init__.py rename homeassistant/components/{sensor/london_underground.py => london_underground/sensor.py} (100%) create mode 100644 homeassistant/components/loopenergy/__init__.py rename homeassistant/components/{sensor/loopenergy.py => loopenergy/sensor.py} (100%) create mode 100644 homeassistant/components/luci/__init__.py rename homeassistant/components/{device_tracker/luci.py => luci/device_tracker.py} (100%) create mode 100644 homeassistant/components/lw12wifi/__init__.py rename homeassistant/components/{light/lw12wifi.py => lw12wifi/light.py} (100%) create mode 100644 homeassistant/components/lyft/__init__.py rename homeassistant/components/{sensor/lyft.py => lyft/sensor.py} (100%) create mode 100644 homeassistant/components/magicseaweed/__init__.py rename homeassistant/components/{sensor/magicseaweed.py => magicseaweed/sensor.py} (100%) create mode 100644 homeassistant/components/mediaroom/__init__.py rename homeassistant/components/{media_player/mediaroom.py => mediaroom/media_player.py} (100%) create mode 100644 homeassistant/components/met/__init__.py rename homeassistant/components/{weather/met.py => met/weather.py} (100%) create mode 100644 homeassistant/components/metoffice/__init__.py rename homeassistant/components/{sensor/metoffice.py => metoffice/sensor.py} (100%) rename homeassistant/components/{weather/metoffice.py => metoffice/weather.py} (98%) create mode 100644 homeassistant/components/miflora/__init__.py rename homeassistant/components/{sensor/miflora.py => miflora/sensor.py} (100%) create mode 100644 homeassistant/components/mikrotik/__init__.py rename homeassistant/components/{device_tracker/mikrotik.py => mikrotik/device_tracker.py} (100%) create mode 100644 homeassistant/components/mill/__init__.py rename homeassistant/components/{climate/mill.py => mill/climate.py} (100%) create mode 100644 homeassistant/components/mitemp_bt/__init__.py rename homeassistant/components/{sensor/mitemp_bt.py => mitemp_bt/sensor.py} (100%) create mode 100644 homeassistant/components/mjpeg/__init__.py rename homeassistant/components/{camera/mjpeg.py => mjpeg/camera.py} (100%) create mode 100644 homeassistant/components/modem_callerid/__init__.py rename homeassistant/components/{sensor/modem_callerid.py => modem_callerid/sensor.py} (100%) create mode 100644 homeassistant/components/mopar/__init__.py rename homeassistant/components/{sensor/mopar.py => mopar/sensor.py} (100%) create mode 100644 homeassistant/components/mpchc/__init__.py rename homeassistant/components/{media_player/mpchc.py => mpchc/media_player.py} (100%) create mode 100644 homeassistant/components/mpd/__init__.py rename homeassistant/components/{media_player/mpd.py => mpd/media_player.py} (100%) create mode 100644 homeassistant/components/mvglive/__init__.py rename homeassistant/components/{sensor/mvglive.py => mvglive/sensor.py} (100%) create mode 100644 homeassistant/components/myq/__init__.py rename homeassistant/components/{cover/myq.py => myq/cover.py} (100%) create mode 100644 homeassistant/components/mystrom/__init__.py rename homeassistant/components/{binary_sensor/mystrom.py => mystrom/binary_sensor.py} (100%) rename homeassistant/components/{light/mystrom.py => mystrom/light.py} (100%) rename homeassistant/components/{switch/mystrom.py => mystrom/switch.py} (100%) create mode 100644 homeassistant/components/nad/__init__.py rename homeassistant/components/{media_player/nad.py => nad/media_player.py} (100%) create mode 100644 homeassistant/components/nanoleaf/__init__.py rename homeassistant/components/{light/nanoleaf.py => nanoleaf/light.py} (100%) create mode 100644 homeassistant/components/nederlandse_spoorwegen/__init__.py rename homeassistant/components/{sensor/nederlandse_spoorwegen.py => nederlandse_spoorwegen/sensor.py} (100%) create mode 100644 homeassistant/components/nello/__init__.py rename homeassistant/components/{lock/nello.py => nello/lock.py} (100%) rename homeassistant/components/{alarm_control_panel/ness_alarm.py => ness_alarm/alarm_control_panel.py} (100%) rename homeassistant/components/{binary_sensor/ness_alarm.py => ness_alarm/binary_sensor.py} (100%) create mode 100644 homeassistant/components/netatmo_public/__init__.py rename homeassistant/components/{sensor/netatmo_public.py => netatmo_public/sensor.py} (100%) create mode 100644 homeassistant/components/netdata/__init__.py rename homeassistant/components/{sensor/netdata.py => netdata/sensor.py} (100%) create mode 100644 homeassistant/components/netgear/__init__.py rename homeassistant/components/{device_tracker/netgear.py => netgear/device_tracker.py} (100%) create mode 100644 homeassistant/components/netio/__init__.py rename homeassistant/components/{switch/netio.py => netio/switch.py} (100%) create mode 100644 homeassistant/components/neurio_energy/__init__.py rename homeassistant/components/{sensor/neurio_energy.py => neurio_energy/sensor.py} (100%) create mode 100644 homeassistant/components/niko_home_control/__init__.py rename homeassistant/components/{light/niko_home_control.py => niko_home_control/light.py} (100%) create mode 100644 homeassistant/components/nilu/__init__.py rename homeassistant/components/{air_quality/nilu.py => nilu/air_quality.py} (100%) create mode 100644 homeassistant/components/nmap_tracker/__init__.py rename homeassistant/components/{device_tracker/nmap_tracker.py => nmap_tracker/device_tracker.py} (100%) create mode 100644 homeassistant/components/nmbs/__init__.py rename homeassistant/components/{sensor/nmbs.py => nmbs/sensor.py} (100%) create mode 100644 homeassistant/components/noaa_tides/__init__.py rename homeassistant/components/{sensor/noaa_tides.py => noaa_tides/sensor.py} (100%) create mode 100644 homeassistant/components/norway_air/__init__.py rename homeassistant/components/{air_quality/norway_air.py => norway_air/air_quality.py} (100%) create mode 100644 homeassistant/components/nuki/__init__.py rename homeassistant/components/{lock/nuki.py => nuki/lock.py} (100%) create mode 100644 homeassistant/components/nut/__init__.py rename homeassistant/components/{sensor/nut.py => nut/sensor.py} (100%) rename homeassistant/components/{alarm_control_panel/nx584.py => nx584/alarm_control_panel.py} (100%) create mode 100644 homeassistant/components/nzbget/__init__.py rename homeassistant/components/{sensor/nzbget.py => nzbget/sensor.py} (100%) create mode 100644 homeassistant/components/oem/__init__.py rename homeassistant/components/{climate/oem.py => oem/climate.py} (100%) create mode 100644 homeassistant/components/ohmconnect/__init__.py rename homeassistant/components/{sensor/ohmconnect.py => ohmconnect/sensor.py} (100%) create mode 100644 homeassistant/components/onewire/__init__.py rename homeassistant/components/{sensor/onewire.py => onewire/sensor.py} (100%) create mode 100644 homeassistant/components/onkyo/__init__.py rename homeassistant/components/{media_player/onkyo.py => onkyo/media_player.py} (100%) create mode 100644 homeassistant/components/onvif/__init__.py rename homeassistant/components/{camera/onvif.py => onvif/camera.py} (100%) create mode 100644 homeassistant/components/opencv/__init__.py rename homeassistant/components/{image_processing/opencv.py => opencv/image_processing.py} (100%) create mode 100644 homeassistant/components/openevse/__init__.py rename homeassistant/components/{sensor/openevse.py => openevse/sensor.py} (100%) create mode 100644 homeassistant/components/openexchangerates/__init__.py rename homeassistant/components/{sensor/openexchangerates.py => openexchangerates/sensor.py} (100%) create mode 100644 homeassistant/components/opengarage/__init__.py rename homeassistant/components/{cover/opengarage.py => opengarage/cover.py} (100%) create mode 100644 homeassistant/components/openhome/__init__.py rename homeassistant/components/{media_player/openhome.py => openhome/media_player.py} (100%) create mode 100644 homeassistant/components/opensensemap/__init__.py rename homeassistant/components/{air_quality/opensensemap.py => opensensemap/air_quality.py} (100%) create mode 100644 homeassistant/components/opensky/__init__.py rename homeassistant/components/{sensor/opensky.py => opensky/sensor.py} (100%) create mode 100644 homeassistant/components/openweathermap/__init__.py rename homeassistant/components/{sensor/openweathermap.py => openweathermap/sensor.py} (100%) rename homeassistant/components/{weather/openweathermap.py => openweathermap/weather.py} (100%) create mode 100644 homeassistant/components/opple/__init__.py rename homeassistant/components/{light/opple.py => opple/light.py} (100%) create mode 100644 homeassistant/components/orvibo/__init__.py rename homeassistant/components/{switch/orvibo.py => orvibo/switch.py} (100%) create mode 100644 homeassistant/components/osramlightify/__init__.py rename homeassistant/components/{light/osramlightify.py => osramlightify/light.py} (100%) create mode 100644 homeassistant/components/otp/__init__.py rename homeassistant/components/{sensor/otp.py => otp/sensor.py} (100%) create mode 100644 homeassistant/components/panasonic_bluray/__init__.py rename homeassistant/components/{media_player/panasonic_bluray.py => panasonic_bluray/media_player.py} (100%) create mode 100644 homeassistant/components/panasonic_viera/__init__.py rename homeassistant/components/{media_player/panasonic_viera.py => panasonic_viera/media_player.py} (100%) create mode 100644 homeassistant/components/pandora/__init__.py rename homeassistant/components/{media_player/pandora.py => pandora/media_player.py} (100%) create mode 100644 homeassistant/components/pencom/__init__.py rename homeassistant/components/{switch/pencom.py => pencom/switch.py} (100%) create mode 100644 homeassistant/components/philips_js/__init__.py rename homeassistant/components/{media_player/philips_js.py => philips_js/media_player.py} (100%) create mode 100644 homeassistant/components/pi_hole/__init__.py rename homeassistant/components/{sensor/pi_hole.py => pi_hole/sensor.py} (100%) create mode 100644 homeassistant/components/piglow/__init__.py rename homeassistant/components/{light/piglow.py => piglow/light.py} (100%) create mode 100644 homeassistant/components/ping/__init__.py rename homeassistant/components/{binary_sensor/ping.py => ping/binary_sensor.py} (100%) rename homeassistant/components/{device_tracker/ping.py => ping/device_tracker.py} (100%) create mode 100644 homeassistant/components/pioneer/__init__.py rename homeassistant/components/{media_player/pioneer.py => pioneer/media_player.py} (100%) create mode 100644 homeassistant/components/pjlink/__init__.py rename homeassistant/components/{media_player/pjlink.py => pjlink/media_player.py} (100%) create mode 100644 homeassistant/components/plex/__init__.py rename homeassistant/components/{media_player/plex.py => plex/media_player.py} (100%) rename homeassistant/components/{sensor/plex.py => plex/sensor.py} (100%) create mode 100644 homeassistant/components/pocketcasts/__init__.py rename homeassistant/components/{sensor/pocketcasts.py => pocketcasts/sensor.py} (100%) create mode 100644 homeassistant/components/pollen/__init__.py rename homeassistant/components/{sensor/pollen.py => pollen/sensor.py} (100%) create mode 100644 homeassistant/components/postnl/__init__.py rename homeassistant/components/{sensor/postnl.py => postnl/sensor.py} (100%) create mode 100644 homeassistant/components/prezzibenzina/__init__.py rename homeassistant/components/{sensor/prezzibenzina.py => prezzibenzina/sensor.py} (100%) create mode 100644 homeassistant/components/proliphix/__init__.py rename homeassistant/components/{climate/proliphix.py => proliphix/climate.py} (100%) create mode 100644 homeassistant/components/proxy/__init__.py rename homeassistant/components/{camera/proxy.py => proxy/camera.py} (100%) create mode 100644 homeassistant/components/pulseaudio_loopback/__init__.py rename homeassistant/components/{switch/pulseaudio_loopback.py => pulseaudio_loopback/switch.py} (100%) create mode 100644 homeassistant/components/pushbullet/__init__.py rename homeassistant/components/{sensor/pushbullet.py => pushbullet/sensor.py} (100%) create mode 100644 homeassistant/components/pvoutput/__init__.py rename homeassistant/components/{sensor/pvoutput.py => pvoutput/sensor.py} (100%) create mode 100644 homeassistant/components/pyload/__init__.py rename homeassistant/components/{sensor/pyload.py => pyload/sensor.py} (100%) create mode 100644 homeassistant/components/qbittorrent/__init__.py rename homeassistant/components/{sensor/qbittorrent.py => qbittorrent/sensor.py} (100%) create mode 100644 homeassistant/components/qnap/__init__.py rename homeassistant/components/{sensor/qnap.py => qnap/sensor.py} (100%) create mode 100644 homeassistant/components/qrcode/__init__.py rename homeassistant/components/{image_processing/qrcode.py => qrcode/image_processing.py} (100%) create mode 100644 homeassistant/components/quantum_gateway/__init__.py rename homeassistant/components/{device_tracker/quantum_gateway.py => quantum_gateway/device_tracker.py} (100%) create mode 100644 homeassistant/components/radiotherm/__init__.py rename homeassistant/components/{climate/radiotherm.py => radiotherm/climate.py} (100%) rename homeassistant/components/{sensor/rainbird.py => rainbird/sensor.py} (100%) rename homeassistant/components/{switch/rainbird.py => rainbird/switch.py} (100%) create mode 100644 homeassistant/components/raspyrfm/__init__.py rename homeassistant/components/{switch/raspyrfm.py => raspyrfm/switch.py} (100%) create mode 100644 homeassistant/components/recollect_waste/__init__.py rename homeassistant/components/{sensor/recollect_waste.py => recollect_waste/sensor.py} (100%) create mode 100644 homeassistant/components/recswitch/__init__.py rename homeassistant/components/{switch/recswitch.py => recswitch/switch.py} (100%) create mode 100644 homeassistant/components/rejseplanen/__init__.py rename homeassistant/components/{sensor/rejseplanen.py => rejseplanen/sensor.py} (100%) rename homeassistant/components/{camera/ring.py => ring/camera.py} (100%) create mode 100644 homeassistant/components/ripple/__init__.py rename homeassistant/components/{sensor/ripple.py => ripple/sensor.py} (100%) create mode 100644 homeassistant/components/ritassist/__init__.py rename homeassistant/components/{device_tracker/ritassist.py => ritassist/device_tracker.py} (100%) create mode 100644 homeassistant/components/roomba/__init__.py rename homeassistant/components/{vacuum/roomba.py => roomba/vacuum.py} (100%) create mode 100644 homeassistant/components/rova/__init__.py rename homeassistant/components/{sensor/rova.py => rova/sensor.py} (100%) create mode 100644 homeassistant/components/rpi_camera/__init__.py rename homeassistant/components/{camera/rpi_camera.py => rpi_camera/camera.py} (100%) create mode 100644 homeassistant/components/rpi_gpio_pwm/__init__.py rename homeassistant/components/{light/rpi_gpio_pwm.py => rpi_gpio_pwm/light.py} (100%) create mode 100644 homeassistant/components/rpi_rf/__init__.py rename homeassistant/components/{switch/rpi_rf.py => rpi_rf/switch.py} (98%) create mode 100644 homeassistant/components/rtorrent/__init__.py rename homeassistant/components/{sensor/rtorrent.py => rtorrent/sensor.py} (100%) create mode 100644 homeassistant/components/russound_rio/__init__.py rename homeassistant/components/{media_player/russound_rio.py => russound_rio/media_player.py} (100%) create mode 100644 homeassistant/components/russound_rnet/__init__.py rename homeassistant/components/{media_player/russound_rnet.py => russound_rnet/media_player.py} (100%) create mode 100644 homeassistant/components/ruter/__init__.py rename homeassistant/components/{sensor/ruter.py => ruter/sensor.py} (100%) create mode 100644 homeassistant/components/scrape/__init__.py rename homeassistant/components/{sensor/scrape.py => scrape/sensor.py} (100%) create mode 100644 homeassistant/components/sensehat/__init__.py rename homeassistant/components/{light/sensehat.py => sensehat/light.py} (100%) rename homeassistant/components/{sensor/sensehat.py => sensehat/sensor.py} (100%) create mode 100644 homeassistant/components/sensibo/__init__.py rename homeassistant/components/{climate/sensibo.py => sensibo/climate.py} (100%) create mode 100644 homeassistant/components/serial/__init__.py rename homeassistant/components/{sensor/serial.py => serial/sensor.py} (100%) create mode 100644 homeassistant/components/serial_pm/__init__.py rename homeassistant/components/{sensor/serial_pm.py => serial_pm/sensor.py} (100%) create mode 100644 homeassistant/components/sesame/__init__.py rename homeassistant/components/{lock/sesame.py => sesame/lock.py} (100%) create mode 100644 homeassistant/components/seven_segments/__init__.py rename homeassistant/components/{image_processing/seven_segments.py => seven_segments/image_processing.py} (100%) create mode 100644 homeassistant/components/seventeentrack/__init__.py rename homeassistant/components/{sensor/seventeentrack.py => seventeentrack/sensor.py} (100%) create mode 100644 homeassistant/components/shodan/__init__.py rename homeassistant/components/{sensor/shodan.py => shodan/sensor.py} (100%) create mode 100644 homeassistant/components/sht31/__init__.py rename homeassistant/components/{sensor/sht31.py => sht31/sensor.py} (100%) create mode 100644 homeassistant/components/sky_hub/__init__.py rename homeassistant/components/{device_tracker/sky_hub.py => sky_hub/device_tracker.py} (100%) create mode 100644 homeassistant/components/skybeacon/__init__.py rename homeassistant/components/{sensor/skybeacon.py => skybeacon/sensor.py} (100%) create mode 100644 homeassistant/components/sma/__init__.py rename homeassistant/components/{sensor/sma.py => sma/sensor.py} (100%) create mode 100644 homeassistant/components/snapcast/__init__.py rename homeassistant/components/{media_player/snapcast.py => snapcast/media_player.py} (100%) create mode 100644 homeassistant/components/snmp/__init__.py rename homeassistant/components/{device_tracker/snmp.py => snmp/device_tracker.py} (100%) rename homeassistant/components/{sensor/snmp.py => snmp/sensor.py} (100%) rename homeassistant/components/{switch/snmp.py => snmp/switch.py} (100%) create mode 100644 homeassistant/components/sochain/__init__.py rename homeassistant/components/{sensor/sochain.py => sochain/sensor.py} (100%) create mode 100644 homeassistant/components/socialblade/__init__.py rename homeassistant/components/{sensor/socialblade.py => socialblade/sensor.py} (100%) create mode 100644 homeassistant/components/solaredge/__init__.py rename homeassistant/components/{sensor/solaredge.py => solaredge/sensor.py} (100%) create mode 100644 homeassistant/components/songpal/__init__.py rename homeassistant/components/{media_player/songpal.py => songpal/media_player.py} (100%) create mode 100644 homeassistant/components/sony_projector/__init__.py rename homeassistant/components/{switch/sony_projector.py => sony_projector/switch.py} (100%) rename homeassistant/components/{alarm_control_panel/spc.py => spc/alarm_control_panel.py} (100%) rename homeassistant/components/{binary_sensor/spc.py => spc/binary_sensor.py} (100%) create mode 100644 homeassistant/components/spotcrime/__init__.py rename homeassistant/components/{sensor/spotcrime.py => spotcrime/sensor.py} (100%) create mode 100644 homeassistant/components/spotify/__init__.py rename homeassistant/components/{media_player/spotify.py => spotify/media_player.py} (100%) create mode 100644 homeassistant/components/squeezebox/__init__.py rename homeassistant/components/{media_player/squeezebox.py => squeezebox/media_player.py} (100%) create mode 100644 homeassistant/components/starlingbank/__init__.py rename homeassistant/components/{sensor/starlingbank.py => starlingbank/sensor.py} (100%) create mode 100644 homeassistant/components/steam_online/__init__.py rename homeassistant/components/{sensor/steam_online.py => steam_online/sensor.py} (100%) create mode 100644 homeassistant/components/supervisord/__init__.py rename homeassistant/components/{sensor/supervisord.py => supervisord/sensor.py} (100%) create mode 100644 homeassistant/components/swiss_hydrological_data/__init__.py rename homeassistant/components/{sensor/swiss_hydrological_data.py => swiss_hydrological_data/sensor.py} (100%) create mode 100644 homeassistant/components/swiss_public_transport/__init__.py rename homeassistant/components/{sensor/swiss_public_transport.py => swiss_public_transport/sensor.py} (100%) create mode 100644 homeassistant/components/swisscom/__init__.py rename homeassistant/components/{device_tracker/swisscom.py => swisscom/device_tracker.py} (100%) create mode 100644 homeassistant/components/switchbot/__init__.py rename homeassistant/components/{switch/switchbot.py => switchbot/switch.py} (100%) create mode 100644 homeassistant/components/switchmate/__init__.py rename homeassistant/components/{switch/switchmate.py => switchmate/switch.py} (100%) create mode 100644 homeassistant/components/syncthru/__init__.py rename homeassistant/components/{sensor/syncthru.py => syncthru/sensor.py} (100%) create mode 100644 homeassistant/components/synology/__init__.py rename homeassistant/components/{camera/synology.py => synology/camera.py} (100%) create mode 100644 homeassistant/components/synology_srm/__init__.py rename homeassistant/components/{device_tracker/synology_srm.py => synology_srm/device_tracker.py} (100%) create mode 100644 homeassistant/components/synologydsm/__init__.py rename homeassistant/components/{sensor/synologydsm.py => synologydsm/sensor.py} (100%) create mode 100644 homeassistant/components/systemmonitor/__init__.py rename homeassistant/components/{sensor/systemmonitor.py => systemmonitor/sensor.py} (100%) create mode 100644 homeassistant/components/sytadin/__init__.py rename homeassistant/components/{sensor/sytadin.py => sytadin/sensor.py} (100%) create mode 100644 homeassistant/components/tank_utility/__init__.py rename homeassistant/components/{sensor/tank_utility.py => tank_utility/sensor.py} (100%) create mode 100644 homeassistant/components/tapsaff/__init__.py rename homeassistant/components/{binary_sensor/tapsaff.py => tapsaff/binary_sensor.py} (100%) create mode 100644 homeassistant/components/tautulli/__init__.py rename homeassistant/components/{sensor/tautulli.py => tautulli/sensor.py} (100%) create mode 100644 homeassistant/components/ted5000/__init__.py rename homeassistant/components/{sensor/ted5000.py => ted5000/sensor.py} (100%) create mode 100644 homeassistant/components/telnet/__init__.py rename homeassistant/components/{switch/telnet.py => telnet/switch.py} (100%) create mode 100644 homeassistant/components/temper/__init__.py rename homeassistant/components/{sensor/temper.py => temper/sensor.py} (100%) create mode 100644 homeassistant/components/tensorflow/__init__.py rename homeassistant/components/{image_processing/tensorflow.py => tensorflow/image_processing.py} (100%) create mode 100644 homeassistant/components/thermoworks_smoke/__init__.py rename homeassistant/components/{sensor/thermoworks_smoke.py => thermoworks_smoke/sensor.py} (100%) create mode 100644 homeassistant/components/thomson/__init__.py rename homeassistant/components/{device_tracker/thomson.py => thomson/device_tracker.py} (100%) create mode 100644 homeassistant/components/tikteck/__init__.py rename homeassistant/components/{light/tikteck.py => tikteck/light.py} (100%) create mode 100644 homeassistant/components/tile/__init__.py rename homeassistant/components/{device_tracker/tile.py => tile/device_tracker.py} (100%) create mode 100644 homeassistant/components/todoist/__init__.py rename homeassistant/components/{calendar/todoist.py => todoist/calendar.py} (100%) create mode 100644 homeassistant/components/torque/__init__.py rename homeassistant/components/{sensor/torque.py => torque/sensor.py} (100%) create mode 100644 homeassistant/components/totalconnect/__init__.py rename homeassistant/components/{alarm_control_panel/totalconnect.py => totalconnect/alarm_control_panel.py} (100%) create mode 100644 homeassistant/components/touchline/__init__.py rename homeassistant/components/{climate/touchline.py => touchline/climate.py} (100%) create mode 100644 homeassistant/components/traccar/__init__.py rename homeassistant/components/{device_tracker/traccar.py => traccar/device_tracker.py} (100%) create mode 100644 homeassistant/components/trackr/__init__.py rename homeassistant/components/{device_tracker/trackr.py => trackr/device_tracker.py} (100%) create mode 100644 homeassistant/components/trafikverket_weatherstation/__init__.py rename homeassistant/components/{sensor/trafikverket_weatherstation.py => trafikverket_weatherstation/sensor.py} (100%) create mode 100644 homeassistant/components/travisci/__init__.py rename homeassistant/components/{sensor/travisci.py => travisci/sensor.py} (100%) create mode 100644 homeassistant/components/twitch/__init__.py rename homeassistant/components/{sensor/twitch.py => twitch/sensor.py} (100%) create mode 100644 homeassistant/components/ubee/__init__.py rename homeassistant/components/{device_tracker/ubee.py => ubee/device_tracker.py} (100%) create mode 100644 homeassistant/components/uber/__init__.py rename homeassistant/components/{sensor/uber.py => uber/sensor.py} (100%) create mode 100644 homeassistant/components/ubus/__init__.py rename homeassistant/components/{device_tracker/ubus.py => ubus/device_tracker.py} (100%) create mode 100644 homeassistant/components/ue_smart_radio/__init__.py rename homeassistant/components/{media_player/ue_smart_radio.py => ue_smart_radio/media_player.py} (100%) create mode 100644 homeassistant/components/ups/__init__.py rename homeassistant/components/{sensor/ups.py => ups/sensor.py} (100%) create mode 100644 homeassistant/components/uptimerobot/__init__.py rename homeassistant/components/{binary_sensor/uptimerobot.py => uptimerobot/binary_sensor.py} (100%) create mode 100644 homeassistant/components/uscis/__init__.py rename homeassistant/components/{sensor/uscis.py => uscis/sensor.py} (100%) create mode 100644 homeassistant/components/vasttrafik/__init__.py rename homeassistant/components/{sensor/vasttrafik.py => vasttrafik/sensor.py} (100%) create mode 100644 homeassistant/components/venstar/__init__.py rename homeassistant/components/{climate/venstar.py => venstar/climate.py} (100%) create mode 100644 homeassistant/components/vesync/__init__.py rename homeassistant/components/{switch/vesync.py => vesync/switch.py} (100%) create mode 100644 homeassistant/components/viaggiatreno/__init__.py rename homeassistant/components/{sensor/viaggiatreno.py => viaggiatreno/sensor.py} (100%) create mode 100644 homeassistant/components/vizio/__init__.py rename homeassistant/components/{media_player/vizio.py => vizio/media_player.py} (100%) create mode 100644 homeassistant/components/vlc/__init__.py rename homeassistant/components/{media_player/vlc.py => vlc/media_player.py} (100%) create mode 100644 homeassistant/components/volkszaehler/__init__.py rename homeassistant/components/{sensor/volkszaehler.py => volkszaehler/sensor.py} (100%) create mode 100644 homeassistant/components/volumio/__init__.py rename homeassistant/components/{media_player/volumio.py => volumio/media_player.py} (100%) create mode 100644 homeassistant/components/waqi/__init__.py rename homeassistant/components/{sensor/waqi.py => waqi/sensor.py} (100%) create mode 100644 homeassistant/components/waze_travel_time/__init__.py rename homeassistant/components/{sensor/waze_travel_time.py => waze_travel_time/sensor.py} (100%) create mode 100644 homeassistant/components/whois/__init__.py rename homeassistant/components/{sensor/whois.py => whois/sensor.py} (100%) create mode 100644 homeassistant/components/worldtidesinfo/__init__.py rename homeassistant/components/{sensor/worldtidesinfo.py => worldtidesinfo/sensor.py} (100%) create mode 100644 homeassistant/components/worxlandroid/__init__.py rename homeassistant/components/{sensor/worxlandroid.py => worxlandroid/sensor.py} (100%) create mode 100644 homeassistant/components/x10/__init__.py rename homeassistant/components/{light/x10.py => x10/light.py} (100%) create mode 100644 homeassistant/components/xbox_live/__init__.py rename homeassistant/components/{sensor/xbox_live.py => xbox_live/sensor.py} (100%) create mode 100644 homeassistant/components/xeoma/__init__.py rename homeassistant/components/{camera/xeoma.py => xeoma/camera.py} (100%) create mode 100644 homeassistant/components/xfinity/__init__.py rename homeassistant/components/{device_tracker/xfinity.py => xfinity/device_tracker.py} (100%) create mode 100644 homeassistant/components/xiaomi/__init__.py rename homeassistant/components/{camera/xiaomi.py => xiaomi/camera.py} (100%) create mode 100644 homeassistant/components/xiaomi_tv/__init__.py rename homeassistant/components/{media_player/xiaomi_tv.py => xiaomi_tv/media_player.py} (100%) create mode 100644 homeassistant/components/yale_smart_alarm/__init__.py rename homeassistant/components/{alarm_control_panel/yale_smart_alarm.py => yale_smart_alarm/alarm_control_panel.py} (100%) create mode 100644 homeassistant/components/yamaha_musiccast/__init__.py rename homeassistant/components/{media_player/yamaha_musiccast.py => yamaha_musiccast/media_player.py} (100%) create mode 100644 homeassistant/components/yeelight/__init__.py rename homeassistant/components/{light/yeelight.py => yeelight/light.py} (100%) create mode 100644 homeassistant/components/yeelightsunflower/__init__.py rename homeassistant/components/{light/yeelightsunflower.py => yeelightsunflower/light.py} (100%) create mode 100644 homeassistant/components/yi/__init__.py rename homeassistant/components/{camera/yi.py => yi/camera.py} (100%) create mode 100644 homeassistant/components/zamg/__init__.py rename homeassistant/components/{sensor/zamg.py => zamg/sensor.py} (100%) rename homeassistant/components/{weather/zamg.py => zamg/weather.py} (98%) create mode 100644 homeassistant/components/zengge/__init__.py rename homeassistant/components/{light/zengge.py => zengge/light.py} (100%) create mode 100644 homeassistant/components/zestimate/__init__.py rename homeassistant/components/{sensor/zestimate.py => zestimate/sensor.py} (100%) create mode 100644 homeassistant/components/zhong_hong/__init__.py rename homeassistant/components/{climate/zhong_hong.py => zhong_hong/climate.py} (100%) create mode 100644 homeassistant/components/ziggo_mediabox_xl/__init__.py rename homeassistant/components/{media_player/ziggo_mediabox_xl.py => ziggo_mediabox_xl/media_player.py} (100%) rename tests/components/sensor/{test_moldindicator.py => test_mold_indicator.py} (100%) diff --git a/.coveragerc b/.coveragerc index b7f2961f14d..7c5ca1f3a0b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -12,17 +12,17 @@ omit = # omit pieces of code that rely on external devices being present homeassistant/components/abode/* homeassistant/components/ads/* - homeassistant/components/air_quality/nilu.py - homeassistant/components/air_quality/norway_air.py - homeassistant/components/air_quality/opensensemap.py - homeassistant/components/alarm_control_panel/alarmdotcom.py - homeassistant/components/alarm_control_panel/canary.py - homeassistant/components/alarm_control_panel/concord232.py - homeassistant/components/alarm_control_panel/ialarm.py + 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/alarm_control_panel/manual_mqtt.py - homeassistant/components/alarm_control_panel/nx584.py - homeassistant/components/alarm_control_panel/totalconnect.py - homeassistant/components/alarm_control_panel/yale_smart_alarm.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/ambient_station/* homeassistant/components/amcrest/* @@ -38,110 +38,110 @@ omit = homeassistant/components/august/* homeassistant/components/axis/* homeassistant/components/bbb_gpio/* - homeassistant/components/binary_sensor/arest.py - homeassistant/components/binary_sensor/concord232.py - homeassistant/components/binary_sensor/flic.py - homeassistant/components/binary_sensor/hikvision.py - homeassistant/components/binary_sensor/iss.py - homeassistant/components/binary_sensor/mystrom.py - homeassistant/components/binary_sensor/ping.py + 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/binary_sensor/rest.py - homeassistant/components/binary_sensor/tapsaff.py - homeassistant/components/binary_sensor/uptimerobot.py + homeassistant/components/tapsaff/binary_sensor.py + homeassistant/components/uptimerobot/binary_sensor.py homeassistant/components/blink/* homeassistant/components/bloomsky/* homeassistant/components/bmw_connected_drive/* homeassistant/components/browser/* homeassistant/components/calendar/caldav.py - homeassistant/components/calendar/todoist.py + homeassistant/components/todoist/calendar.py homeassistant/components/camera/bloomsky.py - homeassistant/components/camera/canary.py - homeassistant/components/camera/familyhub.py - homeassistant/components/camera/ffmpeg.py - homeassistant/components/camera/foscam.py - homeassistant/components/camera/mjpeg.py - homeassistant/components/camera/onvif.py - homeassistant/components/camera/proxy.py - homeassistant/components/camera/ring.py - homeassistant/components/camera/rpi_camera.py - homeassistant/components/camera/synology.py - homeassistant/components/camera/xeoma.py - homeassistant/components/camera/xiaomi.py - homeassistant/components/camera/yi.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/cisco_mobility_express/device_tracker.py - homeassistant/components/climate/coolmaster.py - homeassistant/components/climate/ephember.py - homeassistant/components/climate/eq3btsmart.py - homeassistant/components/climate/flexit.py - homeassistant/components/climate/heatmiser.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/climate/homematic.py homeassistant/components/climate/honeywell.py homeassistant/components/climate/knx.py - homeassistant/components/climate/mill.py - homeassistant/components/climate/oem.py - homeassistant/components/climate/proliphix.py - homeassistant/components/climate/radiotherm.py - homeassistant/components/climate/sensibo.py - homeassistant/components/climate/touchline.py - homeassistant/components/climate/venstar.py - homeassistant/components/climate/zhong_hong.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/touchline/climate.py + homeassistant/components/venstar/climate.py + homeassistant/components/zhong_hong/climate.py homeassistant/components/cloudflare/* homeassistant/components/coinbase/* homeassistant/components/comfoconnect/* - homeassistant/components/cover/aladdin_connect.py - homeassistant/components/cover/brunt.py - homeassistant/components/cover/garadget.py - homeassistant/components/cover/gogogate2.py + homeassistant/components/aladdin_connect/cover.py + homeassistant/components/brunt/cover.py + homeassistant/components/garadget/cover.py + homeassistant/components/gogogate2/cover.py homeassistant/components/cover/homematic.py homeassistant/components/cover/knx.py - homeassistant/components/cover/myq.py - homeassistant/components/cover/opengarage.py + homeassistant/components/myq/cover.py + homeassistant/components/opengarage/cover.py homeassistant/components/cover/rpi_gpio.py homeassistant/components/cover/scsgate.py homeassistant/components/daikin/* homeassistant/components/danfoss_air/* - homeassistant/components/device_tracker/actiontec.py - homeassistant/components/device_tracker/aruba.py + homeassistant/components/actiontec/device_tracker.py + homeassistant/components/aruba/device_tracker.py homeassistant/components/device_tracker/asuswrt.py homeassistant/components/device_tracker/automatic.py - homeassistant/components/device_tracker/bbox.py - homeassistant/components/device_tracker/bluetooth_le_tracker.py - homeassistant/components/device_tracker/bluetooth_tracker.py - homeassistant/components/device_tracker/bt_home_hub_5.py - homeassistant/components/device_tracker/bt_smarthub.py - homeassistant/components/device_tracker/cisco_ios.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/device_tracker/ddwrt.py - homeassistant/components/device_tracker/fritz.py - homeassistant/components/device_tracker/google_maps.py - homeassistant/components/device_tracker/hitron_coda.py - homeassistant/components/device_tracker/huawei_router.py - homeassistant/components/device_tracker/icloud.py - homeassistant/components/device_tracker/keenetic_ndms2.py - homeassistant/components/device_tracker/linksys_ap.py - homeassistant/components/device_tracker/linksys_smart.py - homeassistant/components/device_tracker/luci.py - homeassistant/components/device_tracker/mikrotik.py - homeassistant/components/device_tracker/netgear.py - homeassistant/components/device_tracker/nmap_tracker.py - homeassistant/components/device_tracker/ping.py - homeassistant/components/device_tracker/quantum_gateway.py - homeassistant/components/device_tracker/ritassist.py - homeassistant/components/device_tracker/sky_hub.py - homeassistant/components/device_tracker/snmp.py - homeassistant/components/device_tracker/swisscom.py - homeassistant/components/device_tracker/synology_srm.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/device_tracker/tado.py - homeassistant/components/device_tracker/thomson.py - homeassistant/components/device_tracker/tile.py + homeassistant/components/thomson/device_tracker.py + homeassistant/components/tile/device_tracker.py homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tplink.py - homeassistant/components/device_tracker/traccar.py - homeassistant/components/device_tracker/trackr.py - homeassistant/components/device_tracker/ubee.py - homeassistant/components/device_tracker/ubus.py - homeassistant/components/device_tracker/xfinity.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/digital_ocean/* homeassistant/components/dominos/* homeassistant/components/doorbird/* @@ -200,11 +200,11 @@ omit = homeassistant/components/idteck_prox/* homeassistant/components/ifttt/* homeassistant/components/ihc/* - homeassistant/components/image_processing/dlib_face_detect.py - homeassistant/components/image_processing/dlib_face_identify.py - homeassistant/components/image_processing/qrcode.py - homeassistant/components/image_processing/seven_segments.py - homeassistant/components/image_processing/tensorflow.py + 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/insteon_local/* homeassistant/components/insteon_plm/* homeassistant/components/insteon/* @@ -222,107 +222,107 @@ omit = homeassistant/components/lametric/* homeassistant/components/lcn/* homeassistant/components/lifx/* - homeassistant/components/light/avion.py - homeassistant/components/light/blinksticklight.py - homeassistant/components/light/blinkt.py - homeassistant/components/light/decora_wifi.py - homeassistant/components/light/decora.py + 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/light/everlights.py - homeassistant/components/light/flux_led.py - homeassistant/components/light/futurenow.py - homeassistant/components/light/greenwave.py + homeassistant/components/flux_led/light.py + homeassistant/components/futurenow/light.py + homeassistant/components/greenwave/light.py homeassistant/components/light/hue.py - homeassistant/components/light/hyperion.py - homeassistant/components/light/iglo.py - homeassistant/components/light/lifx_legacy.py - homeassistant/components/light/limitlessled.py - homeassistant/components/light/lw12wifi.py - homeassistant/components/light/mystrom.py - homeassistant/components/light/nanoleaf.py - homeassistant/components/light/niko_home_control.py - homeassistant/components/light/opple.py - homeassistant/components/light/osramlightify.py - homeassistant/components/light/piglow.py - homeassistant/components/light/rpi_gpio_pwm.py - homeassistant/components/light/sensehat.py - homeassistant/components/light/tikteck.py + homeassistant/components/hyperion/light.py + homeassistant/components/iglo/light.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/light/tplink.py homeassistant/components/light/tradfri.py - homeassistant/components/light/x10.py - homeassistant/components/light/yeelight.py - homeassistant/components/light/yeelightsunflower.py - homeassistant/components/light/zengge.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/linode/* homeassistant/components/lirc/* - homeassistant/components/lock/kiwi.py - homeassistant/components/lock/lockitron.py - homeassistant/components/lock/nello.py - homeassistant/components/lock/nuki.py - homeassistant/components/lock/sesame.py + homeassistant/components/kiwi/lock.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/luftdaten/* homeassistant/components/lupusec/* homeassistant/components/lutron_caseta/* homeassistant/components/lutron/* - homeassistant/components/mailbox/asterisk_cdr.py + homeassistant/components/asterisk_cdr/mailbox.py homeassistant/components/mailgun/notify.py homeassistant/components/map/* homeassistant/components/matrix/* homeassistant/components/maxcube/* homeassistant/components/media_extractor/* - homeassistant/components/media_player/anthemav.py - homeassistant/components/media_player/aquostv.py - homeassistant/components/media_player/bluesound.py - homeassistant/components/media_player/braviatv.py - homeassistant/components/media_player/channels.py - homeassistant/components/media_player/clementine.py - homeassistant/components/media_player/cmus.py - homeassistant/components/media_player/denon.py - homeassistant/components/media_player/denonavr.py + 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/media_player/directv.py - homeassistant/components/media_player/dlna_dmr.py - homeassistant/components/media_player/dunehd.py - homeassistant/components/media_player/emby.py - homeassistant/components/media_player/epson.py - homeassistant/components/media_player/frontier_silicon.py - homeassistant/components/media_player/gpmdp.py - homeassistant/components/media_player/gstreamer.py - homeassistant/components/media_player/harman_kardon_avr.py - homeassistant/components/media_player/horizon.py - homeassistant/components/media_player/itunes.py - homeassistant/components/media_player/kodi.py - homeassistant/components/media_player/lg_netcast.py - homeassistant/components/media_player/lg_soundbar.py - homeassistant/components/media_player/liveboxplaytv.py - homeassistant/components/media_player/mediaroom.py - homeassistant/components/media_player/mpchc.py - homeassistant/components/media_player/mpd.py - homeassistant/components/media_player/nad.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/media_player/nadtcp.py - homeassistant/components/media_player/onkyo.py - homeassistant/components/media_player/openhome.py - homeassistant/components/media_player/panasonic_bluray.py - homeassistant/components/media_player/panasonic_viera.py - homeassistant/components/media_player/pandora.py - homeassistant/components/media_player/philips_js.py - homeassistant/components/media_player/pioneer.py - homeassistant/components/media_player/pjlink.py - homeassistant/components/media_player/plex.py - homeassistant/components/media_player/russound_rio.py - homeassistant/components/media_player/russound_rnet.py - homeassistant/components/media_player/snapcast.py - homeassistant/components/media_player/songpal.py - homeassistant/components/media_player/spotify.py - homeassistant/components/media_player/squeezebox.py - homeassistant/components/media_player/ue_smart_radio.py - homeassistant/components/media_player/vizio.py - homeassistant/components/media_player/vlc.py - homeassistant/components/media_player/volumio.py - homeassistant/components/media_player/xiaomi_tv.py - homeassistant/components/media_player/yamaha_musiccast.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/media_player/yamaha.py - homeassistant/components/media_player/ziggo_mediabox_xl.py + homeassistant/components/ziggo_mediabox_xl/media_player.py homeassistant/components/meteo_france/* homeassistant/components/mobile_app/* homeassistant/components/mochad/* @@ -411,183 +411,183 @@ omit = homeassistant/components/rpi_pfio/* homeassistant/components/sabnzbd/* homeassistant/components/satel_integra/* - homeassistant/components/scene/hunterdouglas_powerview.py - homeassistant/components/scene/lifx_cloud.py + homeassistant/components/hunterdouglas_powerview/scene.py + homeassistant/components/lifx_cloud/scene.py homeassistant/components/scsgate/* homeassistant/components/sense/* - homeassistant/components/sensor/aftership.py - homeassistant/components/sensor/airvisual.py - homeassistant/components/sensor/alpha_vantage.py - homeassistant/components/sensor/arest.py - homeassistant/components/sensor/arwn.py - homeassistant/components/sensor/bbox.py - homeassistant/components/sensor/bh1750.py - homeassistant/components/sensor/bitcoin.py - homeassistant/components/sensor/blockchain.py - homeassistant/components/sensor/bme280.py - homeassistant/components/sensor/bme680.py + 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/sensor/bom.py - homeassistant/components/sensor/broadlink.py - homeassistant/components/sensor/brottsplatskartan.py - homeassistant/components/sensor/buienradar.py - homeassistant/components/sensor/cert_expiry.py - homeassistant/components/sensor/citybikes.py - homeassistant/components/sensor/coinbase.py - homeassistant/components/sensor/comed_hourly_pricing.py - homeassistant/components/sensor/cpuspeed.py - homeassistant/components/sensor/crimereports.py - homeassistant/components/sensor/cups.py - homeassistant/components/sensor/currencylayer.py - homeassistant/components/sensor/deluge.py - homeassistant/components/sensor/deutsche_bahn.py - homeassistant/components/sensor/dht.py - homeassistant/components/sensor/discogs.py - homeassistant/components/sensor/dnsip.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/sensor/domain_expiry.py homeassistant/components/sensor/dte_energy_bridge.py - homeassistant/components/sensor/dublin_bus_transport.py - homeassistant/components/sensor/duke_energy.py - homeassistant/components/sensor/dwd_weather_warnings.py - homeassistant/components/sensor/ebox.py - homeassistant/components/sensor/eddystone_temperature.py - homeassistant/components/sensor/eliqonline.py - homeassistant/components/sensor/emoncms.py - homeassistant/components/sensor/enphase_envoy.py - homeassistant/components/sensor/envirophat.py - homeassistant/components/sensor/etherscan.py - homeassistant/components/sensor/fedex.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/sensor/filesize.py - homeassistant/components/sensor/fints.py - homeassistant/components/sensor/fitbit.py - homeassistant/components/sensor/fixer.py - homeassistant/components/sensor/flunearyou.py + homeassistant/components/fints/sensor.py + homeassistant/components/fitbit/sensor.py + homeassistant/components/fixer/sensor.py + homeassistant/components/flunearyou/sensor.py homeassistant/components/sensor/folder.py homeassistant/components/sensor/foobot.py - homeassistant/components/sensor/fritzbox_callmonitor.py - homeassistant/components/sensor/fritzbox_netmonitor.py - homeassistant/components/sensor/gearbest.py - homeassistant/components/sensor/geizhals.py - homeassistant/components/sensor/github.py - homeassistant/components/sensor/gitlab_ci.py - homeassistant/components/sensor/gitter.py - homeassistant/components/sensor/glances.py - homeassistant/components/sensor/google_travel_time.py - homeassistant/components/sensor/gpsd.py - homeassistant/components/sensor/greeneye_monitor.py - homeassistant/components/sensor/gtfs.py - homeassistant/components/sensor/gtt.py - homeassistant/components/sensor/haveibeenpwned.py - homeassistant/components/sensor/hp_ilo.py - homeassistant/components/sensor/htu21d.py - homeassistant/components/sensor/iliad_italy.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/sensor/imap_email_content.py - homeassistant/components/sensor/imap.py - homeassistant/components/sensor/influxdb.py - homeassistant/components/sensor/irish_rail_transport.py - homeassistant/components/sensor/kwb.py - homeassistant/components/sensor/lacrosse.py - homeassistant/components/sensor/lastfm.py - homeassistant/components/sensor/launch_library.py - homeassistant/components/sensor/linky.py - homeassistant/components/sensor/linux_battery.py - homeassistant/components/sensor/london_underground.py - homeassistant/components/sensor/loopenergy.py - homeassistant/components/sensor/lyft.py - homeassistant/components/sensor/magicseaweed.py - homeassistant/components/sensor/metoffice.py - homeassistant/components/sensor/miflora.py - homeassistant/components/sensor/mitemp_bt.py - homeassistant/components/sensor/modem_callerid.py - homeassistant/components/sensor/mopar.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/sensor.py homeassistant/components/sensor/mqtt_room.py - homeassistant/components/sensor/mvglive.py - homeassistant/components/sensor/nederlandse_spoorwegen.py - homeassistant/components/sensor/netatmo_public.py + homeassistant/components/mvglive/sensor.py + homeassistant/components/nederlandse_spoorwegen/sensor.py + homeassistant/components/netatmo_public/sensor.py homeassistant/components/sensor/netdata_public.py - homeassistant/components/sensor/netdata.py - homeassistant/components/sensor/neurio_energy.py - homeassistant/components/sensor/nmbs.py - homeassistant/components/sensor/noaa_tides.py + homeassistant/components/netdata/sensor.py + homeassistant/components/neurio_energy/sensor.py + homeassistant/components/nmbs/sensor.py + homeassistant/components/noaa_tides/sensor.py homeassistant/components/sensor/nsw_fuel_station.py - homeassistant/components/sensor/nut.py - homeassistant/components/sensor/nzbget.py - homeassistant/components/sensor/ohmconnect.py - homeassistant/components/sensor/onewire.py - homeassistant/components/sensor/openevse.py - homeassistant/components/sensor/openexchangerates.py - homeassistant/components/sensor/opensky.py - homeassistant/components/sensor/openweathermap.py - homeassistant/components/sensor/otp.py - homeassistant/components/sensor/pi_hole.py - homeassistant/components/sensor/plex.py - homeassistant/components/sensor/pocketcasts.py - homeassistant/components/sensor/pollen.py - homeassistant/components/sensor/postnl.py - homeassistant/components/sensor/prezzibenzina.py - homeassistant/components/sensor/pushbullet.py - homeassistant/components/sensor/pvoutput.py - homeassistant/components/sensor/pyload.py - homeassistant/components/sensor/qbittorrent.py - homeassistant/components/sensor/qnap.py + homeassistant/components/nut/sensor.py + homeassistant/components/nzbget/sensor.py + homeassistant/components/ohmconnect/sensor.py + homeassistant/components/onewire/sensor.py + homeassistant/components/openevse/sensor.py + homeassistant/components/openexchangerates/sensor.py + homeassistant/components/opensky/sensor.py + homeassistant/components/openweathermap/sensor.py + homeassistant/components/otp/sensor.py + homeassistant/components/pi_hole/sensor.py + homeassistant/components/plex/sensor.py + homeassistant/components/pocketcasts/sensor.py + homeassistant/components/pollen/sensor.py + homeassistant/components/postnl/sensor.py + homeassistant/components/prezzibenzina/sensor.py + homeassistant/components/pushbullet/sensor.py + homeassistant/components/pvoutput/sensor.py + homeassistant/components/pyload/sensor.py + homeassistant/components/qbittorrent/sensor.py + homeassistant/components/qnap/sensor.py homeassistant/components/sensor/radarr.py - homeassistant/components/sensor/rainbird.py - homeassistant/components/sensor/recollect_waste.py - homeassistant/components/sensor/rejseplanen.py - homeassistant/components/sensor/ripple.py - homeassistant/components/sensor/rova.py - homeassistant/components/sensor/rtorrent.py - homeassistant/components/sensor/ruter.py - homeassistant/components/sensor/scrape.py - homeassistant/components/sensor/sensehat.py - homeassistant/components/sensor/serial_pm.py - homeassistant/components/sensor/serial.py - homeassistant/components/sensor/seventeentrack.py - homeassistant/components/sensor/shodan.py - homeassistant/components/sensor/sht31.py + homeassistant/components/rainbird/sensor.py + homeassistant/components/recollect_waste/sensor.py + homeassistant/components/rejseplanen/sensor.py + homeassistant/components/ripple/sensor.py + homeassistant/components/rova/sensor.py + homeassistant/components/rtorrent/sensor.py + homeassistant/components/ruter/sensor.py + homeassistant/components/scrape/sensor.py + homeassistant/components/sensehat/sensor.py + homeassistant/components/serial_pm/sensor.py + homeassistant/components/serial/sensor.py + homeassistant/components/seventeentrack/sensor.py + homeassistant/components/shodan/sensor.py + homeassistant/components/sht31/sensor.py homeassistant/components/sensor/sigfox.py homeassistant/components/sensor/simulated.py - homeassistant/components/sensor/skybeacon.py - homeassistant/components/sensor/sma.py - homeassistant/components/sensor/snmp.py - homeassistant/components/sensor/sochain.py - homeassistant/components/sensor/socialblade.py - homeassistant/components/sensor/solaredge.py + homeassistant/components/skybeacon/sensor.py + homeassistant/components/sma/sensor.py + homeassistant/components/snmp/sensor.py + homeassistant/components/sochain/sensor.py + homeassistant/components/socialblade/sensor.py + homeassistant/components/solaredge/sensor.py homeassistant/components/sensor/sonarr.py - homeassistant/components/sensor/spotcrime.py + homeassistant/components/spotcrime/sensor.py homeassistant/components/sensor/srp_energy.py - homeassistant/components/sensor/starlingbank.py - homeassistant/components/sensor/steam_online.py - homeassistant/components/sensor/supervisord.py - homeassistant/components/sensor/swiss_hydrological_data.py - homeassistant/components/sensor/swiss_public_transport.py - homeassistant/components/sensor/syncthru.py - homeassistant/components/sensor/synologydsm.py - homeassistant/components/sensor/systemmonitor.py - homeassistant/components/sensor/sytadin.py - homeassistant/components/sensor/tank_utility.py - homeassistant/components/sensor/tautulli.py - homeassistant/components/sensor/ted5000.py - homeassistant/components/sensor/temper.py - homeassistant/components/sensor/thermoworks_smoke.py + homeassistant/components/starlingbank/sensor.py + homeassistant/components/steam_online/sensor.py + homeassistant/components/supervisord/sensor.py + homeassistant/components/swiss_hydrological_data/sensor.py + homeassistant/components/swiss_public_transport/sensor.py + homeassistant/components/syncthru/sensor.py + homeassistant/components/synologydsm/sensor.py + homeassistant/components/systemmonitor/sensor.py + homeassistant/components/sytadin/sensor.py + homeassistant/components/tank_utility/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/sensor/time_date.py - homeassistant/components/sensor/torque.py - homeassistant/components/sensor/trafikverket_weatherstation.py - homeassistant/components/sensor/travisci.py - homeassistant/components/sensor/twitch.py - homeassistant/components/sensor/uber.py - homeassistant/components/sensor/ups.py - homeassistant/components/sensor/uscis.py - homeassistant/components/sensor/vasttrafik.py - homeassistant/components/sensor/viaggiatreno.py - homeassistant/components/sensor/volkszaehler.py - homeassistant/components/sensor/waqi.py - homeassistant/components/sensor/waze_travel_time.py - homeassistant/components/sensor/whois.py - homeassistant/components/sensor/worldtidesinfo.py - homeassistant/components/sensor/worxlandroid.py - homeassistant/components/sensor/xbox_live.py - homeassistant/components/sensor/zamg.py - homeassistant/components/sensor/zestimate.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 @@ -598,34 +598,34 @@ omit = homeassistant/components/spc/* homeassistant/components/speedtestdotnet/* homeassistant/components/spider/* - homeassistant/components/switch/acer_projector.py - homeassistant/components/switch/anel_pwrctrl.py - homeassistant/components/switch/arest.py - homeassistant/components/switch/broadlink.py - homeassistant/components/switch/deluge.py - homeassistant/components/switch/digitalloggers.py - homeassistant/components/switch/dlink.py - homeassistant/components/switch/edimax.py - homeassistant/components/switch/fritzdect.py - homeassistant/components/switch/hikvisioncam.py - homeassistant/components/switch/hook.py - homeassistant/components/switch/kankun.py - homeassistant/components/switch/mystrom.py - homeassistant/components/switch/netio.py - homeassistant/components/switch/orvibo.py - homeassistant/components/switch/pencom.py - homeassistant/components/switch/pulseaudio_loopback.py - homeassistant/components/switch/rainbird.py - homeassistant/components/switch/recswitch.py + 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/switch/rest.py - homeassistant/components/switch/rpi_rf.py - homeassistant/components/switch/snmp.py - homeassistant/components/switch/sony_projector.py - homeassistant/components/switch/switchbot.py - homeassistant/components/switch/switchmate.py - homeassistant/components/switch/telnet.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/switch/tplink.py - homeassistant/components/switch/vesync.py + homeassistant/components/vesync/switch.py homeassistant/components/tado/* homeassistant/components/tahoma/* homeassistant/components/telegram_bot/* @@ -649,23 +649,23 @@ omit = homeassistant/components/upcloud/* homeassistant/components/upnp/* homeassistant/components/usps/* - homeassistant/components/vacuum/roomba.py + homeassistant/components/roomba/vacuum.py homeassistant/components/velbus/* homeassistant/components/velux/* homeassistant/components/vera/* homeassistant/components/verisure/* homeassistant/components/volvooncall/* homeassistant/components/w800rf32/* - homeassistant/components/water_heater/econet.py + homeassistant/components/econet/water_heater.py homeassistant/components/waterfurnace/* homeassistant/components/watson_iot/* - homeassistant/components/weather/bom.py - homeassistant/components/weather/buienradar.py + homeassistant/components/bom/weather.py + homeassistant/components/buienradar/weather.py homeassistant/components/weather/darksky.py - homeassistant/components/weather/met.py - homeassistant/components/weather/metoffice.py - homeassistant/components/weather/openweathermap.py - homeassistant/components/weather/zamg.py + homeassistant/components/met/weather.py + homeassistant/components/metoffice/weather.py + homeassistant/components/openweathermap/weather.py + homeassistant/components/zamg/weather.py homeassistant/components/webostv/* homeassistant/components/wemo/* homeassistant/components/wink/* diff --git a/CODEOWNERS b/CODEOWNERS index 96de36290b4..c70aec85fc7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -48,43 +48,43 @@ homeassistant/components/hassio/* @home-assistant/hassio # Individual platforms homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell -homeassistant/components/binary_sensor/hikvision.py @mezz64 +homeassistant/components/hikvision/binary_sensor.py @mezz64 homeassistant/components/binary_sensor/threshold.py @fabaff -homeassistant/components/binary_sensor/uptimerobot.py @ludeeus +homeassistant/components/uptimerobot/binary_sensor.py @ludeeus homeassistant/components/camera/push.py @dgomes -homeassistant/components/camera/yi.py @bachya -homeassistant/components/climate/coolmaster.py @OnFreund -homeassistant/components/climate/ephember.py @ttroy50 -homeassistant/components/climate/eq3btsmart.py @rytilahti -homeassistant/components/climate/mill.py @danielhiversen -homeassistant/components/climate/sensibo.py @andrey-git -homeassistant/components/cover/brunt.py @eavanvalkenburg +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/cover/template.py @PhracturedBlue homeassistant/components/device_tracker/asuswrt.py @kennedyshead homeassistant/components/device_tracker/automatic.py @armills -homeassistant/components/device_tracker/bt_smarthub.py @jxwolstenholme -homeassistant/components/device_tracker/huawei_router.py @abmantis -homeassistant/components/device_tracker/quantum_gateway.py @cisasteelersfan -homeassistant/components/device_tracker/synology_srm.py @aerialls -homeassistant/components/device_tracker/tile.py @bachya -homeassistant/components/device_tracker/traccar.py @ludeeus -homeassistant/components/device_tracker/xfinity.py @cisasteelersfan +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/light/lifx_legacy.py @amelchio -homeassistant/components/light/yeelight.py @rytilahti -homeassistant/components/light/yeelightsunflower.py @lindsaymarkward -homeassistant/components/lock/nello.py @pschmitt -homeassistant/components/lock/nuki.py @pschmitt -homeassistant/components/media_player/braviatv.py @robbiet480 -homeassistant/components/media_player/emby.py @mezz64 -homeassistant/components/media_player/kodi.py @armills -homeassistant/components/media_player/liveboxplaytv.py @pschmitt -homeassistant/components/media_player/mediaroom.py @dgomes +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/media_player/monoprice.py @etsinko -homeassistant/components/media_player/mpd.py @fabaff -homeassistant/components/media_player/xiaomi_tv.py @fattdev -homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth +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,66 +99,66 @@ 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/scene/lifx_cloud.py @amelchio -homeassistant/components/sensor/airvisual.py @bachya -homeassistant/components/sensor/alpha_vantage.py @fabaff -homeassistant/components/sensor/bitcoin.py @fabaff -homeassistant/components/sensor/cpuspeed.py @fabaff -homeassistant/components/sensor/cups.py @fabaff +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/sensor/darksky.py @fabaff -homeassistant/components/sensor/discogs.py @thibmaek +homeassistant/components/discogs/sensor.py @thibmaek homeassistant/components/sensor/file.py @fabaff homeassistant/components/sensor/filter.py @dgomes -homeassistant/components/sensor/fitbit.py @robbiet480 -homeassistant/components/sensor/fixer.py @fabaff -homeassistant/components/sensor/flunearyou.py @bachya -homeassistant/components/sensor/gearbest.py @HerrHofrat -homeassistant/components/sensor/gitter.py @fabaff -homeassistant/components/sensor/glances.py @fabaff -homeassistant/components/sensor/google_travel_time.py @robbiet480 -homeassistant/components/sensor/gpsd.py @fabaff -homeassistant/components/sensor/gtfs.py @robbiet480 +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/sensor/integration.py @dgomes -homeassistant/components/sensor/irish_rail_transport.py @ttroy50 +homeassistant/components/irish_rail_transport/sensor.py @ttroy50 homeassistant/components/sensor/jewish_calendar.py @tsvi -homeassistant/components/sensor/launch_library.py @ludeeus -homeassistant/components/sensor/linux_battery.py @fabaff -homeassistant/components/sensor/miflora.py @danielhiversen @ChristianKuehnel +homeassistant/components/launch_library/sensor.py @ludeeus +homeassistant/components/linux_battery/sensor.py @fabaff +homeassistant/components/miflora/sensor.py @danielhiversen @ChristianKuehnel homeassistant/components/sensor/min_max.py @fabaff homeassistant/components/sensor/moon.py @fabaff -homeassistant/components/sensor/netdata.py @fabaff -homeassistant/components/sensor/nmbs.py @thibmaek +homeassistant/components/netdata/sensor.py @fabaff +homeassistant/components/nmbs/sensor.py @thibmaek homeassistant/components/sensor/nsw_fuel_station.py @nickw444 -homeassistant/components/sensor/ohmconnect.py @robbiet480 -homeassistant/components/sensor/pi_hole.py @fabaff -homeassistant/components/sensor/pollen.py @bachya -homeassistant/components/sensor/pvoutput.py @fabaff -homeassistant/components/sensor/qnap.py @colinodell -homeassistant/components/sensor/ruter.py @ludeeus -homeassistant/components/sensor/scrape.py @fabaff -homeassistant/components/sensor/serial.py @fabaff -homeassistant/components/sensor/seventeentrack.py @bachya -homeassistant/components/sensor/shodan.py @fabaff -homeassistant/components/sensor/sma.py @kellerza +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/sensor/sql.py @dgomes homeassistant/components/sensor/statistics.py @fabaff homeassistant/components/sensor/swiss*.py @fabaff -homeassistant/components/sensor/sytadin.py @gautric -homeassistant/components/sensor/tautulli.py @ludeeus +homeassistant/components/sytadin/sensor.py @gautric +homeassistant/components/tautulli/sensor.py @ludeeus homeassistant/components/sensor/time_date.py @fabaff -homeassistant/components/sensor/uber.py @robbiet480 +homeassistant/components/uber/sensor.py @robbiet480 homeassistant/components/sensor/version.py @fabaff -homeassistant/components/sensor/waqi.py @andrey-git +homeassistant/components/waqi/sensor.py @andrey-git homeassistant/components/sensor/worldclock.py @fabaff -homeassistant/components/switch/switchbot.py @danielhiversen -homeassistant/components/switch/switchmate.py @danielhiversen +homeassistant/components/switchbot/switch.py @danielhiversen +homeassistant/components/switchmate/switch.py @danielhiversen homeassistant/components/tts/amazon_polly.py @robbiet480 -homeassistant/components/vacuum/roomba.py @pschmitt +homeassistant/components/roomba/vacuum.py @pschmitt homeassistant/components/weather/__init__.py @fabaff homeassistant/components/weather/darksky.py @fabaff -homeassistant/components/weather/demo.py @fabaff -homeassistant/components/weather/met.py @danielhiversen -homeassistant/components/weather/openweathermap.py @fabaff +homeassistant/components/demo/weather.py @fabaff +homeassistant/components/met/weather.py @danielhiversen +homeassistant/components/openweathermap/weather.py @fabaff # A homeassistant/components/ambient_station/* @bachya diff --git a/homeassistant/components/acer_projector/__init__.py b/homeassistant/components/acer_projector/__init__.py new file mode 100644 index 00000000000..39896d203b1 --- /dev/null +++ b/homeassistant/components/acer_projector/__init__.py @@ -0,0 +1 @@ +"""The acer_projector component.""" diff --git a/homeassistant/components/switch/acer_projector.py b/homeassistant/components/acer_projector/switch.py similarity index 100% rename from homeassistant/components/switch/acer_projector.py rename to homeassistant/components/acer_projector/switch.py diff --git a/homeassistant/components/actiontec/__init__.py b/homeassistant/components/actiontec/__init__.py new file mode 100644 index 00000000000..fa59cc87063 --- /dev/null +++ b/homeassistant/components/actiontec/__init__.py @@ -0,0 +1 @@ +"""The actiontec component.""" diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/actiontec/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/actiontec.py rename to homeassistant/components/actiontec/device_tracker.py diff --git a/homeassistant/components/aftership/__init__.py b/homeassistant/components/aftership/__init__.py new file mode 100644 index 00000000000..b063c919f18 --- /dev/null +++ b/homeassistant/components/aftership/__init__.py @@ -0,0 +1 @@ +"""The aftership component.""" diff --git a/homeassistant/components/sensor/aftership.py b/homeassistant/components/aftership/sensor.py similarity index 100% rename from homeassistant/components/sensor/aftership.py rename to homeassistant/components/aftership/sensor.py diff --git a/homeassistant/components/airvisual/__init__.py b/homeassistant/components/airvisual/__init__.py new file mode 100644 index 00000000000..b1f79d17241 --- /dev/null +++ b/homeassistant/components/airvisual/__init__.py @@ -0,0 +1 @@ +"""The airvisual component.""" diff --git a/homeassistant/components/sensor/airvisual.py b/homeassistant/components/airvisual/sensor.py similarity index 100% rename from homeassistant/components/sensor/airvisual.py rename to homeassistant/components/airvisual/sensor.py diff --git a/homeassistant/components/aladdin_connect/__init__.py b/homeassistant/components/aladdin_connect/__init__.py new file mode 100644 index 00000000000..90196616dc5 --- /dev/null +++ b/homeassistant/components/aladdin_connect/__init__.py @@ -0,0 +1 @@ +"""The aladdin_connect component.""" diff --git a/homeassistant/components/cover/aladdin_connect.py b/homeassistant/components/aladdin_connect/cover.py similarity index 100% rename from homeassistant/components/cover/aladdin_connect.py rename to homeassistant/components/aladdin_connect/cover.py diff --git a/homeassistant/components/alarmdotcom/__init__.py b/homeassistant/components/alarmdotcom/__init__.py new file mode 100644 index 00000000000..0a715230e9f --- /dev/null +++ b/homeassistant/components/alarmdotcom/__init__.py @@ -0,0 +1 @@ +"""The alarmdotcom component.""" diff --git a/homeassistant/components/alarm_control_panel/alarmdotcom.py b/homeassistant/components/alarmdotcom/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/alarmdotcom.py rename to homeassistant/components/alarmdotcom/alarm_control_panel.py diff --git a/homeassistant/components/alpha_vantage/__init__.py b/homeassistant/components/alpha_vantage/__init__.py new file mode 100644 index 00000000000..f8220c2cb81 --- /dev/null +++ b/homeassistant/components/alpha_vantage/__init__.py @@ -0,0 +1 @@ +"""The alpha_vantage component.""" diff --git a/homeassistant/components/sensor/alpha_vantage.py b/homeassistant/components/alpha_vantage/sensor.py similarity index 100% rename from homeassistant/components/sensor/alpha_vantage.py rename to homeassistant/components/alpha_vantage/sensor.py diff --git a/homeassistant/components/android_ip_webcam/__init__.py b/homeassistant/components/android_ip_webcam/__init__.py index c5424b3d0fa..600efd55a16 100644 --- a/homeassistant/components/android_ip_webcam/__init__.py +++ b/homeassistant/components/android_ip_webcam/__init__.py @@ -18,7 +18,7 @@ from homeassistant.helpers.dispatcher import ( from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -from homeassistant.components.camera.mjpeg import ( +from homeassistant.components.mjpeg.camera import ( CONF_MJPEG_URL, CONF_STILL_IMAGE_URL) REQUIREMENTS = ['pydroid-ipcam==0.8'] diff --git a/homeassistant/components/anel_pwrctrl/__init__.py b/homeassistant/components/anel_pwrctrl/__init__.py new file mode 100644 index 00000000000..bd06aa87b36 --- /dev/null +++ b/homeassistant/components/anel_pwrctrl/__init__.py @@ -0,0 +1 @@ +"""The anel_pwrctrl component.""" diff --git a/homeassistant/components/switch/anel_pwrctrl.py b/homeassistant/components/anel_pwrctrl/switch.py similarity index 100% rename from homeassistant/components/switch/anel_pwrctrl.py rename to homeassistant/components/anel_pwrctrl/switch.py diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py new file mode 100644 index 00000000000..56b06e865c2 --- /dev/null +++ b/homeassistant/components/anthemav/__init__.py @@ -0,0 +1 @@ +"""The anthemav component.""" diff --git a/homeassistant/components/media_player/anthemav.py b/homeassistant/components/anthemav/media_player.py similarity index 100% rename from homeassistant/components/media_player/anthemav.py rename to homeassistant/components/anthemav/media_player.py diff --git a/homeassistant/components/aquostv/__init__.py b/homeassistant/components/aquostv/__init__.py new file mode 100644 index 00000000000..a7f39037fe1 --- /dev/null +++ b/homeassistant/components/aquostv/__init__.py @@ -0,0 +1 @@ +"""The aquostv component.""" diff --git a/homeassistant/components/media_player/aquostv.py b/homeassistant/components/aquostv/media_player.py similarity index 100% rename from homeassistant/components/media_player/aquostv.py rename to homeassistant/components/aquostv/media_player.py diff --git a/homeassistant/components/arest/__init__.py b/homeassistant/components/arest/__init__.py new file mode 100644 index 00000000000..37a104c08fe --- /dev/null +++ b/homeassistant/components/arest/__init__.py @@ -0,0 +1 @@ +"""The arest component.""" diff --git a/homeassistant/components/binary_sensor/arest.py b/homeassistant/components/arest/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/arest.py rename to homeassistant/components/arest/binary_sensor.py diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/arest/sensor.py similarity index 100% rename from homeassistant/components/sensor/arest.py rename to homeassistant/components/arest/sensor.py diff --git a/homeassistant/components/switch/arest.py b/homeassistant/components/arest/switch.py similarity index 100% rename from homeassistant/components/switch/arest.py rename to homeassistant/components/arest/switch.py diff --git a/homeassistant/components/aruba/__init__.py b/homeassistant/components/aruba/__init__.py new file mode 100644 index 00000000000..cd52f7310f3 --- /dev/null +++ b/homeassistant/components/aruba/__init__.py @@ -0,0 +1 @@ +"""The aruba component.""" diff --git a/homeassistant/components/device_tracker/aruba.py b/homeassistant/components/aruba/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/aruba.py rename to homeassistant/components/aruba/device_tracker.py diff --git a/homeassistant/components/arwn/__init__.py b/homeassistant/components/arwn/__init__.py new file mode 100644 index 00000000000..726f3dba5de --- /dev/null +++ b/homeassistant/components/arwn/__init__.py @@ -0,0 +1 @@ +"""The arwn component.""" diff --git a/homeassistant/components/sensor/arwn.py b/homeassistant/components/arwn/sensor.py similarity index 100% rename from homeassistant/components/sensor/arwn.py rename to homeassistant/components/arwn/sensor.py diff --git a/homeassistant/components/asterisk_cdr/__init__.py b/homeassistant/components/asterisk_cdr/__init__.py new file mode 100644 index 00000000000..d681a392c56 --- /dev/null +++ b/homeassistant/components/asterisk_cdr/__init__.py @@ -0,0 +1 @@ +"""The asterisk_cdr component.""" diff --git a/homeassistant/components/mailbox/asterisk_cdr.py b/homeassistant/components/asterisk_cdr/mailbox.py similarity index 100% rename from homeassistant/components/mailbox/asterisk_cdr.py rename to homeassistant/components/asterisk_cdr/mailbox.py diff --git a/homeassistant/components/sensor/asuswrt.py b/homeassistant/components/asuswrt/sensor.py similarity index 100% rename from homeassistant/components/sensor/asuswrt.py rename to homeassistant/components/asuswrt/sensor.py diff --git a/homeassistant/components/avion/__init__.py b/homeassistant/components/avion/__init__.py new file mode 100644 index 00000000000..79e88222545 --- /dev/null +++ b/homeassistant/components/avion/__init__.py @@ -0,0 +1 @@ +"""The avion component.""" diff --git a/homeassistant/components/light/avion.py b/homeassistant/components/avion/light.py similarity index 97% rename from homeassistant/components/light/avion.py rename to homeassistant/components/avion/light.py index 00fc4f33741..617198b2c8c 100644 --- a/homeassistant/components/light/avion.py +++ b/homeassistant/components/avion/light.py @@ -4,6 +4,7 @@ Support for Avion dimmers. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.avion/ """ +import importlib import logging import time @@ -38,7 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an Avion switch.""" # pylint: disable=no-member - import avion + avion = importlib.import_module('avion') lights = [] if CONF_USERNAME in config and CONF_PASSWORD in config: @@ -108,7 +109,7 @@ class AvionLight(Light): def set_state(self, brightness): """Set the state of this lamp to the provided brightness.""" # pylint: disable=no-member - import avion + avion = importlib.import_module('avion') # Bluetooth LE is unreliable, and the connection may drop at any # time. Make an effort to re-establish the link. diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index b9e969efec1..adf380eee43 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -1,7 +1,7 @@ """Support for Axis camera streaming.""" import logging -from homeassistant.components.camera.mjpeg import ( +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, diff --git a/homeassistant/components/bbox/__init__.py b/homeassistant/components/bbox/__init__.py new file mode 100644 index 00000000000..8c3bbf0d57f --- /dev/null +++ b/homeassistant/components/bbox/__init__.py @@ -0,0 +1 @@ +"""The bbox component.""" diff --git a/homeassistant/components/device_tracker/bbox.py b/homeassistant/components/bbox/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/bbox.py rename to homeassistant/components/bbox/device_tracker.py diff --git a/homeassistant/components/sensor/bbox.py b/homeassistant/components/bbox/sensor.py similarity index 100% rename from homeassistant/components/sensor/bbox.py rename to homeassistant/components/bbox/sensor.py diff --git a/homeassistant/components/bh1750/__init__.py b/homeassistant/components/bh1750/__init__.py new file mode 100644 index 00000000000..ce7ecc65366 --- /dev/null +++ b/homeassistant/components/bh1750/__init__.py @@ -0,0 +1 @@ +"""The bh1750 component.""" diff --git a/homeassistant/components/sensor/bh1750.py b/homeassistant/components/bh1750/sensor.py similarity index 100% rename from homeassistant/components/sensor/bh1750.py rename to homeassistant/components/bh1750/sensor.py diff --git a/homeassistant/components/bitcoin/__init__.py b/homeassistant/components/bitcoin/__init__.py new file mode 100644 index 00000000000..cfdfb53c044 --- /dev/null +++ b/homeassistant/components/bitcoin/__init__.py @@ -0,0 +1 @@ +"""The bitcoin component.""" diff --git a/homeassistant/components/sensor/bitcoin.py b/homeassistant/components/bitcoin/sensor.py similarity index 100% rename from homeassistant/components/sensor/bitcoin.py rename to homeassistant/components/bitcoin/sensor.py diff --git a/homeassistant/components/blinksticklight/__init__.py b/homeassistant/components/blinksticklight/__init__.py new file mode 100644 index 00000000000..dd45fbcd690 --- /dev/null +++ b/homeassistant/components/blinksticklight/__init__.py @@ -0,0 +1 @@ +"""The blinksticklight component.""" diff --git a/homeassistant/components/light/blinksticklight.py b/homeassistant/components/blinksticklight/light.py similarity index 100% rename from homeassistant/components/light/blinksticklight.py rename to homeassistant/components/blinksticklight/light.py diff --git a/homeassistant/components/blinkt/__init__.py b/homeassistant/components/blinkt/__init__.py new file mode 100644 index 00000000000..0f61a211559 --- /dev/null +++ b/homeassistant/components/blinkt/__init__.py @@ -0,0 +1 @@ +"""The blinkt component.""" diff --git a/homeassistant/components/light/blinkt.py b/homeassistant/components/blinkt/light.py similarity index 98% rename from homeassistant/components/light/blinkt.py rename to homeassistant/components/blinkt/light.py index d8f819492a5..0704881bff9 100644 --- a/homeassistant/components/light/blinkt.py +++ b/homeassistant/components/blinkt/light.py @@ -4,6 +4,7 @@ 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/ """ +import importlib import logging import voluptuous as vol @@ -31,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Blinkt Light platform.""" # pylint: disable=no-member - import blinkt + blinkt = importlib.import_module('blinkt') # ensure that the lights are off when exiting blinkt.set_clear_on_exit() diff --git a/homeassistant/components/blockchain/__init__.py b/homeassistant/components/blockchain/__init__.py new file mode 100644 index 00000000000..a8ee9884bba --- /dev/null +++ b/homeassistant/components/blockchain/__init__.py @@ -0,0 +1 @@ +"""The blockchain component.""" diff --git a/homeassistant/components/sensor/blockchain.py b/homeassistant/components/blockchain/sensor.py similarity index 100% rename from homeassistant/components/sensor/blockchain.py rename to homeassistant/components/blockchain/sensor.py diff --git a/homeassistant/components/bluesound/__init__.py b/homeassistant/components/bluesound/__init__.py new file mode 100644 index 00000000000..9dbe0f754fb --- /dev/null +++ b/homeassistant/components/bluesound/__init__.py @@ -0,0 +1 @@ +"""The bluesound component.""" diff --git a/homeassistant/components/media_player/bluesound.py b/homeassistant/components/bluesound/media_player.py similarity index 100% rename from homeassistant/components/media_player/bluesound.py rename to homeassistant/components/bluesound/media_player.py diff --git a/homeassistant/components/bluetooth_le_tracker/__init__.py b/homeassistant/components/bluetooth_le_tracker/__init__.py new file mode 100644 index 00000000000..d6886e1b356 --- /dev/null +++ b/homeassistant/components/bluetooth_le_tracker/__init__.py @@ -0,0 +1 @@ +"""The bluetooth_le_tracker component.""" diff --git a/homeassistant/components/device_tracker/bluetooth_le_tracker.py b/homeassistant/components/bluetooth_le_tracker/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/bluetooth_le_tracker.py rename to homeassistant/components/bluetooth_le_tracker/device_tracker.py diff --git a/homeassistant/components/bluetooth_tracker/__init__.py b/homeassistant/components/bluetooth_tracker/__init__.py new file mode 100644 index 00000000000..e58d5abab4a --- /dev/null +++ b/homeassistant/components/bluetooth_tracker/__init__.py @@ -0,0 +1 @@ +"""The bluetooth_tracker component.""" diff --git a/homeassistant/components/device_tracker/bluetooth_tracker.py b/homeassistant/components/bluetooth_tracker/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/bluetooth_tracker.py rename to homeassistant/components/bluetooth_tracker/device_tracker.py diff --git a/homeassistant/components/bme280/__init__.py b/homeassistant/components/bme280/__init__.py new file mode 100644 index 00000000000..87de36fdf02 --- /dev/null +++ b/homeassistant/components/bme280/__init__.py @@ -0,0 +1 @@ +"""The bme280 component.""" diff --git a/homeassistant/components/sensor/bme280.py b/homeassistant/components/bme280/sensor.py similarity index 100% rename from homeassistant/components/sensor/bme280.py rename to homeassistant/components/bme280/sensor.py diff --git a/homeassistant/components/bme680/__init__.py b/homeassistant/components/bme680/__init__.py new file mode 100644 index 00000000000..dc88286a603 --- /dev/null +++ b/homeassistant/components/bme680/__init__.py @@ -0,0 +1 @@ +"""The bme680 component.""" diff --git a/homeassistant/components/sensor/bme680.py b/homeassistant/components/bme680/sensor.py similarity index 99% rename from homeassistant/components/sensor/bme680.py rename to homeassistant/components/bme680/sensor.py index 2fa592bf864..8d620e459d0 100644 --- a/homeassistant/components/sensor/bme680.py +++ b/homeassistant/components/bme680/sensor.py @@ -7,6 +7,7 @@ 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/ """ +import importlib import logging from time import time, sleep @@ -122,7 +123,7 @@ async def async_setup_platform(hass, config, async_add_entities, def _setup_bme680(config): """Set up and configure the BME680 sensor.""" from smbus import SMBus # pylint: disable=import-error - import bme680 + bme680 = importlib.import_module('bme680') sensor_handler = None sensor = None diff --git a/homeassistant/components/bom/__init__.py b/homeassistant/components/bom/__init__.py new file mode 100644 index 00000000000..7b83a5c981b --- /dev/null +++ b/homeassistant/components/bom/__init__.py @@ -0,0 +1 @@ +"""The bom component.""" diff --git a/homeassistant/components/weather/bom.py b/homeassistant/components/bom/weather.py similarity index 100% rename from homeassistant/components/weather/bom.py rename to homeassistant/components/bom/weather.py diff --git a/homeassistant/components/braviatv/__init__.py b/homeassistant/components/braviatv/__init__.py new file mode 100644 index 00000000000..47c6f4cf24d --- /dev/null +++ b/homeassistant/components/braviatv/__init__.py @@ -0,0 +1 @@ +"""The braviatv component.""" diff --git a/homeassistant/components/media_player/braviatv.py b/homeassistant/components/braviatv/media_player.py similarity index 100% rename from homeassistant/components/media_player/braviatv.py rename to homeassistant/components/braviatv/media_player.py diff --git a/homeassistant/components/broadlink/__init__.py b/homeassistant/components/broadlink/__init__.py new file mode 100644 index 00000000000..5055c7fa597 --- /dev/null +++ b/homeassistant/components/broadlink/__init__.py @@ -0,0 +1 @@ +"""The broadlink component.""" diff --git a/homeassistant/components/sensor/broadlink.py b/homeassistant/components/broadlink/sensor.py similarity index 100% rename from homeassistant/components/sensor/broadlink.py rename to homeassistant/components/broadlink/sensor.py diff --git a/homeassistant/components/switch/broadlink.py b/homeassistant/components/broadlink/switch.py similarity index 100% rename from homeassistant/components/switch/broadlink.py rename to homeassistant/components/broadlink/switch.py diff --git a/homeassistant/components/brottsplatskartan/__init__.py b/homeassistant/components/brottsplatskartan/__init__.py new file mode 100644 index 00000000000..d519909b290 --- /dev/null +++ b/homeassistant/components/brottsplatskartan/__init__.py @@ -0,0 +1 @@ +"""The brottsplatskartan component.""" diff --git a/homeassistant/components/sensor/brottsplatskartan.py b/homeassistant/components/brottsplatskartan/sensor.py similarity index 100% rename from homeassistant/components/sensor/brottsplatskartan.py rename to homeassistant/components/brottsplatskartan/sensor.py diff --git a/homeassistant/components/brunt/__init__.py b/homeassistant/components/brunt/__init__.py new file mode 100644 index 00000000000..f89d57cdec1 --- /dev/null +++ b/homeassistant/components/brunt/__init__.py @@ -0,0 +1 @@ +"""The brunt component.""" diff --git a/homeassistant/components/cover/brunt.py b/homeassistant/components/brunt/cover.py similarity index 100% rename from homeassistant/components/cover/brunt.py rename to homeassistant/components/brunt/cover.py diff --git a/homeassistant/components/bt_home_hub_5/__init__.py b/homeassistant/components/bt_home_hub_5/__init__.py new file mode 100644 index 00000000000..54816d6556d --- /dev/null +++ b/homeassistant/components/bt_home_hub_5/__init__.py @@ -0,0 +1 @@ +"""The bt_home_hub_5 component.""" diff --git a/homeassistant/components/device_tracker/bt_home_hub_5.py b/homeassistant/components/bt_home_hub_5/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/bt_home_hub_5.py rename to homeassistant/components/bt_home_hub_5/device_tracker.py diff --git a/homeassistant/components/bt_smarthub/__init__.py b/homeassistant/components/bt_smarthub/__init__.py new file mode 100644 index 00000000000..07419a2bacd --- /dev/null +++ b/homeassistant/components/bt_smarthub/__init__.py @@ -0,0 +1 @@ +"""The bt_smarthub component.""" diff --git a/homeassistant/components/device_tracker/bt_smarthub.py b/homeassistant/components/bt_smarthub/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/bt_smarthub.py rename to homeassistant/components/bt_smarthub/device_tracker.py diff --git a/homeassistant/components/buienradar/__init__.py b/homeassistant/components/buienradar/__init__.py new file mode 100644 index 00000000000..680351f9b81 --- /dev/null +++ b/homeassistant/components/buienradar/__init__.py @@ -0,0 +1 @@ +"""The buienradar component.""" diff --git a/homeassistant/components/sensor/buienradar.py b/homeassistant/components/buienradar/sensor.py similarity index 99% rename from homeassistant/components/sensor/buienradar.py rename to homeassistant/components/buienradar/sensor.py index 4ceb1b221c0..eeb65ab3ce5 100644 --- a/homeassistant/components/sensor/buienradar.py +++ b/homeassistant/components/buienradar/sensor.py @@ -144,7 +144,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Create the buienradar sensor.""" - from homeassistant.components.weather.buienradar import DEFAULT_TIMEFRAME + from homeassistant.components.buienradar.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/weather/buienradar.py b/homeassistant/components/buienradar/weather.py similarity index 98% rename from homeassistant/components/weather/buienradar.py rename to homeassistant/components/buienradar/weather.py index 31f51824146..585f9ac55b9 100644 --- a/homeassistant/components/weather/buienradar.py +++ b/homeassistant/components/buienradar/weather.py @@ -4,7 +4,7 @@ import logging import voluptuous as vol # Reuse data and API logic from the sensor implementation -from homeassistant.components.sensor.buienradar import BrData +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) diff --git a/homeassistant/components/alarm_control_panel/canary.py b/homeassistant/components/canary/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/canary.py rename to homeassistant/components/canary/alarm_control_panel.py diff --git a/homeassistant/components/camera/canary.py b/homeassistant/components/canary/camera.py similarity index 100% rename from homeassistant/components/camera/canary.py rename to homeassistant/components/canary/camera.py diff --git a/homeassistant/components/cert_expiry/__init__.py b/homeassistant/components/cert_expiry/__init__.py new file mode 100644 index 00000000000..78ceb60dd40 --- /dev/null +++ b/homeassistant/components/cert_expiry/__init__.py @@ -0,0 +1 @@ +"""The cert_expiry component.""" diff --git a/homeassistant/components/sensor/cert_expiry.py b/homeassistant/components/cert_expiry/sensor.py similarity index 100% rename from homeassistant/components/sensor/cert_expiry.py rename to homeassistant/components/cert_expiry/sensor.py diff --git a/homeassistant/components/channels/__init__.py b/homeassistant/components/channels/__init__.py new file mode 100644 index 00000000000..70a8c0677de --- /dev/null +++ b/homeassistant/components/channels/__init__.py @@ -0,0 +1 @@ +"""The channels component.""" diff --git a/homeassistant/components/media_player/channels.py b/homeassistant/components/channels/media_player.py similarity index 100% rename from homeassistant/components/media_player/channels.py rename to homeassistant/components/channels/media_player.py diff --git a/homeassistant/components/cisco_ios/__init__.py b/homeassistant/components/cisco_ios/__init__.py new file mode 100644 index 00000000000..77e7c6621eb --- /dev/null +++ b/homeassistant/components/cisco_ios/__init__.py @@ -0,0 +1 @@ +"""The cisco_ios component.""" diff --git a/homeassistant/components/device_tracker/cisco_ios.py b/homeassistant/components/cisco_ios/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/cisco_ios.py rename to homeassistant/components/cisco_ios/device_tracker.py diff --git a/homeassistant/components/citybikes/__init__.py b/homeassistant/components/citybikes/__init__.py new file mode 100644 index 00000000000..03ae63eb034 --- /dev/null +++ b/homeassistant/components/citybikes/__init__.py @@ -0,0 +1 @@ +"""The citybikes component.""" diff --git a/homeassistant/components/sensor/citybikes.py b/homeassistant/components/citybikes/sensor.py similarity index 100% rename from homeassistant/components/sensor/citybikes.py rename to homeassistant/components/citybikes/sensor.py diff --git a/homeassistant/components/clementine/__init__.py b/homeassistant/components/clementine/__init__.py new file mode 100644 index 00000000000..668ba937345 --- /dev/null +++ b/homeassistant/components/clementine/__init__.py @@ -0,0 +1 @@ +"""The clementine component.""" diff --git a/homeassistant/components/media_player/clementine.py b/homeassistant/components/clementine/media_player.py similarity index 100% rename from homeassistant/components/media_player/clementine.py rename to homeassistant/components/clementine/media_player.py diff --git a/homeassistant/components/cmus/__init__.py b/homeassistant/components/cmus/__init__.py new file mode 100644 index 00000000000..f46f661ece3 --- /dev/null +++ b/homeassistant/components/cmus/__init__.py @@ -0,0 +1 @@ +"""The cmus component.""" diff --git a/homeassistant/components/media_player/cmus.py b/homeassistant/components/cmus/media_player.py similarity index 100% rename from homeassistant/components/media_player/cmus.py rename to homeassistant/components/cmus/media_player.py diff --git a/homeassistant/components/co2signal/__init__.py b/homeassistant/components/co2signal/__init__.py new file mode 100644 index 00000000000..a9c6422b4c6 --- /dev/null +++ b/homeassistant/components/co2signal/__init__.py @@ -0,0 +1 @@ +"""The co2signal component.""" diff --git a/homeassistant/components/sensor/co2signal.py b/homeassistant/components/co2signal/sensor.py similarity index 100% rename from homeassistant/components/sensor/co2signal.py rename to homeassistant/components/co2signal/sensor.py diff --git a/homeassistant/components/sensor/coinbase.py b/homeassistant/components/coinbase/sensor.py similarity index 100% rename from homeassistant/components/sensor/coinbase.py rename to homeassistant/components/coinbase/sensor.py diff --git a/homeassistant/components/comed_hourly_pricing/__init__.py b/homeassistant/components/comed_hourly_pricing/__init__.py new file mode 100644 index 00000000000..db6c2100e48 --- /dev/null +++ b/homeassistant/components/comed_hourly_pricing/__init__.py @@ -0,0 +1 @@ +"""The comed_hourly_pricing component.""" diff --git a/homeassistant/components/sensor/comed_hourly_pricing.py b/homeassistant/components/comed_hourly_pricing/sensor.py similarity index 100% rename from homeassistant/components/sensor/comed_hourly_pricing.py rename to homeassistant/components/comed_hourly_pricing/sensor.py diff --git a/homeassistant/components/concord232/__init__.py b/homeassistant/components/concord232/__init__.py new file mode 100644 index 00000000000..aec6c38ed5c --- /dev/null +++ b/homeassistant/components/concord232/__init__.py @@ -0,0 +1 @@ +"""The concord232 component.""" diff --git a/homeassistant/components/alarm_control_panel/concord232.py b/homeassistant/components/concord232/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/concord232.py rename to homeassistant/components/concord232/alarm_control_panel.py diff --git a/homeassistant/components/binary_sensor/concord232.py b/homeassistant/components/concord232/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/concord232.py rename to homeassistant/components/concord232/binary_sensor.py diff --git a/homeassistant/components/coolmaster/__init__.py b/homeassistant/components/coolmaster/__init__.py new file mode 100644 index 00000000000..b27ae5f25b4 --- /dev/null +++ b/homeassistant/components/coolmaster/__init__.py @@ -0,0 +1 @@ +"""The coolmaster component.""" diff --git a/homeassistant/components/climate/coolmaster.py b/homeassistant/components/coolmaster/climate.py similarity index 100% rename from homeassistant/components/climate/coolmaster.py rename to homeassistant/components/coolmaster/climate.py diff --git a/homeassistant/components/cpuspeed/__init__.py b/homeassistant/components/cpuspeed/__init__.py new file mode 100644 index 00000000000..c6121a68835 --- /dev/null +++ b/homeassistant/components/cpuspeed/__init__.py @@ -0,0 +1 @@ +"""The cpuspeed component.""" diff --git a/homeassistant/components/sensor/cpuspeed.py b/homeassistant/components/cpuspeed/sensor.py similarity index 100% rename from homeassistant/components/sensor/cpuspeed.py rename to homeassistant/components/cpuspeed/sensor.py diff --git a/homeassistant/components/crimereports/__init__.py b/homeassistant/components/crimereports/__init__.py new file mode 100644 index 00000000000..57af9df4dbf --- /dev/null +++ b/homeassistant/components/crimereports/__init__.py @@ -0,0 +1 @@ +"""The crimereports component.""" diff --git a/homeassistant/components/sensor/crimereports.py b/homeassistant/components/crimereports/sensor.py similarity index 100% rename from homeassistant/components/sensor/crimereports.py rename to homeassistant/components/crimereports/sensor.py diff --git a/homeassistant/components/cups/__init__.py b/homeassistant/components/cups/__init__.py new file mode 100644 index 00000000000..7cd5ce4ca0a --- /dev/null +++ b/homeassistant/components/cups/__init__.py @@ -0,0 +1 @@ +"""The cups component.""" diff --git a/homeassistant/components/sensor/cups.py b/homeassistant/components/cups/sensor.py similarity index 97% rename from homeassistant/components/sensor/cups.py rename to homeassistant/components/cups/sensor.py index b002d39352a..99dadcfe596 100644 --- a/homeassistant/components/sensor/cups.py +++ b/homeassistant/components/cups/sensor.py @@ -4,6 +4,7 @@ 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/ """ +import importlib import logging from datetime import timedelta @@ -140,7 +141,7 @@ class CupsData: def update(self): """Get the latest data from CUPS.""" - from cups import Connection + cups = importlib.import_module('cups') - conn = Connection(host=self._host, port=self._port) + conn = cups.Connection(host=self._host, port=self._port) self.printers = conn.getPrinters() diff --git a/homeassistant/components/currencylayer/__init__.py b/homeassistant/components/currencylayer/__init__.py new file mode 100644 index 00000000000..237392ec13d --- /dev/null +++ b/homeassistant/components/currencylayer/__init__.py @@ -0,0 +1 @@ +"""The currencylayer component.""" diff --git a/homeassistant/components/sensor/currencylayer.py b/homeassistant/components/currencylayer/sensor.py similarity index 100% rename from homeassistant/components/sensor/currencylayer.py rename to homeassistant/components/currencylayer/sensor.py diff --git a/homeassistant/components/ddwrt/__init__.py b/homeassistant/components/ddwrt/__init__.py new file mode 100644 index 00000000000..a2c36811285 --- /dev/null +++ b/homeassistant/components/ddwrt/__init__.py @@ -0,0 +1 @@ +"""The ddwrt component.""" diff --git a/homeassistant/components/device_tracker/ddwrt.py b/homeassistant/components/ddwrt/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/ddwrt.py rename to homeassistant/components/ddwrt/device_tracker.py diff --git a/homeassistant/components/decora/__init__.py b/homeassistant/components/decora/__init__.py new file mode 100644 index 00000000000..694ff77fdb3 --- /dev/null +++ b/homeassistant/components/decora/__init__.py @@ -0,0 +1 @@ +"""The decora component.""" diff --git a/homeassistant/components/light/decora.py b/homeassistant/components/decora/light.py similarity index 98% rename from homeassistant/components/light/decora.py rename to homeassistant/components/decora/light.py index 5de8e03aea5..7c3274cf83b 100644 --- a/homeassistant/components/light/decora.py +++ b/homeassistant/components/decora/light.py @@ -4,6 +4,7 @@ Support for Decora dimmers. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.decora/ """ +import importlib import logging from functools import wraps import time @@ -76,7 +77,7 @@ class DecoraLight(Light): def __init__(self, device): """Initialize the light.""" # pylint: disable=no-member - import decora + decora = importlib.import_module('decora') self._name = device['name'] self._address = device['address'] diff --git a/homeassistant/components/decora_wifi/__init__.py b/homeassistant/components/decora_wifi/__init__.py new file mode 100644 index 00000000000..b4bea73456e --- /dev/null +++ b/homeassistant/components/decora_wifi/__init__.py @@ -0,0 +1 @@ +"""The decora_wifi component.""" diff --git a/homeassistant/components/light/decora_wifi.py b/homeassistant/components/decora_wifi/light.py similarity index 100% rename from homeassistant/components/light/decora_wifi.py rename to homeassistant/components/decora_wifi/light.py diff --git a/homeassistant/components/deluge/__init__.py b/homeassistant/components/deluge/__init__.py new file mode 100644 index 00000000000..ad40b688fcf --- /dev/null +++ b/homeassistant/components/deluge/__init__.py @@ -0,0 +1 @@ +"""The deluge component.""" diff --git a/homeassistant/components/sensor/deluge.py b/homeassistant/components/deluge/sensor.py similarity index 100% rename from homeassistant/components/sensor/deluge.py rename to homeassistant/components/deluge/sensor.py diff --git a/homeassistant/components/switch/deluge.py b/homeassistant/components/deluge/switch.py similarity index 100% rename from homeassistant/components/switch/deluge.py rename to homeassistant/components/deluge/switch.py diff --git a/homeassistant/components/air_quality/demo.py b/homeassistant/components/demo/air_quality.py similarity index 100% rename from homeassistant/components/air_quality/demo.py rename to homeassistant/components/demo/air_quality.py diff --git a/homeassistant/components/alarm_control_panel/demo.py b/homeassistant/components/demo/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/demo.py rename to homeassistant/components/demo/alarm_control_panel.py diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/demo/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/demo.py rename to homeassistant/components/demo/binary_sensor.py diff --git a/homeassistant/components/device_tracker/demo.py b/homeassistant/components/demo/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/demo.py rename to homeassistant/components/demo/device_tracker.py diff --git a/homeassistant/components/image_processing/demo.py b/homeassistant/components/demo/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/demo.py rename to homeassistant/components/demo/image_processing.py diff --git a/homeassistant/components/mailbox/demo.py b/homeassistant/components/demo/mailbox.py similarity index 100% rename from homeassistant/components/mailbox/demo.py rename to homeassistant/components/demo/mailbox.py diff --git a/homeassistant/components/sensor/demo.py b/homeassistant/components/demo/sensor.py similarity index 100% rename from homeassistant/components/sensor/demo.py rename to homeassistant/components/demo/sensor.py diff --git a/homeassistant/components/switch/demo.py b/homeassistant/components/demo/switch.py similarity index 100% rename from homeassistant/components/switch/demo.py rename to homeassistant/components/demo/switch.py diff --git a/homeassistant/components/weather/demo.py b/homeassistant/components/demo/weather.py similarity index 100% rename from homeassistant/components/weather/demo.py rename to homeassistant/components/demo/weather.py diff --git a/homeassistant/components/denon/__init__.py b/homeassistant/components/denon/__init__.py new file mode 100644 index 00000000000..ab8cd1b896e --- /dev/null +++ b/homeassistant/components/denon/__init__.py @@ -0,0 +1 @@ +"""The denon component.""" diff --git a/homeassistant/components/media_player/denon.py b/homeassistant/components/denon/media_player.py similarity index 100% rename from homeassistant/components/media_player/denon.py rename to homeassistant/components/denon/media_player.py diff --git a/homeassistant/components/denonavr/__init__.py b/homeassistant/components/denonavr/__init__.py new file mode 100644 index 00000000000..dee84449d13 --- /dev/null +++ b/homeassistant/components/denonavr/__init__.py @@ -0,0 +1 @@ +"""The denonavr component.""" diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/denonavr/media_player.py similarity index 100% rename from homeassistant/components/media_player/denonavr.py rename to homeassistant/components/denonavr/media_player.py diff --git a/homeassistant/components/deutsche_bahn/__init__.py b/homeassistant/components/deutsche_bahn/__init__.py new file mode 100644 index 00000000000..0b696174fd5 --- /dev/null +++ b/homeassistant/components/deutsche_bahn/__init__.py @@ -0,0 +1 @@ +"""The deutsche_bahn component.""" diff --git a/homeassistant/components/sensor/deutsche_bahn.py b/homeassistant/components/deutsche_bahn/sensor.py similarity index 100% rename from homeassistant/components/sensor/deutsche_bahn.py rename to homeassistant/components/deutsche_bahn/sensor.py diff --git a/homeassistant/components/dht/__init__.py b/homeassistant/components/dht/__init__.py new file mode 100644 index 00000000000..23aa2b9d9df --- /dev/null +++ b/homeassistant/components/dht/__init__.py @@ -0,0 +1 @@ +"""The dht component.""" diff --git a/homeassistant/components/sensor/dht.py b/homeassistant/components/dht/sensor.py similarity index 100% rename from homeassistant/components/sensor/dht.py rename to homeassistant/components/dht/sensor.py diff --git a/homeassistant/components/digitalloggers/__init__.py b/homeassistant/components/digitalloggers/__init__.py new file mode 100644 index 00000000000..6db88ca93d2 --- /dev/null +++ b/homeassistant/components/digitalloggers/__init__.py @@ -0,0 +1 @@ +"""The digitalloggers component.""" diff --git a/homeassistant/components/switch/digitalloggers.py b/homeassistant/components/digitalloggers/switch.py similarity index 100% rename from homeassistant/components/switch/digitalloggers.py rename to homeassistant/components/digitalloggers/switch.py diff --git a/homeassistant/components/discogs/__init__.py b/homeassistant/components/discogs/__init__.py new file mode 100644 index 00000000000..90a17763ea6 --- /dev/null +++ b/homeassistant/components/discogs/__init__.py @@ -0,0 +1 @@ +"""The discogs component.""" diff --git a/homeassistant/components/sensor/discogs.py b/homeassistant/components/discogs/sensor.py similarity index 100% rename from homeassistant/components/sensor/discogs.py rename to homeassistant/components/discogs/sensor.py diff --git a/homeassistant/components/dlib_face_detect/__init__.py b/homeassistant/components/dlib_face_detect/__init__.py new file mode 100644 index 00000000000..a732132955f --- /dev/null +++ b/homeassistant/components/dlib_face_detect/__init__.py @@ -0,0 +1 @@ +"""The dlib_face_detect component.""" diff --git a/homeassistant/components/image_processing/dlib_face_detect.py b/homeassistant/components/dlib_face_detect/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/dlib_face_detect.py rename to homeassistant/components/dlib_face_detect/image_processing.py diff --git a/homeassistant/components/dlib_face_identify/__init__.py b/homeassistant/components/dlib_face_identify/__init__.py new file mode 100644 index 00000000000..79b9e4ec4bc --- /dev/null +++ b/homeassistant/components/dlib_face_identify/__init__.py @@ -0,0 +1 @@ +"""The dlib_face_identify component.""" diff --git a/homeassistant/components/image_processing/dlib_face_identify.py b/homeassistant/components/dlib_face_identify/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/dlib_face_identify.py rename to homeassistant/components/dlib_face_identify/image_processing.py diff --git a/homeassistant/components/dlink/__init__.py b/homeassistant/components/dlink/__init__.py new file mode 100644 index 00000000000..644e7975a0e --- /dev/null +++ b/homeassistant/components/dlink/__init__.py @@ -0,0 +1 @@ +"""The dlink component.""" diff --git a/homeassistant/components/switch/dlink.py b/homeassistant/components/dlink/switch.py similarity index 100% rename from homeassistant/components/switch/dlink.py rename to homeassistant/components/dlink/switch.py diff --git a/homeassistant/components/dlna_dmr/__init__.py b/homeassistant/components/dlna_dmr/__init__.py new file mode 100644 index 00000000000..f38456ec6ee --- /dev/null +++ b/homeassistant/components/dlna_dmr/__init__.py @@ -0,0 +1 @@ +"""The dlna_dmr component.""" diff --git a/homeassistant/components/media_player/dlna_dmr.py b/homeassistant/components/dlna_dmr/media_player.py similarity index 100% rename from homeassistant/components/media_player/dlna_dmr.py rename to homeassistant/components/dlna_dmr/media_player.py diff --git a/homeassistant/components/dnsip/__init__.py b/homeassistant/components/dnsip/__init__.py new file mode 100644 index 00000000000..603e8403e74 --- /dev/null +++ b/homeassistant/components/dnsip/__init__.py @@ -0,0 +1 @@ +"""The dnsip component.""" diff --git a/homeassistant/components/sensor/dnsip.py b/homeassistant/components/dnsip/sensor.py similarity index 100% rename from homeassistant/components/sensor/dnsip.py rename to homeassistant/components/dnsip/sensor.py diff --git a/homeassistant/components/dublin_bus_transport/__init__.py b/homeassistant/components/dublin_bus_transport/__init__.py new file mode 100644 index 00000000000..138950af2b5 --- /dev/null +++ b/homeassistant/components/dublin_bus_transport/__init__.py @@ -0,0 +1 @@ +"""The dublin_bus_transport component.""" diff --git a/homeassistant/components/sensor/dublin_bus_transport.py b/homeassistant/components/dublin_bus_transport/sensor.py similarity index 100% rename from homeassistant/components/sensor/dublin_bus_transport.py rename to homeassistant/components/dublin_bus_transport/sensor.py diff --git a/homeassistant/components/duke_energy/__init__.py b/homeassistant/components/duke_energy/__init__.py new file mode 100644 index 00000000000..5a1f29add43 --- /dev/null +++ b/homeassistant/components/duke_energy/__init__.py @@ -0,0 +1 @@ +"""The duke_energy component.""" diff --git a/homeassistant/components/sensor/duke_energy.py b/homeassistant/components/duke_energy/sensor.py similarity index 100% rename from homeassistant/components/sensor/duke_energy.py rename to homeassistant/components/duke_energy/sensor.py diff --git a/homeassistant/components/dunehd/__init__.py b/homeassistant/components/dunehd/__init__.py new file mode 100644 index 00000000000..a8b8a8cf7af --- /dev/null +++ b/homeassistant/components/dunehd/__init__.py @@ -0,0 +1 @@ +"""The dunehd component.""" diff --git a/homeassistant/components/media_player/dunehd.py b/homeassistant/components/dunehd/media_player.py similarity index 100% rename from homeassistant/components/media_player/dunehd.py rename to homeassistant/components/dunehd/media_player.py diff --git a/homeassistant/components/dwd_weather_warnings/__init__.py b/homeassistant/components/dwd_weather_warnings/__init__.py new file mode 100644 index 00000000000..1841291f7a9 --- /dev/null +++ b/homeassistant/components/dwd_weather_warnings/__init__.py @@ -0,0 +1 @@ +"""The dwd_weather_warnings component.""" diff --git a/homeassistant/components/sensor/dwd_weather_warnings.py b/homeassistant/components/dwd_weather_warnings/sensor.py similarity index 100% rename from homeassistant/components/sensor/dwd_weather_warnings.py rename to homeassistant/components/dwd_weather_warnings/sensor.py diff --git a/homeassistant/components/ebox/__init__.py b/homeassistant/components/ebox/__init__.py new file mode 100644 index 00000000000..3f807666a4b --- /dev/null +++ b/homeassistant/components/ebox/__init__.py @@ -0,0 +1 @@ +"""The ebox component.""" diff --git a/homeassistant/components/sensor/ebox.py b/homeassistant/components/ebox/sensor.py similarity index 100% rename from homeassistant/components/sensor/ebox.py rename to homeassistant/components/ebox/sensor.py diff --git a/homeassistant/components/econet/__init__.py b/homeassistant/components/econet/__init__.py new file mode 100644 index 00000000000..48b7dad4c7c --- /dev/null +++ b/homeassistant/components/econet/__init__.py @@ -0,0 +1 @@ +"""The econet component.""" diff --git a/homeassistant/components/water_heater/econet.py b/homeassistant/components/econet/water_heater.py similarity index 100% rename from homeassistant/components/water_heater/econet.py rename to homeassistant/components/econet/water_heater.py diff --git a/homeassistant/components/eddystone_temperature/__init__.py b/homeassistant/components/eddystone_temperature/__init__.py new file mode 100644 index 00000000000..2d6f92498bd --- /dev/null +++ b/homeassistant/components/eddystone_temperature/__init__.py @@ -0,0 +1 @@ +"""The eddystone_temperature component.""" diff --git a/homeassistant/components/sensor/eddystone_temperature.py b/homeassistant/components/eddystone_temperature/sensor.py similarity index 100% rename from homeassistant/components/sensor/eddystone_temperature.py rename to homeassistant/components/eddystone_temperature/sensor.py diff --git a/homeassistant/components/edimax/__init__.py b/homeassistant/components/edimax/__init__.py new file mode 100644 index 00000000000..33614bf4f95 --- /dev/null +++ b/homeassistant/components/edimax/__init__.py @@ -0,0 +1 @@ +"""The edimax component.""" diff --git a/homeassistant/components/switch/edimax.py b/homeassistant/components/edimax/switch.py similarity index 100% rename from homeassistant/components/switch/edimax.py rename to homeassistant/components/edimax/switch.py diff --git a/homeassistant/components/eliqonline/__init__.py b/homeassistant/components/eliqonline/__init__.py new file mode 100644 index 00000000000..4cb38436ee4 --- /dev/null +++ b/homeassistant/components/eliqonline/__init__.py @@ -0,0 +1 @@ +"""The eliqonline component.""" diff --git a/homeassistant/components/sensor/eliqonline.py b/homeassistant/components/eliqonline/sensor.py similarity index 100% rename from homeassistant/components/sensor/eliqonline.py rename to homeassistant/components/eliqonline/sensor.py diff --git a/homeassistant/components/emby/__init__.py b/homeassistant/components/emby/__init__.py new file mode 100644 index 00000000000..053da956c64 --- /dev/null +++ b/homeassistant/components/emby/__init__.py @@ -0,0 +1 @@ +"""The emby component.""" diff --git a/homeassistant/components/media_player/emby.py b/homeassistant/components/emby/media_player.py similarity index 100% rename from homeassistant/components/media_player/emby.py rename to homeassistant/components/emby/media_player.py diff --git a/homeassistant/components/emoncms/__init__.py b/homeassistant/components/emoncms/__init__.py new file mode 100644 index 00000000000..5e7adbcd6e7 --- /dev/null +++ b/homeassistant/components/emoncms/__init__.py @@ -0,0 +1 @@ +"""The emoncms component.""" diff --git a/homeassistant/components/sensor/emoncms.py b/homeassistant/components/emoncms/sensor.py similarity index 100% rename from homeassistant/components/sensor/emoncms.py rename to homeassistant/components/emoncms/sensor.py diff --git a/homeassistant/components/enphase_envoy/__init__.py b/homeassistant/components/enphase_envoy/__init__.py new file mode 100644 index 00000000000..c4101fbcdf2 --- /dev/null +++ b/homeassistant/components/enphase_envoy/__init__.py @@ -0,0 +1 @@ +"""The enphase_envoy component.""" diff --git a/homeassistant/components/sensor/enphase_envoy.py b/homeassistant/components/enphase_envoy/sensor.py similarity index 100% rename from homeassistant/components/sensor/enphase_envoy.py rename to homeassistant/components/enphase_envoy/sensor.py diff --git a/homeassistant/components/envirophat/__init__.py b/homeassistant/components/envirophat/__init__.py new file mode 100644 index 00000000000..68d3a99441c --- /dev/null +++ b/homeassistant/components/envirophat/__init__.py @@ -0,0 +1 @@ +"""The envirophat component.""" diff --git a/homeassistant/components/sensor/envirophat.py b/homeassistant/components/envirophat/sensor.py similarity index 98% rename from homeassistant/components/sensor/envirophat.py rename to homeassistant/components/envirophat/sensor.py index 1c90f5998e8..7683c06b69c 100644 --- a/homeassistant/components/sensor/envirophat.py +++ b/homeassistant/components/envirophat/sensor.py @@ -4,6 +4,7 @@ Support for Enviro pHAT sensors. For more details about this component, please refer to the documentation at https://home-assistant.io/components/sensor.envirophat """ +import importlib import logging from datetime import timedelta @@ -55,7 +56,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sense HAT sensor platform.""" try: - import envirophat + envirophat = importlib.import_module('envirophat') except OSError: _LOGGER.error("No Enviro pHAT was found.") return False diff --git a/homeassistant/components/ephember/__init__.py b/homeassistant/components/ephember/__init__.py new file mode 100644 index 00000000000..97758383c11 --- /dev/null +++ b/homeassistant/components/ephember/__init__.py @@ -0,0 +1 @@ +"""The ephember component.""" diff --git a/homeassistant/components/climate/ephember.py b/homeassistant/components/ephember/climate.py similarity index 100% rename from homeassistant/components/climate/ephember.py rename to homeassistant/components/ephember/climate.py diff --git a/homeassistant/components/epson/__init__.py b/homeassistant/components/epson/__init__.py new file mode 100644 index 00000000000..eed342f77f9 --- /dev/null +++ b/homeassistant/components/epson/__init__.py @@ -0,0 +1 @@ +"""The epson component.""" diff --git a/homeassistant/components/media_player/epson.py b/homeassistant/components/epson/media_player.py similarity index 100% rename from homeassistant/components/media_player/epson.py rename to homeassistant/components/epson/media_player.py diff --git a/homeassistant/components/eq3btsmart/__init__.py b/homeassistant/components/eq3btsmart/__init__.py new file mode 100644 index 00000000000..f32eba6944f --- /dev/null +++ b/homeassistant/components/eq3btsmart/__init__.py @@ -0,0 +1 @@ +"""The eq3btsmart component.""" diff --git a/homeassistant/components/climate/eq3btsmart.py b/homeassistant/components/eq3btsmart/climate.py similarity index 100% rename from homeassistant/components/climate/eq3btsmart.py rename to homeassistant/components/eq3btsmart/climate.py diff --git a/homeassistant/components/etherscan/__init__.py b/homeassistant/components/etherscan/__init__.py new file mode 100644 index 00000000000..0e983bd6bea --- /dev/null +++ b/homeassistant/components/etherscan/__init__.py @@ -0,0 +1 @@ +"""The etherscan component.""" diff --git a/homeassistant/components/sensor/etherscan.py b/homeassistant/components/etherscan/sensor.py similarity index 100% rename from homeassistant/components/sensor/etherscan.py rename to homeassistant/components/etherscan/sensor.py diff --git a/homeassistant/components/familyhub/__init__.py b/homeassistant/components/familyhub/__init__.py new file mode 100644 index 00000000000..1ac09b44a9d --- /dev/null +++ b/homeassistant/components/familyhub/__init__.py @@ -0,0 +1 @@ +"""The familyhub component.""" diff --git a/homeassistant/components/camera/familyhub.py b/homeassistant/components/familyhub/camera.py similarity index 100% rename from homeassistant/components/camera/familyhub.py rename to homeassistant/components/familyhub/camera.py diff --git a/homeassistant/components/fedex/__init__.py b/homeassistant/components/fedex/__init__.py new file mode 100644 index 00000000000..d685ab50372 --- /dev/null +++ b/homeassistant/components/fedex/__init__.py @@ -0,0 +1 @@ +"""The fedex component.""" diff --git a/homeassistant/components/sensor/fedex.py b/homeassistant/components/fedex/sensor.py similarity index 100% rename from homeassistant/components/sensor/fedex.py rename to homeassistant/components/fedex/sensor.py diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/ffmpeg/camera.py similarity index 100% rename from homeassistant/components/camera/ffmpeg.py rename to homeassistant/components/ffmpeg/camera.py diff --git a/homeassistant/components/ffmpeg_motion/__init__.py b/homeassistant/components/ffmpeg_motion/__init__.py new file mode 100644 index 00000000000..b13c0efeacf --- /dev/null +++ b/homeassistant/components/ffmpeg_motion/__init__.py @@ -0,0 +1 @@ +"""The ffmpeg_motion component.""" diff --git a/homeassistant/components/binary_sensor/ffmpeg_motion.py b/homeassistant/components/ffmpeg_motion/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/ffmpeg_motion.py rename to homeassistant/components/ffmpeg_motion/binary_sensor.py diff --git a/homeassistant/components/ffmpeg_noise/__init__.py b/homeassistant/components/ffmpeg_noise/__init__.py new file mode 100644 index 00000000000..ab233df98ce --- /dev/null +++ b/homeassistant/components/ffmpeg_noise/__init__.py @@ -0,0 +1 @@ +"""The ffmpeg_noise component.""" diff --git a/homeassistant/components/binary_sensor/ffmpeg_noise.py b/homeassistant/components/ffmpeg_noise/binary_sensor.py similarity index 97% rename from homeassistant/components/binary_sensor/ffmpeg_noise.py rename to homeassistant/components/ffmpeg_noise/binary_sensor.py index 3c2397d692b..070c8c61b00 100644 --- a/homeassistant/components/binary_sensor/ffmpeg_noise.py +++ b/homeassistant/components/ffmpeg_noise/binary_sensor.py @@ -10,7 +10,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.binary_sensor import PLATFORM_SCHEMA -from homeassistant.components.binary_sensor.ffmpeg_motion import ( +from homeassistant.components.ffmpeg_motion.binary_sensor import ( FFmpegBinarySensor) from homeassistant.components.ffmpeg import ( DATA_FFMPEG, CONF_INPUT, CONF_OUTPUT, CONF_EXTRA_ARGUMENTS, diff --git a/homeassistant/components/fints/__init__.py b/homeassistant/components/fints/__init__.py new file mode 100644 index 00000000000..0113fa75234 --- /dev/null +++ b/homeassistant/components/fints/__init__.py @@ -0,0 +1 @@ +"""The fints component.""" diff --git a/homeassistant/components/sensor/fints.py b/homeassistant/components/fints/sensor.py similarity index 100% rename from homeassistant/components/sensor/fints.py rename to homeassistant/components/fints/sensor.py diff --git a/homeassistant/components/fitbit/__init__.py b/homeassistant/components/fitbit/__init__.py new file mode 100644 index 00000000000..04946f6386f --- /dev/null +++ b/homeassistant/components/fitbit/__init__.py @@ -0,0 +1 @@ +"""The fitbit component.""" diff --git a/homeassistant/components/sensor/fitbit.py b/homeassistant/components/fitbit/sensor.py similarity index 100% rename from homeassistant/components/sensor/fitbit.py rename to homeassistant/components/fitbit/sensor.py diff --git a/homeassistant/components/fixer/__init__.py b/homeassistant/components/fixer/__init__.py new file mode 100644 index 00000000000..a5023b5db70 --- /dev/null +++ b/homeassistant/components/fixer/__init__.py @@ -0,0 +1 @@ +"""The fixer component.""" diff --git a/homeassistant/components/sensor/fixer.py b/homeassistant/components/fixer/sensor.py similarity index 100% rename from homeassistant/components/sensor/fixer.py rename to homeassistant/components/fixer/sensor.py diff --git a/homeassistant/components/flexit/__init__.py b/homeassistant/components/flexit/__init__.py new file mode 100644 index 00000000000..4ace1a38945 --- /dev/null +++ b/homeassistant/components/flexit/__init__.py @@ -0,0 +1 @@ +"""The flexit component.""" diff --git a/homeassistant/components/climate/flexit.py b/homeassistant/components/flexit/climate.py similarity index 100% rename from homeassistant/components/climate/flexit.py rename to homeassistant/components/flexit/climate.py diff --git a/homeassistant/components/flic/__init__.py b/homeassistant/components/flic/__init__.py new file mode 100644 index 00000000000..b15b06217c1 --- /dev/null +++ b/homeassistant/components/flic/__init__.py @@ -0,0 +1 @@ +"""The flic component.""" diff --git a/homeassistant/components/binary_sensor/flic.py b/homeassistant/components/flic/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/flic.py rename to homeassistant/components/flic/binary_sensor.py diff --git a/homeassistant/components/flunearyou/__init__.py b/homeassistant/components/flunearyou/__init__.py new file mode 100644 index 00000000000..5657e646be5 --- /dev/null +++ b/homeassistant/components/flunearyou/__init__.py @@ -0,0 +1 @@ +"""The flunearyou component.""" diff --git a/homeassistant/components/sensor/flunearyou.py b/homeassistant/components/flunearyou/sensor.py similarity index 100% rename from homeassistant/components/sensor/flunearyou.py rename to homeassistant/components/flunearyou/sensor.py diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py new file mode 100644 index 00000000000..572d6e3c983 --- /dev/null +++ b/homeassistant/components/flux_led/__init__.py @@ -0,0 +1 @@ +"""The flux_led component.""" diff --git a/homeassistant/components/light/flux_led.py b/homeassistant/components/flux_led/light.py similarity index 100% rename from homeassistant/components/light/flux_led.py rename to homeassistant/components/flux_led/light.py diff --git a/homeassistant/components/foscam/__init__.py b/homeassistant/components/foscam/__init__.py new file mode 100644 index 00000000000..5c63f7b2a15 --- /dev/null +++ b/homeassistant/components/foscam/__init__.py @@ -0,0 +1 @@ +"""The foscam component.""" diff --git a/homeassistant/components/camera/foscam.py b/homeassistant/components/foscam/camera.py similarity index 100% rename from homeassistant/components/camera/foscam.py rename to homeassistant/components/foscam/camera.py diff --git a/homeassistant/components/fritz/__init__.py b/homeassistant/components/fritz/__init__.py new file mode 100644 index 00000000000..7069a29f163 --- /dev/null +++ b/homeassistant/components/fritz/__init__.py @@ -0,0 +1 @@ +"""The fritz component.""" diff --git a/homeassistant/components/device_tracker/fritz.py b/homeassistant/components/fritz/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/fritz.py rename to homeassistant/components/fritz/device_tracker.py diff --git a/homeassistant/components/fritzbox_callmonitor/__init__.py b/homeassistant/components/fritzbox_callmonitor/__init__.py new file mode 100644 index 00000000000..f9a52021606 --- /dev/null +++ b/homeassistant/components/fritzbox_callmonitor/__init__.py @@ -0,0 +1 @@ +"""The fritzbox_callmonitor component.""" diff --git a/homeassistant/components/sensor/fritzbox_callmonitor.py b/homeassistant/components/fritzbox_callmonitor/sensor.py similarity index 100% rename from homeassistant/components/sensor/fritzbox_callmonitor.py rename to homeassistant/components/fritzbox_callmonitor/sensor.py diff --git a/homeassistant/components/fritzbox_netmonitor/__init__.py b/homeassistant/components/fritzbox_netmonitor/__init__.py new file mode 100644 index 00000000000..8bea1da4a44 --- /dev/null +++ b/homeassistant/components/fritzbox_netmonitor/__init__.py @@ -0,0 +1 @@ +"""The fritzbox_netmonitor component.""" diff --git a/homeassistant/components/sensor/fritzbox_netmonitor.py b/homeassistant/components/fritzbox_netmonitor/sensor.py similarity index 100% rename from homeassistant/components/sensor/fritzbox_netmonitor.py rename to homeassistant/components/fritzbox_netmonitor/sensor.py diff --git a/homeassistant/components/fritzdect/__init__.py b/homeassistant/components/fritzdect/__init__.py new file mode 100644 index 00000000000..d64990bc3f0 --- /dev/null +++ b/homeassistant/components/fritzdect/__init__.py @@ -0,0 +1 @@ +"""The fritzdect component.""" diff --git a/homeassistant/components/switch/fritzdect.py b/homeassistant/components/fritzdect/switch.py similarity index 100% rename from homeassistant/components/switch/fritzdect.py rename to homeassistant/components/fritzdect/switch.py diff --git a/homeassistant/components/frontier_silicon/__init__.py b/homeassistant/components/frontier_silicon/__init__.py new file mode 100644 index 00000000000..ddd74ca8efe --- /dev/null +++ b/homeassistant/components/frontier_silicon/__init__.py @@ -0,0 +1 @@ +"""The frontier_silicon component.""" diff --git a/homeassistant/components/media_player/frontier_silicon.py b/homeassistant/components/frontier_silicon/media_player.py similarity index 100% rename from homeassistant/components/media_player/frontier_silicon.py rename to homeassistant/components/frontier_silicon/media_player.py diff --git a/homeassistant/components/futurenow/__init__.py b/homeassistant/components/futurenow/__init__.py new file mode 100644 index 00000000000..530911fecf9 --- /dev/null +++ b/homeassistant/components/futurenow/__init__.py @@ -0,0 +1 @@ +"""The futurenow component.""" diff --git a/homeassistant/components/light/futurenow.py b/homeassistant/components/futurenow/light.py similarity index 100% rename from homeassistant/components/light/futurenow.py rename to homeassistant/components/futurenow/light.py diff --git a/homeassistant/components/garadget/__init__.py b/homeassistant/components/garadget/__init__.py new file mode 100644 index 00000000000..3d3977e9596 --- /dev/null +++ b/homeassistant/components/garadget/__init__.py @@ -0,0 +1 @@ +"""The garadget component.""" diff --git a/homeassistant/components/cover/garadget.py b/homeassistant/components/garadget/cover.py similarity index 100% rename from homeassistant/components/cover/garadget.py rename to homeassistant/components/garadget/cover.py diff --git a/homeassistant/components/gearbest/__init__.py b/homeassistant/components/gearbest/__init__.py new file mode 100644 index 00000000000..c97d9469296 --- /dev/null +++ b/homeassistant/components/gearbest/__init__.py @@ -0,0 +1 @@ +"""The gearbest component.""" diff --git a/homeassistant/components/sensor/gearbest.py b/homeassistant/components/gearbest/sensor.py similarity index 100% rename from homeassistant/components/sensor/gearbest.py rename to homeassistant/components/gearbest/sensor.py diff --git a/homeassistant/components/geizhals/__init__.py b/homeassistant/components/geizhals/__init__.py new file mode 100644 index 00000000000..28b1d623073 --- /dev/null +++ b/homeassistant/components/geizhals/__init__.py @@ -0,0 +1 @@ +"""The geizhals component.""" diff --git a/homeassistant/components/sensor/geizhals.py b/homeassistant/components/geizhals/sensor.py similarity index 100% rename from homeassistant/components/sensor/geizhals.py rename to homeassistant/components/geizhals/sensor.py diff --git a/homeassistant/components/github/__init__.py b/homeassistant/components/github/__init__.py new file mode 100644 index 00000000000..6dd5d7f16ff --- /dev/null +++ b/homeassistant/components/github/__init__.py @@ -0,0 +1 @@ +"""The github component.""" diff --git a/homeassistant/components/sensor/github.py b/homeassistant/components/github/sensor.py similarity index 100% rename from homeassistant/components/sensor/github.py rename to homeassistant/components/github/sensor.py diff --git a/homeassistant/components/gitlab_ci/__init__.py b/homeassistant/components/gitlab_ci/__init__.py new file mode 100644 index 00000000000..93b2a08c714 --- /dev/null +++ b/homeassistant/components/gitlab_ci/__init__.py @@ -0,0 +1 @@ +"""The gitlab_ci component.""" diff --git a/homeassistant/components/sensor/gitlab_ci.py b/homeassistant/components/gitlab_ci/sensor.py similarity index 100% rename from homeassistant/components/sensor/gitlab_ci.py rename to homeassistant/components/gitlab_ci/sensor.py diff --git a/homeassistant/components/gitter/__init__.py b/homeassistant/components/gitter/__init__.py new file mode 100644 index 00000000000..25656f70f56 --- /dev/null +++ b/homeassistant/components/gitter/__init__.py @@ -0,0 +1 @@ +"""The gitter component.""" diff --git a/homeassistant/components/sensor/gitter.py b/homeassistant/components/gitter/sensor.py similarity index 100% rename from homeassistant/components/sensor/gitter.py rename to homeassistant/components/gitter/sensor.py diff --git a/homeassistant/components/glances/__init__.py b/homeassistant/components/glances/__init__.py new file mode 100644 index 00000000000..b458d8788fc --- /dev/null +++ b/homeassistant/components/glances/__init__.py @@ -0,0 +1 @@ +"""The glances component.""" diff --git a/homeassistant/components/sensor/glances.py b/homeassistant/components/glances/sensor.py similarity index 100% rename from homeassistant/components/sensor/glances.py rename to homeassistant/components/glances/sensor.py diff --git a/homeassistant/components/gogogate2/__init__.py b/homeassistant/components/gogogate2/__init__.py new file mode 100644 index 00000000000..ef802a4aa59 --- /dev/null +++ b/homeassistant/components/gogogate2/__init__.py @@ -0,0 +1 @@ +"""The gogogate2 component.""" diff --git a/homeassistant/components/cover/gogogate2.py b/homeassistant/components/gogogate2/cover.py similarity index 100% rename from homeassistant/components/cover/gogogate2.py rename to homeassistant/components/gogogate2/cover.py diff --git a/homeassistant/components/google_maps/__init__.py b/homeassistant/components/google_maps/__init__.py new file mode 100644 index 00000000000..929df26fa0f --- /dev/null +++ b/homeassistant/components/google_maps/__init__.py @@ -0,0 +1 @@ +"""The google_maps component.""" diff --git a/homeassistant/components/device_tracker/google_maps.py b/homeassistant/components/google_maps/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/google_maps.py rename to homeassistant/components/google_maps/device_tracker.py diff --git a/homeassistant/components/google_travel_time/__init__.py b/homeassistant/components/google_travel_time/__init__.py new file mode 100644 index 00000000000..9d9a7cffe1d --- /dev/null +++ b/homeassistant/components/google_travel_time/__init__.py @@ -0,0 +1 @@ +"""The google_travel_time component.""" diff --git a/homeassistant/components/sensor/google_travel_time.py b/homeassistant/components/google_travel_time/sensor.py similarity index 100% rename from homeassistant/components/sensor/google_travel_time.py rename to homeassistant/components/google_travel_time/sensor.py diff --git a/homeassistant/components/gpmdp/__init__.py b/homeassistant/components/gpmdp/__init__.py new file mode 100644 index 00000000000..a8aa82c69c3 --- /dev/null +++ b/homeassistant/components/gpmdp/__init__.py @@ -0,0 +1 @@ +"""The gpmdp component.""" diff --git a/homeassistant/components/media_player/gpmdp.py b/homeassistant/components/gpmdp/media_player.py similarity index 100% rename from homeassistant/components/media_player/gpmdp.py rename to homeassistant/components/gpmdp/media_player.py diff --git a/homeassistant/components/gpsd/__init__.py b/homeassistant/components/gpsd/__init__.py new file mode 100644 index 00000000000..71656d4d13d --- /dev/null +++ b/homeassistant/components/gpsd/__init__.py @@ -0,0 +1 @@ +"""The gpsd component.""" diff --git a/homeassistant/components/sensor/gpsd.py b/homeassistant/components/gpsd/sensor.py similarity index 100% rename from homeassistant/components/sensor/gpsd.py rename to homeassistant/components/gpsd/sensor.py diff --git a/homeassistant/components/gpslogger/__init__.py b/homeassistant/components/gpslogger/__init__.py index 2e956bd7fc5..12da63d8ebb 100644 --- a/homeassistant/components/gpslogger/__init__.py +++ b/homeassistant/components/gpslogger/__init__.py @@ -6,7 +6,6 @@ from aiohttp import web import homeassistant.helpers.config_validation as cv from homeassistant.components.device_tracker import ATTR_BATTERY -from homeassistant.components.device_tracker.tile import ATTR_ALTITUDE from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, \ HTTP_OK, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_WEBHOOK_ID from homeassistant.helpers import config_entry_flow @@ -20,6 +19,7 @@ DEPENDENCIES = ['webhook'] TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN) +ATTR_ALTITUDE = 'altitude' ATTR_ACCURACY = 'accuracy' ATTR_ACTIVITY = 'activity' ATTR_DEVICE = 'device' diff --git a/homeassistant/components/sensor/greeneye_monitor.py b/homeassistant/components/greeneye_monitor/sensor.py similarity index 100% rename from homeassistant/components/sensor/greeneye_monitor.py rename to homeassistant/components/greeneye_monitor/sensor.py diff --git a/homeassistant/components/greenwave/__init__.py b/homeassistant/components/greenwave/__init__.py new file mode 100644 index 00000000000..a7bd0cf437e --- /dev/null +++ b/homeassistant/components/greenwave/__init__.py @@ -0,0 +1 @@ +"""The greenwave component.""" diff --git a/homeassistant/components/light/greenwave.py b/homeassistant/components/greenwave/light.py similarity index 100% rename from homeassistant/components/light/greenwave.py rename to homeassistant/components/greenwave/light.py diff --git a/homeassistant/components/gstreamer/__init__.py b/homeassistant/components/gstreamer/__init__.py new file mode 100644 index 00000000000..9fb97d25744 --- /dev/null +++ b/homeassistant/components/gstreamer/__init__.py @@ -0,0 +1 @@ +"""The gstreamer component.""" diff --git a/homeassistant/components/media_player/gstreamer.py b/homeassistant/components/gstreamer/media_player.py similarity index 100% rename from homeassistant/components/media_player/gstreamer.py rename to homeassistant/components/gstreamer/media_player.py diff --git a/homeassistant/components/gtfs/__init__.py b/homeassistant/components/gtfs/__init__.py new file mode 100644 index 00000000000..9c503c2bb96 --- /dev/null +++ b/homeassistant/components/gtfs/__init__.py @@ -0,0 +1 @@ +"""The gtfs component.""" diff --git a/homeassistant/components/sensor/gtfs.py b/homeassistant/components/gtfs/sensor.py similarity index 100% rename from homeassistant/components/sensor/gtfs.py rename to homeassistant/components/gtfs/sensor.py diff --git a/homeassistant/components/gtt/__init__.py b/homeassistant/components/gtt/__init__.py new file mode 100644 index 00000000000..cbb508154dd --- /dev/null +++ b/homeassistant/components/gtt/__init__.py @@ -0,0 +1 @@ +"""The gtt component.""" diff --git a/homeassistant/components/sensor/gtt.py b/homeassistant/components/gtt/sensor.py similarity index 100% rename from homeassistant/components/sensor/gtt.py rename to homeassistant/components/gtt/sensor.py diff --git a/homeassistant/components/harman_kardon_avr/__init__.py b/homeassistant/components/harman_kardon_avr/__init__.py new file mode 100644 index 00000000000..c9e3afd6be3 --- /dev/null +++ b/homeassistant/components/harman_kardon_avr/__init__.py @@ -0,0 +1 @@ +"""The harman_kardon_avr component.""" diff --git a/homeassistant/components/media_player/harman_kardon_avr.py b/homeassistant/components/harman_kardon_avr/media_player.py similarity index 100% rename from homeassistant/components/media_player/harman_kardon_avr.py rename to homeassistant/components/harman_kardon_avr/media_player.py diff --git a/homeassistant/components/haveibeenpwned/__init__.py b/homeassistant/components/haveibeenpwned/__init__.py new file mode 100644 index 00000000000..adead4ec46e --- /dev/null +++ b/homeassistant/components/haveibeenpwned/__init__.py @@ -0,0 +1 @@ +"""The haveibeenpwned component.""" diff --git a/homeassistant/components/sensor/haveibeenpwned.py b/homeassistant/components/haveibeenpwned/sensor.py similarity index 100% rename from homeassistant/components/sensor/haveibeenpwned.py rename to homeassistant/components/haveibeenpwned/sensor.py diff --git a/homeassistant/components/heatmiser/__init__.py b/homeassistant/components/heatmiser/__init__.py new file mode 100644 index 00000000000..bc6313f9e3d --- /dev/null +++ b/homeassistant/components/heatmiser/__init__.py @@ -0,0 +1 @@ +"""The heatmiser component.""" diff --git a/homeassistant/components/climate/heatmiser.py b/homeassistant/components/heatmiser/climate.py similarity index 100% rename from homeassistant/components/climate/heatmiser.py rename to homeassistant/components/heatmiser/climate.py diff --git a/homeassistant/components/hikvision/__init__.py b/homeassistant/components/hikvision/__init__.py new file mode 100644 index 00000000000..dbf7991b3c4 --- /dev/null +++ b/homeassistant/components/hikvision/__init__.py @@ -0,0 +1 @@ +"""The hikvision component.""" diff --git a/homeassistant/components/binary_sensor/hikvision.py b/homeassistant/components/hikvision/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/hikvision.py rename to homeassistant/components/hikvision/binary_sensor.py diff --git a/homeassistant/components/hikvisioncam/__init__.py b/homeassistant/components/hikvisioncam/__init__.py new file mode 100644 index 00000000000..32a2a86b28f --- /dev/null +++ b/homeassistant/components/hikvisioncam/__init__.py @@ -0,0 +1 @@ +"""The hikvisioncam component.""" diff --git a/homeassistant/components/switch/hikvisioncam.py b/homeassistant/components/hikvisioncam/switch.py similarity index 100% rename from homeassistant/components/switch/hikvisioncam.py rename to homeassistant/components/hikvisioncam/switch.py diff --git a/homeassistant/components/hitron_coda/__init__.py b/homeassistant/components/hitron_coda/__init__.py new file mode 100644 index 00000000000..de65a34f3a4 --- /dev/null +++ b/homeassistant/components/hitron_coda/__init__.py @@ -0,0 +1 @@ +"""The hitron_coda component.""" diff --git a/homeassistant/components/device_tracker/hitron_coda.py b/homeassistant/components/hitron_coda/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/hitron_coda.py rename to homeassistant/components/hitron_coda/device_tracker.py diff --git a/homeassistant/components/hook/__init__.py b/homeassistant/components/hook/__init__.py new file mode 100644 index 00000000000..bc85e27d742 --- /dev/null +++ b/homeassistant/components/hook/__init__.py @@ -0,0 +1 @@ +"""The hook component.""" diff --git a/homeassistant/components/switch/hook.py b/homeassistant/components/hook/switch.py similarity index 100% rename from homeassistant/components/switch/hook.py rename to homeassistant/components/hook/switch.py diff --git a/homeassistant/components/horizon/__init__.py b/homeassistant/components/horizon/__init__.py new file mode 100644 index 00000000000..77ac25098c3 --- /dev/null +++ b/homeassistant/components/horizon/__init__.py @@ -0,0 +1 @@ +"""The horizon component.""" diff --git a/homeassistant/components/media_player/horizon.py b/homeassistant/components/horizon/media_player.py similarity index 100% rename from homeassistant/components/media_player/horizon.py rename to homeassistant/components/horizon/media_player.py diff --git a/homeassistant/components/hp_ilo/__init__.py b/homeassistant/components/hp_ilo/__init__.py new file mode 100644 index 00000000000..3cd6b10b260 --- /dev/null +++ b/homeassistant/components/hp_ilo/__init__.py @@ -0,0 +1 @@ +"""The hp_ilo component.""" diff --git a/homeassistant/components/sensor/hp_ilo.py b/homeassistant/components/hp_ilo/sensor.py similarity index 100% rename from homeassistant/components/sensor/hp_ilo.py rename to homeassistant/components/hp_ilo/sensor.py diff --git a/homeassistant/components/htu21d/__init__.py b/homeassistant/components/htu21d/__init__.py new file mode 100644 index 00000000000..c36c8bfcffb --- /dev/null +++ b/homeassistant/components/htu21d/__init__.py @@ -0,0 +1 @@ +"""The htu21d component.""" diff --git a/homeassistant/components/sensor/htu21d.py b/homeassistant/components/htu21d/sensor.py similarity index 100% rename from homeassistant/components/sensor/htu21d.py rename to homeassistant/components/htu21d/sensor.py diff --git a/homeassistant/components/huawei_router/__init__.py b/homeassistant/components/huawei_router/__init__.py new file mode 100644 index 00000000000..861809992c6 --- /dev/null +++ b/homeassistant/components/huawei_router/__init__.py @@ -0,0 +1 @@ +"""The huawei_router component.""" diff --git a/homeassistant/components/device_tracker/huawei_router.py b/homeassistant/components/huawei_router/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/huawei_router.py rename to homeassistant/components/huawei_router/device_tracker.py diff --git a/homeassistant/components/hunterdouglas_powerview/__init__.py b/homeassistant/components/hunterdouglas_powerview/__init__.py new file mode 100644 index 00000000000..14ede545576 --- /dev/null +++ b/homeassistant/components/hunterdouglas_powerview/__init__.py @@ -0,0 +1 @@ +"""The hunterdouglas_powerview component.""" diff --git a/homeassistant/components/scene/hunterdouglas_powerview.py b/homeassistant/components/hunterdouglas_powerview/scene.py similarity index 100% rename from homeassistant/components/scene/hunterdouglas_powerview.py rename to homeassistant/components/hunterdouglas_powerview/scene.py diff --git a/homeassistant/components/hyperion/__init__.py b/homeassistant/components/hyperion/__init__.py new file mode 100644 index 00000000000..2e78b777f7d --- /dev/null +++ b/homeassistant/components/hyperion/__init__.py @@ -0,0 +1 @@ +"""The hyperion component.""" diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/hyperion/light.py similarity index 100% rename from homeassistant/components/light/hyperion.py rename to homeassistant/components/hyperion/light.py diff --git a/homeassistant/components/ialarm/__init__.py b/homeassistant/components/ialarm/__init__.py new file mode 100644 index 00000000000..d03609bc1d0 --- /dev/null +++ b/homeassistant/components/ialarm/__init__.py @@ -0,0 +1 @@ +"""The ialarm component.""" diff --git a/homeassistant/components/alarm_control_panel/ialarm.py b/homeassistant/components/ialarm/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/ialarm.py rename to homeassistant/components/ialarm/alarm_control_panel.py diff --git a/homeassistant/components/icloud/__init__.py b/homeassistant/components/icloud/__init__.py new file mode 100644 index 00000000000..1169104c99d --- /dev/null +++ b/homeassistant/components/icloud/__init__.py @@ -0,0 +1 @@ +"""The icloud component.""" diff --git a/homeassistant/components/device_tracker/icloud.py b/homeassistant/components/icloud/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/icloud.py rename to homeassistant/components/icloud/device_tracker.py diff --git a/homeassistant/components/iglo/__init__.py b/homeassistant/components/iglo/__init__.py new file mode 100644 index 00000000000..6e5ca1ad93b --- /dev/null +++ b/homeassistant/components/iglo/__init__.py @@ -0,0 +1 @@ +"""The iglo component.""" diff --git a/homeassistant/components/light/iglo.py b/homeassistant/components/iglo/light.py similarity index 100% rename from homeassistant/components/light/iglo.py rename to homeassistant/components/iglo/light.py diff --git a/homeassistant/components/iliad_italy/__init__.py b/homeassistant/components/iliad_italy/__init__.py new file mode 100644 index 00000000000..c171befd196 --- /dev/null +++ b/homeassistant/components/iliad_italy/__init__.py @@ -0,0 +1 @@ +"""The iliad_italy component.""" diff --git a/homeassistant/components/sensor/iliad_italy.py b/homeassistant/components/iliad_italy/sensor.py similarity index 100% rename from homeassistant/components/sensor/iliad_italy.py rename to homeassistant/components/iliad_italy/sensor.py diff --git a/homeassistant/components/imap/__init__.py b/homeassistant/components/imap/__init__.py new file mode 100644 index 00000000000..d85f295a43e --- /dev/null +++ b/homeassistant/components/imap/__init__.py @@ -0,0 +1 @@ +"""The imap component.""" diff --git a/homeassistant/components/sensor/imap.py b/homeassistant/components/imap/sensor.py similarity index 100% rename from homeassistant/components/sensor/imap.py rename to homeassistant/components/imap/sensor.py diff --git a/homeassistant/components/sensor/influxdb.py b/homeassistant/components/influxdb/sensor.py similarity index 100% rename from homeassistant/components/sensor/influxdb.py rename to homeassistant/components/influxdb/sensor.py diff --git a/homeassistant/components/irish_rail_transport/__init__.py b/homeassistant/components/irish_rail_transport/__init__.py new file mode 100644 index 00000000000..197b5fe7e99 --- /dev/null +++ b/homeassistant/components/irish_rail_transport/__init__.py @@ -0,0 +1 @@ +"""The irish_rail_transport component.""" diff --git a/homeassistant/components/sensor/irish_rail_transport.py b/homeassistant/components/irish_rail_transport/sensor.py similarity index 100% rename from homeassistant/components/sensor/irish_rail_transport.py rename to homeassistant/components/irish_rail_transport/sensor.py diff --git a/homeassistant/components/iss/__init__.py b/homeassistant/components/iss/__init__.py new file mode 100644 index 00000000000..51487bdfaf2 --- /dev/null +++ b/homeassistant/components/iss/__init__.py @@ -0,0 +1 @@ +"""The iss component.""" diff --git a/homeassistant/components/binary_sensor/iss.py b/homeassistant/components/iss/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/iss.py rename to homeassistant/components/iss/binary_sensor.py diff --git a/homeassistant/components/itunes/__init__.py b/homeassistant/components/itunes/__init__.py new file mode 100644 index 00000000000..561d9d47b37 --- /dev/null +++ b/homeassistant/components/itunes/__init__.py @@ -0,0 +1 @@ +"""The itunes component.""" diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/itunes/media_player.py similarity index 100% rename from homeassistant/components/media_player/itunes.py rename to homeassistant/components/itunes/media_player.py diff --git a/homeassistant/components/kankun/__init__.py b/homeassistant/components/kankun/__init__.py new file mode 100644 index 00000000000..dca32748c1c --- /dev/null +++ b/homeassistant/components/kankun/__init__.py @@ -0,0 +1 @@ +"""The kankun component.""" diff --git a/homeassistant/components/switch/kankun.py b/homeassistant/components/kankun/switch.py similarity index 100% rename from homeassistant/components/switch/kankun.py rename to homeassistant/components/kankun/switch.py diff --git a/homeassistant/components/keenetic_ndms2/__init__.py b/homeassistant/components/keenetic_ndms2/__init__.py new file mode 100644 index 00000000000..cb0a718d716 --- /dev/null +++ b/homeassistant/components/keenetic_ndms2/__init__.py @@ -0,0 +1 @@ +"""The keenetic_ndms2 component.""" diff --git a/homeassistant/components/device_tracker/keenetic_ndms2.py b/homeassistant/components/keenetic_ndms2/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/keenetic_ndms2.py rename to homeassistant/components/keenetic_ndms2/device_tracker.py diff --git a/homeassistant/components/kiwi/__init__.py b/homeassistant/components/kiwi/__init__.py new file mode 100644 index 00000000000..00e903b0c0d --- /dev/null +++ b/homeassistant/components/kiwi/__init__.py @@ -0,0 +1 @@ +"""The kiwi component.""" diff --git a/homeassistant/components/lock/kiwi.py b/homeassistant/components/kiwi/lock.py similarity index 100% rename from homeassistant/components/lock/kiwi.py rename to homeassistant/components/kiwi/lock.py diff --git a/homeassistant/components/kodi/__init__.py b/homeassistant/components/kodi/__init__.py new file mode 100644 index 00000000000..cbe20384103 --- /dev/null +++ b/homeassistant/components/kodi/__init__.py @@ -0,0 +1 @@ +"""The kodi component.""" diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/kodi/media_player.py similarity index 100% rename from homeassistant/components/media_player/kodi.py rename to homeassistant/components/kodi/media_player.py diff --git a/homeassistant/components/kwb/__init__.py b/homeassistant/components/kwb/__init__.py new file mode 100644 index 00000000000..e48a7b79d40 --- /dev/null +++ b/homeassistant/components/kwb/__init__.py @@ -0,0 +1 @@ +"""The kwb component.""" diff --git a/homeassistant/components/sensor/kwb.py b/homeassistant/components/kwb/sensor.py similarity index 100% rename from homeassistant/components/sensor/kwb.py rename to homeassistant/components/kwb/sensor.py diff --git a/homeassistant/components/lacrosse/__init__.py b/homeassistant/components/lacrosse/__init__.py new file mode 100644 index 00000000000..5334c696aee --- /dev/null +++ b/homeassistant/components/lacrosse/__init__.py @@ -0,0 +1 @@ +"""The lacrosse component.""" diff --git a/homeassistant/components/sensor/lacrosse.py b/homeassistant/components/lacrosse/sensor.py similarity index 100% rename from homeassistant/components/sensor/lacrosse.py rename to homeassistant/components/lacrosse/sensor.py diff --git a/homeassistant/components/lastfm/__init__.py b/homeassistant/components/lastfm/__init__.py new file mode 100644 index 00000000000..20128342931 --- /dev/null +++ b/homeassistant/components/lastfm/__init__.py @@ -0,0 +1 @@ +"""The lastfm component.""" diff --git a/homeassistant/components/sensor/lastfm.py b/homeassistant/components/lastfm/sensor.py similarity index 100% rename from homeassistant/components/sensor/lastfm.py rename to homeassistant/components/lastfm/sensor.py diff --git a/homeassistant/components/launch_library/__init__.py b/homeassistant/components/launch_library/__init__.py new file mode 100644 index 00000000000..ba4b78ab31f --- /dev/null +++ b/homeassistant/components/launch_library/__init__.py @@ -0,0 +1 @@ +"""The launch_library component.""" diff --git a/homeassistant/components/sensor/launch_library.py b/homeassistant/components/launch_library/sensor.py similarity index 100% rename from homeassistant/components/sensor/launch_library.py rename to homeassistant/components/launch_library/sensor.py diff --git a/homeassistant/components/lg_netcast/__init__.py b/homeassistant/components/lg_netcast/__init__.py new file mode 100644 index 00000000000..232d7bd10b8 --- /dev/null +++ b/homeassistant/components/lg_netcast/__init__.py @@ -0,0 +1 @@ +"""The lg_netcast component.""" diff --git a/homeassistant/components/media_player/lg_netcast.py b/homeassistant/components/lg_netcast/media_player.py similarity index 100% rename from homeassistant/components/media_player/lg_netcast.py rename to homeassistant/components/lg_netcast/media_player.py diff --git a/homeassistant/components/lg_soundbar/__init__.py b/homeassistant/components/lg_soundbar/__init__.py new file mode 100644 index 00000000000..175153556f9 --- /dev/null +++ b/homeassistant/components/lg_soundbar/__init__.py @@ -0,0 +1 @@ +"""The lg_soundbar component.""" diff --git a/homeassistant/components/media_player/lg_soundbar.py b/homeassistant/components/lg_soundbar/media_player.py similarity index 100% rename from homeassistant/components/media_player/lg_soundbar.py rename to homeassistant/components/lg_soundbar/media_player.py diff --git a/homeassistant/components/lifx_cloud/__init__.py b/homeassistant/components/lifx_cloud/__init__.py new file mode 100644 index 00000000000..c524b629671 --- /dev/null +++ b/homeassistant/components/lifx_cloud/__init__.py @@ -0,0 +1 @@ +"""The lifx_cloud component.""" diff --git a/homeassistant/components/scene/lifx_cloud.py b/homeassistant/components/lifx_cloud/scene.py similarity index 100% rename from homeassistant/components/scene/lifx_cloud.py rename to homeassistant/components/lifx_cloud/scene.py diff --git a/homeassistant/components/lifx_legacy/__init__.py b/homeassistant/components/lifx_legacy/__init__.py new file mode 100644 index 00000000000..83d5a0e5048 --- /dev/null +++ b/homeassistant/components/lifx_legacy/__init__.py @@ -0,0 +1 @@ +"""The lifx_legacy component.""" diff --git a/homeassistant/components/light/lifx_legacy.py b/homeassistant/components/lifx_legacy/light.py similarity index 100% rename from homeassistant/components/light/lifx_legacy.py rename to homeassistant/components/lifx_legacy/light.py diff --git a/homeassistant/components/limitlessled/__init__.py b/homeassistant/components/limitlessled/__init__.py new file mode 100644 index 00000000000..dd3c339456c --- /dev/null +++ b/homeassistant/components/limitlessled/__init__.py @@ -0,0 +1 @@ +"""The limitlessled component.""" diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/limitlessled/light.py similarity index 100% rename from homeassistant/components/light/limitlessled.py rename to homeassistant/components/limitlessled/light.py diff --git a/homeassistant/components/linksys_ap/__init__.py b/homeassistant/components/linksys_ap/__init__.py new file mode 100644 index 00000000000..5898aa36e98 --- /dev/null +++ b/homeassistant/components/linksys_ap/__init__.py @@ -0,0 +1 @@ +"""The linksys_ap component.""" diff --git a/homeassistant/components/device_tracker/linksys_ap.py b/homeassistant/components/linksys_ap/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/linksys_ap.py rename to homeassistant/components/linksys_ap/device_tracker.py diff --git a/homeassistant/components/linksys_smart/__init__.py b/homeassistant/components/linksys_smart/__init__.py new file mode 100644 index 00000000000..489596c7ec6 --- /dev/null +++ b/homeassistant/components/linksys_smart/__init__.py @@ -0,0 +1 @@ +"""The linksys_smart component.""" diff --git a/homeassistant/components/device_tracker/linksys_smart.py b/homeassistant/components/linksys_smart/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/linksys_smart.py rename to homeassistant/components/linksys_smart/device_tracker.py diff --git a/homeassistant/components/linky/__init__.py b/homeassistant/components/linky/__init__.py new file mode 100644 index 00000000000..345f13e8a57 --- /dev/null +++ b/homeassistant/components/linky/__init__.py @@ -0,0 +1 @@ +"""The linky component.""" diff --git a/homeassistant/components/sensor/linky.py b/homeassistant/components/linky/sensor.py similarity index 100% rename from homeassistant/components/sensor/linky.py rename to homeassistant/components/linky/sensor.py diff --git a/homeassistant/components/linux_battery/__init__.py b/homeassistant/components/linux_battery/__init__.py new file mode 100644 index 00000000000..a0882bd0f89 --- /dev/null +++ b/homeassistant/components/linux_battery/__init__.py @@ -0,0 +1 @@ +"""The linux_battery component.""" diff --git a/homeassistant/components/sensor/linux_battery.py b/homeassistant/components/linux_battery/sensor.py similarity index 100% rename from homeassistant/components/sensor/linux_battery.py rename to homeassistant/components/linux_battery/sensor.py diff --git a/homeassistant/components/liveboxplaytv/__init__.py b/homeassistant/components/liveboxplaytv/__init__.py new file mode 100644 index 00000000000..384c0e4c34b --- /dev/null +++ b/homeassistant/components/liveboxplaytv/__init__.py @@ -0,0 +1 @@ +"""The liveboxplaytv component.""" diff --git a/homeassistant/components/media_player/liveboxplaytv.py b/homeassistant/components/liveboxplaytv/media_player.py similarity index 100% rename from homeassistant/components/media_player/liveboxplaytv.py rename to homeassistant/components/liveboxplaytv/media_player.py diff --git a/homeassistant/components/lockitron/__init__.py b/homeassistant/components/lockitron/__init__.py new file mode 100644 index 00000000000..d2f9f749533 --- /dev/null +++ b/homeassistant/components/lockitron/__init__.py @@ -0,0 +1 @@ +"""The lockitron component.""" diff --git a/homeassistant/components/lock/lockitron.py b/homeassistant/components/lockitron/lock.py similarity index 100% rename from homeassistant/components/lock/lockitron.py rename to homeassistant/components/lockitron/lock.py diff --git a/homeassistant/components/london_underground/__init__.py b/homeassistant/components/london_underground/__init__.py new file mode 100644 index 00000000000..b38aba6dbc3 --- /dev/null +++ b/homeassistant/components/london_underground/__init__.py @@ -0,0 +1 @@ +"""The london_underground component.""" diff --git a/homeassistant/components/sensor/london_underground.py b/homeassistant/components/london_underground/sensor.py similarity index 100% rename from homeassistant/components/sensor/london_underground.py rename to homeassistant/components/london_underground/sensor.py diff --git a/homeassistant/components/loopenergy/__init__.py b/homeassistant/components/loopenergy/__init__.py new file mode 100644 index 00000000000..4e963f2828a --- /dev/null +++ b/homeassistant/components/loopenergy/__init__.py @@ -0,0 +1 @@ +"""The loopenergy component.""" diff --git a/homeassistant/components/sensor/loopenergy.py b/homeassistant/components/loopenergy/sensor.py similarity index 100% rename from homeassistant/components/sensor/loopenergy.py rename to homeassistant/components/loopenergy/sensor.py diff --git a/homeassistant/components/luci/__init__.py b/homeassistant/components/luci/__init__.py new file mode 100644 index 00000000000..b0efa61ae77 --- /dev/null +++ b/homeassistant/components/luci/__init__.py @@ -0,0 +1 @@ +"""The luci component.""" diff --git a/homeassistant/components/device_tracker/luci.py b/homeassistant/components/luci/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/luci.py rename to homeassistant/components/luci/device_tracker.py diff --git a/homeassistant/components/lw12wifi/__init__.py b/homeassistant/components/lw12wifi/__init__.py new file mode 100644 index 00000000000..d356a51547c --- /dev/null +++ b/homeassistant/components/lw12wifi/__init__.py @@ -0,0 +1 @@ +"""The lw12wifi component.""" diff --git a/homeassistant/components/light/lw12wifi.py b/homeassistant/components/lw12wifi/light.py similarity index 100% rename from homeassistant/components/light/lw12wifi.py rename to homeassistant/components/lw12wifi/light.py diff --git a/homeassistant/components/lyft/__init__.py b/homeassistant/components/lyft/__init__.py new file mode 100644 index 00000000000..a7ffe972cc9 --- /dev/null +++ b/homeassistant/components/lyft/__init__.py @@ -0,0 +1 @@ +"""The lyft component.""" diff --git a/homeassistant/components/sensor/lyft.py b/homeassistant/components/lyft/sensor.py similarity index 100% rename from homeassistant/components/sensor/lyft.py rename to homeassistant/components/lyft/sensor.py diff --git a/homeassistant/components/magicseaweed/__init__.py b/homeassistant/components/magicseaweed/__init__.py new file mode 100644 index 00000000000..848d02967fe --- /dev/null +++ b/homeassistant/components/magicseaweed/__init__.py @@ -0,0 +1 @@ +"""The magicseaweed component.""" diff --git a/homeassistant/components/sensor/magicseaweed.py b/homeassistant/components/magicseaweed/sensor.py similarity index 100% rename from homeassistant/components/sensor/magicseaweed.py rename to homeassistant/components/magicseaweed/sensor.py diff --git a/homeassistant/components/mediaroom/__init__.py b/homeassistant/components/mediaroom/__init__.py new file mode 100644 index 00000000000..71ed614773a --- /dev/null +++ b/homeassistant/components/mediaroom/__init__.py @@ -0,0 +1 @@ +"""The mediaroom component.""" diff --git a/homeassistant/components/media_player/mediaroom.py b/homeassistant/components/mediaroom/media_player.py similarity index 100% rename from homeassistant/components/media_player/mediaroom.py rename to homeassistant/components/mediaroom/media_player.py diff --git a/homeassistant/components/met/__init__.py b/homeassistant/components/met/__init__.py new file mode 100644 index 00000000000..67bd64f3e16 --- /dev/null +++ b/homeassistant/components/met/__init__.py @@ -0,0 +1 @@ +"""The met component.""" diff --git a/homeassistant/components/weather/met.py b/homeassistant/components/met/weather.py similarity index 100% rename from homeassistant/components/weather/met.py rename to homeassistant/components/met/weather.py diff --git a/homeassistant/components/metoffice/__init__.py b/homeassistant/components/metoffice/__init__.py new file mode 100644 index 00000000000..94cc8b636d4 --- /dev/null +++ b/homeassistant/components/metoffice/__init__.py @@ -0,0 +1 @@ +"""The metoffice component.""" diff --git a/homeassistant/components/sensor/metoffice.py b/homeassistant/components/metoffice/sensor.py similarity index 100% rename from homeassistant/components/sensor/metoffice.py rename to homeassistant/components/metoffice/sensor.py diff --git a/homeassistant/components/weather/metoffice.py b/homeassistant/components/metoffice/weather.py similarity index 98% rename from homeassistant/components/weather/metoffice.py rename to homeassistant/components/metoffice/weather.py index 3b52eebcff6..b4984bc3cad 100644 --- a/homeassistant/components/weather/metoffice.py +++ b/homeassistant/components/metoffice/weather.py @@ -3,7 +3,7 @@ import logging import voluptuous as vol -from homeassistant.components.sensor.metoffice import ( +from homeassistant.components.metoffice.sensor import ( CONDITION_CLASSES, ATTRIBUTION, MetOfficeCurrentData) from homeassistant.components.weather import PLATFORM_SCHEMA, WeatherEntity from homeassistant.const import ( diff --git a/homeassistant/components/miflora/__init__.py b/homeassistant/components/miflora/__init__.py new file mode 100644 index 00000000000..ed1569e1af0 --- /dev/null +++ b/homeassistant/components/miflora/__init__.py @@ -0,0 +1 @@ +"""The miflora component.""" diff --git a/homeassistant/components/sensor/miflora.py b/homeassistant/components/miflora/sensor.py similarity index 100% rename from homeassistant/components/sensor/miflora.py rename to homeassistant/components/miflora/sensor.py diff --git a/homeassistant/components/mikrotik/__init__.py b/homeassistant/components/mikrotik/__init__.py new file mode 100644 index 00000000000..0fe5a1c70b1 --- /dev/null +++ b/homeassistant/components/mikrotik/__init__.py @@ -0,0 +1 @@ +"""The mikrotik component.""" diff --git a/homeassistant/components/device_tracker/mikrotik.py b/homeassistant/components/mikrotik/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/mikrotik.py rename to homeassistant/components/mikrotik/device_tracker.py diff --git a/homeassistant/components/mill/__init__.py b/homeassistant/components/mill/__init__.py new file mode 100644 index 00000000000..157ea345efd --- /dev/null +++ b/homeassistant/components/mill/__init__.py @@ -0,0 +1 @@ +"""The mill component.""" diff --git a/homeassistant/components/climate/mill.py b/homeassistant/components/mill/climate.py similarity index 100% rename from homeassistant/components/climate/mill.py rename to homeassistant/components/mill/climate.py diff --git a/homeassistant/components/mitemp_bt/__init__.py b/homeassistant/components/mitemp_bt/__init__.py new file mode 100644 index 00000000000..785956572af --- /dev/null +++ b/homeassistant/components/mitemp_bt/__init__.py @@ -0,0 +1 @@ +"""The mitemp_bt component.""" diff --git a/homeassistant/components/sensor/mitemp_bt.py b/homeassistant/components/mitemp_bt/sensor.py similarity index 100% rename from homeassistant/components/sensor/mitemp_bt.py rename to homeassistant/components/mitemp_bt/sensor.py diff --git a/homeassistant/components/mjpeg/__init__.py b/homeassistant/components/mjpeg/__init__.py new file mode 100644 index 00000000000..3e7469cff00 --- /dev/null +++ b/homeassistant/components/mjpeg/__init__.py @@ -0,0 +1 @@ +"""The mjpeg component.""" diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/mjpeg/camera.py similarity index 100% rename from homeassistant/components/camera/mjpeg.py rename to homeassistant/components/mjpeg/camera.py diff --git a/homeassistant/components/modem_callerid/__init__.py b/homeassistant/components/modem_callerid/__init__.py new file mode 100644 index 00000000000..0ce41b0ea03 --- /dev/null +++ b/homeassistant/components/modem_callerid/__init__.py @@ -0,0 +1 @@ +"""The modem_callerid component.""" diff --git a/homeassistant/components/sensor/modem_callerid.py b/homeassistant/components/modem_callerid/sensor.py similarity index 100% rename from homeassistant/components/sensor/modem_callerid.py rename to homeassistant/components/modem_callerid/sensor.py diff --git a/homeassistant/components/mopar/__init__.py b/homeassistant/components/mopar/__init__.py new file mode 100644 index 00000000000..f13076fb763 --- /dev/null +++ b/homeassistant/components/mopar/__init__.py @@ -0,0 +1 @@ +"""The mopar component.""" diff --git a/homeassistant/components/sensor/mopar.py b/homeassistant/components/mopar/sensor.py similarity index 100% rename from homeassistant/components/sensor/mopar.py rename to homeassistant/components/mopar/sensor.py diff --git a/homeassistant/components/mpchc/__init__.py b/homeassistant/components/mpchc/__init__.py new file mode 100644 index 00000000000..e8a0057a9b6 --- /dev/null +++ b/homeassistant/components/mpchc/__init__.py @@ -0,0 +1 @@ +"""The mpchc component.""" diff --git a/homeassistant/components/media_player/mpchc.py b/homeassistant/components/mpchc/media_player.py similarity index 100% rename from homeassistant/components/media_player/mpchc.py rename to homeassistant/components/mpchc/media_player.py diff --git a/homeassistant/components/mpd/__init__.py b/homeassistant/components/mpd/__init__.py new file mode 100644 index 00000000000..bf917ff19aa --- /dev/null +++ b/homeassistant/components/mpd/__init__.py @@ -0,0 +1 @@ +"""The mpd component.""" diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/mpd/media_player.py similarity index 100% rename from homeassistant/components/media_player/mpd.py rename to homeassistant/components/mpd/media_player.py diff --git a/homeassistant/components/mvglive/__init__.py b/homeassistant/components/mvglive/__init__.py new file mode 100644 index 00000000000..b475746c440 --- /dev/null +++ b/homeassistant/components/mvglive/__init__.py @@ -0,0 +1 @@ +"""The mvglive component.""" diff --git a/homeassistant/components/sensor/mvglive.py b/homeassistant/components/mvglive/sensor.py similarity index 100% rename from homeassistant/components/sensor/mvglive.py rename to homeassistant/components/mvglive/sensor.py diff --git a/homeassistant/components/myq/__init__.py b/homeassistant/components/myq/__init__.py new file mode 100644 index 00000000000..e9fa7900d90 --- /dev/null +++ b/homeassistant/components/myq/__init__.py @@ -0,0 +1 @@ +"""The myq component.""" diff --git a/homeassistant/components/cover/myq.py b/homeassistant/components/myq/cover.py similarity index 100% rename from homeassistant/components/cover/myq.py rename to homeassistant/components/myq/cover.py diff --git a/homeassistant/components/mystrom/__init__.py b/homeassistant/components/mystrom/__init__.py new file mode 100644 index 00000000000..54a24b9b4af --- /dev/null +++ b/homeassistant/components/mystrom/__init__.py @@ -0,0 +1 @@ +"""The mystrom component.""" diff --git a/homeassistant/components/binary_sensor/mystrom.py b/homeassistant/components/mystrom/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/mystrom.py rename to homeassistant/components/mystrom/binary_sensor.py diff --git a/homeassistant/components/light/mystrom.py b/homeassistant/components/mystrom/light.py similarity index 100% rename from homeassistant/components/light/mystrom.py rename to homeassistant/components/mystrom/light.py diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/mystrom/switch.py similarity index 100% rename from homeassistant/components/switch/mystrom.py rename to homeassistant/components/mystrom/switch.py diff --git a/homeassistant/components/nad/__init__.py b/homeassistant/components/nad/__init__.py new file mode 100644 index 00000000000..4fd52c874a0 --- /dev/null +++ b/homeassistant/components/nad/__init__.py @@ -0,0 +1 @@ +"""The nad component.""" diff --git a/homeassistant/components/media_player/nad.py b/homeassistant/components/nad/media_player.py similarity index 100% rename from homeassistant/components/media_player/nad.py rename to homeassistant/components/nad/media_player.py diff --git a/homeassistant/components/nanoleaf/__init__.py b/homeassistant/components/nanoleaf/__init__.py new file mode 100644 index 00000000000..776d6a61772 --- /dev/null +++ b/homeassistant/components/nanoleaf/__init__.py @@ -0,0 +1 @@ +"""The nanoleaf component.""" diff --git a/homeassistant/components/light/nanoleaf.py b/homeassistant/components/nanoleaf/light.py similarity index 100% rename from homeassistant/components/light/nanoleaf.py rename to homeassistant/components/nanoleaf/light.py diff --git a/homeassistant/components/nederlandse_spoorwegen/__init__.py b/homeassistant/components/nederlandse_spoorwegen/__init__.py new file mode 100644 index 00000000000..b052df36e34 --- /dev/null +++ b/homeassistant/components/nederlandse_spoorwegen/__init__.py @@ -0,0 +1 @@ +"""The nederlandse_spoorwegen component.""" diff --git a/homeassistant/components/sensor/nederlandse_spoorwegen.py b/homeassistant/components/nederlandse_spoorwegen/sensor.py similarity index 100% rename from homeassistant/components/sensor/nederlandse_spoorwegen.py rename to homeassistant/components/nederlandse_spoorwegen/sensor.py diff --git a/homeassistant/components/nello/__init__.py b/homeassistant/components/nello/__init__.py new file mode 100644 index 00000000000..dfe556f7f29 --- /dev/null +++ b/homeassistant/components/nello/__init__.py @@ -0,0 +1 @@ +"""The nello component.""" diff --git a/homeassistant/components/lock/nello.py b/homeassistant/components/nello/lock.py similarity index 100% rename from homeassistant/components/lock/nello.py rename to homeassistant/components/nello/lock.py diff --git a/homeassistant/components/alarm_control_panel/ness_alarm.py b/homeassistant/components/ness_alarm/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/ness_alarm.py rename to homeassistant/components/ness_alarm/alarm_control_panel.py diff --git a/homeassistant/components/binary_sensor/ness_alarm.py b/homeassistant/components/ness_alarm/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/ness_alarm.py rename to homeassistant/components/ness_alarm/binary_sensor.py diff --git a/homeassistant/components/netatmo_public/__init__.py b/homeassistant/components/netatmo_public/__init__.py new file mode 100644 index 00000000000..c332d208ddb --- /dev/null +++ b/homeassistant/components/netatmo_public/__init__.py @@ -0,0 +1 @@ +"""The netatmo_public component.""" diff --git a/homeassistant/components/sensor/netatmo_public.py b/homeassistant/components/netatmo_public/sensor.py similarity index 100% rename from homeassistant/components/sensor/netatmo_public.py rename to homeassistant/components/netatmo_public/sensor.py diff --git a/homeassistant/components/netdata/__init__.py b/homeassistant/components/netdata/__init__.py new file mode 100644 index 00000000000..34104716aa0 --- /dev/null +++ b/homeassistant/components/netdata/__init__.py @@ -0,0 +1 @@ +"""The netdata component.""" diff --git a/homeassistant/components/sensor/netdata.py b/homeassistant/components/netdata/sensor.py similarity index 100% rename from homeassistant/components/sensor/netdata.py rename to homeassistant/components/netdata/sensor.py diff --git a/homeassistant/components/netgear/__init__.py b/homeassistant/components/netgear/__init__.py new file mode 100644 index 00000000000..1b55d01b463 --- /dev/null +++ b/homeassistant/components/netgear/__init__.py @@ -0,0 +1 @@ +"""The netgear component.""" diff --git a/homeassistant/components/device_tracker/netgear.py b/homeassistant/components/netgear/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/netgear.py rename to homeassistant/components/netgear/device_tracker.py diff --git a/homeassistant/components/netio/__init__.py b/homeassistant/components/netio/__init__.py new file mode 100644 index 00000000000..0b1b7d2c128 --- /dev/null +++ b/homeassistant/components/netio/__init__.py @@ -0,0 +1 @@ +"""The netio component.""" diff --git a/homeassistant/components/switch/netio.py b/homeassistant/components/netio/switch.py similarity index 100% rename from homeassistant/components/switch/netio.py rename to homeassistant/components/netio/switch.py diff --git a/homeassistant/components/neurio_energy/__init__.py b/homeassistant/components/neurio_energy/__init__.py new file mode 100644 index 00000000000..631556329e4 --- /dev/null +++ b/homeassistant/components/neurio_energy/__init__.py @@ -0,0 +1 @@ +"""The neurio_energy component.""" diff --git a/homeassistant/components/sensor/neurio_energy.py b/homeassistant/components/neurio_energy/sensor.py similarity index 100% rename from homeassistant/components/sensor/neurio_energy.py rename to homeassistant/components/neurio_energy/sensor.py diff --git a/homeassistant/components/niko_home_control/__init__.py b/homeassistant/components/niko_home_control/__init__.py new file mode 100644 index 00000000000..2cb5c70d1dd --- /dev/null +++ b/homeassistant/components/niko_home_control/__init__.py @@ -0,0 +1 @@ +"""The niko_home_control component.""" diff --git a/homeassistant/components/light/niko_home_control.py b/homeassistant/components/niko_home_control/light.py similarity index 100% rename from homeassistant/components/light/niko_home_control.py rename to homeassistant/components/niko_home_control/light.py diff --git a/homeassistant/components/nilu/__init__.py b/homeassistant/components/nilu/__init__.py new file mode 100644 index 00000000000..d45739ef2d6 --- /dev/null +++ b/homeassistant/components/nilu/__init__.py @@ -0,0 +1 @@ +"""The nilu component.""" diff --git a/homeassistant/components/air_quality/nilu.py b/homeassistant/components/nilu/air_quality.py similarity index 100% rename from homeassistant/components/air_quality/nilu.py rename to homeassistant/components/nilu/air_quality.py diff --git a/homeassistant/components/nmap_tracker/__init__.py b/homeassistant/components/nmap_tracker/__init__.py new file mode 100644 index 00000000000..da699caaa73 --- /dev/null +++ b/homeassistant/components/nmap_tracker/__init__.py @@ -0,0 +1 @@ +"""The nmap_tracker component.""" diff --git a/homeassistant/components/device_tracker/nmap_tracker.py b/homeassistant/components/nmap_tracker/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/nmap_tracker.py rename to homeassistant/components/nmap_tracker/device_tracker.py diff --git a/homeassistant/components/nmbs/__init__.py b/homeassistant/components/nmbs/__init__.py new file mode 100644 index 00000000000..11013d471b5 --- /dev/null +++ b/homeassistant/components/nmbs/__init__.py @@ -0,0 +1 @@ +"""The nmbs component.""" diff --git a/homeassistant/components/sensor/nmbs.py b/homeassistant/components/nmbs/sensor.py similarity index 100% rename from homeassistant/components/sensor/nmbs.py rename to homeassistant/components/nmbs/sensor.py diff --git a/homeassistant/components/noaa_tides/__init__.py b/homeassistant/components/noaa_tides/__init__.py new file mode 100644 index 00000000000..398b9e6963c --- /dev/null +++ b/homeassistant/components/noaa_tides/__init__.py @@ -0,0 +1 @@ +"""The noaa_tides component.""" diff --git a/homeassistant/components/sensor/noaa_tides.py b/homeassistant/components/noaa_tides/sensor.py similarity index 100% rename from homeassistant/components/sensor/noaa_tides.py rename to homeassistant/components/noaa_tides/sensor.py diff --git a/homeassistant/components/norway_air/__init__.py b/homeassistant/components/norway_air/__init__.py new file mode 100644 index 00000000000..95955c60c44 --- /dev/null +++ b/homeassistant/components/norway_air/__init__.py @@ -0,0 +1 @@ +"""The norway_air component.""" diff --git a/homeassistant/components/air_quality/norway_air.py b/homeassistant/components/norway_air/air_quality.py similarity index 100% rename from homeassistant/components/air_quality/norway_air.py rename to homeassistant/components/norway_air/air_quality.py diff --git a/homeassistant/components/nuki/__init__.py b/homeassistant/components/nuki/__init__.py new file mode 100644 index 00000000000..2e15ac8a68d --- /dev/null +++ b/homeassistant/components/nuki/__init__.py @@ -0,0 +1 @@ +"""The nuki component.""" diff --git a/homeassistant/components/lock/nuki.py b/homeassistant/components/nuki/lock.py similarity index 100% rename from homeassistant/components/lock/nuki.py rename to homeassistant/components/nuki/lock.py diff --git a/homeassistant/components/nut/__init__.py b/homeassistant/components/nut/__init__.py new file mode 100644 index 00000000000..e51145c8eaa --- /dev/null +++ b/homeassistant/components/nut/__init__.py @@ -0,0 +1 @@ +"""The nut component.""" diff --git a/homeassistant/components/sensor/nut.py b/homeassistant/components/nut/sensor.py similarity index 100% rename from homeassistant/components/sensor/nut.py rename to homeassistant/components/nut/sensor.py diff --git a/homeassistant/components/alarm_control_panel/nx584.py b/homeassistant/components/nx584/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/nx584.py rename to homeassistant/components/nx584/alarm_control_panel.py diff --git a/homeassistant/components/nzbget/__init__.py b/homeassistant/components/nzbget/__init__.py new file mode 100644 index 00000000000..2480daf2ead --- /dev/null +++ b/homeassistant/components/nzbget/__init__.py @@ -0,0 +1 @@ +"""The nzbget component.""" diff --git a/homeassistant/components/sensor/nzbget.py b/homeassistant/components/nzbget/sensor.py similarity index 100% rename from homeassistant/components/sensor/nzbget.py rename to homeassistant/components/nzbget/sensor.py diff --git a/homeassistant/components/oem/__init__.py b/homeassistant/components/oem/__init__.py new file mode 100644 index 00000000000..f78dfee9a5b --- /dev/null +++ b/homeassistant/components/oem/__init__.py @@ -0,0 +1 @@ +"""The oem component.""" diff --git a/homeassistant/components/climate/oem.py b/homeassistant/components/oem/climate.py similarity index 100% rename from homeassistant/components/climate/oem.py rename to homeassistant/components/oem/climate.py diff --git a/homeassistant/components/ohmconnect/__init__.py b/homeassistant/components/ohmconnect/__init__.py new file mode 100644 index 00000000000..1713f82a59b --- /dev/null +++ b/homeassistant/components/ohmconnect/__init__.py @@ -0,0 +1 @@ +"""The ohmconnect component.""" diff --git a/homeassistant/components/sensor/ohmconnect.py b/homeassistant/components/ohmconnect/sensor.py similarity index 100% rename from homeassistant/components/sensor/ohmconnect.py rename to homeassistant/components/ohmconnect/sensor.py diff --git a/homeassistant/components/onewire/__init__.py b/homeassistant/components/onewire/__init__.py new file mode 100644 index 00000000000..ac5d1393378 --- /dev/null +++ b/homeassistant/components/onewire/__init__.py @@ -0,0 +1 @@ +"""The onewire component.""" diff --git a/homeassistant/components/sensor/onewire.py b/homeassistant/components/onewire/sensor.py similarity index 100% rename from homeassistant/components/sensor/onewire.py rename to homeassistant/components/onewire/sensor.py diff --git a/homeassistant/components/onkyo/__init__.py b/homeassistant/components/onkyo/__init__.py new file mode 100644 index 00000000000..02c026d1973 --- /dev/null +++ b/homeassistant/components/onkyo/__init__.py @@ -0,0 +1 @@ +"""The onkyo component.""" diff --git a/homeassistant/components/media_player/onkyo.py b/homeassistant/components/onkyo/media_player.py similarity index 100% rename from homeassistant/components/media_player/onkyo.py rename to homeassistant/components/onkyo/media_player.py diff --git a/homeassistant/components/onvif/__init__.py b/homeassistant/components/onvif/__init__.py new file mode 100644 index 00000000000..ea4c875ac20 --- /dev/null +++ b/homeassistant/components/onvif/__init__.py @@ -0,0 +1 @@ +"""The onvif component.""" diff --git a/homeassistant/components/camera/onvif.py b/homeassistant/components/onvif/camera.py similarity index 100% rename from homeassistant/components/camera/onvif.py rename to homeassistant/components/onvif/camera.py diff --git a/homeassistant/components/opencv/__init__.py b/homeassistant/components/opencv/__init__.py new file mode 100644 index 00000000000..0e4a755b2b9 --- /dev/null +++ b/homeassistant/components/opencv/__init__.py @@ -0,0 +1 @@ +"""The opencv component.""" diff --git a/homeassistant/components/image_processing/opencv.py b/homeassistant/components/opencv/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/opencv.py rename to homeassistant/components/opencv/image_processing.py diff --git a/homeassistant/components/openevse/__init__.py b/homeassistant/components/openevse/__init__.py new file mode 100644 index 00000000000..48d45591854 --- /dev/null +++ b/homeassistant/components/openevse/__init__.py @@ -0,0 +1 @@ +"""The openevse component.""" diff --git a/homeassistant/components/sensor/openevse.py b/homeassistant/components/openevse/sensor.py similarity index 100% rename from homeassistant/components/sensor/openevse.py rename to homeassistant/components/openevse/sensor.py diff --git a/homeassistant/components/openexchangerates/__init__.py b/homeassistant/components/openexchangerates/__init__.py new file mode 100644 index 00000000000..93d53614bdb --- /dev/null +++ b/homeassistant/components/openexchangerates/__init__.py @@ -0,0 +1 @@ +"""The openexchangerates component.""" diff --git a/homeassistant/components/sensor/openexchangerates.py b/homeassistant/components/openexchangerates/sensor.py similarity index 100% rename from homeassistant/components/sensor/openexchangerates.py rename to homeassistant/components/openexchangerates/sensor.py diff --git a/homeassistant/components/opengarage/__init__.py b/homeassistant/components/opengarage/__init__.py new file mode 100644 index 00000000000..2f4d2e09cfb --- /dev/null +++ b/homeassistant/components/opengarage/__init__.py @@ -0,0 +1 @@ +"""The opengarage component.""" diff --git a/homeassistant/components/cover/opengarage.py b/homeassistant/components/opengarage/cover.py similarity index 100% rename from homeassistant/components/cover/opengarage.py rename to homeassistant/components/opengarage/cover.py diff --git a/homeassistant/components/openhome/__init__.py b/homeassistant/components/openhome/__init__.py new file mode 100644 index 00000000000..78294ceb6f4 --- /dev/null +++ b/homeassistant/components/openhome/__init__.py @@ -0,0 +1 @@ +"""The openhome component.""" diff --git a/homeassistant/components/media_player/openhome.py b/homeassistant/components/openhome/media_player.py similarity index 100% rename from homeassistant/components/media_player/openhome.py rename to homeassistant/components/openhome/media_player.py diff --git a/homeassistant/components/opensensemap/__init__.py b/homeassistant/components/opensensemap/__init__.py new file mode 100644 index 00000000000..e03f4133d88 --- /dev/null +++ b/homeassistant/components/opensensemap/__init__.py @@ -0,0 +1 @@ +"""The opensensemap component.""" diff --git a/homeassistant/components/air_quality/opensensemap.py b/homeassistant/components/opensensemap/air_quality.py similarity index 100% rename from homeassistant/components/air_quality/opensensemap.py rename to homeassistant/components/opensensemap/air_quality.py diff --git a/homeassistant/components/opensky/__init__.py b/homeassistant/components/opensky/__init__.py new file mode 100644 index 00000000000..da805999d53 --- /dev/null +++ b/homeassistant/components/opensky/__init__.py @@ -0,0 +1 @@ +"""The opensky component.""" diff --git a/homeassistant/components/sensor/opensky.py b/homeassistant/components/opensky/sensor.py similarity index 100% rename from homeassistant/components/sensor/opensky.py rename to homeassistant/components/opensky/sensor.py diff --git a/homeassistant/components/openweathermap/__init__.py b/homeassistant/components/openweathermap/__init__.py new file mode 100644 index 00000000000..43cad1520ca --- /dev/null +++ b/homeassistant/components/openweathermap/__init__.py @@ -0,0 +1 @@ +"""The openweathermap component.""" diff --git a/homeassistant/components/sensor/openweathermap.py b/homeassistant/components/openweathermap/sensor.py similarity index 100% rename from homeassistant/components/sensor/openweathermap.py rename to homeassistant/components/openweathermap/sensor.py diff --git a/homeassistant/components/weather/openweathermap.py b/homeassistant/components/openweathermap/weather.py similarity index 100% rename from homeassistant/components/weather/openweathermap.py rename to homeassistant/components/openweathermap/weather.py diff --git a/homeassistant/components/opple/__init__.py b/homeassistant/components/opple/__init__.py new file mode 100644 index 00000000000..41ef2b0fdd8 --- /dev/null +++ b/homeassistant/components/opple/__init__.py @@ -0,0 +1 @@ +"""The opple component.""" diff --git a/homeassistant/components/light/opple.py b/homeassistant/components/opple/light.py similarity index 100% rename from homeassistant/components/light/opple.py rename to homeassistant/components/opple/light.py diff --git a/homeassistant/components/orvibo/__init__.py b/homeassistant/components/orvibo/__init__.py new file mode 100644 index 00000000000..81cddecb672 --- /dev/null +++ b/homeassistant/components/orvibo/__init__.py @@ -0,0 +1 @@ +"""The orvibo component.""" diff --git a/homeassistant/components/switch/orvibo.py b/homeassistant/components/orvibo/switch.py similarity index 100% rename from homeassistant/components/switch/orvibo.py rename to homeassistant/components/orvibo/switch.py diff --git a/homeassistant/components/osramlightify/__init__.py b/homeassistant/components/osramlightify/__init__.py new file mode 100644 index 00000000000..582d3a5e3fb --- /dev/null +++ b/homeassistant/components/osramlightify/__init__.py @@ -0,0 +1 @@ +"""The osramlightify component.""" diff --git a/homeassistant/components/light/osramlightify.py b/homeassistant/components/osramlightify/light.py similarity index 100% rename from homeassistant/components/light/osramlightify.py rename to homeassistant/components/osramlightify/light.py diff --git a/homeassistant/components/otp/__init__.py b/homeassistant/components/otp/__init__.py new file mode 100644 index 00000000000..bf80d41a92d --- /dev/null +++ b/homeassistant/components/otp/__init__.py @@ -0,0 +1 @@ +"""The otp component.""" diff --git a/homeassistant/components/sensor/otp.py b/homeassistant/components/otp/sensor.py similarity index 100% rename from homeassistant/components/sensor/otp.py rename to homeassistant/components/otp/sensor.py diff --git a/homeassistant/components/panasonic_bluray/__init__.py b/homeassistant/components/panasonic_bluray/__init__.py new file mode 100644 index 00000000000..a39b070b3c5 --- /dev/null +++ b/homeassistant/components/panasonic_bluray/__init__.py @@ -0,0 +1 @@ +"""The panasonic_bluray component.""" diff --git a/homeassistant/components/media_player/panasonic_bluray.py b/homeassistant/components/panasonic_bluray/media_player.py similarity index 100% rename from homeassistant/components/media_player/panasonic_bluray.py rename to homeassistant/components/panasonic_bluray/media_player.py diff --git a/homeassistant/components/panasonic_viera/__init__.py b/homeassistant/components/panasonic_viera/__init__.py new file mode 100644 index 00000000000..bb63c98079e --- /dev/null +++ b/homeassistant/components/panasonic_viera/__init__.py @@ -0,0 +1 @@ +"""The panasonic_viera component.""" diff --git a/homeassistant/components/media_player/panasonic_viera.py b/homeassistant/components/panasonic_viera/media_player.py similarity index 100% rename from homeassistant/components/media_player/panasonic_viera.py rename to homeassistant/components/panasonic_viera/media_player.py diff --git a/homeassistant/components/pandora/__init__.py b/homeassistant/components/pandora/__init__.py new file mode 100644 index 00000000000..9664730bdab --- /dev/null +++ b/homeassistant/components/pandora/__init__.py @@ -0,0 +1 @@ +"""The pandora component.""" diff --git a/homeassistant/components/media_player/pandora.py b/homeassistant/components/pandora/media_player.py similarity index 100% rename from homeassistant/components/media_player/pandora.py rename to homeassistant/components/pandora/media_player.py diff --git a/homeassistant/components/pencom/__init__.py b/homeassistant/components/pencom/__init__.py new file mode 100644 index 00000000000..5e53d8f59ab --- /dev/null +++ b/homeassistant/components/pencom/__init__.py @@ -0,0 +1 @@ +"""The pencom component.""" diff --git a/homeassistant/components/switch/pencom.py b/homeassistant/components/pencom/switch.py similarity index 100% rename from homeassistant/components/switch/pencom.py rename to homeassistant/components/pencom/switch.py diff --git a/homeassistant/components/philips_js/__init__.py b/homeassistant/components/philips_js/__init__.py new file mode 100644 index 00000000000..4b011c9f207 --- /dev/null +++ b/homeassistant/components/philips_js/__init__.py @@ -0,0 +1 @@ +"""The philips_js component.""" diff --git a/homeassistant/components/media_player/philips_js.py b/homeassistant/components/philips_js/media_player.py similarity index 100% rename from homeassistant/components/media_player/philips_js.py rename to homeassistant/components/philips_js/media_player.py diff --git a/homeassistant/components/pi_hole/__init__.py b/homeassistant/components/pi_hole/__init__.py new file mode 100644 index 00000000000..432e0f3fa11 --- /dev/null +++ b/homeassistant/components/pi_hole/__init__.py @@ -0,0 +1 @@ +"""The pi_hole component.""" diff --git a/homeassistant/components/sensor/pi_hole.py b/homeassistant/components/pi_hole/sensor.py similarity index 100% rename from homeassistant/components/sensor/pi_hole.py rename to homeassistant/components/pi_hole/sensor.py diff --git a/homeassistant/components/piglow/__init__.py b/homeassistant/components/piglow/__init__.py new file mode 100644 index 00000000000..e6d4bbd3ec2 --- /dev/null +++ b/homeassistant/components/piglow/__init__.py @@ -0,0 +1 @@ +"""The piglow component.""" diff --git a/homeassistant/components/light/piglow.py b/homeassistant/components/piglow/light.py similarity index 100% rename from homeassistant/components/light/piglow.py rename to homeassistant/components/piglow/light.py diff --git a/homeassistant/components/ping/__init__.py b/homeassistant/components/ping/__init__.py new file mode 100644 index 00000000000..e55f13dc717 --- /dev/null +++ b/homeassistant/components/ping/__init__.py @@ -0,0 +1 @@ +"""The ping component.""" diff --git a/homeassistant/components/binary_sensor/ping.py b/homeassistant/components/ping/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/ping.py rename to homeassistant/components/ping/binary_sensor.py diff --git a/homeassistant/components/device_tracker/ping.py b/homeassistant/components/ping/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/ping.py rename to homeassistant/components/ping/device_tracker.py diff --git a/homeassistant/components/pioneer/__init__.py b/homeassistant/components/pioneer/__init__.py new file mode 100644 index 00000000000..331b578dccf --- /dev/null +++ b/homeassistant/components/pioneer/__init__.py @@ -0,0 +1 @@ +"""The pioneer component.""" diff --git a/homeassistant/components/media_player/pioneer.py b/homeassistant/components/pioneer/media_player.py similarity index 100% rename from homeassistant/components/media_player/pioneer.py rename to homeassistant/components/pioneer/media_player.py diff --git a/homeassistant/components/pjlink/__init__.py b/homeassistant/components/pjlink/__init__.py new file mode 100644 index 00000000000..ab4d7fd377d --- /dev/null +++ b/homeassistant/components/pjlink/__init__.py @@ -0,0 +1 @@ +"""The pjlink component.""" diff --git a/homeassistant/components/media_player/pjlink.py b/homeassistant/components/pjlink/media_player.py similarity index 100% rename from homeassistant/components/media_player/pjlink.py rename to homeassistant/components/pjlink/media_player.py diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py new file mode 100644 index 00000000000..6e4e02026ab --- /dev/null +++ b/homeassistant/components/plex/__init__.py @@ -0,0 +1 @@ +"""The plex component.""" diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/plex/media_player.py similarity index 100% rename from homeassistant/components/media_player/plex.py rename to homeassistant/components/plex/media_player.py diff --git a/homeassistant/components/sensor/plex.py b/homeassistant/components/plex/sensor.py similarity index 100% rename from homeassistant/components/sensor/plex.py rename to homeassistant/components/plex/sensor.py diff --git a/homeassistant/components/pocketcasts/__init__.py b/homeassistant/components/pocketcasts/__init__.py new file mode 100644 index 00000000000..0bca6c6f893 --- /dev/null +++ b/homeassistant/components/pocketcasts/__init__.py @@ -0,0 +1 @@ +"""The pocketcasts component.""" diff --git a/homeassistant/components/sensor/pocketcasts.py b/homeassistant/components/pocketcasts/sensor.py similarity index 100% rename from homeassistant/components/sensor/pocketcasts.py rename to homeassistant/components/pocketcasts/sensor.py diff --git a/homeassistant/components/pollen/__init__.py b/homeassistant/components/pollen/__init__.py new file mode 100644 index 00000000000..566297ecb14 --- /dev/null +++ b/homeassistant/components/pollen/__init__.py @@ -0,0 +1 @@ +"""The pollen component.""" diff --git a/homeassistant/components/sensor/pollen.py b/homeassistant/components/pollen/sensor.py similarity index 100% rename from homeassistant/components/sensor/pollen.py rename to homeassistant/components/pollen/sensor.py diff --git a/homeassistant/components/postnl/__init__.py b/homeassistant/components/postnl/__init__.py new file mode 100644 index 00000000000..96c3212c7a1 --- /dev/null +++ b/homeassistant/components/postnl/__init__.py @@ -0,0 +1 @@ +"""The postnl component.""" diff --git a/homeassistant/components/sensor/postnl.py b/homeassistant/components/postnl/sensor.py similarity index 100% rename from homeassistant/components/sensor/postnl.py rename to homeassistant/components/postnl/sensor.py diff --git a/homeassistant/components/prezzibenzina/__init__.py b/homeassistant/components/prezzibenzina/__init__.py new file mode 100644 index 00000000000..af68e845bbc --- /dev/null +++ b/homeassistant/components/prezzibenzina/__init__.py @@ -0,0 +1 @@ +"""The prezzibenzina component.""" diff --git a/homeassistant/components/sensor/prezzibenzina.py b/homeassistant/components/prezzibenzina/sensor.py similarity index 100% rename from homeassistant/components/sensor/prezzibenzina.py rename to homeassistant/components/prezzibenzina/sensor.py diff --git a/homeassistant/components/proliphix/__init__.py b/homeassistant/components/proliphix/__init__.py new file mode 100644 index 00000000000..0611e88211b --- /dev/null +++ b/homeassistant/components/proliphix/__init__.py @@ -0,0 +1 @@ +"""The proliphix component.""" diff --git a/homeassistant/components/climate/proliphix.py b/homeassistant/components/proliphix/climate.py similarity index 100% rename from homeassistant/components/climate/proliphix.py rename to homeassistant/components/proliphix/climate.py diff --git a/homeassistant/components/proxy/__init__.py b/homeassistant/components/proxy/__init__.py new file mode 100644 index 00000000000..311c0732726 --- /dev/null +++ b/homeassistant/components/proxy/__init__.py @@ -0,0 +1 @@ +"""The proxy component.""" diff --git a/homeassistant/components/camera/proxy.py b/homeassistant/components/proxy/camera.py similarity index 100% rename from homeassistant/components/camera/proxy.py rename to homeassistant/components/proxy/camera.py diff --git a/homeassistant/components/pulseaudio_loopback/__init__.py b/homeassistant/components/pulseaudio_loopback/__init__.py new file mode 100644 index 00000000000..14f05080f5f --- /dev/null +++ b/homeassistant/components/pulseaudio_loopback/__init__.py @@ -0,0 +1 @@ +"""The pulseaudio_loopback component.""" diff --git a/homeassistant/components/switch/pulseaudio_loopback.py b/homeassistant/components/pulseaudio_loopback/switch.py similarity index 100% rename from homeassistant/components/switch/pulseaudio_loopback.py rename to homeassistant/components/pulseaudio_loopback/switch.py diff --git a/homeassistant/components/pushbullet/__init__.py b/homeassistant/components/pushbullet/__init__.py new file mode 100644 index 00000000000..153fa389fcc --- /dev/null +++ b/homeassistant/components/pushbullet/__init__.py @@ -0,0 +1 @@ +"""The pushbullet component.""" diff --git a/homeassistant/components/sensor/pushbullet.py b/homeassistant/components/pushbullet/sensor.py similarity index 100% rename from homeassistant/components/sensor/pushbullet.py rename to homeassistant/components/pushbullet/sensor.py diff --git a/homeassistant/components/pvoutput/__init__.py b/homeassistant/components/pvoutput/__init__.py new file mode 100644 index 00000000000..0ea3aabe9eb --- /dev/null +++ b/homeassistant/components/pvoutput/__init__.py @@ -0,0 +1 @@ +"""The pvoutput component.""" diff --git a/homeassistant/components/sensor/pvoutput.py b/homeassistant/components/pvoutput/sensor.py similarity index 100% rename from homeassistant/components/sensor/pvoutput.py rename to homeassistant/components/pvoutput/sensor.py diff --git a/homeassistant/components/pyload/__init__.py b/homeassistant/components/pyload/__init__.py new file mode 100644 index 00000000000..19103572e0b --- /dev/null +++ b/homeassistant/components/pyload/__init__.py @@ -0,0 +1 @@ +"""The pyload component.""" diff --git a/homeassistant/components/sensor/pyload.py b/homeassistant/components/pyload/sensor.py similarity index 100% rename from homeassistant/components/sensor/pyload.py rename to homeassistant/components/pyload/sensor.py diff --git a/homeassistant/components/qbittorrent/__init__.py b/homeassistant/components/qbittorrent/__init__.py new file mode 100644 index 00000000000..a5274f7a5a9 --- /dev/null +++ b/homeassistant/components/qbittorrent/__init__.py @@ -0,0 +1 @@ +"""The qbittorrent component.""" diff --git a/homeassistant/components/sensor/qbittorrent.py b/homeassistant/components/qbittorrent/sensor.py similarity index 100% rename from homeassistant/components/sensor/qbittorrent.py rename to homeassistant/components/qbittorrent/sensor.py diff --git a/homeassistant/components/qnap/__init__.py b/homeassistant/components/qnap/__init__.py new file mode 100644 index 00000000000..534096628df --- /dev/null +++ b/homeassistant/components/qnap/__init__.py @@ -0,0 +1 @@ +"""The qnap component.""" diff --git a/homeassistant/components/sensor/qnap.py b/homeassistant/components/qnap/sensor.py similarity index 100% rename from homeassistant/components/sensor/qnap.py rename to homeassistant/components/qnap/sensor.py diff --git a/homeassistant/components/qrcode/__init__.py b/homeassistant/components/qrcode/__init__.py new file mode 100644 index 00000000000..bcc1985a2dc --- /dev/null +++ b/homeassistant/components/qrcode/__init__.py @@ -0,0 +1 @@ +"""The qrcode component.""" diff --git a/homeassistant/components/image_processing/qrcode.py b/homeassistant/components/qrcode/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/qrcode.py rename to homeassistant/components/qrcode/image_processing.py diff --git a/homeassistant/components/quantum_gateway/__init__.py b/homeassistant/components/quantum_gateway/__init__.py new file mode 100644 index 00000000000..d502c2b216c --- /dev/null +++ b/homeassistant/components/quantum_gateway/__init__.py @@ -0,0 +1 @@ +"""The quantum_gateway component.""" diff --git a/homeassistant/components/device_tracker/quantum_gateway.py b/homeassistant/components/quantum_gateway/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/quantum_gateway.py rename to homeassistant/components/quantum_gateway/device_tracker.py diff --git a/homeassistant/components/radiotherm/__init__.py b/homeassistant/components/radiotherm/__init__.py new file mode 100644 index 00000000000..adc8cdbd6ee --- /dev/null +++ b/homeassistant/components/radiotherm/__init__.py @@ -0,0 +1 @@ +"""The radiotherm component.""" diff --git a/homeassistant/components/climate/radiotherm.py b/homeassistant/components/radiotherm/climate.py similarity index 100% rename from homeassistant/components/climate/radiotherm.py rename to homeassistant/components/radiotherm/climate.py diff --git a/homeassistant/components/sensor/rainbird.py b/homeassistant/components/rainbird/sensor.py similarity index 100% rename from homeassistant/components/sensor/rainbird.py rename to homeassistant/components/rainbird/sensor.py diff --git a/homeassistant/components/switch/rainbird.py b/homeassistant/components/rainbird/switch.py similarity index 100% rename from homeassistant/components/switch/rainbird.py rename to homeassistant/components/rainbird/switch.py diff --git a/homeassistant/components/raspyrfm/__init__.py b/homeassistant/components/raspyrfm/__init__.py new file mode 100644 index 00000000000..67522764824 --- /dev/null +++ b/homeassistant/components/raspyrfm/__init__.py @@ -0,0 +1 @@ +"""The raspyrfm component.""" diff --git a/homeassistant/components/switch/raspyrfm.py b/homeassistant/components/raspyrfm/switch.py similarity index 100% rename from homeassistant/components/switch/raspyrfm.py rename to homeassistant/components/raspyrfm/switch.py diff --git a/homeassistant/components/recollect_waste/__init__.py b/homeassistant/components/recollect_waste/__init__.py new file mode 100644 index 00000000000..8ba2fc676f4 --- /dev/null +++ b/homeassistant/components/recollect_waste/__init__.py @@ -0,0 +1 @@ +"""The recollect_waste component.""" diff --git a/homeassistant/components/sensor/recollect_waste.py b/homeassistant/components/recollect_waste/sensor.py similarity index 100% rename from homeassistant/components/sensor/recollect_waste.py rename to homeassistant/components/recollect_waste/sensor.py diff --git a/homeassistant/components/recswitch/__init__.py b/homeassistant/components/recswitch/__init__.py new file mode 100644 index 00000000000..38006ad3aeb --- /dev/null +++ b/homeassistant/components/recswitch/__init__.py @@ -0,0 +1 @@ +"""The recswitch component.""" diff --git a/homeassistant/components/switch/recswitch.py b/homeassistant/components/recswitch/switch.py similarity index 100% rename from homeassistant/components/switch/recswitch.py rename to homeassistant/components/recswitch/switch.py diff --git a/homeassistant/components/rejseplanen/__init__.py b/homeassistant/components/rejseplanen/__init__.py new file mode 100644 index 00000000000..c67ab71dbb9 --- /dev/null +++ b/homeassistant/components/rejseplanen/__init__.py @@ -0,0 +1 @@ +"""The rejseplanen component.""" diff --git a/homeassistant/components/sensor/rejseplanen.py b/homeassistant/components/rejseplanen/sensor.py similarity index 100% rename from homeassistant/components/sensor/rejseplanen.py rename to homeassistant/components/rejseplanen/sensor.py diff --git a/homeassistant/components/camera/ring.py b/homeassistant/components/ring/camera.py similarity index 100% rename from homeassistant/components/camera/ring.py rename to homeassistant/components/ring/camera.py diff --git a/homeassistant/components/ripple/__init__.py b/homeassistant/components/ripple/__init__.py new file mode 100644 index 00000000000..d55a7d09487 --- /dev/null +++ b/homeassistant/components/ripple/__init__.py @@ -0,0 +1 @@ +"""The ripple component.""" diff --git a/homeassistant/components/sensor/ripple.py b/homeassistant/components/ripple/sensor.py similarity index 100% rename from homeassistant/components/sensor/ripple.py rename to homeassistant/components/ripple/sensor.py diff --git a/homeassistant/components/ritassist/__init__.py b/homeassistant/components/ritassist/__init__.py new file mode 100644 index 00000000000..653e18874d3 --- /dev/null +++ b/homeassistant/components/ritassist/__init__.py @@ -0,0 +1 @@ +"""The ritassist component.""" diff --git a/homeassistant/components/device_tracker/ritassist.py b/homeassistant/components/ritassist/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/ritassist.py rename to homeassistant/components/ritassist/device_tracker.py diff --git a/homeassistant/components/roomba/__init__.py b/homeassistant/components/roomba/__init__.py new file mode 100644 index 00000000000..c0e5f68483e --- /dev/null +++ b/homeassistant/components/roomba/__init__.py @@ -0,0 +1 @@ +"""The roomba component.""" diff --git a/homeassistant/components/vacuum/roomba.py b/homeassistant/components/roomba/vacuum.py similarity index 100% rename from homeassistant/components/vacuum/roomba.py rename to homeassistant/components/roomba/vacuum.py diff --git a/homeassistant/components/rova/__init__.py b/homeassistant/components/rova/__init__.py new file mode 100644 index 00000000000..411ea6c7239 --- /dev/null +++ b/homeassistant/components/rova/__init__.py @@ -0,0 +1 @@ +"""The rova component.""" diff --git a/homeassistant/components/sensor/rova.py b/homeassistant/components/rova/sensor.py similarity index 100% rename from homeassistant/components/sensor/rova.py rename to homeassistant/components/rova/sensor.py diff --git a/homeassistant/components/rpi_camera/__init__.py b/homeassistant/components/rpi_camera/__init__.py new file mode 100644 index 00000000000..04638e463a1 --- /dev/null +++ b/homeassistant/components/rpi_camera/__init__.py @@ -0,0 +1 @@ +"""The rpi_camera component.""" diff --git a/homeassistant/components/camera/rpi_camera.py b/homeassistant/components/rpi_camera/camera.py similarity index 100% rename from homeassistant/components/camera/rpi_camera.py rename to homeassistant/components/rpi_camera/camera.py diff --git a/homeassistant/components/rpi_gpio_pwm/__init__.py b/homeassistant/components/rpi_gpio_pwm/__init__.py new file mode 100644 index 00000000000..46aa24c12e6 --- /dev/null +++ b/homeassistant/components/rpi_gpio_pwm/__init__.py @@ -0,0 +1 @@ +"""The rpi_gpio_pwm component.""" diff --git a/homeassistant/components/light/rpi_gpio_pwm.py b/homeassistant/components/rpi_gpio_pwm/light.py similarity index 100% rename from homeassistant/components/light/rpi_gpio_pwm.py rename to homeassistant/components/rpi_gpio_pwm/light.py diff --git a/homeassistant/components/rpi_rf/__init__.py b/homeassistant/components/rpi_rf/__init__.py new file mode 100644 index 00000000000..6e4d58099d9 --- /dev/null +++ b/homeassistant/components/rpi_rf/__init__.py @@ -0,0 +1 @@ +"""The rpi_rf component.""" diff --git a/homeassistant/components/switch/rpi_rf.py b/homeassistant/components/rpi_rf/switch.py similarity index 98% rename from homeassistant/components/switch/rpi_rf.py rename to homeassistant/components/rpi_rf/switch.py index e7f14685eb2..6844cb0f383 100644 --- a/homeassistant/components/switch/rpi_rf.py +++ b/homeassistant/components/rpi_rf/switch.py @@ -4,6 +4,7 @@ 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/ """ +import importlib import logging import voluptuous as vol @@ -47,7 +48,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ # pylint: disable=no-member def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return switches controlled by a generic RF device via GPIO.""" - import rpi_rf + rpi_rf = importlib.import_module('rpi_rf') from threading import RLock gpio = config.get(CONF_GPIO) diff --git a/homeassistant/components/rtorrent/__init__.py b/homeassistant/components/rtorrent/__init__.py new file mode 100644 index 00000000000..adc4d329539 --- /dev/null +++ b/homeassistant/components/rtorrent/__init__.py @@ -0,0 +1 @@ +"""The rtorrent component.""" diff --git a/homeassistant/components/sensor/rtorrent.py b/homeassistant/components/rtorrent/sensor.py similarity index 100% rename from homeassistant/components/sensor/rtorrent.py rename to homeassistant/components/rtorrent/sensor.py diff --git a/homeassistant/components/russound_rio/__init__.py b/homeassistant/components/russound_rio/__init__.py new file mode 100644 index 00000000000..6d7fe3b1215 --- /dev/null +++ b/homeassistant/components/russound_rio/__init__.py @@ -0,0 +1 @@ +"""The russound_rio component.""" diff --git a/homeassistant/components/media_player/russound_rio.py b/homeassistant/components/russound_rio/media_player.py similarity index 100% rename from homeassistant/components/media_player/russound_rio.py rename to homeassistant/components/russound_rio/media_player.py diff --git a/homeassistant/components/russound_rnet/__init__.py b/homeassistant/components/russound_rnet/__init__.py new file mode 100644 index 00000000000..04bb6c50f7f --- /dev/null +++ b/homeassistant/components/russound_rnet/__init__.py @@ -0,0 +1 @@ +"""The russound_rnet component.""" diff --git a/homeassistant/components/media_player/russound_rnet.py b/homeassistant/components/russound_rnet/media_player.py similarity index 100% rename from homeassistant/components/media_player/russound_rnet.py rename to homeassistant/components/russound_rnet/media_player.py diff --git a/homeassistant/components/ruter/__init__.py b/homeassistant/components/ruter/__init__.py new file mode 100644 index 00000000000..84e25904d9e --- /dev/null +++ b/homeassistant/components/ruter/__init__.py @@ -0,0 +1 @@ +"""The ruter component.""" diff --git a/homeassistant/components/sensor/ruter.py b/homeassistant/components/ruter/sensor.py similarity index 100% rename from homeassistant/components/sensor/ruter.py rename to homeassistant/components/ruter/sensor.py diff --git a/homeassistant/components/scrape/__init__.py b/homeassistant/components/scrape/__init__.py new file mode 100644 index 00000000000..f9222c126b5 --- /dev/null +++ b/homeassistant/components/scrape/__init__.py @@ -0,0 +1 @@ +"""The scrape component.""" diff --git a/homeassistant/components/sensor/scrape.py b/homeassistant/components/scrape/sensor.py similarity index 100% rename from homeassistant/components/sensor/scrape.py rename to homeassistant/components/scrape/sensor.py diff --git a/homeassistant/components/sensehat/__init__.py b/homeassistant/components/sensehat/__init__.py new file mode 100644 index 00000000000..baef85d7f53 --- /dev/null +++ b/homeassistant/components/sensehat/__init__.py @@ -0,0 +1 @@ +"""The sensehat component.""" diff --git a/homeassistant/components/light/sensehat.py b/homeassistant/components/sensehat/light.py similarity index 100% rename from homeassistant/components/light/sensehat.py rename to homeassistant/components/sensehat/light.py diff --git a/homeassistant/components/sensor/sensehat.py b/homeassistant/components/sensehat/sensor.py similarity index 100% rename from homeassistant/components/sensor/sensehat.py rename to homeassistant/components/sensehat/sensor.py diff --git a/homeassistant/components/sensibo/__init__.py b/homeassistant/components/sensibo/__init__.py new file mode 100644 index 00000000000..41959bdc9b2 --- /dev/null +++ b/homeassistant/components/sensibo/__init__.py @@ -0,0 +1 @@ +"""The sensibo component.""" diff --git a/homeassistant/components/climate/sensibo.py b/homeassistant/components/sensibo/climate.py similarity index 100% rename from homeassistant/components/climate/sensibo.py rename to homeassistant/components/sensibo/climate.py diff --git a/homeassistant/components/serial/__init__.py b/homeassistant/components/serial/__init__.py new file mode 100644 index 00000000000..50bf5c4573b --- /dev/null +++ b/homeassistant/components/serial/__init__.py @@ -0,0 +1 @@ +"""The serial component.""" diff --git a/homeassistant/components/sensor/serial.py b/homeassistant/components/serial/sensor.py similarity index 100% rename from homeassistant/components/sensor/serial.py rename to homeassistant/components/serial/sensor.py diff --git a/homeassistant/components/serial_pm/__init__.py b/homeassistant/components/serial_pm/__init__.py new file mode 100644 index 00000000000..713d0bad66c --- /dev/null +++ b/homeassistant/components/serial_pm/__init__.py @@ -0,0 +1 @@ +"""The serial_pm component.""" diff --git a/homeassistant/components/sensor/serial_pm.py b/homeassistant/components/serial_pm/sensor.py similarity index 100% rename from homeassistant/components/sensor/serial_pm.py rename to homeassistant/components/serial_pm/sensor.py diff --git a/homeassistant/components/sesame/__init__.py b/homeassistant/components/sesame/__init__.py new file mode 100644 index 00000000000..f3c57e5cd07 --- /dev/null +++ b/homeassistant/components/sesame/__init__.py @@ -0,0 +1 @@ +"""The sesame component.""" diff --git a/homeassistant/components/lock/sesame.py b/homeassistant/components/sesame/lock.py similarity index 100% rename from homeassistant/components/lock/sesame.py rename to homeassistant/components/sesame/lock.py diff --git a/homeassistant/components/seven_segments/__init__.py b/homeassistant/components/seven_segments/__init__.py new file mode 100644 index 00000000000..6826c4396bb --- /dev/null +++ b/homeassistant/components/seven_segments/__init__.py @@ -0,0 +1 @@ +"""The seven_segments component.""" diff --git a/homeassistant/components/image_processing/seven_segments.py b/homeassistant/components/seven_segments/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/seven_segments.py rename to homeassistant/components/seven_segments/image_processing.py diff --git a/homeassistant/components/seventeentrack/__init__.py b/homeassistant/components/seventeentrack/__init__.py new file mode 100644 index 00000000000..43eebf346f0 --- /dev/null +++ b/homeassistant/components/seventeentrack/__init__.py @@ -0,0 +1 @@ +"""The seventeentrack component.""" diff --git a/homeassistant/components/sensor/seventeentrack.py b/homeassistant/components/seventeentrack/sensor.py similarity index 100% rename from homeassistant/components/sensor/seventeentrack.py rename to homeassistant/components/seventeentrack/sensor.py diff --git a/homeassistant/components/shodan/__init__.py b/homeassistant/components/shodan/__init__.py new file mode 100644 index 00000000000..76439c48080 --- /dev/null +++ b/homeassistant/components/shodan/__init__.py @@ -0,0 +1 @@ +"""The shodan component.""" diff --git a/homeassistant/components/sensor/shodan.py b/homeassistant/components/shodan/sensor.py similarity index 100% rename from homeassistant/components/sensor/shodan.py rename to homeassistant/components/shodan/sensor.py diff --git a/homeassistant/components/sht31/__init__.py b/homeassistant/components/sht31/__init__.py new file mode 100644 index 00000000000..16bfe384d94 --- /dev/null +++ b/homeassistant/components/sht31/__init__.py @@ -0,0 +1 @@ +"""The sht31 component.""" diff --git a/homeassistant/components/sensor/sht31.py b/homeassistant/components/sht31/sensor.py similarity index 100% rename from homeassistant/components/sensor/sht31.py rename to homeassistant/components/sht31/sensor.py diff --git a/homeassistant/components/sky_hub/__init__.py b/homeassistant/components/sky_hub/__init__.py new file mode 100644 index 00000000000..a5b8969018f --- /dev/null +++ b/homeassistant/components/sky_hub/__init__.py @@ -0,0 +1 @@ +"""The sky_hub component.""" diff --git a/homeassistant/components/device_tracker/sky_hub.py b/homeassistant/components/sky_hub/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/sky_hub.py rename to homeassistant/components/sky_hub/device_tracker.py diff --git a/homeassistant/components/skybeacon/__init__.py b/homeassistant/components/skybeacon/__init__.py new file mode 100644 index 00000000000..5e0a7b7a4a3 --- /dev/null +++ b/homeassistant/components/skybeacon/__init__.py @@ -0,0 +1 @@ +"""The skybeacon component.""" diff --git a/homeassistant/components/sensor/skybeacon.py b/homeassistant/components/skybeacon/sensor.py similarity index 100% rename from homeassistant/components/sensor/skybeacon.py rename to homeassistant/components/skybeacon/sensor.py diff --git a/homeassistant/components/sma/__init__.py b/homeassistant/components/sma/__init__.py new file mode 100644 index 00000000000..97d7147596c --- /dev/null +++ b/homeassistant/components/sma/__init__.py @@ -0,0 +1 @@ +"""The sma component.""" diff --git a/homeassistant/components/sensor/sma.py b/homeassistant/components/sma/sensor.py similarity index 100% rename from homeassistant/components/sensor/sma.py rename to homeassistant/components/sma/sensor.py diff --git a/homeassistant/components/snapcast/__init__.py b/homeassistant/components/snapcast/__init__.py new file mode 100644 index 00000000000..b5279fa3ce0 --- /dev/null +++ b/homeassistant/components/snapcast/__init__.py @@ -0,0 +1 @@ +"""The snapcast component.""" diff --git a/homeassistant/components/media_player/snapcast.py b/homeassistant/components/snapcast/media_player.py similarity index 100% rename from homeassistant/components/media_player/snapcast.py rename to homeassistant/components/snapcast/media_player.py diff --git a/homeassistant/components/snmp/__init__.py b/homeassistant/components/snmp/__init__.py new file mode 100644 index 00000000000..a4c922877f3 --- /dev/null +++ b/homeassistant/components/snmp/__init__.py @@ -0,0 +1 @@ +"""The snmp component.""" diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/snmp/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/snmp.py rename to homeassistant/components/snmp/device_tracker.py diff --git a/homeassistant/components/sensor/snmp.py b/homeassistant/components/snmp/sensor.py similarity index 100% rename from homeassistant/components/sensor/snmp.py rename to homeassistant/components/snmp/sensor.py diff --git a/homeassistant/components/switch/snmp.py b/homeassistant/components/snmp/switch.py similarity index 100% rename from homeassistant/components/switch/snmp.py rename to homeassistant/components/snmp/switch.py diff --git a/homeassistant/components/sochain/__init__.py b/homeassistant/components/sochain/__init__.py new file mode 100644 index 00000000000..31a000d3947 --- /dev/null +++ b/homeassistant/components/sochain/__init__.py @@ -0,0 +1 @@ +"""The sochain component.""" diff --git a/homeassistant/components/sensor/sochain.py b/homeassistant/components/sochain/sensor.py similarity index 100% rename from homeassistant/components/sensor/sochain.py rename to homeassistant/components/sochain/sensor.py diff --git a/homeassistant/components/socialblade/__init__.py b/homeassistant/components/socialblade/__init__.py new file mode 100644 index 00000000000..c497d99d32c --- /dev/null +++ b/homeassistant/components/socialblade/__init__.py @@ -0,0 +1 @@ +"""The socialblade component.""" diff --git a/homeassistant/components/sensor/socialblade.py b/homeassistant/components/socialblade/sensor.py similarity index 100% rename from homeassistant/components/sensor/socialblade.py rename to homeassistant/components/socialblade/sensor.py diff --git a/homeassistant/components/solaredge/__init__.py b/homeassistant/components/solaredge/__init__.py new file mode 100644 index 00000000000..b675126c5fd --- /dev/null +++ b/homeassistant/components/solaredge/__init__.py @@ -0,0 +1 @@ +"""The solaredge component.""" diff --git a/homeassistant/components/sensor/solaredge.py b/homeassistant/components/solaredge/sensor.py similarity index 100% rename from homeassistant/components/sensor/solaredge.py rename to homeassistant/components/solaredge/sensor.py diff --git a/homeassistant/components/songpal/__init__.py b/homeassistant/components/songpal/__init__.py new file mode 100644 index 00000000000..7b181d375a5 --- /dev/null +++ b/homeassistant/components/songpal/__init__.py @@ -0,0 +1 @@ +"""The songpal component.""" diff --git a/homeassistant/components/media_player/songpal.py b/homeassistant/components/songpal/media_player.py similarity index 100% rename from homeassistant/components/media_player/songpal.py rename to homeassistant/components/songpal/media_player.py diff --git a/homeassistant/components/sony_projector/__init__.py b/homeassistant/components/sony_projector/__init__.py new file mode 100644 index 00000000000..dfe52c7fa75 --- /dev/null +++ b/homeassistant/components/sony_projector/__init__.py @@ -0,0 +1 @@ +"""The sony_projector component.""" diff --git a/homeassistant/components/switch/sony_projector.py b/homeassistant/components/sony_projector/switch.py similarity index 100% rename from homeassistant/components/switch/sony_projector.py rename to homeassistant/components/sony_projector/switch.py diff --git a/homeassistant/components/alarm_control_panel/spc.py b/homeassistant/components/spc/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/spc.py rename to homeassistant/components/spc/alarm_control_panel.py diff --git a/homeassistant/components/binary_sensor/spc.py b/homeassistant/components/spc/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/spc.py rename to homeassistant/components/spc/binary_sensor.py diff --git a/homeassistant/components/spotcrime/__init__.py b/homeassistant/components/spotcrime/__init__.py new file mode 100644 index 00000000000..26bb50b8b02 --- /dev/null +++ b/homeassistant/components/spotcrime/__init__.py @@ -0,0 +1 @@ +"""The spotcrime component.""" diff --git a/homeassistant/components/sensor/spotcrime.py b/homeassistant/components/spotcrime/sensor.py similarity index 100% rename from homeassistant/components/sensor/spotcrime.py rename to homeassistant/components/spotcrime/sensor.py diff --git a/homeassistant/components/spotify/__init__.py b/homeassistant/components/spotify/__init__.py new file mode 100644 index 00000000000..fdfce7e498b --- /dev/null +++ b/homeassistant/components/spotify/__init__.py @@ -0,0 +1 @@ +"""The spotify component.""" diff --git a/homeassistant/components/media_player/spotify.py b/homeassistant/components/spotify/media_player.py similarity index 100% rename from homeassistant/components/media_player/spotify.py rename to homeassistant/components/spotify/media_player.py diff --git a/homeassistant/components/squeezebox/__init__.py b/homeassistant/components/squeezebox/__init__.py new file mode 100644 index 00000000000..5250a6dc267 --- /dev/null +++ b/homeassistant/components/squeezebox/__init__.py @@ -0,0 +1 @@ +"""The squeezebox component.""" diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/squeezebox/media_player.py similarity index 100% rename from homeassistant/components/media_player/squeezebox.py rename to homeassistant/components/squeezebox/media_player.py diff --git a/homeassistant/components/starlingbank/__init__.py b/homeassistant/components/starlingbank/__init__.py new file mode 100644 index 00000000000..3d2e657fcd9 --- /dev/null +++ b/homeassistant/components/starlingbank/__init__.py @@ -0,0 +1 @@ +"""The starlingbank component.""" diff --git a/homeassistant/components/sensor/starlingbank.py b/homeassistant/components/starlingbank/sensor.py similarity index 100% rename from homeassistant/components/sensor/starlingbank.py rename to homeassistant/components/starlingbank/sensor.py diff --git a/homeassistant/components/steam_online/__init__.py b/homeassistant/components/steam_online/__init__.py new file mode 100644 index 00000000000..99f384322df --- /dev/null +++ b/homeassistant/components/steam_online/__init__.py @@ -0,0 +1 @@ +"""The steam_online component.""" diff --git a/homeassistant/components/sensor/steam_online.py b/homeassistant/components/steam_online/sensor.py similarity index 100% rename from homeassistant/components/sensor/steam_online.py rename to homeassistant/components/steam_online/sensor.py diff --git a/homeassistant/components/supervisord/__init__.py b/homeassistant/components/supervisord/__init__.py new file mode 100644 index 00000000000..5819bb6c965 --- /dev/null +++ b/homeassistant/components/supervisord/__init__.py @@ -0,0 +1 @@ +"""The supervisord component.""" diff --git a/homeassistant/components/sensor/supervisord.py b/homeassistant/components/supervisord/sensor.py similarity index 100% rename from homeassistant/components/sensor/supervisord.py rename to homeassistant/components/supervisord/sensor.py diff --git a/homeassistant/components/swiss_hydrological_data/__init__.py b/homeassistant/components/swiss_hydrological_data/__init__.py new file mode 100644 index 00000000000..2ee369c05eb --- /dev/null +++ b/homeassistant/components/swiss_hydrological_data/__init__.py @@ -0,0 +1 @@ +"""The swiss_hydrological_data component.""" diff --git a/homeassistant/components/sensor/swiss_hydrological_data.py b/homeassistant/components/swiss_hydrological_data/sensor.py similarity index 100% rename from homeassistant/components/sensor/swiss_hydrological_data.py rename to homeassistant/components/swiss_hydrological_data/sensor.py diff --git a/homeassistant/components/swiss_public_transport/__init__.py b/homeassistant/components/swiss_public_transport/__init__.py new file mode 100644 index 00000000000..c53cb1f6934 --- /dev/null +++ b/homeassistant/components/swiss_public_transport/__init__.py @@ -0,0 +1 @@ +"""The swiss_public_transport component.""" diff --git a/homeassistant/components/sensor/swiss_public_transport.py b/homeassistant/components/swiss_public_transport/sensor.py similarity index 100% rename from homeassistant/components/sensor/swiss_public_transport.py rename to homeassistant/components/swiss_public_transport/sensor.py diff --git a/homeassistant/components/swisscom/__init__.py b/homeassistant/components/swisscom/__init__.py new file mode 100644 index 00000000000..5e0c11af090 --- /dev/null +++ b/homeassistant/components/swisscom/__init__.py @@ -0,0 +1 @@ +"""The swisscom component.""" diff --git a/homeassistant/components/device_tracker/swisscom.py b/homeassistant/components/swisscom/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/swisscom.py rename to homeassistant/components/swisscom/device_tracker.py diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py new file mode 100644 index 00000000000..a8768a9cd44 --- /dev/null +++ b/homeassistant/components/switchbot/__init__.py @@ -0,0 +1 @@ +"""The switchbot component.""" diff --git a/homeassistant/components/switch/switchbot.py b/homeassistant/components/switchbot/switch.py similarity index 100% rename from homeassistant/components/switch/switchbot.py rename to homeassistant/components/switchbot/switch.py diff --git a/homeassistant/components/switchmate/__init__.py b/homeassistant/components/switchmate/__init__.py new file mode 100644 index 00000000000..8c965cdb086 --- /dev/null +++ b/homeassistant/components/switchmate/__init__.py @@ -0,0 +1 @@ +"""The switchmate component.""" diff --git a/homeassistant/components/switch/switchmate.py b/homeassistant/components/switchmate/switch.py similarity index 100% rename from homeassistant/components/switch/switchmate.py rename to homeassistant/components/switchmate/switch.py diff --git a/homeassistant/components/syncthru/__init__.py b/homeassistant/components/syncthru/__init__.py new file mode 100644 index 00000000000..e523e3fd722 --- /dev/null +++ b/homeassistant/components/syncthru/__init__.py @@ -0,0 +1 @@ +"""The syncthru component.""" diff --git a/homeassistant/components/sensor/syncthru.py b/homeassistant/components/syncthru/sensor.py similarity index 100% rename from homeassistant/components/sensor/syncthru.py rename to homeassistant/components/syncthru/sensor.py diff --git a/homeassistant/components/synology/__init__.py b/homeassistant/components/synology/__init__.py new file mode 100644 index 00000000000..0ab4b45e298 --- /dev/null +++ b/homeassistant/components/synology/__init__.py @@ -0,0 +1 @@ +"""The synology component.""" diff --git a/homeassistant/components/camera/synology.py b/homeassistant/components/synology/camera.py similarity index 100% rename from homeassistant/components/camera/synology.py rename to homeassistant/components/synology/camera.py diff --git a/homeassistant/components/synology_srm/__init__.py b/homeassistant/components/synology_srm/__init__.py new file mode 100644 index 00000000000..cd77bce1014 --- /dev/null +++ b/homeassistant/components/synology_srm/__init__.py @@ -0,0 +1 @@ +"""The synology_srm component.""" diff --git a/homeassistant/components/device_tracker/synology_srm.py b/homeassistant/components/synology_srm/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/synology_srm.py rename to homeassistant/components/synology_srm/device_tracker.py diff --git a/homeassistant/components/synologydsm/__init__.py b/homeassistant/components/synologydsm/__init__.py new file mode 100644 index 00000000000..137a3975b99 --- /dev/null +++ b/homeassistant/components/synologydsm/__init__.py @@ -0,0 +1 @@ +"""The synologydsm component.""" diff --git a/homeassistant/components/sensor/synologydsm.py b/homeassistant/components/synologydsm/sensor.py similarity index 100% rename from homeassistant/components/sensor/synologydsm.py rename to homeassistant/components/synologydsm/sensor.py diff --git a/homeassistant/components/systemmonitor/__init__.py b/homeassistant/components/systemmonitor/__init__.py new file mode 100644 index 00000000000..27dc9d367c2 --- /dev/null +++ b/homeassistant/components/systemmonitor/__init__.py @@ -0,0 +1 @@ +"""The systemmonitor component.""" diff --git a/homeassistant/components/sensor/systemmonitor.py b/homeassistant/components/systemmonitor/sensor.py similarity index 100% rename from homeassistant/components/sensor/systemmonitor.py rename to homeassistant/components/systemmonitor/sensor.py diff --git a/homeassistant/components/sytadin/__init__.py b/homeassistant/components/sytadin/__init__.py new file mode 100644 index 00000000000..5243fe379a7 --- /dev/null +++ b/homeassistant/components/sytadin/__init__.py @@ -0,0 +1 @@ +"""The sytadin component.""" diff --git a/homeassistant/components/sensor/sytadin.py b/homeassistant/components/sytadin/sensor.py similarity index 100% rename from homeassistant/components/sensor/sytadin.py rename to homeassistant/components/sytadin/sensor.py diff --git a/homeassistant/components/tank_utility/__init__.py b/homeassistant/components/tank_utility/__init__.py new file mode 100644 index 00000000000..1590c90738f --- /dev/null +++ b/homeassistant/components/tank_utility/__init__.py @@ -0,0 +1 @@ +"""The tank_utility component.""" diff --git a/homeassistant/components/sensor/tank_utility.py b/homeassistant/components/tank_utility/sensor.py similarity index 100% rename from homeassistant/components/sensor/tank_utility.py rename to homeassistant/components/tank_utility/sensor.py diff --git a/homeassistant/components/tapsaff/__init__.py b/homeassistant/components/tapsaff/__init__.py new file mode 100644 index 00000000000..95f6c663dd6 --- /dev/null +++ b/homeassistant/components/tapsaff/__init__.py @@ -0,0 +1 @@ +"""The tapsaff component.""" diff --git a/homeassistant/components/binary_sensor/tapsaff.py b/homeassistant/components/tapsaff/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/tapsaff.py rename to homeassistant/components/tapsaff/binary_sensor.py diff --git a/homeassistant/components/tautulli/__init__.py b/homeassistant/components/tautulli/__init__.py new file mode 100644 index 00000000000..f8ce5ca08b5 --- /dev/null +++ b/homeassistant/components/tautulli/__init__.py @@ -0,0 +1 @@ +"""The tautulli component.""" diff --git a/homeassistant/components/sensor/tautulli.py b/homeassistant/components/tautulli/sensor.py similarity index 100% rename from homeassistant/components/sensor/tautulli.py rename to homeassistant/components/tautulli/sensor.py diff --git a/homeassistant/components/ted5000/__init__.py b/homeassistant/components/ted5000/__init__.py new file mode 100644 index 00000000000..a10d5778b38 --- /dev/null +++ b/homeassistant/components/ted5000/__init__.py @@ -0,0 +1 @@ +"""The ted5000 component.""" diff --git a/homeassistant/components/sensor/ted5000.py b/homeassistant/components/ted5000/sensor.py similarity index 100% rename from homeassistant/components/sensor/ted5000.py rename to homeassistant/components/ted5000/sensor.py diff --git a/homeassistant/components/telnet/__init__.py b/homeassistant/components/telnet/__init__.py new file mode 100644 index 00000000000..12444ab1691 --- /dev/null +++ b/homeassistant/components/telnet/__init__.py @@ -0,0 +1 @@ +"""The telnet component.""" diff --git a/homeassistant/components/switch/telnet.py b/homeassistant/components/telnet/switch.py similarity index 100% rename from homeassistant/components/switch/telnet.py rename to homeassistant/components/telnet/switch.py diff --git a/homeassistant/components/temper/__init__.py b/homeassistant/components/temper/__init__.py new file mode 100644 index 00000000000..587da1c6309 --- /dev/null +++ b/homeassistant/components/temper/__init__.py @@ -0,0 +1 @@ +"""The temper component.""" diff --git a/homeassistant/components/sensor/temper.py b/homeassistant/components/temper/sensor.py similarity index 100% rename from homeassistant/components/sensor/temper.py rename to homeassistant/components/temper/sensor.py diff --git a/homeassistant/components/tensorflow/__init__.py b/homeassistant/components/tensorflow/__init__.py new file mode 100644 index 00000000000..00a695d6aa8 --- /dev/null +++ b/homeassistant/components/tensorflow/__init__.py @@ -0,0 +1 @@ +"""The tensorflow component.""" diff --git a/homeassistant/components/image_processing/tensorflow.py b/homeassistant/components/tensorflow/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/tensorflow.py rename to homeassistant/components/tensorflow/image_processing.py diff --git a/homeassistant/components/thermoworks_smoke/__init__.py b/homeassistant/components/thermoworks_smoke/__init__.py new file mode 100644 index 00000000000..4fea3085a3c --- /dev/null +++ b/homeassistant/components/thermoworks_smoke/__init__.py @@ -0,0 +1 @@ +"""The thermoworks_smoke component.""" diff --git a/homeassistant/components/sensor/thermoworks_smoke.py b/homeassistant/components/thermoworks_smoke/sensor.py similarity index 100% rename from homeassistant/components/sensor/thermoworks_smoke.py rename to homeassistant/components/thermoworks_smoke/sensor.py diff --git a/homeassistant/components/thomson/__init__.py b/homeassistant/components/thomson/__init__.py new file mode 100644 index 00000000000..3c1ce045f39 --- /dev/null +++ b/homeassistant/components/thomson/__init__.py @@ -0,0 +1 @@ +"""The thomson component.""" diff --git a/homeassistant/components/device_tracker/thomson.py b/homeassistant/components/thomson/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/thomson.py rename to homeassistant/components/thomson/device_tracker.py diff --git a/homeassistant/components/tikteck/__init__.py b/homeassistant/components/tikteck/__init__.py new file mode 100644 index 00000000000..59511d5bea9 --- /dev/null +++ b/homeassistant/components/tikteck/__init__.py @@ -0,0 +1 @@ +"""The tikteck component.""" diff --git a/homeassistant/components/light/tikteck.py b/homeassistant/components/tikteck/light.py similarity index 100% rename from homeassistant/components/light/tikteck.py rename to homeassistant/components/tikteck/light.py diff --git a/homeassistant/components/tile/__init__.py b/homeassistant/components/tile/__init__.py new file mode 100644 index 00000000000..f0192d0ed32 --- /dev/null +++ b/homeassistant/components/tile/__init__.py @@ -0,0 +1 @@ +"""The tile component.""" diff --git a/homeassistant/components/device_tracker/tile.py b/homeassistant/components/tile/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/tile.py rename to homeassistant/components/tile/device_tracker.py diff --git a/homeassistant/components/todoist/__init__.py b/homeassistant/components/todoist/__init__.py new file mode 100644 index 00000000000..78a9cb89624 --- /dev/null +++ b/homeassistant/components/todoist/__init__.py @@ -0,0 +1 @@ +"""The todoist component.""" diff --git a/homeassistant/components/calendar/todoist.py b/homeassistant/components/todoist/calendar.py similarity index 100% rename from homeassistant/components/calendar/todoist.py rename to homeassistant/components/todoist/calendar.py diff --git a/homeassistant/components/torque/__init__.py b/homeassistant/components/torque/__init__.py new file mode 100644 index 00000000000..2f680bcca13 --- /dev/null +++ b/homeassistant/components/torque/__init__.py @@ -0,0 +1 @@ +"""The torque component.""" diff --git a/homeassistant/components/sensor/torque.py b/homeassistant/components/torque/sensor.py similarity index 100% rename from homeassistant/components/sensor/torque.py rename to homeassistant/components/torque/sensor.py diff --git a/homeassistant/components/totalconnect/__init__.py b/homeassistant/components/totalconnect/__init__.py new file mode 100644 index 00000000000..084846a8b85 --- /dev/null +++ b/homeassistant/components/totalconnect/__init__.py @@ -0,0 +1 @@ +"""The totalconnect component.""" diff --git a/homeassistant/components/alarm_control_panel/totalconnect.py b/homeassistant/components/totalconnect/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/totalconnect.py rename to homeassistant/components/totalconnect/alarm_control_panel.py diff --git a/homeassistant/components/touchline/__init__.py b/homeassistant/components/touchline/__init__.py new file mode 100644 index 00000000000..284870313d8 --- /dev/null +++ b/homeassistant/components/touchline/__init__.py @@ -0,0 +1 @@ +"""The touchline component.""" diff --git a/homeassistant/components/climate/touchline.py b/homeassistant/components/touchline/climate.py similarity index 100% rename from homeassistant/components/climate/touchline.py rename to homeassistant/components/touchline/climate.py diff --git a/homeassistant/components/traccar/__init__.py b/homeassistant/components/traccar/__init__.py new file mode 100644 index 00000000000..03805760c53 --- /dev/null +++ b/homeassistant/components/traccar/__init__.py @@ -0,0 +1 @@ +"""The traccar component.""" diff --git a/homeassistant/components/device_tracker/traccar.py b/homeassistant/components/traccar/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/traccar.py rename to homeassistant/components/traccar/device_tracker.py diff --git a/homeassistant/components/trackr/__init__.py b/homeassistant/components/trackr/__init__.py new file mode 100644 index 00000000000..b78eb8078a2 --- /dev/null +++ b/homeassistant/components/trackr/__init__.py @@ -0,0 +1 @@ +"""The trackr component.""" diff --git a/homeassistant/components/device_tracker/trackr.py b/homeassistant/components/trackr/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/trackr.py rename to homeassistant/components/trackr/device_tracker.py diff --git a/homeassistant/components/trafikverket_weatherstation/__init__.py b/homeassistant/components/trafikverket_weatherstation/__init__.py new file mode 100644 index 00000000000..7feac4aad27 --- /dev/null +++ b/homeassistant/components/trafikverket_weatherstation/__init__.py @@ -0,0 +1 @@ +"""The trafikverket_weatherstation component.""" diff --git a/homeassistant/components/sensor/trafikverket_weatherstation.py b/homeassistant/components/trafikverket_weatherstation/sensor.py similarity index 100% rename from homeassistant/components/sensor/trafikverket_weatherstation.py rename to homeassistant/components/trafikverket_weatherstation/sensor.py diff --git a/homeassistant/components/travisci/__init__.py b/homeassistant/components/travisci/__init__.py new file mode 100644 index 00000000000..9337f87592f --- /dev/null +++ b/homeassistant/components/travisci/__init__.py @@ -0,0 +1 @@ +"""The travisci component.""" diff --git a/homeassistant/components/sensor/travisci.py b/homeassistant/components/travisci/sensor.py similarity index 100% rename from homeassistant/components/sensor/travisci.py rename to homeassistant/components/travisci/sensor.py diff --git a/homeassistant/components/twitch/__init__.py b/homeassistant/components/twitch/__init__.py new file mode 100644 index 00000000000..0cdeb813945 --- /dev/null +++ b/homeassistant/components/twitch/__init__.py @@ -0,0 +1 @@ +"""The twitch component.""" diff --git a/homeassistant/components/sensor/twitch.py b/homeassistant/components/twitch/sensor.py similarity index 100% rename from homeassistant/components/sensor/twitch.py rename to homeassistant/components/twitch/sensor.py diff --git a/homeassistant/components/ubee/__init__.py b/homeassistant/components/ubee/__init__.py new file mode 100644 index 00000000000..cc7b131a2bd --- /dev/null +++ b/homeassistant/components/ubee/__init__.py @@ -0,0 +1 @@ +"""The ubee component.""" diff --git a/homeassistant/components/device_tracker/ubee.py b/homeassistant/components/ubee/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/ubee.py rename to homeassistant/components/ubee/device_tracker.py diff --git a/homeassistant/components/uber/__init__.py b/homeassistant/components/uber/__init__.py new file mode 100644 index 00000000000..b555f83fed9 --- /dev/null +++ b/homeassistant/components/uber/__init__.py @@ -0,0 +1 @@ +"""The uber component.""" diff --git a/homeassistant/components/sensor/uber.py b/homeassistant/components/uber/sensor.py similarity index 100% rename from homeassistant/components/sensor/uber.py rename to homeassistant/components/uber/sensor.py diff --git a/homeassistant/components/ubus/__init__.py b/homeassistant/components/ubus/__init__.py new file mode 100644 index 00000000000..227825ac7c0 --- /dev/null +++ b/homeassistant/components/ubus/__init__.py @@ -0,0 +1 @@ +"""The ubus component.""" diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/ubus/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/ubus.py rename to homeassistant/components/ubus/device_tracker.py diff --git a/homeassistant/components/ue_smart_radio/__init__.py b/homeassistant/components/ue_smart_radio/__init__.py new file mode 100644 index 00000000000..2d686b7c5ea --- /dev/null +++ b/homeassistant/components/ue_smart_radio/__init__.py @@ -0,0 +1 @@ +"""The ue_smart_radio component.""" diff --git a/homeassistant/components/media_player/ue_smart_radio.py b/homeassistant/components/ue_smart_radio/media_player.py similarity index 100% rename from homeassistant/components/media_player/ue_smart_radio.py rename to homeassistant/components/ue_smart_radio/media_player.py diff --git a/homeassistant/components/ups/__init__.py b/homeassistant/components/ups/__init__.py new file mode 100644 index 00000000000..690d3102f9c --- /dev/null +++ b/homeassistant/components/ups/__init__.py @@ -0,0 +1 @@ +"""The ups component.""" diff --git a/homeassistant/components/sensor/ups.py b/homeassistant/components/ups/sensor.py similarity index 100% rename from homeassistant/components/sensor/ups.py rename to homeassistant/components/ups/sensor.py diff --git a/homeassistant/components/uptimerobot/__init__.py b/homeassistant/components/uptimerobot/__init__.py new file mode 100644 index 00000000000..3dad1b00fff --- /dev/null +++ b/homeassistant/components/uptimerobot/__init__.py @@ -0,0 +1 @@ +"""The uptimerobot component.""" diff --git a/homeassistant/components/binary_sensor/uptimerobot.py b/homeassistant/components/uptimerobot/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/uptimerobot.py rename to homeassistant/components/uptimerobot/binary_sensor.py diff --git a/homeassistant/components/uscis/__init__.py b/homeassistant/components/uscis/__init__.py new file mode 100644 index 00000000000..f45e0ab9353 --- /dev/null +++ b/homeassistant/components/uscis/__init__.py @@ -0,0 +1 @@ +"""The uscis component.""" diff --git a/homeassistant/components/sensor/uscis.py b/homeassistant/components/uscis/sensor.py similarity index 100% rename from homeassistant/components/sensor/uscis.py rename to homeassistant/components/uscis/sensor.py diff --git a/homeassistant/components/vasttrafik/__init__.py b/homeassistant/components/vasttrafik/__init__.py new file mode 100644 index 00000000000..25846435c7a --- /dev/null +++ b/homeassistant/components/vasttrafik/__init__.py @@ -0,0 +1 @@ +"""The vasttrafik component.""" diff --git a/homeassistant/components/sensor/vasttrafik.py b/homeassistant/components/vasttrafik/sensor.py similarity index 100% rename from homeassistant/components/sensor/vasttrafik.py rename to homeassistant/components/vasttrafik/sensor.py diff --git a/homeassistant/components/venstar/__init__.py b/homeassistant/components/venstar/__init__.py new file mode 100644 index 00000000000..abc35a0d6bd --- /dev/null +++ b/homeassistant/components/venstar/__init__.py @@ -0,0 +1 @@ +"""The venstar component.""" diff --git a/homeassistant/components/climate/venstar.py b/homeassistant/components/venstar/climate.py similarity index 100% rename from homeassistant/components/climate/venstar.py rename to homeassistant/components/venstar/climate.py diff --git a/homeassistant/components/vesync/__init__.py b/homeassistant/components/vesync/__init__.py new file mode 100644 index 00000000000..73b28a3d008 --- /dev/null +++ b/homeassistant/components/vesync/__init__.py @@ -0,0 +1 @@ +"""The vesync component.""" diff --git a/homeassistant/components/switch/vesync.py b/homeassistant/components/vesync/switch.py similarity index 100% rename from homeassistant/components/switch/vesync.py rename to homeassistant/components/vesync/switch.py diff --git a/homeassistant/components/viaggiatreno/__init__.py b/homeassistant/components/viaggiatreno/__init__.py new file mode 100644 index 00000000000..2eb6ed6cddc --- /dev/null +++ b/homeassistant/components/viaggiatreno/__init__.py @@ -0,0 +1 @@ +"""The viaggiatreno component.""" diff --git a/homeassistant/components/sensor/viaggiatreno.py b/homeassistant/components/viaggiatreno/sensor.py similarity index 100% rename from homeassistant/components/sensor/viaggiatreno.py rename to homeassistant/components/viaggiatreno/sensor.py diff --git a/homeassistant/components/vizio/__init__.py b/homeassistant/components/vizio/__init__.py new file mode 100644 index 00000000000..3575f2cf648 --- /dev/null +++ b/homeassistant/components/vizio/__init__.py @@ -0,0 +1 @@ +"""The vizio component.""" diff --git a/homeassistant/components/media_player/vizio.py b/homeassistant/components/vizio/media_player.py similarity index 100% rename from homeassistant/components/media_player/vizio.py rename to homeassistant/components/vizio/media_player.py diff --git a/homeassistant/components/vlc/__init__.py b/homeassistant/components/vlc/__init__.py new file mode 100644 index 00000000000..91a3eb35444 --- /dev/null +++ b/homeassistant/components/vlc/__init__.py @@ -0,0 +1 @@ +"""The vlc component.""" diff --git a/homeassistant/components/media_player/vlc.py b/homeassistant/components/vlc/media_player.py similarity index 100% rename from homeassistant/components/media_player/vlc.py rename to homeassistant/components/vlc/media_player.py diff --git a/homeassistant/components/volkszaehler/__init__.py b/homeassistant/components/volkszaehler/__init__.py new file mode 100644 index 00000000000..a1a6533e4f9 --- /dev/null +++ b/homeassistant/components/volkszaehler/__init__.py @@ -0,0 +1 @@ +"""The volkszaehler component.""" diff --git a/homeassistant/components/sensor/volkszaehler.py b/homeassistant/components/volkszaehler/sensor.py similarity index 100% rename from homeassistant/components/sensor/volkszaehler.py rename to homeassistant/components/volkszaehler/sensor.py diff --git a/homeassistant/components/volumio/__init__.py b/homeassistant/components/volumio/__init__.py new file mode 100644 index 00000000000..823533336ba --- /dev/null +++ b/homeassistant/components/volumio/__init__.py @@ -0,0 +1 @@ +"""The volumio component.""" diff --git a/homeassistant/components/media_player/volumio.py b/homeassistant/components/volumio/media_player.py similarity index 100% rename from homeassistant/components/media_player/volumio.py rename to homeassistant/components/volumio/media_player.py diff --git a/homeassistant/components/waqi/__init__.py b/homeassistant/components/waqi/__init__.py new file mode 100644 index 00000000000..5cacd9e5e1b --- /dev/null +++ b/homeassistant/components/waqi/__init__.py @@ -0,0 +1 @@ +"""The waqi component.""" diff --git a/homeassistant/components/sensor/waqi.py b/homeassistant/components/waqi/sensor.py similarity index 100% rename from homeassistant/components/sensor/waqi.py rename to homeassistant/components/waqi/sensor.py diff --git a/homeassistant/components/waze_travel_time/__init__.py b/homeassistant/components/waze_travel_time/__init__.py new file mode 100644 index 00000000000..9674bd9850e --- /dev/null +++ b/homeassistant/components/waze_travel_time/__init__.py @@ -0,0 +1 @@ +"""The waze_travel_time component.""" diff --git a/homeassistant/components/sensor/waze_travel_time.py b/homeassistant/components/waze_travel_time/sensor.py similarity index 100% rename from homeassistant/components/sensor/waze_travel_time.py rename to homeassistant/components/waze_travel_time/sensor.py diff --git a/homeassistant/components/whois/__init__.py b/homeassistant/components/whois/__init__.py new file mode 100644 index 00000000000..3f3ffefde48 --- /dev/null +++ b/homeassistant/components/whois/__init__.py @@ -0,0 +1 @@ +"""The whois component.""" diff --git a/homeassistant/components/sensor/whois.py b/homeassistant/components/whois/sensor.py similarity index 100% rename from homeassistant/components/sensor/whois.py rename to homeassistant/components/whois/sensor.py diff --git a/homeassistant/components/worldtidesinfo/__init__.py b/homeassistant/components/worldtidesinfo/__init__.py new file mode 100644 index 00000000000..313beb529e4 --- /dev/null +++ b/homeassistant/components/worldtidesinfo/__init__.py @@ -0,0 +1 @@ +"""The worldtidesinfo component.""" diff --git a/homeassistant/components/sensor/worldtidesinfo.py b/homeassistant/components/worldtidesinfo/sensor.py similarity index 100% rename from homeassistant/components/sensor/worldtidesinfo.py rename to homeassistant/components/worldtidesinfo/sensor.py diff --git a/homeassistant/components/worxlandroid/__init__.py b/homeassistant/components/worxlandroid/__init__.py new file mode 100644 index 00000000000..dae50f24dc9 --- /dev/null +++ b/homeassistant/components/worxlandroid/__init__.py @@ -0,0 +1 @@ +"""The worxlandroid component.""" diff --git a/homeassistant/components/sensor/worxlandroid.py b/homeassistant/components/worxlandroid/sensor.py similarity index 100% rename from homeassistant/components/sensor/worxlandroid.py rename to homeassistant/components/worxlandroid/sensor.py diff --git a/homeassistant/components/x10/__init__.py b/homeassistant/components/x10/__init__.py new file mode 100644 index 00000000000..4c3b9bc5ce4 --- /dev/null +++ b/homeassistant/components/x10/__init__.py @@ -0,0 +1 @@ +"""The x10 component.""" diff --git a/homeassistant/components/light/x10.py b/homeassistant/components/x10/light.py similarity index 100% rename from homeassistant/components/light/x10.py rename to homeassistant/components/x10/light.py diff --git a/homeassistant/components/xbox_live/__init__.py b/homeassistant/components/xbox_live/__init__.py new file mode 100644 index 00000000000..cc9e8ac3518 --- /dev/null +++ b/homeassistant/components/xbox_live/__init__.py @@ -0,0 +1 @@ +"""The xbox_live component.""" diff --git a/homeassistant/components/sensor/xbox_live.py b/homeassistant/components/xbox_live/sensor.py similarity index 100% rename from homeassistant/components/sensor/xbox_live.py rename to homeassistant/components/xbox_live/sensor.py diff --git a/homeassistant/components/xeoma/__init__.py b/homeassistant/components/xeoma/__init__.py new file mode 100644 index 00000000000..e68d3a24035 --- /dev/null +++ b/homeassistant/components/xeoma/__init__.py @@ -0,0 +1 @@ +"""The xeoma component.""" diff --git a/homeassistant/components/camera/xeoma.py b/homeassistant/components/xeoma/camera.py similarity index 100% rename from homeassistant/components/camera/xeoma.py rename to homeassistant/components/xeoma/camera.py diff --git a/homeassistant/components/xfinity/__init__.py b/homeassistant/components/xfinity/__init__.py new file mode 100644 index 00000000000..22e37eccde9 --- /dev/null +++ b/homeassistant/components/xfinity/__init__.py @@ -0,0 +1 @@ +"""The xfinity component.""" diff --git a/homeassistant/components/device_tracker/xfinity.py b/homeassistant/components/xfinity/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/xfinity.py rename to homeassistant/components/xfinity/device_tracker.py diff --git a/homeassistant/components/xiaomi/__init__.py b/homeassistant/components/xiaomi/__init__.py new file mode 100644 index 00000000000..6fc7294864d --- /dev/null +++ b/homeassistant/components/xiaomi/__init__.py @@ -0,0 +1 @@ +"""The xiaomi component.""" diff --git a/homeassistant/components/camera/xiaomi.py b/homeassistant/components/xiaomi/camera.py similarity index 100% rename from homeassistant/components/camera/xiaomi.py rename to homeassistant/components/xiaomi/camera.py diff --git a/homeassistant/components/xiaomi_tv/__init__.py b/homeassistant/components/xiaomi_tv/__init__.py new file mode 100644 index 00000000000..4dd89753b06 --- /dev/null +++ b/homeassistant/components/xiaomi_tv/__init__.py @@ -0,0 +1 @@ +"""The xiaomi_tv component.""" diff --git a/homeassistant/components/media_player/xiaomi_tv.py b/homeassistant/components/xiaomi_tv/media_player.py similarity index 100% rename from homeassistant/components/media_player/xiaomi_tv.py rename to homeassistant/components/xiaomi_tv/media_player.py diff --git a/homeassistant/components/yale_smart_alarm/__init__.py b/homeassistant/components/yale_smart_alarm/__init__.py new file mode 100644 index 00000000000..2ce2fb13495 --- /dev/null +++ b/homeassistant/components/yale_smart_alarm/__init__.py @@ -0,0 +1 @@ +"""The yale_smart_alarm component.""" diff --git a/homeassistant/components/alarm_control_panel/yale_smart_alarm.py b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/yale_smart_alarm.py rename to homeassistant/components/yale_smart_alarm/alarm_control_panel.py diff --git a/homeassistant/components/yamaha_musiccast/__init__.py b/homeassistant/components/yamaha_musiccast/__init__.py new file mode 100644 index 00000000000..bf270b508d9 --- /dev/null +++ b/homeassistant/components/yamaha_musiccast/__init__.py @@ -0,0 +1 @@ +"""The yamaha_musiccast component.""" diff --git a/homeassistant/components/media_player/yamaha_musiccast.py b/homeassistant/components/yamaha_musiccast/media_player.py similarity index 100% rename from homeassistant/components/media_player/yamaha_musiccast.py rename to homeassistant/components/yamaha_musiccast/media_player.py diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py new file mode 100644 index 00000000000..d8c1f23bcbb --- /dev/null +++ b/homeassistant/components/yeelight/__init__.py @@ -0,0 +1 @@ +"""The yeelight component.""" diff --git a/homeassistant/components/light/yeelight.py b/homeassistant/components/yeelight/light.py similarity index 100% rename from homeassistant/components/light/yeelight.py rename to homeassistant/components/yeelight/light.py diff --git a/homeassistant/components/yeelightsunflower/__init__.py b/homeassistant/components/yeelightsunflower/__init__.py new file mode 100644 index 00000000000..4f0421eeb3c --- /dev/null +++ b/homeassistant/components/yeelightsunflower/__init__.py @@ -0,0 +1 @@ +"""The yeelightsunflower component.""" diff --git a/homeassistant/components/light/yeelightsunflower.py b/homeassistant/components/yeelightsunflower/light.py similarity index 100% rename from homeassistant/components/light/yeelightsunflower.py rename to homeassistant/components/yeelightsunflower/light.py diff --git a/homeassistant/components/yi/__init__.py b/homeassistant/components/yi/__init__.py new file mode 100644 index 00000000000..a37399d930d --- /dev/null +++ b/homeassistant/components/yi/__init__.py @@ -0,0 +1 @@ +"""The yi component.""" diff --git a/homeassistant/components/camera/yi.py b/homeassistant/components/yi/camera.py similarity index 100% rename from homeassistant/components/camera/yi.py rename to homeassistant/components/yi/camera.py diff --git a/homeassistant/components/zamg/__init__.py b/homeassistant/components/zamg/__init__.py new file mode 100644 index 00000000000..a0f80956d98 --- /dev/null +++ b/homeassistant/components/zamg/__init__.py @@ -0,0 +1 @@ +"""The zamg component.""" diff --git a/homeassistant/components/sensor/zamg.py b/homeassistant/components/zamg/sensor.py similarity index 100% rename from homeassistant/components/sensor/zamg.py rename to homeassistant/components/zamg/sensor.py diff --git a/homeassistant/components/weather/zamg.py b/homeassistant/components/zamg/weather.py similarity index 98% rename from homeassistant/components/weather/zamg.py rename to homeassistant/components/zamg/weather.py index 60707fa5e30..01a927afbf8 100644 --- a/homeassistant/components/weather/zamg.py +++ b/homeassistant/components/zamg/weather.py @@ -4,7 +4,7 @@ import logging import voluptuous as vol # Reuse data and API logic from the sensor implementation -from homeassistant.components.sensor.zamg import ( +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, diff --git a/homeassistant/components/zengge/__init__.py b/homeassistant/components/zengge/__init__.py new file mode 100644 index 00000000000..ab4fe5e35c3 --- /dev/null +++ b/homeassistant/components/zengge/__init__.py @@ -0,0 +1 @@ +"""The zengge component.""" diff --git a/homeassistant/components/light/zengge.py b/homeassistant/components/zengge/light.py similarity index 100% rename from homeassistant/components/light/zengge.py rename to homeassistant/components/zengge/light.py diff --git a/homeassistant/components/zestimate/__init__.py b/homeassistant/components/zestimate/__init__.py new file mode 100644 index 00000000000..5742ae56f35 --- /dev/null +++ b/homeassistant/components/zestimate/__init__.py @@ -0,0 +1 @@ +"""The zestimate component.""" diff --git a/homeassistant/components/sensor/zestimate.py b/homeassistant/components/zestimate/sensor.py similarity index 100% rename from homeassistant/components/sensor/zestimate.py rename to homeassistant/components/zestimate/sensor.py diff --git a/homeassistant/components/zhong_hong/__init__.py b/homeassistant/components/zhong_hong/__init__.py new file mode 100644 index 00000000000..f14ec68593b --- /dev/null +++ b/homeassistant/components/zhong_hong/__init__.py @@ -0,0 +1 @@ +"""The zhong_hong component.""" diff --git a/homeassistant/components/climate/zhong_hong.py b/homeassistant/components/zhong_hong/climate.py similarity index 100% rename from homeassistant/components/climate/zhong_hong.py rename to homeassistant/components/zhong_hong/climate.py diff --git a/homeassistant/components/ziggo_mediabox_xl/__init__.py b/homeassistant/components/ziggo_mediabox_xl/__init__.py new file mode 100644 index 00000000000..4627f7cef7a --- /dev/null +++ b/homeassistant/components/ziggo_mediabox_xl/__init__.py @@ -0,0 +1 @@ +"""The ziggo_mediabox_xl component.""" diff --git a/homeassistant/components/media_player/ziggo_mediabox_xl.py b/homeassistant/components/ziggo_mediabox_xl/media_player.py similarity index 100% rename from homeassistant/components/media_player/ziggo_mediabox_xl.py rename to homeassistant/components/ziggo_mediabox_xl/media_player.py diff --git a/homeassistant/components/zoneminder/camera.py b/homeassistant/components/zoneminder/camera.py index 7f74712335c..f9dd8718b8f 100644 --- a/homeassistant/components/zoneminder/camera.py +++ b/homeassistant/components/zoneminder/camera.py @@ -2,7 +2,7 @@ import logging from homeassistant.const import CONF_NAME, CONF_VERIFY_SSL -from homeassistant.components.camera.mjpeg import ( +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 diff --git a/requirements_all.txt b/requirements_all.txt index d5a4cde09ac..b4ad602d7b3 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.sensor.dht +# homeassistant.components.dht.sensor # Adafruit-DHT==1.4.0 -# homeassistant.components.sensor.sht31 +# homeassistant.components.sht31.sensor Adafruit-GPIO==1.0.3 -# homeassistant.components.sensor.sht31 +# homeassistant.components.sht31.sensor Adafruit-SHT31==1.0.2 # homeassistant.components.bbb_gpio @@ -38,13 +38,13 @@ HAP-python==2.4.2 # homeassistant.components.notify.mastodon Mastodon.py==1.3.1 -# homeassistant.components.sensor.github +# homeassistant.components.github.sensor PyGithub==1.43.5 # homeassistant.components.isy994 PyISY==1.1.1 -# homeassistant.components.sensor.mvglive +# homeassistant.components.mvglive.sensor PyMVGLive==1.1.4 # homeassistant.components.arduino @@ -60,7 +60,7 @@ PyQRCode==1.2.1 # homeassistant.components.sensor.rmvtransport PyRMVtransport==0.1.3 -# homeassistant.components.switch.switchbot +# homeassistant.components.switchbot.switch # PySwitchbot==0.5 # homeassistant.components.sensor.transport_nsw @@ -75,7 +75,7 @@ PyXiaomiGateway==0.12.2 # homeassistant.components.remember_the_milk RtmAPI==0.7.0 -# homeassistant.components.sensor.travisci +# homeassistant.components.travisci.sensor TravisPy==0.3.5 # homeassistant.components.notify.twitter @@ -84,7 +84,7 @@ TwitterAPI==2.5.9 # homeassistant.components.tof.sensor # VL53L1X2==0.1.5 -# homeassistant.components.sensor.waze_travel_time +# homeassistant.components.waze_travel_time.sensor WazeRouteCalculator==0.9 # homeassistant.components.notify.yessssms @@ -93,7 +93,7 @@ YesssSMS==0.2.3 # homeassistant.components.abode abodepy==0.15.0 -# homeassistant.components.media_player.frontier_silicon +# homeassistant.components.frontier_silicon.media_player afsapi==0.0.4 # homeassistant.components.ambient_station @@ -105,7 +105,7 @@ aioasuswrt==1.1.21 # homeassistant.components.device_tracker.automatic aioautomatic==0.6.5 -# homeassistant.components.sensor.dnsip +# homeassistant.components.dnsip.sensor aiodns==1.1.1 # homeassistant.components.esphome @@ -114,7 +114,7 @@ aioesphomeapi==1.6.0 # homeassistant.components.freebox aiofreepybox==0.0.6 -# homeassistant.components.camera.yi +# homeassistant.components.yi.camera aioftp==0.12.0 # homeassistant.components.harmony.remote @@ -127,10 +127,10 @@ aiohttp_cors==0.7.0 # homeassistant.components.hue aiohue==1.9.1 -# homeassistant.components.sensor.iliad_italy +# homeassistant.components.iliad_italy.sensor aioiliad==0.1.1 -# homeassistant.components.sensor.imap +# homeassistant.components.imap.sensor aioimaplib==0.7.15 # homeassistant.components.lifx @@ -139,19 +139,19 @@ aiolifx==0.6.7 # homeassistant.components.lifx.light aiolifx_effects==0.2.1 -# homeassistant.components.scene.hunterdouglas_powerview +# homeassistant.components.hunterdouglas_powerview.scene aiopvapi==1.6.14 # homeassistant.components.unifi aiounifi==4 -# homeassistant.components.cover.aladdin_connect +# homeassistant.components.aladdin_connect.cover aladdin_connect==0.3 # homeassistant.components.alarmdecoder alarmdecoder==1.13.2 -# homeassistant.components.sensor.alpha_vantage +# homeassistant.components.alpha_vantage.sensor alpha_vantage==2.1.0 # homeassistant.components.amcrest @@ -160,10 +160,10 @@ amcrest==1.2.7 # homeassistant.components.androidtv.media_player androidtv==0.0.12 -# homeassistant.components.switch.anel_pwrctrl +# homeassistant.components.anel_pwrctrl.switch anel_pwrctrl-homeassistant==0.0.1.dev2 -# homeassistant.components.media_player.anthemav +# homeassistant.components.anthemav.media_player anthemav==1.1.10 # homeassistant.components.apcupsd @@ -179,13 +179,13 @@ aqualogic==1.0 asterisk_mbox==0.5.0 # homeassistant.components.upnp -# homeassistant.components.media_player.dlna_dmr +# homeassistant.components.dlna_dmr.media_player async-upnp-client==0.14.5 # homeassistant.components.stream av==6.1.2 -# homeassistant.components.light.avion +# homeassistant.components.avion.light # avion==0.10 # homeassistant.components.axis @@ -194,18 +194,18 @@ axis==16 # homeassistant.components.tts.baidu baidu-aip==1.6.6 -# homeassistant.components.sensor.modem_callerid +# homeassistant.components.modem_callerid.sensor basicmodem==0.7 -# homeassistant.components.sensor.linux_battery +# homeassistant.components.linux_battery.sensor batinfo==0.4.2 -# homeassistant.components.sensor.eddystone_temperature +# homeassistant.components.eddystone_temperature.sensor # beacontools[scan]==1.2.3 -# homeassistant.components.device_tracker.linksys_ap -# homeassistant.components.sensor.scrape -# homeassistant.components.sensor.sytadin +# homeassistant.components.linksys_ap.device_tracker +# homeassistant.components.scrape.sensor +# homeassistant.components.sytadin.sensor beautifulsoup4==4.7.1 # homeassistant.components.zha @@ -217,19 +217,19 @@ bimmer_connected==0.5.3 # homeassistant.components.blink blinkpy==0.13.1 -# homeassistant.components.light.blinksticklight +# homeassistant.components.blinksticklight.light blinkstick==1.1.8 -# homeassistant.components.light.blinkt +# homeassistant.components.blinkt.light # blinkt==0.1.0 -# homeassistant.components.sensor.bitcoin +# homeassistant.components.bitcoin.sensor blockchain==1.4.4 -# homeassistant.components.light.decora +# homeassistant.components.decora.light # bluepy==1.1.4 -# homeassistant.components.sensor.bme680 +# homeassistant.components.bme680.sensor # bme680==1.0.5 # homeassistant.components.route53 @@ -242,30 +242,30 @@ boto3==1.9.16 # homeassistant.scripts.credstash botocore==1.7.34 -# homeassistant.components.media_player.braviatv +# homeassistant.components.braviatv.media_player braviarc-homeassistant==0.3.7.dev0 -# homeassistant.components.sensor.broadlink -# homeassistant.components.switch.broadlink +# homeassistant.components.broadlink.sensor +# homeassistant.components.broadlink.switch broadlink==0.9.0 -# homeassistant.components.sensor.brottsplatskartan +# homeassistant.components.brottsplatskartan.sensor brottsplatskartan==0.0.1 -# homeassistant.components.cover.brunt +# homeassistant.components.brunt.cover brunt==0.1.3 -# homeassistant.components.device_tracker.bluetooth_tracker +# homeassistant.components.bluetooth_tracker.device_tracker bt_proximity==0.1.2 -# homeassistant.components.device_tracker.bt_home_hub_5 +# homeassistant.components.bt_home_hub_5.device_tracker bthomehub5-devicelist==0.1.1 -# homeassistant.components.device_tracker.bt_smarthub +# homeassistant.components.bt_smarthub.device_tracker btsmarthub_devicelist==0.1.3 -# homeassistant.components.sensor.buienradar -# homeassistant.components.weather.buienradar +# homeassistant.components.buienradar.sensor +# homeassistant.components.buienradar.weather buienradar==0.91 # homeassistant.components.calendar.caldav @@ -280,7 +280,7 @@ ciscosparkapi==0.4.2 # homeassistant.components.cppm_tracker.device_tracker clearpasspy==1.0.2 -# homeassistant.components.sensor.co2signal +# homeassistant.components.co2signal.sensor co2signal==0.4.2 # homeassistant.components.coinbase @@ -292,12 +292,12 @@ coinmarketcap==5.0.3 # homeassistant.scripts.check_config colorlog==4.0.2 -# homeassistant.components.alarm_control_panel.concord232 -# homeassistant.components.binary_sensor.concord232 +# homeassistant.components.concord232.alarm_control_panel +# homeassistant.components.concord232.binary_sensor concord232==0.15 -# homeassistant.components.climate.eq3btsmart -# homeassistant.components.sensor.eddystone_temperature +# 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 @@ -310,39 +310,39 @@ construct==2.9.45 # homeassistant.scripts.credstash # credstash==1.15.0 -# homeassistant.components.sensor.crimereports +# homeassistant.components.crimereports.sensor crimereports==1.0.1 # homeassistant.components.datadog datadog==0.15.0 -# homeassistant.components.sensor.metoffice -# homeassistant.components.weather.metoffice +# homeassistant.components.metoffice.sensor +# homeassistant.components.metoffice.weather datapoint==0.4.3 -# homeassistant.components.light.decora +# homeassistant.components.decora.light # decora==0.6 -# homeassistant.components.light.decora_wifi +# homeassistant.components.decora_wifi.light # decora_wifi==1.3 # homeassistant.components.ihc # homeassistant.components.namecheapdns # homeassistant.components.device_tracker.upc_connect -# homeassistant.components.sensor.ohmconnect +# homeassistant.components.ohmconnect.sensor defusedxml==0.5.0 -# homeassistant.components.sensor.deluge -# homeassistant.components.switch.deluge +# homeassistant.components.deluge.sensor +# homeassistant.components.deluge.switch deluge-client==1.4.0 -# homeassistant.components.media_player.denonavr +# homeassistant.components.denonavr.media_player denonavr==0.7.8 # homeassistant.components.media_player.directv directpy==0.5 -# homeassistant.components.sensor.discogs +# homeassistant.components.discogs.sensor discogs_client==2.2.1 # homeassistant.components.notify.discord @@ -351,7 +351,7 @@ discord.py==0.16.12 # homeassistant.components.updater distro==1.4.0 -# homeassistant.components.switch.digitalloggers +# homeassistant.components.digitalloggers.switch dlipower==0.7.165 # homeassistant.components.doorbird @@ -379,10 +379,10 @@ edp_redy==0.0.3 # homeassistant.components.device_tracker.ee_brightbox eebrightbox==0.0.4 -# homeassistant.components.media_player.horizon +# homeassistant.components.horizon.media_player einder==0.3.1 -# homeassistant.components.sensor.eliqonline +# homeassistant.components.eliqonline.sensor eliqonline==1.2.2 # homeassistant.components.elkm1 @@ -397,16 +397,16 @@ enocean==0.40 # homeassistant.components.sensor.entur_public_transport enturclient==0.1.3 -# homeassistant.components.sensor.envirophat +# homeassistant.components.envirophat.sensor # envirophat==0.0.6 -# homeassistant.components.sensor.enphase_envoy +# homeassistant.components.enphase_envoy.sensor envoy_reader==0.3 # homeassistant.components.sensor.season ephem==3.7.6.0 -# homeassistant.components.media_player.epson +# homeassistant.components.epson.media_player epson-projector==0.1.3 # homeassistant.components.netgear_lte @@ -419,14 +419,14 @@ eternalegypt==0.0.5 # homeassistant.components.climate.honeywell evohomeclient==0.2.8 -# homeassistant.components.image_processing.dlib_face_detect -# homeassistant.components.image_processing.dlib_face_identify +# homeassistant.components.dlib_face_detect.image_processing +# homeassistant.components.dlib_face_identify.image_processing # face_recognition==1.0.0 # homeassistant.components.fastdotcom fastdotcom==0.0.3 -# homeassistant.components.sensor.fedex +# homeassistant.components.fedex.sensor fedexdeliverymanager==1.0.6 # homeassistant.components.feedreader @@ -435,16 +435,16 @@ feedparser-homeassistant==5.2.2.dev1 # homeassistant.components.fibaro fiblary3==0.1.7 -# homeassistant.components.sensor.fints +# homeassistant.components.fints.sensor fints==1.0.1 -# homeassistant.components.sensor.fitbit +# homeassistant.components.fitbit.sensor fitbit==0.3.0 -# homeassistant.components.sensor.fixer +# homeassistant.components.fixer.sensor fixerio==1.0.0a0 -# homeassistant.components.light.flux_led +# homeassistant.components.flux_led.light flux_led==0.22 # homeassistant.components.sensor.foobot @@ -453,21 +453,21 @@ foobot_async==0.3.1 # homeassistant.components.notify.free_mobile freesms==0.1.2 -# homeassistant.components.device_tracker.fritz -# homeassistant.components.sensor.fritzbox_callmonitor -# homeassistant.components.sensor.fritzbox_netmonitor +# homeassistant.components.fritz.device_tracker +# homeassistant.components.fritzbox_callmonitor.sensor +# homeassistant.components.fritzbox_netmonitor.sensor # fritzconnection==0.6.5 -# homeassistant.components.switch.fritzdect +# homeassistant.components.fritzdect.switch fritzhome==1.0.4 # homeassistant.components.google.tts gTTS-token==1.1.3 -# homeassistant.components.sensor.gearbest +# homeassistant.components.gearbest.sensor gearbest_parser==1.0.7 -# homeassistant.components.sensor.geizhals +# homeassistant.components.geizhals.sensor geizhals==0.0.9 # homeassistant.components.geo_location.geo_json_events @@ -478,10 +478,10 @@ geojson_client==0.3 # homeassistant.components.sensor.geo_rss_events georss_client==0.5 -# homeassistant.components.sensor.gitter +# homeassistant.components.gitter.sensor gitterpy==0.1.7 -# homeassistant.components.sensor.glances +# homeassistant.components.glances.sensor glances_api==0.2.0 # homeassistant.components.notify.gntp @@ -496,25 +496,25 @@ google-cloud-pubsub==0.39.1 # homeassistant.components.googlehome googledevices==1.0.2 -# homeassistant.components.sensor.google_travel_time +# homeassistant.components.google_travel_time.sensor googlemaps==2.5.1 -# homeassistant.components.sensor.gpsd +# homeassistant.components.gpsd.sensor gps3==0.33.3 # homeassistant.components.greeneye_monitor greeneye_monitor==1.0 -# homeassistant.components.light.greenwave +# homeassistant.components.greenwave.light greenwavereality==0.5.1 -# homeassistant.components.media_player.gstreamer +# homeassistant.components.gstreamer.media_player gstreamer-player==1.1.2 # homeassistant.components.ffmpeg ha-ffmpeg==1.11 -# homeassistant.components.media_player.philips_js +# homeassistant.components.philips_js.media_player ha-philipsjs==0.0.5 # homeassistant.components.habitica @@ -532,22 +532,22 @@ hbmqtt==0.9.4 # homeassistant.components.sensor.jewish_calendar hdate==0.8.7 -# homeassistant.components.climate.heatmiser +# homeassistant.components.heatmiser.climate heatmiserV3==0.9.1 -# homeassistant.components.switch.hikvisioncam +# homeassistant.components.hikvisioncam.switch hikvision==0.4 # homeassistant.components.notify.hipchat hipnotify==1.0.8 -# homeassistant.components.media_player.harman_kardon_avr +# homeassistant.components.harman_kardon_avr.media_player hkavr==0.0.5 # homeassistant.components.hlk_sw16 hlk-sw16==0.0.6 -# homeassistant.components.sensor.pi_hole +# homeassistant.components.pi_hole.sensor hole==0.3.0 # homeassistant.components.binary_sensor.workday @@ -575,22 +575,22 @@ huawei-lte-api==1.1.5 # homeassistant.components.hydrawise hydrawiser==0.1.1 -# homeassistant.components.sensor.bh1750 -# homeassistant.components.sensor.bme280 -# homeassistant.components.sensor.htu21d +# homeassistant.components.bh1750.sensor +# homeassistant.components.bme280.sensor +# homeassistant.components.htu21d.sensor # i2csense==0.0.4 # homeassistant.components.watson_iot ibmiotf==0.3.4 -# homeassistant.components.light.iglo +# homeassistant.components.iglo.light iglo==1.2.7 # homeassistant.components.ihc ihcsdk==2.3.0 # homeassistant.components.influxdb -# homeassistant.components.sensor.influxdb +# homeassistant.components.influxdb.sensor influxdb==5.2.0 # homeassistant.components.insteon @@ -605,11 +605,11 @@ ipify==1.0.0 # homeassistant.components.verisure jsonpath==0.75 -# homeassistant.components.media_player.kodi +# homeassistant.components.kodi.media_player # homeassistant.components.notify.kodi jsonrpc-async==0.6 -# homeassistant.components.media_player.kodi +# homeassistant.components.kodi.media_player jsonrpc-websocket==0.6 # homeassistant.scripts.keyring @@ -618,7 +618,7 @@ keyring==17.1.1 # homeassistant.scripts.keyring keyrings.alt==3.1.1 -# homeassistant.components.lock.kiwi +# homeassistant.components.kiwi.lock kiwiki-client==0.1.1 # homeassistant.components.konnected @@ -630,44 +630,44 @@ lakeside==0.12 # homeassistant.components.dyson libpurecoollink==0.4.2 -# homeassistant.components.camera.foscam +# homeassistant.components.foscam.camera libpyfoscam==1.0 -# homeassistant.components.device_tracker.mikrotik +# homeassistant.components.mikrotik.device_tracker librouteros==2.2.0 # homeassistant.components.media_player.soundtouch libsoundtouch==0.7.2 -# homeassistant.components.light.lifx_legacy +# homeassistant.components.lifx_legacy.light liffylights==0.9.4 -# homeassistant.components.light.osramlightify +# homeassistant.components.osramlightify.light lightify==1.0.6.1 # homeassistant.components.lightwave lightwave==0.15 -# homeassistant.components.light.limitlessled +# homeassistant.components.limitlessled.light limitlessled==1.1.3 # homeassistant.components.linode linode-api==4.1.9b1 -# homeassistant.components.media_player.liveboxplaytv +# homeassistant.components.liveboxplaytv.media_player liveboxplaytv==2.0.2 # homeassistant.components.lametric # homeassistant.components.lametric.notify lmnotify==0.0.4 -# homeassistant.components.device_tracker.google_maps +# homeassistant.components.google_maps.device_tracker locationsharinglib==3.0.11 # homeassistant.components.logi_circle logi_circle==0.1.7 -# homeassistant.components.sensor.london_underground +# homeassistant.components.london_underground.sensor london-tube-status==0.2 # homeassistant.components.luftdaten @@ -676,13 +676,13 @@ luftdaten==0.3.4 # homeassistant.components.lupusec lupupy==0.0.17 -# homeassistant.components.light.lw12wifi +# homeassistant.components.lw12wifi.light lw12==0.9.2 -# homeassistant.components.sensor.lyft +# homeassistant.components.lyft.sensor lyft_rides==0.2 -# homeassistant.components.sensor.magicseaweed +# homeassistant.components.magicseaweed.sensor magicseaweed==1.0.3 # homeassistant.components.matrix @@ -704,16 +704,16 @@ meteofrance==0.3.4 # homeassistant.components.switch.mfi mficlient==0.3.0 -# homeassistant.components.sensor.miflora +# homeassistant.components.miflora.sensor miflora==0.4.0 -# homeassistant.components.climate.mill +# homeassistant.components.mill.climate millheater==0.3.4 -# homeassistant.components.sensor.mitemp_bt +# homeassistant.components.mitemp_bt.sensor mitemp_bt==0.0.1 -# homeassistant.components.sensor.mopar +# homeassistant.components.mopar.sensor motorparts==1.1.0 # homeassistant.components.tts @@ -728,31 +728,31 @@ mycroftapi==2.0 # homeassistant.components.usps myusps==1.3.2 -# homeassistant.components.media_player.nad +# homeassistant.components.nad.media_player nad_receiver==0.0.11 -# homeassistant.components.device_tracker.keenetic_ndms2 +# homeassistant.components.keenetic_ndms2.device_tracker ndms2_client==0.0.6 # homeassistant.components.ness_alarm nessclient==0.9.14 -# homeassistant.components.sensor.netdata +# homeassistant.components.netdata.sensor netdata==0.1.2 # homeassistant.components.discovery netdisco==2.5.0 -# homeassistant.components.sensor.neurio_energy +# homeassistant.components.neurio_energy.sensor neurio==0.3.1 -# homeassistant.components.light.niko_home_control +# homeassistant.components.niko_home_control.light niko-home-control==0.1.8 -# homeassistant.components.air_quality.nilu +# homeassistant.components.nilu.air_quality niluclient==0.1.2 -# homeassistant.components.sensor.nederlandse_spoorwegen +# homeassistant.components.nederlandse_spoorwegen.sensor nsapi==2.7.4 # homeassistant.components.sensor.nsw_fuel_station @@ -762,61 +762,61 @@ nsw-fuel-api-client==1.0.10 nuheat==0.3.0 # homeassistant.components.binary_sensor.trend -# homeassistant.components.image_processing.opencv -# homeassistant.components.image_processing.tensorflow -# homeassistant.components.sensor.pollen +# homeassistant.components.opencv.image_processing +# homeassistant.components.pollen.sensor +# homeassistant.components.tensorflow.image_processing numpy==1.16.2 # homeassistant.components.google oauth2client==4.0.0 -# homeassistant.components.climate.oem +# homeassistant.components.oem.climate oemthermostat==1.1 -# homeassistant.components.media_player.onkyo +# homeassistant.components.onkyo.media_player onkyo-eiscp==1.2.4 -# homeassistant.components.camera.onvif +# homeassistant.components.onvif.camera onvif-py3==0.1.3 -# homeassistant.components.sensor.openevse +# homeassistant.components.openevse.sensor openevsewifi==0.4 -# homeassistant.components.media_player.openhome +# homeassistant.components.openhome.media_player openhomedevice==0.4.2 -# homeassistant.components.air_quality.opensensemap +# homeassistant.components.opensensemap.air_quality opensensemap-api==0.1.5 # homeassistant.components.enigma2.media_player openwebifpy==1.2.7 -# homeassistant.components.device_tracker.luci +# homeassistant.components.luci.device_tracker openwrt-luci-rpc==1.0.5 -# homeassistant.components.switch.orvibo +# homeassistant.components.orvibo.switch orvibo==1.1.1 # homeassistant.components.mqtt # homeassistant.components.shiftr paho-mqtt==1.4.0 -# homeassistant.components.media_player.panasonic_bluray +# homeassistant.components.panasonic_bluray.media_player panacotta==0.1 -# homeassistant.components.media_player.panasonic_viera +# homeassistant.components.panasonic_viera.media_player panasonic_viera==0.3.1 -# homeassistant.components.media_player.dunehd +# homeassistant.components.dunehd.media_player pdunehd==1.3 -# homeassistant.components.switch.pencom +# homeassistant.components.pencom.switch pencompy==0.0.3 -# homeassistant.components.device_tracker.aruba -# homeassistant.components.device_tracker.cisco_ios +# homeassistant.components.aruba.device_tracker +# homeassistant.components.cisco_ios.device_tracker # homeassistant.components.device_tracker.unifi_direct -# homeassistant.components.media_player.pandora +# homeassistant.components.pandora.media_player pexpect==4.6.0 # homeassistant.components.rpi_pfio @@ -825,35 +825,35 @@ pifacecommon==4.2.2 # homeassistant.components.rpi_pfio pifacedigitalio==3.0.5 -# homeassistant.components.light.piglow +# homeassistant.components.piglow.light piglow==1.2.4 # homeassistant.components.pilight pilight==0.1.1 -# homeassistant.components.camera.proxy -# homeassistant.components.image_processing.qrcode -# homeassistant.components.image_processing.tensorflow +# homeassistant.components.proxy.camera +# homeassistant.components.qrcode.image_processing +# homeassistant.components.tensorflow.image_processing pillow==5.4.1 # homeassistant.components.dominos pizzapi==0.0.3 -# homeassistant.components.media_player.plex -# homeassistant.components.sensor.plex +# homeassistant.components.plex.media_player +# homeassistant.components.plex.sensor plexapi==3.0.6 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 # homeassistant.components.sensor.mhz19 -# homeassistant.components.sensor.serial_pm +# homeassistant.components.serial_pm.sensor pmsensor==0.4 -# homeassistant.components.sensor.pocketcasts +# homeassistant.components.pocketcasts.sensor pocketcasts==0.1 -# homeassistant.components.sensor.postnl +# homeassistant.components.postnl.sensor postnl_api==1.0.2 # homeassistant.components.reddit.sensor @@ -862,32 +862,32 @@ praw==6.1.1 # homeassistant.components.sensor.islamic_prayer_times prayer_times_calculator==0.0.3 -# homeassistant.components.sensor.prezzibenzina +# homeassistant.components.prezzibenzina.sensor prezzibenzina-py==1.1.4 -# homeassistant.components.climate.proliphix +# homeassistant.components.proliphix.climate proliphix==0.4.1 # homeassistant.components.prometheus prometheus_client==0.2.0 -# homeassistant.components.image_processing.tensorflow +# homeassistant.components.tensorflow.image_processing protobuf==3.6.1 -# homeassistant.components.sensor.systemmonitor +# homeassistant.components.systemmonitor.sensor psutil==5.5.1 # homeassistant.components.wink pubnubsub-handler==1.0.3 # homeassistant.components.notify.pushbullet -# homeassistant.components.sensor.pushbullet +# homeassistant.components.pushbullet.sensor pushbullet.py==0.11.0 # homeassistant.components.notify.pushetta pushetta==1.0.15 -# homeassistant.components.light.rpi_gpio_pwm +# homeassistant.components.rpi_gpio_pwm.light pwmled==1.4.1 # homeassistant.components.august @@ -896,16 +896,16 @@ py-august==0.7.0 # homeassistant.components.canary py-canary==0.5.0 -# homeassistant.components.sensor.cpuspeed +# homeassistant.components.cpuspeed.sensor py-cpuinfo==4.0.0 # homeassistant.components.melissa py-melissa-climate==2.0.0 -# homeassistant.components.camera.synology +# homeassistant.components.synology.camera py-synology==0.2.0 -# homeassistant.components.sensor.seventeentrack +# homeassistant.components.seventeentrack.sensor py17track==2.2.2 # homeassistant.components.hdmi_cec @@ -914,38 +914,38 @@ pyCEC==0.4.13 # homeassistant.components.tplink pyHS100==0.3.4 -# homeassistant.components.air_quality.norway_air -# homeassistant.components.weather.met +# homeassistant.components.met.weather +# homeassistant.components.norway_air.air_quality pyMetno==0.4.6 # homeassistant.components.rfxtrx pyRFXtrx==0.23 -# homeassistant.components.switch.switchmate +# homeassistant.components.switchmate.switch # pySwitchmate==0.4.5 # homeassistant.components.tibber pyTibber==0.9.6 -# homeassistant.components.switch.dlink +# homeassistant.components.dlink.switch pyW215==0.6.0 # homeassistant.components.w800rf32 pyW800rf32==0.1 -# homeassistant.components.sensor.noaa_tides +# homeassistant.components.noaa_tides.sensor # py_noaa==0.3.0 # homeassistant.components.ads pyads==3.0.7 -# homeassistant.components.sensor.aftership +# homeassistant.components.aftership.sensor pyaftership==0.1.2 -# homeassistant.components.sensor.airvisual +# homeassistant.components.airvisual.sensor pyairvisual==3.0.1 -# homeassistant.components.alarm_control_panel.alarmdotcom +# homeassistant.components.alarmdotcom.alarm_control_panel pyalarmdotcom==0.3.2 # homeassistant.components.arlo @@ -957,14 +957,14 @@ pyatmo==1.8 # homeassistant.components.apple_tv pyatv==0.3.12 -# homeassistant.components.device_tracker.bbox -# homeassistant.components.sensor.bbox +# homeassistant.components.bbox.device_tracker +# homeassistant.components.bbox.sensor pybbox==0.0.5-alpha # homeassistant.components.media_player.blackbird pyblackbird==0.5 -# homeassistant.components.device_tracker.bluetooth_tracker +# homeassistant.components.bluetooth_tracker.device_tracker # pybluez==0.22 # homeassistant.components.neato @@ -976,25 +976,25 @@ pycarwings2==2.8 # homeassistant.components.cloudflare pycfdns==0.0.1 -# homeassistant.components.media_player.channels +# homeassistant.components.channels.media_player pychannels==1.0.0 # homeassistant.components.cast pychromecast==3.0.0 -# homeassistant.components.media_player.cmus +# homeassistant.components.cmus.media_player pycmus==0.1.1 # homeassistant.components.comfoconnect pycomfoconnect==0.3 -# homeassistant.components.climate.coolmaster +# homeassistant.components.coolmaster.climate pycoolmasternet==0.0.4 # homeassistant.components.tts.microsoft pycsspeechtts==1.0.2 -# homeassistant.components.sensor.cups +# homeassistant.components.cups.sensor # pycups==1.9.73 # homeassistant.components.daikin @@ -1012,28 +1012,28 @@ pydispatcher==2.0.5 # homeassistant.components.android_ip_webcam pydroid-ipcam==0.8 -# homeassistant.components.sensor.duke_energy +# homeassistant.components.duke_energy.sensor pydukeenergy==0.0.6 -# homeassistant.components.sensor.ebox +# homeassistant.components.ebox.sensor pyebox==1.1.4 -# homeassistant.components.water_heater.econet +# homeassistant.components.econet.water_heater pyeconet==0.0.10 -# homeassistant.components.switch.edimax +# homeassistant.components.edimax.switch pyedimax==0.1 # homeassistant.components.eight_sleep pyeight==0.1.1 -# homeassistant.components.media_player.emby +# homeassistant.components.emby.media_player pyemby==1.6 # homeassistant.components.envisalink pyenvisalink==3.8 -# homeassistant.components.climate.ephember +# homeassistant.components.ephember.climate pyephember==0.2.0 # homeassistant.components.light.everlights @@ -1042,16 +1042,16 @@ pyeverlights==0.1.0 # homeassistant.components.sensor.fido pyfido==2.1.1 -# homeassistant.components.climate.flexit +# homeassistant.components.flexit.climate pyflexit==0.3 -# homeassistant.components.binary_sensor.flic +# homeassistant.components.flic.binary_sensor pyflic-homeassistant==0.4.dev0 -# homeassistant.components.sensor.flunearyou +# homeassistant.components.flunearyou.sensor pyflunearyou==1.0.3 -# homeassistant.components.light.futurenow +# homeassistant.components.futurenow.light pyfnip==0.2 # homeassistant.components.fritzbox @@ -1060,23 +1060,23 @@ pyfritzhome==0.4.0 # homeassistant.components.ifttt pyfttt==0.3 -# homeassistant.components.device_tracker.bluetooth_le_tracker -# homeassistant.components.sensor.skybeacon +# homeassistant.components.bluetooth_le_tracker.device_tracker +# homeassistant.components.skybeacon.sensor pygatt[GATTTOOL]==3.2.0 -# homeassistant.components.cover.gogogate2 +# homeassistant.components.gogogate2.cover pygogogate2==0.1.1 -# homeassistant.components.sensor.gtfs +# homeassistant.components.gtfs.sensor pygtfs==0.1.5 -# homeassistant.components.sensor.gtt +# homeassistant.components.gtt.sensor pygtt==1.1.2 # homeassistant.components.sensor.version pyhaversion==2.0.3 -# homeassistant.components.binary_sensor.hikvision +# homeassistant.components.hikvision.binary_sensor pyhik==0.2.2 # homeassistant.components.hive @@ -1091,19 +1091,19 @@ pyhomeworks==0.0.6 # homeassistant.components.sensor.hydroquebec pyhydroquebec==2.2.2 -# homeassistant.components.alarm_control_panel.ialarm +# homeassistant.components.ialarm.alarm_control_panel pyialarm==0.3 -# homeassistant.components.device_tracker.icloud +# homeassistant.components.icloud.device_tracker pyicloud==0.9.1 # homeassistant.components.ipma.weather pyipma==1.2.1 -# homeassistant.components.sensor.irish_rail_transport +# homeassistant.components.irish_rail_transport.sensor pyirishrail==0.0.2 -# homeassistant.components.binary_sensor.iss +# homeassistant.components.iss.binary_sensor pyiss==1.0.1 # homeassistant.components.itach.remote @@ -1112,32 +1112,32 @@ pyitachip2ir==0.0.7 # homeassistant.components.kira pykira==0.1.1 -# homeassistant.components.sensor.kwb +# homeassistant.components.kwb.sensor pykwb==0.0.8 -# homeassistant.components.sensor.lacrosse +# homeassistant.components.lacrosse.sensor pylacrosse==0.3.1 -# homeassistant.components.sensor.lastfm +# homeassistant.components.lastfm.sensor pylast==3.0.0 -# homeassistant.components.sensor.launch_library +# homeassistant.components.launch_library.sensor pylaunches==0.2.0 -# homeassistant.components.media_player.lg_netcast +# homeassistant.components.lg_netcast.media_player pylgnetcast-homeassistant==0.2.0.dev0 # homeassistant.components.webostv.media_player # homeassistant.components.webostv.notify pylgtv==0.1.9 -# homeassistant.components.sensor.linky +# homeassistant.components.linky.sensor pylinky==0.3.0 # homeassistant.components.litejet pylitejet==0.1 -# homeassistant.components.sensor.loopenergy +# homeassistant.components.loopenergy.sensor pyloopenergy==0.1.0 # homeassistant.components.lutron_caseta @@ -1149,10 +1149,10 @@ pylutron==0.2.0 # homeassistant.components.mailgun.notify pymailgunner==1.4 -# homeassistant.components.media_player.mediaroom +# homeassistant.components.mediaroom.media_player pymediaroom==0.6.4 -# homeassistant.components.media_player.xiaomi_tv +# homeassistant.components.xiaomi_tv.media_player pymitv==1.4.3 # homeassistant.components.mochad @@ -1164,41 +1164,40 @@ pymodbus==1.5.2 # homeassistant.components.media_player.monoprice pymonoprice==0.3 -# homeassistant.components.media_player.yamaha_musiccast +# homeassistant.components.yamaha_musiccast.media_player pymusiccast==0.1.6 -# homeassistant.components.cover.myq +# homeassistant.components.myq.cover pymyq==1.1.0 # homeassistant.components.mysensors pymysensors==0.18.0 -# homeassistant.components.light.nanoleaf +# homeassistant.components.nanoleaf.light pynanoleaf==0.0.5 -# homeassistant.components.lock.nello +# homeassistant.components.nello.lock pynello==2.0.2 -# homeassistant.components.device_tracker.netgear +# homeassistant.components.netgear.device_tracker pynetgear==0.5.2 -# homeassistant.components.switch.netio +# homeassistant.components.netio.switch pynetio==0.1.9.1 -# homeassistant.components.lock.nuki +# homeassistant.components.nuki.lock pynuki==1.3.2 -# homeassistant.components.sensor.nut +# homeassistant.components.nut.sensor pynut2==2.1.2 -# homeassistant.components.alarm_control_panel.nx584 # homeassistant.components.binary_sensor.nx584 pynx584==0.4 # homeassistant.components.openuv pyopenuv==1.0.9 -# homeassistant.components.light.opple +# homeassistant.components.opple.light pyoppleio==1.0.5 # homeassistant.components.iota @@ -1209,26 +1208,26 @@ pyotgw==0.4b2 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp -# homeassistant.components.sensor.otp +# homeassistant.components.otp.sensor pyotp==2.2.6 # homeassistant.components.owlet pyowlet==1.0.2 -# homeassistant.components.sensor.openweathermap -# homeassistant.components.weather.openweathermap +# homeassistant.components.openweathermap.sensor +# homeassistant.components.openweathermap.weather pyowm==2.10.0 # homeassistant.components.lcn pypck==0.5.9 -# homeassistant.components.media_player.pjlink +# homeassistant.components.pjlink.media_player pypjlink2==1.2.0 # homeassistant.components.point pypoint==1.1.1 -# homeassistant.components.sensor.pollen +# homeassistant.components.pollen.sensor pypollencom==2.2.3 # homeassistant.components.ps4 @@ -1237,40 +1236,40 @@ pyps4-homeassistant==0.4.8 # homeassistant.components.qwikswitch pyqwikswitch==0.8 -# homeassistant.components.sensor.nmbs +# homeassistant.components.nmbs.sensor pyrail==0.0.3 # homeassistant.components.rainbird pyrainbird==0.1.6 -# homeassistant.components.switch.recswitch +# homeassistant.components.recswitch.switch pyrecswitch==1.0.2 -# homeassistant.components.sensor.ruter +# homeassistant.components.ruter.sensor pyruter==1.1.0 # homeassistant.components.sabnzbd pysabnzbd==1.1.0 -# homeassistant.components.switch.sony_projector +# homeassistant.components.sony_projector.switch pysdcp==1 -# homeassistant.components.climate.sensibo +# homeassistant.components.sensibo.climate pysensibo==1.0.3 -# homeassistant.components.sensor.serial +# homeassistant.components.serial.sensor pyserial-asyncio==0.4 -# homeassistant.components.switch.acer_projector +# homeassistant.components.acer_projector.switch pyserial==3.1.1 -# homeassistant.components.lock.sesame +# homeassistant.components.sesame.lock pysesame==0.1.0 # homeassistant.components.goalfeed pysher==1.0.1 -# homeassistant.components.sensor.sma +# homeassistant.components.sma.sensor pysma==0.3.1 # homeassistant.components.smartthings @@ -1279,9 +1278,9 @@ pysmartapp==0.3.2 # homeassistant.components.smartthings pysmartthings==0.6.7 -# homeassistant.components.device_tracker.snmp -# homeassistant.components.sensor.snmp -# homeassistant.components.switch.snmp +# homeassistant.components.snmp.device_tracker +# homeassistant.components.snmp.sensor +# homeassistant.components.snmp.switch pysnmp==4.4.8 # homeassistant.components.sonos @@ -1293,23 +1292,23 @@ pyspcwebgw==0.4.0 # homeassistant.components.notify.stride pystride==0.1.7 -# homeassistant.components.sensor.syncthru +# homeassistant.components.syncthru.sensor pysyncthru==0.3.1 -# homeassistant.components.sensor.tautulli +# homeassistant.components.tautulli.sensor pytautulli==0.5.0 -# homeassistant.components.media_player.liveboxplaytv +# homeassistant.components.liveboxplaytv.media_player pyteleloisirs==3.4 # homeassistant.components.thinkingcleaner.sensor # homeassistant.components.thinkingcleaner.switch pythinkingcleaner==0.0.3 -# homeassistant.components.sensor.blockchain +# homeassistant.components.blockchain.sensor python-blockchain-api==0.0.2 -# homeassistant.components.media_player.clementine +# homeassistant.components.clementine.media_player python-clementine-remote==1.0.1 # homeassistant.components.digital_ocean @@ -1318,13 +1317,13 @@ python-digitalocean==1.13.2 # homeassistant.components.ecobee python-ecobee-api==0.0.18 -# homeassistant.components.climate.eq3btsmart +# homeassistant.components.eq3btsmart.climate # python-eq3bt==0.1.9 -# homeassistant.components.sensor.etherscan +# homeassistant.components.etherscan.sensor python-etherscan-api==0.0.3 -# homeassistant.components.camera.familyhub +# homeassistant.components.familyhub.camera python-family-hub-local==0.0.2 # homeassistant.components.sensor.darksky @@ -1334,10 +1333,10 @@ python-forecastio==1.4.0 # homeassistant.components.gc100 python-gc100==1.0.3a -# homeassistant.components.sensor.gitlab_ci +# homeassistant.components.gitlab_ci.sensor python-gitlab==1.6.0 -# homeassistant.components.sensor.hp_ilo +# homeassistant.components.hp_ilo.sensor python-hpilo==3.9 # homeassistant.components.joaoapps_join @@ -1359,38 +1358,38 @@ python-juicenet==0.0.5 # homeassistant.components.xiaomi_miio.vacuum python-miio==0.4.4 -# homeassistant.components.media_player.mpd +# homeassistant.components.mpd.media_player python-mpd2==1.0.0 -# homeassistant.components.light.mystrom -# homeassistant.components.switch.mystrom +# homeassistant.components.mystrom.light +# homeassistant.components.mystrom.switch python-mystrom==0.5.0 # homeassistant.components.nest python-nest==4.1.0 -# homeassistant.components.device_tracker.nmap_tracker +# homeassistant.components.nmap_tracker.device_tracker python-nmap==0.6.1 # homeassistant.components.notify.pushover python-pushover==0.3 -# homeassistant.components.sensor.qbittorrent +# homeassistant.components.qbittorrent.sensor python-qbittorrent==0.3.1 -# homeassistant.components.sensor.ripple +# homeassistant.components.ripple.sensor python-ripple-api==0.0.3 # homeassistant.components.roku python-roku==3.1.5 -# homeassistant.components.sensor.sochain +# homeassistant.components.sochain.sensor python-sochain-api==0.0.2 -# homeassistant.components.media_player.songpal +# homeassistant.components.songpal.media_player python-songpal==0.0.9.1 -# homeassistant.components.sensor.synologydsm +# homeassistant.components.synologydsm.sensor python-synology==0.2.0 # homeassistant.components.tado @@ -1399,16 +1398,16 @@ python-tado==0.2.3 # homeassistant.components.telegram_bot python-telegram-bot==11.1.0 -# homeassistant.components.sensor.twitch +# homeassistant.components.twitch.sensor python-twitch-client==0.6.0 # homeassistant.components.velbus python-velbus==2.0.22 -# homeassistant.components.media_player.vlc +# homeassistant.components.vlc.media_player python-vlc==1.1.2 -# homeassistant.components.sensor.whois +# homeassistant.components.whois.sensor python-whois==0.7.1 # homeassistant.components.wink @@ -1417,37 +1416,37 @@ python-wink==1.10.3 # homeassistant.components.sensor.awair python_awair==0.0.3 -# homeassistant.components.sensor.swiss_public_transport +# homeassistant.components.swiss_public_transport.sensor python_opendata_transport==0.1.4 # homeassistant.components.egardia pythonegardia==1.0.39 -# homeassistant.components.device_tracker.tile +# homeassistant.components.tile.device_tracker pytile==2.0.6 -# homeassistant.components.climate.touchline +# homeassistant.components.touchline.climate pytouchline==0.7 -# homeassistant.components.device_tracker.traccar +# homeassistant.components.traccar.device_tracker pytraccar==0.3.0 -# homeassistant.components.device_tracker.trackr +# homeassistant.components.trackr.device_tracker pytrackr==0.0.5 # homeassistant.components.tradfri pytradfri[async]==6.0.1 -# homeassistant.components.sensor.trafikverket_weatherstation +# homeassistant.components.trafikverket_weatherstation.sensor pytrafikverket==0.1.5.8 -# homeassistant.components.device_tracker.ubee +# homeassistant.components.ubee.device_tracker pyubee==0.2 # homeassistant.components.device_tracker.unifi pyunifi==2.16 -# homeassistant.components.binary_sensor.uptimerobot +# homeassistant.components.uptimerobot.binary_sensor pyuptimerobot==0.0.5 # homeassistant.components.keyboard @@ -1456,10 +1455,10 @@ pyuptimerobot==0.0.5 # homeassistant.components.vera pyvera==0.2.45 -# homeassistant.components.switch.vesync +# homeassistant.components.vesync.switch pyvesync_v2==0.9.6 -# homeassistant.components.media_player.vizio +# homeassistant.components.vizio.media_player pyvizio==0.0.4 # homeassistant.components.velux @@ -1471,25 +1470,25 @@ pywebpush==1.6.0 # homeassistant.components.wemo pywemo==0.4.34 -# homeassistant.components.camera.xeoma +# homeassistant.components.xeoma.camera pyxeoma==1.4.1 # homeassistant.components.zabbix pyzabbix==0.7.4 -# homeassistant.components.image_processing.qrcode +# homeassistant.components.qrcode.image_processing pyzbar==0.1.7 -# homeassistant.components.sensor.qnap +# homeassistant.components.qnap.sensor qnapstats==0.2.7 -# homeassistant.components.device_tracker.quantum_gateway +# homeassistant.components.quantum_gateway.device_tracker quantum-gateway==0.0.5 # homeassistant.components.rachio rachiopy==0.1.3 -# homeassistant.components.climate.radiotherm +# homeassistant.components.radiotherm.climate radiotherm==2.0.0 # homeassistant.components.raincloud @@ -1498,10 +1497,10 @@ raincloudy==0.0.5 # homeassistant.components.raspihats # raspihats==2.2.3 -# homeassistant.components.switch.raspyrfm +# homeassistant.components.raspyrfm.switch raspyrfm-client==1.2.8 -# homeassistant.components.sensor.recollect_waste +# homeassistant.components.recollect_waste.sensor recollect-waste==1.0.1 # homeassistant.components.rainmachine @@ -1519,28 +1518,28 @@ rflink==0.0.37 # homeassistant.components.ring ring_doorbell==0.2.2 -# homeassistant.components.device_tracker.ritassist +# homeassistant.components.ritassist.device_tracker ritassist==0.9.2 -# homeassistant.components.sensor.rejseplanen +# homeassistant.components.rejseplanen.sensor rjpl==0.3.5 # homeassistant.components.notify.rocketchat rocketchat-API==0.6.1 -# homeassistant.components.vacuum.roomba +# homeassistant.components.roomba.vacuum roombapy==1.3.1 -# homeassistant.components.sensor.rova +# homeassistant.components.rova.sensor rova==0.1.0 -# homeassistant.components.switch.rpi_rf +# homeassistant.components.rpi_rf.switch # rpi-rf==0.9.7 -# homeassistant.components.media_player.russound_rnet +# homeassistant.components.russound_rnet.media_player russound==0.1.9 -# homeassistant.components.media_player.russound_rio +# homeassistant.components.russound_rio.media_player russound_rio==0.1.4 # homeassistant.components.media_player.yamaha @@ -1552,7 +1551,7 @@ samsungctl[websocket]==0.7.1 # homeassistant.components.satel_integra satel_integra==0.3.2 -# homeassistant.components.sensor.deutsche_bahn +# homeassistant.components.deutsche_bahn.sensor schiene==0.23 # homeassistant.components.scsgate @@ -1561,17 +1560,17 @@ scsgate==0.1.0 # homeassistant.components.notify.sendgrid sendgrid==5.6.0 -# homeassistant.components.light.sensehat -# homeassistant.components.sensor.sensehat +# homeassistant.components.sensehat.light +# homeassistant.components.sensehat.sensor sense-hat==2.2.0 # homeassistant.components.sense sense_energy==0.7.0 -# homeassistant.components.media_player.aquostv +# homeassistant.components.aquostv.media_player sharp_aquos_rc==0.3.2 -# homeassistant.components.sensor.shodan +# homeassistant.components.shodan.sensor shodan==1.11.1 # homeassistant.components.notify.simplepush @@ -1599,23 +1598,23 @@ slixmpp==1.4.2 smappy==0.2.16 # homeassistant.components.raspihats -# homeassistant.components.sensor.bh1750 -# homeassistant.components.sensor.bme280 -# homeassistant.components.sensor.bme680 -# homeassistant.components.sensor.envirophat -# homeassistant.components.sensor.htu21d +# 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.media_player.snapcast +# homeassistant.components.snapcast.media_player snapcast==2.0.9 -# homeassistant.components.sensor.socialblade +# homeassistant.components.socialblade.sensor socialbladeclient==0.2 -# homeassistant.components.sensor.solaredge +# homeassistant.components.solaredge.sensor solaredge==0.0.2 # homeassistant.components.climate.honeywell @@ -1627,10 +1626,10 @@ speedtest-cli==2.0.2 # homeassistant.components.spider spiderpy==1.3.1 -# homeassistant.components.sensor.spotcrime +# homeassistant.components.spotcrime.sensor spotcrime==1.0.3 -# homeassistant.components.media_player.spotify +# homeassistant.components.spotify.media_player spotipy-homeassistant==2.4.4.dev1 # homeassistant.components.recorder @@ -1640,40 +1639,40 @@ sqlalchemy==1.2.18 # homeassistant.components.sensor.srp_energy srpenergy==1.0.5 -# homeassistant.components.sensor.starlingbank +# homeassistant.components.starlingbank.sensor starlingbank==3.1 # homeassistant.components.statsd statsd==3.2.1 -# homeassistant.components.sensor.steam_online +# homeassistant.components.steam_online.sensor steamodd==4.21 -# homeassistant.components.sensor.thermoworks_smoke +# homeassistant.components.thermoworks_smoke.sensor stringcase==1.2.0 # homeassistant.components.ecovacs sucks==0.9.3 -# homeassistant.components.camera.onvif +# homeassistant.components.onvif.camera suds-passworddigest-homeassistant==0.1.2a0.dev0 -# homeassistant.components.camera.onvif +# homeassistant.components.onvif.camera suds-py3==1.3.3.0 -# homeassistant.components.sensor.swiss_hydrological_data +# homeassistant.components.swiss_hydrological_data.sensor swisshydrodata==0.0.3 -# homeassistant.components.device_tracker.synology_srm +# homeassistant.components.synology_srm.device_tracker synology-srm==0.0.6 # homeassistant.components.tahoma tahoma-api==0.0.14 -# homeassistant.components.sensor.tank_utility +# homeassistant.components.tank_utility.sensor tank_utility==1.4.0 -# homeassistant.components.binary_sensor.tapsaff +# homeassistant.components.tapsaff.binary_sensor tapsaff==0.2.0 # homeassistant.components.tellstick @@ -1685,31 +1684,31 @@ tellcore-py==1.1.2 # homeassistant.components.tellduslive tellduslive==0.10.10 -# homeassistant.components.media_player.lg_soundbar +# homeassistant.components.lg_soundbar.media_player temescal==0.1 -# homeassistant.components.sensor.temper +# homeassistant.components.temper.sensor temperusb==1.5.3 # homeassistant.components.tesla teslajsonpy==0.0.25 -# homeassistant.components.sensor.thermoworks_smoke +# homeassistant.components.thermoworks_smoke.sensor thermoworks_smoke==0.1.8 # homeassistant.components.thingspeak thingspeak==0.4.1 -# homeassistant.components.light.tikteck +# homeassistant.components.tikteck.light tikteck==0.4 -# homeassistant.components.calendar.todoist +# homeassistant.components.todoist.calendar todoist-python==7.0.17 # homeassistant.components.toon toonapilib==3.2.1 -# homeassistant.components.alarm_control_panel.totalconnect +# homeassistant.components.totalconnect.alarm_control_panel total_connect_client==0.22 # homeassistant.components.tplink_lte @@ -1727,25 +1726,25 @@ tuyapy==0.1.3 # homeassistant.components.twilio twilio==6.19.1 -# homeassistant.components.sensor.uber +# homeassistant.components.uber.sensor uber_rides==0.6.0 # homeassistant.components.upcloud upcloud-api==0.4.3 -# homeassistant.components.sensor.ups +# homeassistant.components.ups.sensor upsmychoice==1.0.6 -# homeassistant.components.sensor.uscis +# homeassistant.components.uscis.sensor uscisstatus==0.1.1 # homeassistant.components.camera.uvc uvcclient==0.11.0 -# homeassistant.components.climate.venstar +# homeassistant.components.venstar.climate venstarcolortouch==0.6 -# homeassistant.components.sensor.volkszaehler +# homeassistant.components.volkszaehler.sensor volkszaehler==0.1.2 # homeassistant.components.volvooncall @@ -1754,19 +1753,19 @@ volvooncall==0.8.7 # homeassistant.components.verisure vsure==1.5.2 -# homeassistant.components.sensor.vasttrafik +# homeassistant.components.vasttrafik.sensor vtjp==0.1.14 # homeassistant.components.vultr vultr==0.1.2 # homeassistant.components.wake_on_lan -# homeassistant.components.media_player.panasonic_viera # homeassistant.components.media_player.samsungtv +# homeassistant.components.panasonic_viera.media_player # homeassistant.components.switch.wake_on_lan wakeonlan==1.1.6 -# homeassistant.components.sensor.waqi +# homeassistant.components.waqi.sensor waqiasync==1.0.0 # homeassistant.components.folder_watcher @@ -1775,7 +1774,7 @@ watchdog==0.8.3 # homeassistant.components.waterfurnace waterfurnace==1.1.0 -# homeassistant.components.media_player.gpmdp +# homeassistant.components.gpmdp.media_player websocket-client==0.54.0 # homeassistant.components.webostv.media_player @@ -1790,20 +1789,20 @@ wunderpy2==0.1.6 # homeassistant.components.zigbee xbee-helper==0.0.7 -# homeassistant.components.sensor.xbox_live +# homeassistant.components.xbox_live.sensor xboxapi==0.1.1 -# homeassistant.components.device_tracker.xfinity +# homeassistant.components.xfinity.device_tracker xfinity-gateway==0.0.4 # homeassistant.components.knx xknx==0.10.0 -# homeassistant.components.media_player.bluesound +# homeassistant.components.bluesound.media_player # homeassistant.components.sensor.startca -# homeassistant.components.sensor.ted5000 # homeassistant.components.sensor.yr -# homeassistant.components.sensor.zestimate +# homeassistant.components.ted5000.sensor +# homeassistant.components.zestimate.sensor xmltodict==0.11.0 # homeassistant.components.xs1 @@ -1813,19 +1812,19 @@ xs1-api-client==2.3.5 # homeassistant.components.weather.yweather yahooweather==0.10 -# homeassistant.components.alarm_control_panel.yale_smart_alarm +# homeassistant.components.yale_smart_alarm.alarm_control_panel yalesmartalarmclient==0.1.6 -# homeassistant.components.light.yeelight +# homeassistant.components.yeelight.light yeelight==0.4.3 -# homeassistant.components.light.yeelightsunflower +# homeassistant.components.yeelightsunflower.light yeelightsunflower==0.0.10 # homeassistant.components.media_extractor youtube_dl==2019.03.09 -# homeassistant.components.light.zengge +# homeassistant.components.zengge.light zengge==0.2 # homeassistant.components.zeroconf @@ -1834,10 +1833,10 @@ zeroconf==0.21.3 # homeassistant.components.zha zha-quirks==0.0.7 -# homeassistant.components.climate.zhong_hong +# homeassistant.components.zhong_hong.climate zhong_hong_hvac==1.0.9 -# homeassistant.components.media_player.ziggo_mediabox_xl +# homeassistant.components.ziggo_mediabox_xl.media_player ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 32aec10fd15..9301ebbd358 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -68,7 +68,7 @@ coinmarketcap==5.0.3 # homeassistant.components.ihc # homeassistant.components.namecheapdns # homeassistant.components.device_tracker.upc_connect -# homeassistant.components.sensor.ohmconnect +# homeassistant.components.ohmconnect.sensor defusedxml==0.5.0 # homeassistant.components.sensor.dsmr @@ -135,7 +135,7 @@ homekit[IP]==0.13.0 homematicip==0.10.6 # homeassistant.components.influxdb -# homeassistant.components.sensor.influxdb +# homeassistant.components.influxdb.sensor influxdb==5.2.0 # homeassistant.components.verisure @@ -158,33 +158,33 @@ mbddns==0.1.2 mficlient==0.3.0 # homeassistant.components.binary_sensor.trend -# homeassistant.components.image_processing.opencv -# homeassistant.components.image_processing.tensorflow -# homeassistant.components.sensor.pollen +# homeassistant.components.opencv.image_processing +# homeassistant.components.pollen.sensor +# homeassistant.components.tensorflow.image_processing numpy==1.16.2 # homeassistant.components.mqtt # homeassistant.components.shiftr paho-mqtt==1.4.0 -# homeassistant.components.device_tracker.aruba -# homeassistant.components.device_tracker.cisco_ios +# homeassistant.components.aruba.device_tracker +# homeassistant.components.cisco_ios.device_tracker # homeassistant.components.device_tracker.unifi_direct -# homeassistant.components.media_player.pandora +# homeassistant.components.pandora.media_player pexpect==4.6.0 # homeassistant.components.pilight pilight==0.1.1 # homeassistant.components.sensor.mhz19 -# homeassistant.components.sensor.serial_pm +# homeassistant.components.serial_pm.sensor pmsensor==0.4 # homeassistant.components.prometheus prometheus_client==0.2.0 # homeassistant.components.notify.pushbullet -# homeassistant.components.sensor.pushbullet +# homeassistant.components.pushbullet.sensor pushbullet.py==0.11.0 # homeassistant.components.canary @@ -211,7 +211,6 @@ pylitejet==0.1 # homeassistant.components.media_player.monoprice pymonoprice==0.3 -# homeassistant.components.alarm_control_panel.nx584 # homeassistant.components.binary_sensor.nx584 pynx584==0.4 @@ -220,7 +219,7 @@ pyopenuv==1.0.9 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp -# homeassistant.components.sensor.otp +# homeassistant.components.otp.sensor pyotp==2.2.6 # homeassistant.components.ps4 @@ -310,8 +309,8 @@ vsure==1.5.2 vultr==0.1.2 # homeassistant.components.wake_on_lan -# homeassistant.components.media_player.panasonic_viera # homeassistant.components.media_player.samsungtv +# homeassistant.components.panasonic_viera.media_player # homeassistant.components.switch.wake_on_lan wakeonlan==1.1.6 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index ada84d2bbcd..6912c83f770 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -217,11 +217,12 @@ def gather_modules(): explore_module('homeassistant.auth', True)): try: module = importlib.import_module(package) - except ImportError: + except ImportError as err: for pattern in IGNORE_PACKAGES: if fnmatch.fnmatch(package, pattern): break else: + print("{}: {}".format(package, err)) errors.append(package) continue diff --git a/tests/components/alarm_control_panel/test_manual.py b/tests/components/alarm_control_panel/test_manual.py index 36bae21dc32..14326b3f32d 100644 --- a/tests/components/alarm_control_panel/test_manual.py +++ b/tests/components/alarm_control_panel/test_manual.py @@ -1,7 +1,7 @@ """The tests for the manual Alarm Control Panel component.""" from datetime import timedelta from unittest.mock import patch, MagicMock -from homeassistant.components.alarm_control_panel import demo +from homeassistant.components.demo import alarm_control_panel as demo from homeassistant.setup import async_setup_component from homeassistant.const import ( STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY, diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index e5fbced7293..31f2d5865b3 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -175,7 +175,7 @@ async def test_gravatar_and_picture(hass): @patch( 'homeassistant.components.device_tracker.DeviceTracker.see') @patch( - 'homeassistant.components.device_tracker.demo.setup_scanner', + 'homeassistant.components.demo.device_tracker.setup_scanner', autospec=True) async def test_discover_platform(mock_demo_setup_scanner, mock_see, hass): """Test discovery of device_tracker demo platform.""" diff --git a/tests/components/image_processing/test_init.py b/tests/components/image_processing/test_init.py index 7a31b2ffadf..8bec3bd71f5 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -126,7 +126,7 @@ class TestImageProcessingAlpr: }, } - with patch('homeassistant.components.image_processing.demo.' + with patch('homeassistant.components.demo.image_processing.' 'DemoImageProcessingAlpr.should_poll', new_callable=PropertyMock(return_value=False)): setup_component(self.hass, ip.DOMAIN, config) @@ -188,7 +188,7 @@ class TestImageProcessingAlpr: assert event_data[0]['confidence'] == 98.3 assert event_data[0]['entity_id'] == 'image_processing.demo_alpr' - @patch('homeassistant.components.image_processing.demo.' + @patch('homeassistant.components.demo.image_processing.' 'DemoImageProcessingAlpr.confidence', new_callable=PropertyMock(return_value=95)) def test_alpr_event_single_call_confidence(self, confidence_mock, @@ -228,7 +228,7 @@ class TestImageProcessingFace: }, } - with patch('homeassistant.components.image_processing.demo.' + with patch('homeassistant.components.demo.image_processing.' 'DemoImageProcessingFace.should_poll', new_callable=PropertyMock(return_value=False)): setup_component(self.hass, ip.DOMAIN, config) @@ -273,7 +273,7 @@ class TestImageProcessingFace: assert event_data[0]['entity_id'] == \ 'image_processing.demo_face' - @patch('homeassistant.components.image_processing.demo.' + @patch('homeassistant.components.demo.image_processing.' 'DemoImageProcessingFace.confidence', new_callable=PropertyMock(return_value=None)) def test_face_event_call_no_confidence(self, mock_config, aioclient_mock): diff --git a/tests/components/sensor/test_moldindicator.py b/tests/components/sensor/test_mold_indicator.py similarity index 100% rename from tests/components/sensor/test_moldindicator.py rename to tests/components/sensor/test_mold_indicator.py From 54489a3514611c5bba5a2256c4ca97ba82821a39 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 17 Mar 2019 02:16:05 -0400 Subject: [PATCH 054/290] 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 ec00ce3ef5c..575f1fe76f7 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 d16c507f34ebc4b6dab27296c9b0e7b672d509bc Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 16 Mar 2019 23:23:28 -0700 Subject: [PATCH 055/290] 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 5d5585ddd23..286ece850a6 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 b4ad602d7b3..622ea15f8fb 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 9301ebbd358..62cc0be13a4 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 cf5ba7d922d509377c8954e6a1905afd1e5c43b9 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 17 Mar 2019 07:36:31 +0100 Subject: [PATCH 056/290] Add ESPHome Camera (#22107) * Add ESPHome Camera * Bump aioesphomeapi to 1.7.0 --- homeassistant/components/esphome/__init__.py | 11 ++- homeassistant/components/esphome/camera.py | 83 ++++++++++++++++++++ requirements_all.txt | 2 +- 3 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/esphome/camera.py diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 51f565a0980..39422c530b3 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -32,12 +32,10 @@ if TYPE_CHECKING: ServiceCall, UserService DOMAIN = 'esphome' -REQUIREMENTS = ['aioesphomeapi==1.6.0'] +REQUIREMENTS = ['aioesphomeapi==1.7.0'] _LOGGER = logging.getLogger(__name__) -DOMAIN = 'esphome' - DISPATCHER_UPDATE_ENTITY = 'esphome_{entry_id}_update_{component_key}_{key}' DISPATCHER_REMOVE_ENTITY = 'esphome_{entry_id}_remove_{component_key}_{key}' DISPATCHER_ON_LIST = 'esphome_{entry_id}_on_list' @@ -50,6 +48,7 @@ STORAGE_VERSION = 1 # The HA component types this integration supports HA_COMPONENTS = [ 'binary_sensor', + 'camera', 'cover', 'fan', 'light', @@ -543,7 +542,7 @@ class EsphomeEntity(Entity): self._remove_callbacks.append( async_dispatcher_connect(self.hass, DISPATCHER_UPDATE_ENTITY.format(**kwargs), - self.async_schedule_update_ha_state) + self._on_update) ) self._remove_callbacks.append( @@ -558,6 +557,10 @@ class EsphomeEntity(Entity): self.async_schedule_update_ha_state) ) + async def _on_update(self): + """Update the entity state when state or static info changed.""" + self.async_schedule_update_ha_state() + async def async_will_remove_from_hass(self): """Unregister callbacks.""" for remove_callback in self._remove_callbacks: diff --git a/homeassistant/components/esphome/camera.py b/homeassistant/components/esphome/camera.py new file mode 100644 index 00000000000..319a2c2a4d9 --- /dev/null +++ b/homeassistant/components/esphome/camera.py @@ -0,0 +1,83 @@ +"""Support for ESPHome cameras.""" +import asyncio +import logging +from typing import Optional, TYPE_CHECKING + +from homeassistant.components import camera +from homeassistant.components.camera import Camera +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 CameraInfo, CameraState # noqa + +DEPENDENCIES = ['esphome'] +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass: HomeAssistantType, + entry: ConfigEntry, async_add_entities) -> None: + """Set up esphome cameras based on a config entry.""" + # pylint: disable=redefined-outer-name + from aioesphomeapi import CameraInfo, CameraState # noqa + + await platform_async_setup_entry( + hass, entry, async_add_entities, + component_key='camera', + info_type=CameraInfo, entity_type=EsphomeCamera, + state_type=CameraState + ) + + +class EsphomeCamera(Camera, EsphomeEntity): + """A camera implementation for ESPHome.""" + + def __init__(self, entry_id: str, component_key: str, key: int): + """Initialize.""" + Camera.__init__(self) + EsphomeEntity.__init__(self, entry_id, component_key, key) + self._image_cond = asyncio.Condition() + + @property + def _static_info(self) -> 'CameraInfo': + return super()._static_info + + @property + def _state(self) -> Optional['CameraState']: + return super()._state + + async def _on_update(self): + """Notify listeners of new image when update arrives.""" + await super()._on_update() + async with self._image_cond: + self._image_cond.notify_all() + + async def async_camera_image(self) -> Optional[bytes]: + """Return single camera image bytes.""" + if not self.available: + return None + await self._client.request_single_image() + async with self._image_cond: + await self._image_cond.wait() + if not self.available: + return None + return self._state.image[:] + + async def _async_camera_stream_image(self) -> Optional[bytes]: + """Return a single camera image in a stream.""" + if not self.available: + return None + await self._client.request_image_stream() + async with self._image_cond: + await self._image_cond.wait() + if not self.available: + return None + return self._state.image[:] + + async def handle_async_mjpeg_stream(self, request): + """Serve an HTTP MJPEG stream from the camera.""" + return await camera.async_get_still_stream( + request, self._async_camera_stream_image, + camera.DEFAULT_CONTENT_TYPE, 0.0) diff --git a/requirements_all.txt b/requirements_all.txt index 622ea15f8fb..512079a2347 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -109,7 +109,7 @@ aioautomatic==0.6.5 aiodns==1.1.1 # homeassistant.components.esphome -aioesphomeapi==1.6.0 +aioesphomeapi==1.7.0 # homeassistant.components.freebox aiofreepybox==0.0.6 From 54dfc3e2b419fdb5f9346fe75b19863cb38e78aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 17 Mar 2019 09:34:50 +0100 Subject: [PATCH 057/290] Tibber, Add price level (#22085) * Add price level to Tibber sensor * bump pyTibber version --- homeassistant/components/tibber/__init__.py | 2 +- homeassistant/components/tibber/sensor.py | 8 +++++--- requirements_all.txt | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index f254774eea4..9c5f375e19f 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_ACCESS_TOKEN, from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyTibber==0.9.6'] +REQUIREMENTS = ['pyTibber==0.9.8'] DOMAIN = 'tibber' diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index f3e0c39a1e6..fb224a25468 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -69,7 +69,7 @@ class TibberSensorElPrice(Entity): return if (not self._last_data_timestamp or - (self._last_data_timestamp - now).total_seconds()/3600 < 12 + (self._last_data_timestamp - now).total_seconds() / 3600 < 12 or not self._is_available): _LOGGER.debug("Asking for new data.") await self._fetch_data() @@ -136,12 +136,13 @@ class TibberSensorElPrice(Entity): for key, price_total in self._tibber_home.price_total.items(): price_time = dt_util.as_local(dt_util.parse_datetime(key)) price_total = round(price_total, 3) - time_diff = (now - price_time).total_seconds()/60 + time_diff = (now - price_time).total_seconds() / 60 if (not self._last_data_timestamp or price_time > self._last_data_timestamp): self._last_data_timestamp = price_time if 0 <= time_diff < 60: state = price_total + level = self._tibber_home.price_level[key] self._last_updated = price_time if now.date() == price_time.date(): max_price = max(max_price, price_total) @@ -152,6 +153,7 @@ class TibberSensorElPrice(Entity): self._device_state_attributes['max_price'] = max_price self._device_state_attributes['avg_price'] = round(sum_price / num, 3) self._device_state_attributes['min_price'] = min_price + self._device_state_attributes['price_level'] = level return state is not None @@ -232,4 +234,4 @@ class TibberSensorRT(Entity): """Return a unique ID.""" home = self._tibber_home.info['viewer']['home'] _id = home['meteringPointData']['consumptionEan'] - return'{}_rt_consumption'.format(_id) + return '{}_rt_consumption'.format(_id) diff --git a/requirements_all.txt b/requirements_all.txt index 512079a2347..2484c563fe1 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.6 +pyTibber==0.9.8 # homeassistant.components.dlink.switch pyW215==0.6.0 From 1aab551eed242e30edc0b830135aae52c81c5ef0 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sun, 17 Mar 2019 03:42:49 -0700 Subject: [PATCH 058/290] 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 28fedf6434d..17df1ba8f5a 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -39,8 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 9b1491a98d4152ddecee467753709ae4d2a9dd11 Mon Sep 17 00:00:00 2001 From: Tyler Page Date: Sun, 17 Mar 2019 11:06:40 +0000 Subject: [PATCH 059/290] Fix AttributeError traceback with darksky (#22101) * Update darksky.py * fixes --- homeassistant/components/weather/darksky.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/weather/darksky.py b/homeassistant/components/weather/darksky.py index 17e3cbbcf14..d5cbcb4785a 100644 --- a/homeassistant/components/weather/darksky.py +++ b/homeassistant/components/weather/darksky.py @@ -232,4 +232,6 @@ class DarkSkyData: @property def units(self): """Get the unit system of returned data.""" + if self.data is None: + return None return self.data.json.get('flags').get('units') From ba923d2d66eb9f81f0e0a3124e0d663e144a6be2 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Sun, 17 Mar 2019 13:04:09 +0100 Subject: [PATCH 060/290] Add service description for services. (#21897) * Add service description for services. * Make service description more clear --- homeassistant/components/google/services.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 homeassistant/components/google/services.yaml diff --git a/homeassistant/components/google/services.yaml b/homeassistant/components/google/services.yaml new file mode 100644 index 00000000000..34eecb33fd5 --- /dev/null +++ b/homeassistant/components/google/services.yaml @@ -0,0 +1,4 @@ +found_calendar: + description: Add calendar if it has not been already discovered. +scan_for_calendars: + description: Scan for new calendars. From 0d42ed18612a7a3ab574c5cd1086f0e62a210bc6 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 17 Mar 2019 11:55:10 -0600 Subject: [PATCH 061/290] Handle occassional 502 Bad Gateway from SimpliSafe (#22131) --- homeassistant/components/simplisafe/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index f494ccf390e..73a4c61a6ab 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -109,7 +109,13 @@ async def async_setup_entry(hass, config_entry): """Refresh data from the SimpliSafe account.""" for system in systems: _LOGGER.debug('Updating system data: %s', system.system_id) - await system.update() + + try: + await system.update() + except SimplipyError as err: + _LOGGER.error('There was an error while updating: %s', err) + return + async_dispatcher_send(hass, TOPIC_UPDATE.format(system.system_id)) if system.api.refresh_token_dirty: From 8529ad3ba197e154bc66ca2a5672814bf3408a9b Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 17 Mar 2019 11:55:58 -0600 Subject: [PATCH 062/290] Loosen field retrieval from Pollen.com (#22132) --- homeassistant/components/pollen/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/pollen/sensor.py b/homeassistant/components/pollen/sensor.py index 08fe45a22a6..3fc4d1fce3d 100644 --- a/homeassistant/components/pollen/sensor.py +++ b/homeassistant/components/pollen/sensor.py @@ -239,8 +239,8 @@ class ForecastSensor(BaseSensor): if self._kind == TYPE_ALLERGY_FORECAST: outlook = self.pollen.data[TYPE_ALLERGY_OUTLOOK] - self._attrs[ATTR_OUTLOOK] = outlook['Outlook'] - self._attrs[ATTR_SEASON] = outlook['Season'] + self._attrs[ATTR_OUTLOOK] = outlook.get('Outlook') + self._attrs[ATTR_SEASON] = outlook.get('Season') self._state = average From c020b7c47d03998a330cee0f64acc484a27890c8 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 17 Mar 2019 13:06:19 -0600 Subject: [PATCH 063/290] Handle possible exception during Ambient PWS reconnect (#22134) * Handle possible exception during Ambient PWS reconnect * Hound --- .../components/ambient_station/__init__.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 9ef2b3a5263..2383d011945 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -304,15 +304,26 @@ class AmbientStation: self.monitored_conditions = monitored_conditions self.stations = {} - async def ws_connect(self): - """Register handlers and connect to the websocket.""" + async def _attempt_connect(self): + """Attempt to connect to the socket (retrying later on fail).""" from aioambient.errors import WebsocketError + try: + await self.client.websocket.connect() + except WebsocketError as err: + _LOGGER.error("Error with the websocket connection: %s", err) + self._ws_reconnect_delay = min( + 2 * self._ws_reconnect_delay, 480) + async_call_later( + self._hass, self._ws_reconnect_delay, self.ws_connect) + + async def ws_connect(self): + """Register handlers and connect to the websocket.""" async def _ws_reconnect(event_time): """Forcibly disconnect from and reconnect to the websocket.""" _LOGGER.debug('Watchdog expired; forcing socket reconnection') await self.client.websocket.disconnect() - await self.client.websocket.connect() + await self._attempt_connect() def on_connect(): """Define a handler to fire when the websocket is connected.""" @@ -381,15 +392,7 @@ class AmbientStation: self.client.websocket.on_disconnect(on_disconnect) self.client.websocket.on_subscribed(on_subscribed) - try: - await self.client.websocket.connect() - except WebsocketError as err: - _LOGGER.error("Error with the websocket connection: %s", err) - - self._ws_reconnect_delay = min(2 * self._ws_reconnect_delay, 480) - - async_call_later( - self._hass, self._ws_reconnect_delay, self.ws_connect) + await self._attempt_connect() async def ws_disconnect(self): """Disconnect from the websocket.""" From 29131a655dc740287a0d9e8443ca2cc078e4bf63 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 17 Mar 2019 19:13:06 -0700 Subject: [PATCH 064/290] 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 b64fac0ed51..32bbd90aad1 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.as_dict() - )) + 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.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 00000000000..b98b21d184e --- /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 8e0f751abed..4f3be31b22c 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 e14b243336b5964e5876896cfb67b630de54ec82 Mon Sep 17 00:00:00 2001 From: WebSpider Date: Mon, 18 Mar 2019 13:54:24 +0100 Subject: [PATCH 065/290] 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 767e29ba0b9..56fc0cb704c 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -10,7 +10,7 @@ from homeassistant.helpers import config_validation as cv 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 7812bbd812b..8804bef5616 100644 --- a/homeassistant/components/tado/device_tracker.py +++ b/homeassistant/components/tado/device_tracker.py @@ -52,9 +52,9 @@ class TadoDeviceScanner(DeviceScanner): # 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 2484c563fe1..6b96ff6c5ec 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1393,7 +1393,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 ce5cf5803cea4b16e80305e4de7c1856c004accd Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Mon, 18 Mar 2019 09:27:34 -0400 Subject: [PATCH 066/290] 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 1d04791a11a..3f715af0e04 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -120,10 +120,16 @@ class Stream: """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 3d6ffa0e20c..665803d38eb 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 @@ class StreamOutput: 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 @@ class StreamOutput: # 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 @@ class StreamOutput: 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 9f96aab2f48cdb23d2e4d3ad17bd0436e048441d Mon Sep 17 00:00:00 2001 From: endor <1937941+endor-force@users.noreply.github.com> Date: Mon, 18 Mar 2019 18:55:09 +0100 Subject: [PATCH 067/290] Additional sensors and fixed icons, attributes and device classes. (#22139) --- .../trafikverket_weatherstation/sensor.py | 69 +++++++++++++++---- requirements_all.txt | 2 +- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/trafikverket_weatherstation/sensor.py b/homeassistant/components/trafikverket_weatherstation/sensor.py index 38cc23dabbe..2dffd580b7e 100644 --- a/homeassistant/components/trafikverket_weatherstation/sensor.py +++ b/homeassistant/components/trafikverket_weatherstation/sensor.py @@ -14,17 +14,20 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - ATTR_ATTRIBUTION, CONF_API_KEY, CONF_MONITORED_CONDITIONS, CONF_NAME) + ATTR_ATTRIBUTION, CONF_API_KEY, CONF_MONITORED_CONDITIONS, + CONF_NAME, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, 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.util import Throttle -REQUIREMENTS = ['pytrafikverket==0.1.5.8'] +REQUIREMENTS = ['pytrafikverket==0.1.5.9'] _LOGGER = logging.getLogger(__name__) -ATTRIBUTION = "Data provided by Trafikverket API" +ATTRIBUTION = "Data provided by Trafikverket" +ATTR_MEASURE_TIME = 'measure_time' +ATTR_ACTIVE = 'active' CONF_STATION = 'station' @@ -33,13 +36,33 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10) SCAN_INTERVAL = timedelta(seconds=300) SENSOR_TYPES = { - 'air_temp': ['Air temperature', '°C', 'air_temp'], - 'road_temp': ['Road temperature', '°C', 'road_temp'], - 'precipitation': ['Precipitation type', None, 'precipitationtype'], - 'wind_direction': ['Wind direction', '°', 'winddirection'], - 'wind_direction_text': ['Wind direction text', None, 'winddirectiontext'], - 'wind_speed': ['Wind speed', 'm/s', 'windforce'], - 'humidity': ['Humidity', '%', 'humidity'], + 'air_temp': [ + 'Air temperature', TEMP_CELSIUS, + 'air_temp', 'mdi:thermometer', DEVICE_CLASS_TEMPERATURE], + 'road_temp': [ + 'Road temperature', TEMP_CELSIUS, + 'road_temp', 'mdi:thermometer', DEVICE_CLASS_TEMPERATURE], + 'precipitation': [ + 'Precipitation type', None, + 'precipitationtype', 'mdi:weather-snowy-rainy', None], + 'wind_direction': [ + 'Wind direction', '°', + 'winddirection', 'mdi:flag-triangle', None], + 'wind_direction_text': [ + 'Wind direction text', None, + 'winddirectiontext', 'mdi:flag-triangle', None], + 'wind_speed': [ + 'Wind speed', 'm/s', + 'windforce', 'mdi:weather-windy', None], + 'humidity': [ + 'Humidity', '%', + 'humidity', 'mdi:water-percent', DEVICE_CLASS_HUMIDITY], + 'precipitation_amount': [ + 'Precipitation amount', 'mm', + 'precipitation_amount', 'mdi:cup-water', None], + 'precipitation_amountname': [ + 'Precipitation name', None, + 'precipitation_amountname', 'mdi:weather-pouring', None], } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -85,9 +108,8 @@ class TrafikverketWeatherStation(Entity): self._unit = SENSOR_TYPES[sensor_type][1] self._station = sensor_station self._weather_api = weather_api - self._attributes = { - ATTR_ATTRIBUTION: ATTRIBUTION, - } + self._icon = SENSOR_TYPES[sensor_type][3] + self._device_class = SENSOR_TYPES[sensor_type][4] self._weather = None @property @@ -95,6 +117,25 @@ class TrafikverketWeatherStation(Entity): """Return the name of the sensor.""" return '{} {}'.format(self._client, self._name) + @property + def icon(self): + """Icon to use in the frontend.""" + return self._icon + + @property + def device_state_attributes(self): + """Return the state attributes of Trafikverket Weatherstation.""" + return { + ATTR_ATTRIBUTION: ATTRIBUTION, + ATTR_ACTIVE: self._weather.active, + ATTR_MEASURE_TIME: self._weather.measure_time, + } + + @property + def device_class(self): + """Return the device class of the sensor.""" + return self._device_class + @property def state(self): """Return the state of the device.""" @@ -116,4 +157,4 @@ class TrafikverketWeatherStation(Entity): SENSOR_TYPES[self._type][2]) except (asyncio.TimeoutError, aiohttp.ClientError, ValueError) as error: - _LOGGER.error("Couldn't fetch weather data: %s", error) + _LOGGER.error("Could not fetch weather data: %s", error) diff --git a/requirements_all.txt b/requirements_all.txt index 6b96ff6c5ec..a460fbd9be6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1438,7 +1438,7 @@ pytrackr==0.0.5 pytradfri[async]==6.0.1 # homeassistant.components.trafikverket_weatherstation.sensor -pytrafikverket==0.1.5.8 +pytrafikverket==0.1.5.9 # homeassistant.components.ubee.device_tracker pyubee==0.2 From 9c7ef13f9156de65a1eeb82ffa10fe0f3789b420 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Mon, 18 Mar 2019 11:57:44 -0600 Subject: [PATCH 068/290] Fix hlk-sw16 inverted switch state (#22059) --- homeassistant/components/hlk_sw16/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hlk_sw16/__init__.py b/homeassistant/components/hlk_sw16/__init__.py index aab3f79b8b2..acb604bc010 100644 --- a/homeassistant/components/hlk_sw16/__init__.py +++ b/homeassistant/components/hlk_sw16/__init__.py @@ -13,7 +13,7 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import ( async_dispatcher_send, async_dispatcher_connect) -REQUIREMENTS = ['hlk-sw16==0.0.6'] +REQUIREMENTS = ['hlk-sw16==0.0.7'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index a460fbd9be6..873b225dd5d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -545,7 +545,7 @@ hipnotify==1.0.8 hkavr==0.0.5 # homeassistant.components.hlk_sw16 -hlk-sw16==0.0.6 +hlk-sw16==0.0.7 # homeassistant.components.pi_hole.sensor hole==0.3.0 From af473cddf0c1adfc7c207e61f352266b207b5849 Mon Sep 17 00:00:00 2001 From: Ben Lebherz Date: Mon, 18 Mar 2019 20:14:14 +0100 Subject: [PATCH 069/290] replace einder lib with horimote (#22135) --- homeassistant/components/horizon/media_player.py | 8 ++++---- requirements_all.txt | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/horizon/media_player.py b/homeassistant/components/horizon/media_player.py index e7cfcfe62b1..3b1ae36152d 100644 --- a/homeassistant/components/horizon/media_player.py +++ b/homeassistant/components/horizon/media_player.py @@ -21,7 +21,7 @@ from homeassistant.const import ( from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['einder==0.3.1'] +REQUIREMENTS = ['horimote==0.4.1'] _LOGGER = logging.getLogger(__name__) @@ -44,8 +44,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Horizon platform.""" - from einder import Client, keys - from einder.exceptions import AuthenticationError + from horimote import Client, keys + from horimote.exceptions import AuthenticationError host = config[CONF_HOST] name = config[CONF_NAME] @@ -162,7 +162,7 @@ class HorizonDevice(MediaPlayerDevice): def _send(self, key=None, channel=None): """Send a key to the Horizon device.""" - from einder.exceptions import AuthenticationError + from horimote.exceptions import AuthenticationError try: if key: diff --git a/requirements_all.txt b/requirements_all.txt index 873b225dd5d..413008105a3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -379,9 +379,6 @@ edp_redy==0.0.3 # homeassistant.components.device_tracker.ee_brightbox eebrightbox==0.0.4 -# homeassistant.components.horizon.media_player -einder==0.3.1 - # homeassistant.components.eliqonline.sensor eliqonline==1.2.2 @@ -565,6 +562,9 @@ homekit[IP]==0.13.0 # homeassistant.components.homematicip_cloud homematicip==0.10.6 +# homeassistant.components.horizon.media_player +horimote==0.4.1 + # homeassistant.components.google # homeassistant.components.remember_the_milk httplib2==0.10.3 From 9591aa66ba07cc0e5f96580aa404e676600109c0 Mon Sep 17 00:00:00 2001 From: zewelor Date: Mon, 18 Mar 2019 20:51:42 +0100 Subject: [PATCH 070/290] Fixx yeelight flow action param for declared effects (#22159) --- homeassistant/components/yeelight/light.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index b4b540f729b..18a0bf750a1 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -161,14 +161,20 @@ def _cmd(func): 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_TRANSITIONS: transitions} + effects[config[CONF_NAME]] = { + ATTR_COUNT: params[ATTR_COUNT], + ATTR_ACTION: action, + ATTR_TRANSITIONS: transitions + } return effects From e5a2ef9b8d13d9d4e9e3bd032a3a5d46af04b21a Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 19 Mar 2019 00:56:57 +0100 Subject: [PATCH 071/290] 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 0ca0a414fa5..d718b5895e4 100644 --- a/homeassistant/components/toon/__init__.py +++ b/homeassistant/components/toon/__init__.py @@ -16,7 +16,7 @@ from .const import ( 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 a50a67085ec..694b7d1d033 100644 --- a/homeassistant/components/toon/binary_sensor.py +++ b/homeassistant/components/toon/binary_sensor.py @@ -17,7 +17,7 @@ DEPENDENCIES = ['toon'] _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 13f1c1269a1..f09dc010c79 100644 --- a/homeassistant/components/toon/climate.py +++ b/homeassistant/components/toon/climate.py @@ -22,7 +22,7 @@ _LOGGER = logging.getLogger(__name__) 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 e263bda9fc7..f58c8ef4840 100644 --- a/homeassistant/components/toon/sensor.py +++ b/homeassistant/components/toon/sensor.py @@ -16,7 +16,7 @@ DEPENDENCIES = ['toon'] _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 413008105a3..19f2261be76 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1706,7 +1706,7 @@ tikteck==0.4 todoist-python==7.0.17 # homeassistant.components.toon -toonapilib==3.2.1 +toonapilib==3.2.2 # homeassistant.components.totalconnect.alarm_control_panel total_connect_client==0.22 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 62cc0be13a4..dcffc49996e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -297,7 +297,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 ecfe0fc3dda3b7a3482b719bb8dc989e8c0db21a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 19 Mar 2019 00:58:48 +0100 Subject: [PATCH 072/290] 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 | 43 ++++++--------------- 4 files changed, 19 insertions(+), 70 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index e070c889f31..7f85c8cfc3f 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -7,6 +7,7 @@ import voluptuous as vol 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_setup(hass, config): 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 46e32c9f7c3..7eb3245c0df 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -97,13 +97,6 @@ class HassIO: """ 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 db3917a2201..3e7b9e95d92 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 435e03a1755..1326805fc93 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -7,8 +7,7 @@ import pytest 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 == 2 + + 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 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', {})) - - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={ - 'result': 'error', 'message': "Error"}) - - assert (yield from async_check_config(hass)) == "Error" From 05db4448321bf6c7ea55e0a47ac6eae3e3d1ee6d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 18 Mar 2019 16:54:31 -0700 Subject: [PATCH 073/290] 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 286ece850a6..96619face25 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 19f2261be76..27a011c8875 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -551,7 +551,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 dcffc49996e..ce48d51199e 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 46ece3603f0d6ae15608649dab1635c0487e90af Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 18 Mar 2019 22:35:03 -0400 Subject: [PATCH 074/290] Add dynamic subscription for ZHA add device page (#22164) * add ws subscription for zha gateway messages * add debug mode * only relay certain logs * add missing require admin * add devices command * add area_id * fix manufacturer code --- homeassistant/components/zha/api.py | 83 +++++++++-- homeassistant/components/zha/core/const.py | 29 ++++ homeassistant/components/zha/core/gateway.py | 140 ++++++++++++++++++- 3 files changed, 233 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 544e354ba2f..4b4546821a7 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -10,13 +10,15 @@ import logging import voluptuous as vol from homeassistant.components import websocket_api +from homeassistant.core import callback 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) + DATA_ZHA_GATEWAY, DATA_ZHA, MFG_CLUSTER_ID_START) from .core.helpers import get_matched_clusters, async_is_bindable_target _LOGGER = logging.getLogger(__name__) @@ -74,6 +76,38 @@ SERVICE_SCHEMAS = { } +@websocket_api.require_admin +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required('type'): 'zha/devices/permit' +}) +async def websocket_permit_devices(hass, connection, msg): + """Permit ZHA zigbee devices.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + + async def forward_messages(data): + """Forward events to websocket.""" + connection.send_message(websocket_api.event_message(msg['id'], data)) + + remove_dispatcher_function = async_dispatcher_connect( + hass, + "zha_gateway_message", + forward_messages + ) + + @callback + def async_cleanup() -> None: + """Remove signal listener and turn off debug mode.""" + zha_gateway.async_disable_debug_mode() + remove_dispatcher_function() + + connection.subscriptions[msg['id']] = async_cleanup + zha_gateway.async_enable_debug_mode() + await zha_gateway.application_controller.permit(60) + + connection.send_result(msg['id']) + + @websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ @@ -86,22 +120,33 @@ async def websocket_get_devices(hass, connection, msg): devices = [] for device in zha_gateway.devices.values(): - ret_device = {} - ret_device.update(device.device_info) - ret_device['entities'] = [{ - 'entity_id': entity_ref.reference_id, - NAME: entity_ref.device_info[NAME] - } for entity_ref in zha_gateway.device_registry[device.ieee]] + devices.append( + async_get_device_info( + hass, device, ha_device_registry=ha_device_registry + ) + ) + connection.send_result(msg[ID], devices) + +@callback +def async_get_device_info(hass, device, ha_device_registry=None): + """Get ZHA device.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + ret_device = {} + ret_device.update(device.device_info) + ret_device['entities'] = [{ + 'entity_id': entity_ref.reference_id, + NAME: entity_ref.device_info[NAME] + } for entity_ref in zha_gateway.device_registry[device.ieee]] + + if ha_device_registry is not None: reg_device = ha_device_registry.async_get_device( {(DOMAIN, str(device.ieee))}, set()) if reg_device is not None: ret_device['user_given_name'] = reg_device.name_by_user ret_device['device_reg_id'] = reg_device.id - - devices.append(ret_device) - - connection.send_result(msg[ID], devices) + ret_device['area_id'] = reg_device.area_id + return ret_device @websocket_api.require_admin @@ -265,7 +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 = msg.get(ATTR_MANUFACTURER) or None + manufacturer = None + # only use manufacturer code for manufacturer clusters + if cluster_id >= MFG_CLUSTER_ID_START: + manufacturer = msg.get(ATTR_MANUFACTURER) or None zha_device = zha_gateway.get_device(ieee) success = failure = None if zha_device is not None: @@ -428,7 +476,10 @@ def async_load_api(hass): cluster_type = service.data.get(ATTR_CLUSTER_TYPE) attribute = service.data.get(ATTR_ATTRIBUTE) value = service.data.get(ATTR_VALUE) - manufacturer = service.data.get(ATTR_MANUFACTURER) or None + manufacturer = None + # only use manufacturer code for manufacturer clusters + if cluster_id >= MFG_CLUSTER_ID_START: + manufacturer = service.data.get(ATTR_MANUFACTURER) or None zha_device = zha_gateway.get_device(ieee) response = None if zha_device is not None: @@ -466,7 +517,10 @@ def async_load_api(hass): command = service.data.get(ATTR_COMMAND) command_type = service.data.get(ATTR_COMMAND_TYPE) args = service.data.get(ATTR_ARGS) - manufacturer = service.data.get(ATTR_MANUFACTURER) or None + manufacturer = None + # only use manufacturer code for manufacturer clusters + if cluster_id >= MFG_CLUSTER_ID_START: + manufacturer = service.data.get(ATTR_MANUFACTURER) or None zha_device = zha_gateway.get_device(ieee) response = None if zha_device is not None: @@ -497,6 +551,7 @@ def async_load_api(hass): SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND ]) + websocket_api.async_register_command(hass, websocket_permit_devices) websocket_api.async_register_command(hass, websocket_get_devices) websocket_api.async_register_command(hass, websocket_reconfigure_node) websocket_api.async_register_command(hass, websocket_device_clusters) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index c5837cc33e7..58cecb3600f 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -1,5 +1,6 @@ """All constants related to the ZHA component.""" import enum +import logging from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR from homeassistant.components.fan import DOMAIN as FAN @@ -106,6 +107,34 @@ QUIRK_CLASS = 'quirk_class' MANUFACTURER_CODE = 'manufacturer_code' POWER_SOURCE = 'power_source' +BELLOWS = 'bellows' +ZHA = 'homeassistant.components.zha' +ZIGPY = 'zigpy' +ZIGPY_XBEE = 'zigpy_xbee' +ZIGPY_DECONZ = 'zigpy_deconz' +ORIGINAL = 'original' +CURRENT = 'current' +DEBUG_LEVELS = { + BELLOWS: logging.DEBUG, + ZHA: logging.DEBUG, + ZIGPY: logging.DEBUG, + ZIGPY_XBEE: logging.DEBUG, + ZIGPY_DECONZ: logging.DEBUG, +} +ADD_DEVICE_RELAY_LOGGERS = [ZHA, ZIGPY] +TYPE = 'type' +NWK = 'nwk' +SIGNATURE = 'signature' +RAW_INIT = 'raw_device_initialized' +ZHA_GW_MSG = 'zha_gateway_message' +DEVICE_REMOVED = 'device_removed' +DEVICE_INFO = 'device_info' +DEVICE_FULL_INIT = 'device_fully_initialized' +DEVICE_JOINED = 'device_joined' +LOG_OUTPUT = 'log_output' +LOG_ENTRY = 'log_entry' +MFG_CLUSTER_ID_START = 0xfc00 + class RadioType(enum.Enum): """Possible options for radio type.""" diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index adaab0e6616..8ee2c7850e3 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -11,6 +11,8 @@ 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 @@ -18,7 +20,11 @@ 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 + 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 ( @@ -32,6 +38,7 @@ from .discovery import ( from .store import async_get_registry from .patches import apply_application_controller_patch from .registries import RADIO_TYPES +from ..api import async_get_device_info _LOGGER = logging.getLogger(__name__) @@ -54,6 +61,12 @@ class ZHAGateway: self.radio_description = None hass.data[DATA_ZHA][DATA_ZHA_CORE_COMPONENT] = self._component hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] = self + self._log_levels = { + ORIGINAL: async_capture_log_levels(), + CURRENT: async_capture_log_levels() + } + self.debug_enabled = False + self._log_relay_handler = LogRelayHandler(hass, self) async def async_initialize(self, config_entry): """Initialize controller and connect radio.""" @@ -94,13 +107,37 @@ class ZHAGateway: At this point, no information about the device is known other than its address """ - # Wait for device_initialized, instead - pass + async_dispatcher_send( + self._hass, + ZHA_GW_MSG, + { + TYPE: DEVICE_JOINED, + NWK: device.nwk, + IEEE: str(device.ieee) + } + ) def raw_device_initialized(self, device): """Handle a device initialization without quirks loaded.""" - # Wait for device_initialized, instead - pass + endpoint_ids = device.endpoints.keys() + ept_id = next((ept_id for ept_id in endpoint_ids if ept_id != 0), None) + manufacturer = 'Unknown' + model = 'Unknown' + if ept_id is not None: + manufacturer = device.endpoints[ept_id].manufacturer + model = device.endpoints[ept_id].model + async_dispatcher_send( + self._hass, + ZHA_GW_MSG, + { + TYPE: RAW_INIT, + NWK: device.nwk, + IEEE: str(device.ieee), + MODEL: model, + ATTR_MANUFACTURER: manufacturer, + SIGNATURE: device.get_signature() + } + ) def device_initialized(self, device): """Handle device joined and basic information discovered.""" @@ -116,11 +153,21 @@ class ZHAGateway: 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()) async_dispatcher_send( self._hass, "{}_{}".format(SIGNAL_REMOVE, str(device.ieee)) ) + if device_info is not None: + async_dispatcher_send( + self._hass, + ZHA_GW_MSG, + { + TYPE: DEVICE_REMOVED, + DEVICE_INFO: device_info + } + ) def get_device(self, ieee_str): """Return ZHADevice for given ieee.""" @@ -157,6 +204,28 @@ class ZHAGateway: ) ) + @callback + def async_enable_debug_mode(self): + """Enable debug mode for ZHA.""" + self._log_levels[ORIGINAL] = async_capture_log_levels() + async_set_logger_levels(DEBUG_LEVELS) + self._log_levels[CURRENT] = async_capture_log_levels() + + for logger_name in ADD_DEVICE_RELAY_LOGGERS: + logging.getLogger(logger_name).addHandler(self._log_relay_handler) + + self.debug_enabled = True + + @callback + def async_disable_debug_mode(self): + """Disable debug mode for ZHA.""" + async_set_logger_levels(self._log_levels[ORIGINAL]) + self._log_levels[CURRENT] = async_capture_log_levels() + for logger_name in ADD_DEVICE_RELAY_LOGGERS: + logging.getLogger(logger_name).removeHandler( + self._log_relay_handler) + self.debug_enabled = False + @callback def _async_get_or_create_device(self, zigpy_device, is_new_join): """Get or create a ZHA device.""" @@ -231,3 +300,64 @@ class ZHAGateway: device_entity = async_create_device_entity(zha_device) await self._component.async_add_entities([device_entity]) + + if is_new_join: + device_info = async_get_device_info(self._hass, zha_device) + async_dispatcher_send( + self._hass, + ZHA_GW_MSG, + { + TYPE: DEVICE_FULL_INIT, + DEVICE_INFO: device_info + } + ) + + +@callback +def async_capture_log_levels(): + """Capture current logger levels for ZHA.""" + return { + BELLOWS: logging.getLogger(BELLOWS).getEffectiveLevel(), + ZHA: logging.getLogger(ZHA).getEffectiveLevel(), + ZIGPY: logging.getLogger(ZIGPY).getEffectiveLevel(), + ZIGPY_XBEE: logging.getLogger(ZIGPY_XBEE).getEffectiveLevel(), + ZIGPY_DECONZ: logging.getLogger(ZIGPY_DECONZ).getEffectiveLevel(), + } + + +@callback +def async_set_logger_levels(levels): + """Set logger levels for ZHA.""" + logging.getLogger(BELLOWS).setLevel(levels[BELLOWS]) + logging.getLogger(ZHA).setLevel(levels[ZHA]) + logging.getLogger(ZIGPY).setLevel(levels[ZIGPY]) + logging.getLogger(ZIGPY_XBEE).setLevel(levels[ZIGPY_XBEE]) + logging.getLogger(ZIGPY_DECONZ).setLevel(levels[ZIGPY_DECONZ]) + + +class LogRelayHandler(logging.Handler): + """Log handler for error messages.""" + + def __init__(self, hass, gateway): + """Initialize a new LogErrorHandler.""" + super().__init__() + self.hass = hass + self.gateway = gateway + + def emit(self, record): + """Relay log message via dispatcher.""" + stack = [] + if record.levelno >= logging.WARN: + if not record.exc_info: + stack = [f for f, _, _, _ in traceback.extract_stack()] + + entry = LogEntry(record, stack, + _figure_out_source(record, stack, self.hass)) + async_dispatcher_send( + self.hass, + ZHA_GW_MSG, + { + TYPE: LOG_OUTPUT, + LOG_ENTRY: entry.to_dict() + } + ) From f195ecca4b410461cec94664da2f903d99beae46 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Tue, 19 Mar 2019 14:07:39 +0800 Subject: [PATCH 075/290] Consolidate all platforms that have tests (#22109) * Moved climate components with tests into platform dirs. * Updated tests from climate component. * Moved binary_sensor components with tests into platform dirs. * Updated tests from binary_sensor component. * Moved calendar components with tests into platform dirs. * Updated tests from calendar component. * Moved camera components with tests into platform dirs. * Updated tests from camera component. * Moved cover components with tests into platform dirs. * Updated tests from cover component. * Moved device_tracker components with tests into platform dirs. * Updated tests from device_tracker component. * Moved fan components with tests into platform dirs. * Updated tests from fan component. * Moved geo_location components with tests into platform dirs. * Updated tests from geo_location component. * Moved image_processing components with tests into platform dirs. * Updated tests from image_processing component. * Moved light components with tests into platform dirs. * Updated tests from light component. * Moved lock components with tests into platform dirs. * Moved media_player components with tests into platform dirs. * Updated tests from media_player component. * Moved scene components with tests into platform dirs. * Moved sensor components with tests into platform dirs. * Updated tests from sensor component. * Moved switch components with tests into platform dirs. * Updated tests from sensor component. * Moved vacuum components with tests into platform dirs. * Updated tests from vacuum component. * Moved weather components with tests into platform dirs. * Fixed __init__.py files * Fixes for stuff moved as part of this branch. * Fix stuff needed to merge with balloob's branch. * Formatting issues. * Missing __init__.py files. * Fix-ups * Fixup * Regenerated requirements. * Linting errors fixed. * Fixed more broken tests. * Missing init files. * Fix broken tests. * More broken tests * There seems to be a thread race condition. I suspect the logger stuff is running in another thread, which means waiting until the aio loop is done is missing the log messages. Used sleep instead because that allows the logger thread to run. I think the api_streams sensor might not be thread safe. * Disabled tests, will remove sensor in #22147 * Updated coverage and codeowners. --- .coveragerc | 91 ++++++++-------- CODEOWNERS | 62 +++++------ .../components/api_streams/__init__.py | 1 + .../api_streams.py => api_streams/sensor.py} | 0 .../asuswrt.py => asuswrt/device_tracker.py} | 0 homeassistant/components/aurora/__init__.py | 1 + .../aurora.py => aurora/binary_sensor.py} | 0 .../components/automatic/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/awair/__init__.py | 1 + .../{sensor/awair.py => awair/sensor.py} | 0 homeassistant/components/bayesian/__init__.py | 1 + .../bayesian.py => bayesian/binary_sensor.py} | 0 .../components/blackbird/__init__.py | 1 + .../media_player.py} | 0 .../{sensor/bom.py => bom/sensor.py} | 0 homeassistant/components/bom/weather.py | 2 +- homeassistant/components/caldav/__init__.py | 1 + .../caldav.py => caldav/calendar.py} | 0 .../{sensor/canary.py => canary/sensor.py} | 0 .../components/coinmarketcap/__init__.py | 1 + .../sensor.py} | 0 .../components/command_line/__init__.py | 1 + .../binary_sensor.py} | 2 +- .../command_line.py => command_line/cover.py} | 0 .../sensor.py} | 0 .../switch.py} | 0 homeassistant/components/darksky/__init__.py | 1 + .../{sensor/darksky.py => darksky/sensor.py} | 0 .../darksky.py => darksky/weather.py} | 0 .../components/demo/image_processing.py | 2 +- homeassistant/components/directv/__init__.py | 1 + .../directv.py => directv/media_player.py} | 0 homeassistant/components/dsmr/__init__.py | 1 + .../{sensor/dsmr.py => dsmr/sensor.py} | 0 .../components/dte_energy_bridge/__init__.py | 1 + .../sensor.py} | 0 .../components/dwd_weather_warnings/sensor.py | 2 +- .../{climate/dyson.py => dyson/climate.py} | 0 .../components/{fan/dyson.py => dyson/fan.py} | 0 .../{sensor/dyson.py => dyson/sensor.py} | 0 .../{vacuum/dyson.py => dyson/vacuum.py} | 0 .../components/ee_brightbox/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/efergy/__init__.py | 1 + .../{sensor/efergy.py => efergy/sensor.py} | 0 .../entur_public_transport/__init__.py | 1 + .../sensor.py} | 0 .../components/everlights/__init__.py | 1 + .../everlights.py => everlights/light.py} | 0 homeassistant/components/facebox/__init__.py | 1 + .../image_processing.py} | 0 homeassistant/components/fail2ban/__init__.py | 1 + .../fail2ban.py => fail2ban/sensor.py} | 0 homeassistant/components/fido/__init__.py | 1 + .../{sensor/fido.py => fido/sensor.py} | 0 homeassistant/components/file/__init__.py | 1 + .../{sensor/file.py => file/sensor.py} | 0 homeassistant/components/filesize/__init__.py | 1 + .../filesize.py => filesize/sensor.py} | 0 homeassistant/components/filter/__init__.py | 1 + .../{sensor/filter.py => filter/sensor.py} | 0 homeassistant/components/flux/__init__.py | 1 + .../{switch/flux.py => flux/switch.py} | 0 homeassistant/components/folder/__init__.py | 1 + .../{sensor/folder.py => folder/sensor.py} | 0 homeassistant/components/foobot/__init__.py | 1 + .../{sensor/foobot.py => foobot/sensor.py} | 0 homeassistant/components/generic/__init__.py | 1 + .../{camera/generic.py => generic/camera.py} | 0 .../components/geo_json_events/__init__.py | 1 + .../geo_location.py} | 0 .../components/geo_rss_events/__init__.py | 1 + .../sensor.py} | 0 .../components/google_wifi/__init__.py | 1 + .../google_wifi.py => google_wifi/sensor.py} | 0 homeassistant/components/hddtemp/__init__.py | 1 + .../{sensor/hddtemp.py => hddtemp/sensor.py} | 0 .../components/history_stats/__init__.py | 1 + .../sensor.py} | 0 .../components/honeywell/__init__.py | 1 + .../honeywell.py => honeywell/climate.py} | 0 .../components/hydroquebec/__init__.py | 1 + .../hydroquebec.py => hydroquebec/sensor.py} | 0 .../components/imap_email_content/__init__.py | 1 + .../sensor.py} | 0 .../components/integration/__init__.py | 1 + .../integration.py => integration/sensor.py} | 0 .../islamic_prayer_times/__init__.py | 1 + .../sensor.py} | 0 .../components/jewish_calendar/__init__.py | 1 + .../sensor.py} | 0 .../{light/litejet.py => litejet/light.py} | 0 .../{scene/litejet.py => litejet/scene.py} | 0 .../{switch/litejet.py => litejet/switch.py} | 0 .../components/local_file/__init__.py | 1 + .../local_file.py => local_file/camera.py} | 0 .../components/london_air/__init__.py | 1 + .../london_air.py => london_air/sensor.py} | 0 homeassistant/components/manual/__init__.py | 1 + .../components/manual_mqtt/__init__.py | 1 + .../melissa.py => melissa/climate.py} | 0 homeassistant/components/meraki/__init__.py | 1 + .../meraki.py => meraki/device_tracker.py} | 0 homeassistant/components/mfi/__init__.py | 1 + .../{sensor/mfi.py => mfi/sensor.py} | 0 .../{switch/mfi.py => mfi/switch.py} | 0 homeassistant/components/mhz19/__init__.py | 1 + .../{sensor/mhz19.py => mhz19/sensor.py} | 0 .../microsoft_face_detect/__init__.py | 1 + .../image_processing.py} | 0 .../microsoft_face_identify/__init__.py | 1 + .../image_processing.py} | 0 homeassistant/components/min_max/__init__.py | 1 + .../{sensor/min_max.py => min_max/sensor.py} | 0 .../components/monoprice/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/moon/__init__.py | 1 + .../{sensor/moon.py => moon/sensor.py} | 0 .../components/mqtt_json/__init__.py | 1 + .../device_tracker.py} | 0 .../components/mqtt_room/__init__.py | 1 + .../mqtt_room.py => mqtt_room/sensor.py} | 0 .../components/nsw_fuel_station/__init__.py | 1 + .../sensor.py} | 0 .../nsw_rural_fire_service_feed/__init__.py | 1 + .../geo_location.py} | 0 .../{climate/nuheat.py => nuheat/climate.py} | 0 homeassistant/components/nx584/__init__.py | 1 + .../nx584.py => nx584/binary_sensor.py} | 0 .../components/openalpr_cloud/__init__.py | 1 + .../image_processing.py} | 2 +- .../components/openalpr_local/__init__.py | 1 + .../image_processing.py} | 0 .../openhardwaremonitor/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/push/__init__.py | 1 + .../{camera/push.py => push/camera.py} | 0 homeassistant/components/pvoutput/sensor.py | 2 +- homeassistant/components/radarr/__init__.py | 1 + .../{sensor/radarr.py => radarr/sensor.py} | 0 homeassistant/components/random/__init__.py | 1 + .../random.py => random/binary_sensor.py} | 0 .../{sensor/random.py => random/sensor.py} | 0 homeassistant/components/rest/__init__.py | 1 + .../rest.py => rest/binary_sensor.py} | 2 +- .../{sensor/rest.py => rest/sensor.py} | 0 .../{switch/rest.py => rest/switch.py} | 0 .../rflink.py => rflink/binary_sensor.py} | 0 .../{cover/rflink.py => rflink/cover.py} | 0 .../{light/rflink.py => rflink/light.py} | 0 .../{sensor/rflink.py => rflink/sensor.py} | 0 .../{switch/rflink.py => rflink/switch.py} | 0 .../ring.py => ring/binary_sensor.py} | 0 .../{sensor/ring.py => ring/sensor.py} | 0 .../components/rmvtransport/__init__.py | 1 + .../sensor.py} | 0 .../components/samsungtv/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/scrape/sensor.py | 2 +- homeassistant/components/season/__init__.py | 1 + .../{sensor/season.py => season/sensor.py} | 0 homeassistant/components/sigfox/__init__.py | 1 + .../{sensor/sigfox.py => sigfox/sensor.py} | 0 .../components/simulated/__init__.py | 1 + .../simulated.py => simulated/sensor.py} | 0 .../sleepiq.py => sleepiq/binary_sensor.py} | 0 .../{sensor/sleepiq.py => sleepiq/sensor.py} | 0 homeassistant/components/sonarr/__init__.py | 1 + .../{sensor/sonarr.py => sonarr/sensor.py} | 0 .../components/soundtouch/__init__.py | 1 + .../media_player.py} | 0 homeassistant/components/sql/__init__.py | 1 + .../{sensor/sql.py => sql/sensor.py} | 0 .../components/srp_energy/__init__.py | 1 + .../srp_energy.py => srp_energy/sensor.py} | 0 homeassistant/components/startca/__init__.py | 1 + .../{sensor/startca.py => startca/sensor.py} | 0 .../components/statistics/__init__.py | 1 + .../statistics.py => statistics/sensor.py} | 0 homeassistant/components/tcp/__init__.py | 1 + .../tcp.py => tcp/binary_sensor.py} | 2 +- .../{sensor/tcp.py => tcp/sensor.py} | 0 homeassistant/components/teksavvy/__init__.py | 1 + .../teksavvy.py => teksavvy/sensor.py} | 0 homeassistant/components/template/__init__.py | 1 + .../template.py => template/binary_sensor.py} | 0 .../{cover/template.py => template/cover.py} | 0 .../{fan/template.py => template/fan.py} | 0 .../{light/template.py => template/light.py} | 0 .../{lock/template.py => template/lock.py} | 0 .../template.py => template/sensor.py} | 0 .../template.py => template/switch.py} | 0 .../components/threshold/__init__.py | 1 + .../binary_sensor.py} | 0 .../components/time_date/__init__.py | 1 + .../time_date.py => time_date/sensor.py} | 0 homeassistant/components/tod/__init__.py | 1 + .../tod.py => tod/binary_sensor.py} | 0 homeassistant/components/tomato/__init__.py | 1 + .../tomato.py => tomato/device_tracker.py} | 0 .../tplink.py => tplink/device_tracker.py} | 0 .../components/transport_nsw/__init__.py | 1 + .../sensor.py} | 0 homeassistant/components/trend/__init__.py | 1 + .../trend.py => trend/binary_sensor.py} | 0 .../components/uk_transport/__init__.py | 1 + .../sensor.py} | 0 .../unifi.py => unifi/device_tracker.py} | 0 .../components/unifi_direct/__init__.py | 1 + .../device_tracker.py} | 0 .../components/universal/__init__.py | 1 + .../media_player.py} | 0 .../components/upc_connect/__init__.py | 1 + .../device_tracker.py} | 0 homeassistant/components/uptime/__init__.py | 1 + .../{sensor/uptime.py => uptime/sensor.py} | 0 .../usgs_earthquakes_feed/__init__.py | 1 + .../geo_location.py} | 0 homeassistant/components/uvc/__init__.py | 1 + .../{camera/uvc.py => uvc/camera.py} | 0 homeassistant/components/version/__init__.py | 1 + .../{sensor/version.py => version/sensor.py} | 0 .../vultr.py => vultr/binary_sensor.py} | 0 .../{sensor/vultr.py => vultr/sensor.py} | 0 .../{switch/vultr.py => vultr/switch.py} | 0 .../wake_on_lan.py => wake_on_lan/switch.py} | 0 homeassistant/components/workday/__init__.py | 1 + .../workday.py => workday/binary_sensor.py} | 0 .../components/worldclock/__init__.py | 1 + .../worldclock.py => worldclock/sensor.py} | 0 homeassistant/components/wsdot/__init__.py | 1 + .../{sensor/wsdot.py => wsdot/sensor.py} | 0 .../components/wunderground/__init__.py | 1 + .../sensor.py} | 0 .../xiaomi.py => xiaomi/device_tracker.py} | 0 homeassistant/components/yamaha/__init__.py | 1 + .../yamaha.py => yamaha/media_player.py} | 0 homeassistant/components/yr/__init__.py | 1 + .../components/{sensor/yr.py => yr/sensor.py} | 0 homeassistant/components/yweather/__init__.py | 1 + .../yweather.py => yweather/sensor.py} | 0 .../yweather.py => yweather/weather.py} | 0 requirements_all.txt | 103 +++++++++--------- requirements_test_all.txt | 77 ++++++------- tests/components/api_streams/__init__.py | 1 + .../test_sensor.py} | 25 +++-- tests/components/asuswrt/__init__.py | 1 + .../test_device_tracker.py} | 0 tests/components/aurora/__init__.py | 1 + .../test_binary_sensor.py} | 2 +- tests/components/automatic/__init__.py | 1 + .../test_device_tracker.py} | 6 +- tests/components/awair/__init__.py | 1 + .../test_awair.py => awair/test_sensor.py} | 4 +- tests/components/bayesian/__init__.py | 1 + .../test_binary_sensor.py} | 2 +- tests/components/blackbird/__init__.py | 1 + .../test_media_player.py} | 2 +- tests/components/bom/__init__.py | 1 + .../test_bom.py => bom/test_sensor.py} | 2 +- tests/components/caldav/__init__.py | 1 + .../test_calendar.py} | 2 +- .../test_canary.py => canary/test_sensor.py} | 4 +- tests/components/coinmarketcap/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/command_line/__init__.py | 1 + .../test_binary_sensor.py} | 2 +- .../test_cover.py} | 2 +- .../test_sensor.py} | 8 +- .../test_switch.py} | 2 +- tests/components/darksky/__init__.py | 1 + .../test_sensor.py} | 2 +- .../test_weather.py} | 0 tests/components/directv/__init__.py | 1 + .../test_media_player.py} | 2 +- tests/components/dsmr/__init__.py | 1 + .../test_dsmr.py => dsmr/test_sensor.py} | 2 +- .../components/dte_energy_bridge/__init__.py | 1 + .../test_sensor.py} | 0 .../test_dyson.py => dyson/test_climate.py} | 2 +- .../{fan/test_dyson.py => dyson/test_fan.py} | 4 +- .../test_dyson.py => dyson/test_sensor.py} | 2 +- .../test_dyson.py => dyson/test_vacuum.py} | 4 +- tests/components/ee_brightbox/__init__.py | 1 + .../test_device_tracker.py} | 0 tests/components/efergy/__init__.py | 1 + .../test_efergy.py => efergy/test_sensor.py} | 0 .../entur_public_transport/__init__.py | 1 + .../test_sensor.py} | 4 +- tests/components/everlights/__init__.py | 1 + .../test_light.py} | 2 +- tests/components/facebox/__init__.py | 1 + .../test_image_processing.py} | 10 +- tests/components/fail2ban/__init__.py | 1 + .../test_sensor.py} | 20 ++-- tests/components/fido/__init__.py | 1 + .../test_fido.py => fido/test_sensor.py} | 2 +- tests/components/file/__init__.py | 1 + .../test_file.py => file/test_sensor.py} | 6 +- tests/components/filesize/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/filter/__init__.py | 1 + .../test_filter.py => filter/test_sensor.py} | 2 +- tests/components/flux/__init__.py | 1 + .../test_flux.py => flux/test_switch.py} | 24 ++-- tests/components/folder/__init__.py | 1 + .../test_folder.py => folder/test_sensor.py} | 2 +- tests/components/foobot/__init__.py | 1 + .../test_foobot.py => foobot/test_sensor.py} | 2 +- tests/components/generic/__init__.py | 1 + .../test_camera.py} | 0 tests/components/geo_json_events/__init__.py | 1 + .../test_geo_location.py} | 2 +- tests/components/geo_rss_events/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/google_pubsub/__init__.py | 1 + tests/components/google_wifi/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/hddtemp/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/history_stats/__init__.py | 1 + .../test_sensor.py} | 2 +- .../components/homekit_controller/__init__.py | 1 + tests/components/honeywell/__init__.py | 1 + .../test_climate.py} | 18 +-- tests/components/hydroquebec/__init__.py | 1 + .../test_sensor.py} | 2 +- .../components/imap_email_content/__init__.py | 1 + .../test_sensor.py} | 3 +- tests/components/integration/__init__.py | 1 + .../test_sensor.py} | 0 .../islamic_prayer_times/__init__.py | 1 + .../test_sensor.py} | 4 +- tests/components/jewish_calendar/__init__.py | 1 + .../test_sensor.py} | 2 +- .../test_litejet.py => litejet/test_light.py} | 0 .../test_litejet.py => litejet/test_scene.py} | 0 .../test_switch.py} | 0 tests/components/local_file/__init__.py | 1 + .../test_camera.py} | 6 +- tests/components/london_air/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/manual/__init__.py | 1 + tests/components/manual_mqtt/__init__.py | 1 + .../test_climate.py} | 25 +++-- tests/components/meraki/__init__.py | 1 + .../test_device_tracker.py} | 4 +- tests/components/mfi/__init__.py | 1 + .../test_mfi.py => mfi/test_sensor.py} | 4 +- .../test_mfi.py => mfi/test_switch.py} | 6 +- tests/components/mhz19/__init__.py | 1 + .../test_mhz19.py => mhz19/test_sensor.py} | 2 +- .../microsoft_face_detect/__init__.py | 1 + .../test_image_processing.py} | 2 +- .../microsoft_face_identify/__init__.py | 1 + .../test_image_processing.py} | 2 +- tests/components/min_max/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/monoprice/__init__.py | 1 + .../test_media_player.py} | 2 +- tests/components/moon/__init__.py | 1 + .../test_moon.py => moon/test_sensor.py} | 4 +- tests/components/mqtt_json/__init__.py | 1 + .../test_device_tracker.py} | 2 +- tests/components/mqtt_room/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/nsw_fuel_station/__init__.py | 1 + .../test_sensor.py} | 0 .../nsw_rural_fire_service_feed/__init__.py | 1 + .../test_geo_location.py} | 2 +- .../test_nuheat.py => nuheat/test_climate.py} | 6 +- tests/components/nx584/__init__.py | 1 + .../test_binary_sensor.py} | 10 +- tests/components/openalpr_cloud/__init__.py | 1 + .../test_image_processing.py} | 4 +- tests/components/openalpr_local/__init__.py | 1 + .../test_image_processing.py} | 2 +- .../openhardwaremonitor/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/push/__init__.py | 1 + .../test_push.py => push/test_camera.py} | 0 tests/components/radarr/__init__.py | 1 + .../test_radarr.py => radarr/test_sensor.py} | 2 +- tests/components/random/__init__.py | 1 + .../test_binary_sensor.py} | 0 .../test_random.py => random/test_sensor.py} | 0 tests/components/rest/__init__.py | 1 + .../test_binary_sensor.py} | 2 +- .../test_rest.py => rest/test_sensor.py} | 8 +- .../test_rest.py => rest/test_switch.py} | 2 +- .../test_binary_sensor.py} | 0 .../test_rflink.py => rflink/test_cover.py} | 0 .../test_rflink.py => rflink/test_light.py} | 0 .../test_rflink.py => rflink/test_sensor.py} | 0 .../test_rflink.py => rflink/test_switch.py} | 0 .../test_binary_sensor.py} | 2 +- .../test_ring.py => ring/test_sensor.py} | 2 +- tests/components/rmvtransport/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/samsungtv/__init__.py | 1 + .../test_media_player.py} | 12 +- tests/components/season/__init__.py | 1 + .../test_season.py => season/test_sensor.py} | 2 +- tests/components/sigfox/__init__.py | 1 + .../test_sigfox.py => sigfox/test_sensor.py} | 2 +- tests/components/simulated/__init__.py | 1 + .../test_sensor.py} | 2 +- .../test_binary_sensor.py} | 2 +- .../test_sensor.py} | 2 +- tests/components/sonarr/__init__.py | 1 + .../test_sonarr.py => sonarr/test_sensor.py} | 2 +- tests/components/soundtouch/__init__.py | 1 + .../test_media_player.py} | 2 +- tests/components/sql/__init__.py | 1 + .../test_sql.py => sql/test_sensor.py} | 2 +- tests/components/srp_energy/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/startca/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/statistics/__init__.py | 1 + .../test_sensor.py} | 8 +- tests/components/tcp/__init__.py | 1 + .../test_tcp.py => tcp/test_binary_sensor.py} | 17 +-- .../test_tcp.py => tcp/test_sensor.py} | 35 +++--- tests/components/teksavvy/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/template/__init__.py | 1 + .../test_binary_sensor.py} | 4 +- .../test_cover.py} | 0 .../test_template.py => template/test_fan.py} | 0 .../test_light.py} | 0 .../test_lock.py} | 0 .../test_sensor.py} | 0 .../test_switch.py} | 0 tests/components/threshold/__init__.py | 1 + .../test_binary_sensor.py} | 0 tests/components/time_date/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/tod/__init__.py | 1 + .../test_tod.py => tod/test_binary_sensor.py} | 98 ++++++++--------- tests/components/tomato/__init__.py | 1 + .../test_device_tracker.py} | 7 +- .../test_device_tracker.py} | 2 +- tests/components/transport_nsw/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/trend/__init__.py | 1 + .../test_binary_sensor.py} | 0 tests/components/uk_transport/__init__.py | 1 + .../test_sensor.py} | 2 +- .../test_device_tracker.py} | 5 +- tests/components/unifi_direct/__init__.py | 1 + .../test_device_tracker.py} | 6 +- tests/components/universal/__init__.py | 1 + .../test_media_player.py} | 2 +- tests/components/upc_connect/__init__.py | 1 + .../test_device_tracker.py} | 4 +- tests/components/uptime/__init__.py | 1 + .../test_uptime.py => uptime/test_sensor.py} | 2 +- .../usgs_earthquakes_feed/__init__.py | 1 + .../test_geo_location.py} | 9 +- tests/components/uvc/__init__.py | 1 + .../test_uvc.py => uvc/test_camera.py} | 2 +- tests/components/version/__init__.py | 1 + .../test_sensor.py} | 0 .../test_binary_sensor.py} | 2 +- .../test_vultr.py => vultr/test_sensor.py} | 2 +- .../test_vultr.py => vultr/test_switch.py} | 2 +- .../test_switch.py} | 0 tests/components/workday/__init__.py | 1 + .../test_binary_sensor.py} | 4 +- tests/components/worldclock/__init__.py | 1 + .../test_sensor.py} | 0 tests/components/wsdot/__init__.py | 1 + .../test_wsdot.py => wsdot/test_sensor.py} | 4 +- tests/components/wunderground/__init__.py | 1 + .../test_sensor.py} | 2 +- tests/components/xiaomi/__init__.py | 1 + .../test_device_tracker.py} | 9 +- tests/components/yamaha/__init__.py | 1 + .../test_media_player.py} | 2 +- tests/components/yr/__init__.py | 1 + .../{sensor/test_yr.py => yr/test_sensor.py} | 6 +- tests/components/yweather/__init__.py | 1 + .../test_sensor.py} | 0 .../test_weather.py} | 0 486 files changed, 662 insertions(+), 455 deletions(-) create mode 100644 homeassistant/components/api_streams/__init__.py rename homeassistant/components/{sensor/api_streams.py => api_streams/sensor.py} (100%) rename homeassistant/components/{device_tracker/asuswrt.py => asuswrt/device_tracker.py} (100%) create mode 100644 homeassistant/components/aurora/__init__.py rename homeassistant/components/{binary_sensor/aurora.py => aurora/binary_sensor.py} (100%) create mode 100644 homeassistant/components/automatic/__init__.py rename homeassistant/components/{device_tracker/automatic.py => automatic/device_tracker.py} (100%) create mode 100644 homeassistant/components/awair/__init__.py rename homeassistant/components/{sensor/awair.py => awair/sensor.py} (100%) create mode 100644 homeassistant/components/bayesian/__init__.py rename homeassistant/components/{binary_sensor/bayesian.py => bayesian/binary_sensor.py} (100%) create mode 100644 homeassistant/components/blackbird/__init__.py rename homeassistant/components/{media_player/blackbird.py => blackbird/media_player.py} (100%) rename homeassistant/components/{sensor/bom.py => bom/sensor.py} (100%) create mode 100644 homeassistant/components/caldav/__init__.py rename homeassistant/components/{calendar/caldav.py => caldav/calendar.py} (100%) rename homeassistant/components/{sensor/canary.py => canary/sensor.py} (100%) create mode 100644 homeassistant/components/coinmarketcap/__init__.py rename homeassistant/components/{sensor/coinmarketcap.py => coinmarketcap/sensor.py} (100%) create mode 100644 homeassistant/components/command_line/__init__.py rename homeassistant/components/{binary_sensor/command_line.py => command_line/binary_sensor.py} (98%) rename homeassistant/components/{cover/command_line.py => command_line/cover.py} (100%) rename homeassistant/components/{sensor/command_line.py => command_line/sensor.py} (100%) rename homeassistant/components/{switch/command_line.py => command_line/switch.py} (100%) create mode 100644 homeassistant/components/darksky/__init__.py rename homeassistant/components/{sensor/darksky.py => darksky/sensor.py} (100%) rename homeassistant/components/{weather/darksky.py => darksky/weather.py} (100%) create mode 100644 homeassistant/components/directv/__init__.py rename homeassistant/components/{media_player/directv.py => directv/media_player.py} (100%) create mode 100644 homeassistant/components/dsmr/__init__.py rename homeassistant/components/{sensor/dsmr.py => dsmr/sensor.py} (100%) create mode 100644 homeassistant/components/dte_energy_bridge/__init__.py rename homeassistant/components/{sensor/dte_energy_bridge.py => dte_energy_bridge/sensor.py} (100%) rename homeassistant/components/{climate/dyson.py => dyson/climate.py} (100%) rename homeassistant/components/{fan/dyson.py => dyson/fan.py} (100%) rename homeassistant/components/{sensor/dyson.py => dyson/sensor.py} (100%) rename homeassistant/components/{vacuum/dyson.py => dyson/vacuum.py} (100%) create mode 100644 homeassistant/components/ee_brightbox/__init__.py rename homeassistant/components/{device_tracker/ee_brightbox.py => ee_brightbox/device_tracker.py} (100%) create mode 100644 homeassistant/components/efergy/__init__.py rename homeassistant/components/{sensor/efergy.py => efergy/sensor.py} (100%) create mode 100644 homeassistant/components/entur_public_transport/__init__.py rename homeassistant/components/{sensor/entur_public_transport.py => entur_public_transport/sensor.py} (100%) create mode 100644 homeassistant/components/everlights/__init__.py rename homeassistant/components/{light/everlights.py => everlights/light.py} (100%) create mode 100644 homeassistant/components/facebox/__init__.py rename homeassistant/components/{image_processing/facebox.py => facebox/image_processing.py} (100%) create mode 100644 homeassistant/components/fail2ban/__init__.py rename homeassistant/components/{sensor/fail2ban.py => fail2ban/sensor.py} (100%) create mode 100644 homeassistant/components/fido/__init__.py rename homeassistant/components/{sensor/fido.py => fido/sensor.py} (100%) create mode 100644 homeassistant/components/file/__init__.py rename homeassistant/components/{sensor/file.py => file/sensor.py} (100%) create mode 100644 homeassistant/components/filesize/__init__.py rename homeassistant/components/{sensor/filesize.py => filesize/sensor.py} (100%) create mode 100644 homeassistant/components/filter/__init__.py rename homeassistant/components/{sensor/filter.py => filter/sensor.py} (100%) create mode 100644 homeassistant/components/flux/__init__.py rename homeassistant/components/{switch/flux.py => flux/switch.py} (100%) create mode 100644 homeassistant/components/folder/__init__.py rename homeassistant/components/{sensor/folder.py => folder/sensor.py} (100%) create mode 100644 homeassistant/components/foobot/__init__.py rename homeassistant/components/{sensor/foobot.py => foobot/sensor.py} (100%) create mode 100644 homeassistant/components/generic/__init__.py rename homeassistant/components/{camera/generic.py => generic/camera.py} (100%) create mode 100644 homeassistant/components/geo_json_events/__init__.py rename homeassistant/components/{geo_location/geo_json_events.py => geo_json_events/geo_location.py} (100%) create mode 100644 homeassistant/components/geo_rss_events/__init__.py rename homeassistant/components/{sensor/geo_rss_events.py => geo_rss_events/sensor.py} (100%) create mode 100644 homeassistant/components/google_wifi/__init__.py rename homeassistant/components/{sensor/google_wifi.py => google_wifi/sensor.py} (100%) create mode 100644 homeassistant/components/hddtemp/__init__.py rename homeassistant/components/{sensor/hddtemp.py => hddtemp/sensor.py} (100%) create mode 100644 homeassistant/components/history_stats/__init__.py rename homeassistant/components/{sensor/history_stats.py => history_stats/sensor.py} (100%) create mode 100644 homeassistant/components/honeywell/__init__.py rename homeassistant/components/{climate/honeywell.py => honeywell/climate.py} (100%) create mode 100644 homeassistant/components/hydroquebec/__init__.py rename homeassistant/components/{sensor/hydroquebec.py => hydroquebec/sensor.py} (100%) create mode 100644 homeassistant/components/imap_email_content/__init__.py rename homeassistant/components/{sensor/imap_email_content.py => imap_email_content/sensor.py} (100%) create mode 100644 homeassistant/components/integration/__init__.py rename homeassistant/components/{sensor/integration.py => integration/sensor.py} (100%) create mode 100644 homeassistant/components/islamic_prayer_times/__init__.py rename homeassistant/components/{sensor/islamic_prayer_times.py => islamic_prayer_times/sensor.py} (100%) create mode 100644 homeassistant/components/jewish_calendar/__init__.py rename homeassistant/components/{sensor/jewish_calendar.py => jewish_calendar/sensor.py} (100%) rename homeassistant/components/{light/litejet.py => litejet/light.py} (100%) rename homeassistant/components/{scene/litejet.py => litejet/scene.py} (100%) rename homeassistant/components/{switch/litejet.py => litejet/switch.py} (100%) create mode 100644 homeassistant/components/local_file/__init__.py rename homeassistant/components/{camera/local_file.py => local_file/camera.py} (100%) create mode 100644 homeassistant/components/london_air/__init__.py rename homeassistant/components/{sensor/london_air.py => london_air/sensor.py} (100%) create mode 100644 homeassistant/components/manual/__init__.py create mode 100644 homeassistant/components/manual_mqtt/__init__.py rename homeassistant/components/{climate/melissa.py => melissa/climate.py} (100%) create mode 100644 homeassistant/components/meraki/__init__.py rename homeassistant/components/{device_tracker/meraki.py => meraki/device_tracker.py} (100%) create mode 100644 homeassistant/components/mfi/__init__.py rename homeassistant/components/{sensor/mfi.py => mfi/sensor.py} (100%) rename homeassistant/components/{switch/mfi.py => mfi/switch.py} (100%) create mode 100644 homeassistant/components/mhz19/__init__.py rename homeassistant/components/{sensor/mhz19.py => mhz19/sensor.py} (100%) create mode 100644 homeassistant/components/microsoft_face_detect/__init__.py rename homeassistant/components/{image_processing/microsoft_face_detect.py => microsoft_face_detect/image_processing.py} (100%) create mode 100644 homeassistant/components/microsoft_face_identify/__init__.py rename homeassistant/components/{image_processing/microsoft_face_identify.py => microsoft_face_identify/image_processing.py} (100%) create mode 100644 homeassistant/components/min_max/__init__.py rename homeassistant/components/{sensor/min_max.py => min_max/sensor.py} (100%) create mode 100644 homeassistant/components/monoprice/__init__.py rename homeassistant/components/{media_player/monoprice.py => monoprice/media_player.py} (100%) create mode 100644 homeassistant/components/moon/__init__.py rename homeassistant/components/{sensor/moon.py => moon/sensor.py} (100%) create mode 100644 homeassistant/components/mqtt_json/__init__.py rename homeassistant/components/{device_tracker/mqtt_json.py => mqtt_json/device_tracker.py} (100%) create mode 100644 homeassistant/components/mqtt_room/__init__.py rename homeassistant/components/{sensor/mqtt_room.py => mqtt_room/sensor.py} (100%) create mode 100644 homeassistant/components/nsw_fuel_station/__init__.py rename homeassistant/components/{sensor/nsw_fuel_station.py => nsw_fuel_station/sensor.py} (100%) create mode 100644 homeassistant/components/nsw_rural_fire_service_feed/__init__.py rename homeassistant/components/{geo_location/nsw_rural_fire_service_feed.py => nsw_rural_fire_service_feed/geo_location.py} (100%) rename homeassistant/components/{climate/nuheat.py => nuheat/climate.py} (100%) create mode 100644 homeassistant/components/nx584/__init__.py rename homeassistant/components/{binary_sensor/nx584.py => nx584/binary_sensor.py} (100%) create mode 100644 homeassistant/components/openalpr_cloud/__init__.py rename homeassistant/components/{image_processing/openalpr_cloud.py => openalpr_cloud/image_processing.py} (98%) create mode 100644 homeassistant/components/openalpr_local/__init__.py rename homeassistant/components/{image_processing/openalpr_local.py => openalpr_local/image_processing.py} (100%) create mode 100644 homeassistant/components/openhardwaremonitor/__init__.py rename homeassistant/components/{sensor/openhardwaremonitor.py => openhardwaremonitor/sensor.py} (100%) create mode 100644 homeassistant/components/push/__init__.py rename homeassistant/components/{camera/push.py => push/camera.py} (100%) create mode 100644 homeassistant/components/radarr/__init__.py rename homeassistant/components/{sensor/radarr.py => radarr/sensor.py} (100%) create mode 100644 homeassistant/components/random/__init__.py rename homeassistant/components/{binary_sensor/random.py => random/binary_sensor.py} (100%) rename homeassistant/components/{sensor/random.py => random/sensor.py} (100%) create mode 100644 homeassistant/components/rest/__init__.py rename homeassistant/components/{binary_sensor/rest.py => rest/binary_sensor.py} (98%) rename homeassistant/components/{sensor/rest.py => rest/sensor.py} (100%) rename homeassistant/components/{switch/rest.py => rest/switch.py} (100%) rename homeassistant/components/{binary_sensor/rflink.py => rflink/binary_sensor.py} (100%) rename homeassistant/components/{cover/rflink.py => rflink/cover.py} (100%) rename homeassistant/components/{light/rflink.py => rflink/light.py} (100%) rename homeassistant/components/{sensor/rflink.py => rflink/sensor.py} (100%) rename homeassistant/components/{switch/rflink.py => rflink/switch.py} (100%) rename homeassistant/components/{binary_sensor/ring.py => ring/binary_sensor.py} (100%) rename homeassistant/components/{sensor/ring.py => ring/sensor.py} (100%) create mode 100644 homeassistant/components/rmvtransport/__init__.py rename homeassistant/components/{sensor/rmvtransport.py => rmvtransport/sensor.py} (100%) create mode 100644 homeassistant/components/samsungtv/__init__.py rename homeassistant/components/{media_player/samsungtv.py => samsungtv/media_player.py} (100%) create mode 100644 homeassistant/components/season/__init__.py rename homeassistant/components/{sensor/season.py => season/sensor.py} (100%) create mode 100644 homeassistant/components/sigfox/__init__.py rename homeassistant/components/{sensor/sigfox.py => sigfox/sensor.py} (100%) create mode 100644 homeassistant/components/simulated/__init__.py rename homeassistant/components/{sensor/simulated.py => simulated/sensor.py} (100%) rename homeassistant/components/{binary_sensor/sleepiq.py => sleepiq/binary_sensor.py} (100%) rename homeassistant/components/{sensor/sleepiq.py => sleepiq/sensor.py} (100%) create mode 100644 homeassistant/components/sonarr/__init__.py rename homeassistant/components/{sensor/sonarr.py => sonarr/sensor.py} (100%) create mode 100644 homeassistant/components/soundtouch/__init__.py rename homeassistant/components/{media_player/soundtouch.py => soundtouch/media_player.py} (100%) create mode 100644 homeassistant/components/sql/__init__.py rename homeassistant/components/{sensor/sql.py => sql/sensor.py} (100%) create mode 100644 homeassistant/components/srp_energy/__init__.py rename homeassistant/components/{sensor/srp_energy.py => srp_energy/sensor.py} (100%) create mode 100644 homeassistant/components/startca/__init__.py rename homeassistant/components/{sensor/startca.py => startca/sensor.py} (100%) create mode 100644 homeassistant/components/statistics/__init__.py rename homeassistant/components/{sensor/statistics.py => statistics/sensor.py} (100%) create mode 100644 homeassistant/components/tcp/__init__.py rename homeassistant/components/{binary_sensor/tcp.py => tcp/binary_sensor.py} (94%) rename homeassistant/components/{sensor/tcp.py => tcp/sensor.py} (100%) create mode 100644 homeassistant/components/teksavvy/__init__.py rename homeassistant/components/{sensor/teksavvy.py => teksavvy/sensor.py} (100%) create mode 100644 homeassistant/components/template/__init__.py rename homeassistant/components/{binary_sensor/template.py => template/binary_sensor.py} (100%) rename homeassistant/components/{cover/template.py => template/cover.py} (100%) rename homeassistant/components/{fan/template.py => template/fan.py} (100%) rename homeassistant/components/{light/template.py => template/light.py} (100%) rename homeassistant/components/{lock/template.py => template/lock.py} (100%) rename homeassistant/components/{sensor/template.py => template/sensor.py} (100%) rename homeassistant/components/{switch/template.py => template/switch.py} (100%) create mode 100644 homeassistant/components/threshold/__init__.py rename homeassistant/components/{binary_sensor/threshold.py => threshold/binary_sensor.py} (100%) create mode 100644 homeassistant/components/time_date/__init__.py rename homeassistant/components/{sensor/time_date.py => time_date/sensor.py} (100%) create mode 100644 homeassistant/components/tod/__init__.py rename homeassistant/components/{binary_sensor/tod.py => tod/binary_sensor.py} (100%) create mode 100644 homeassistant/components/tomato/__init__.py rename homeassistant/components/{device_tracker/tomato.py => tomato/device_tracker.py} (100%) rename homeassistant/components/{device_tracker/tplink.py => tplink/device_tracker.py} (100%) create mode 100644 homeassistant/components/transport_nsw/__init__.py rename homeassistant/components/{sensor/transport_nsw.py => transport_nsw/sensor.py} (100%) create mode 100644 homeassistant/components/trend/__init__.py rename homeassistant/components/{binary_sensor/trend.py => trend/binary_sensor.py} (100%) create mode 100644 homeassistant/components/uk_transport/__init__.py rename homeassistant/components/{sensor/uk_transport.py => uk_transport/sensor.py} (100%) rename homeassistant/components/{device_tracker/unifi.py => unifi/device_tracker.py} (100%) create mode 100644 homeassistant/components/unifi_direct/__init__.py rename homeassistant/components/{device_tracker/unifi_direct.py => unifi_direct/device_tracker.py} (100%) create mode 100644 homeassistant/components/universal/__init__.py rename homeassistant/components/{media_player/universal.py => universal/media_player.py} (100%) create mode 100644 homeassistant/components/upc_connect/__init__.py rename homeassistant/components/{device_tracker/upc_connect.py => upc_connect/device_tracker.py} (100%) create mode 100644 homeassistant/components/uptime/__init__.py rename homeassistant/components/{sensor/uptime.py => uptime/sensor.py} (100%) create mode 100644 homeassistant/components/usgs_earthquakes_feed/__init__.py rename homeassistant/components/{geo_location/usgs_earthquakes_feed.py => usgs_earthquakes_feed/geo_location.py} (100%) create mode 100644 homeassistant/components/uvc/__init__.py rename homeassistant/components/{camera/uvc.py => uvc/camera.py} (100%) create mode 100644 homeassistant/components/version/__init__.py rename homeassistant/components/{sensor/version.py => version/sensor.py} (100%) rename homeassistant/components/{binary_sensor/vultr.py => vultr/binary_sensor.py} (100%) rename homeassistant/components/{sensor/vultr.py => vultr/sensor.py} (100%) rename homeassistant/components/{switch/vultr.py => vultr/switch.py} (100%) rename homeassistant/components/{switch/wake_on_lan.py => wake_on_lan/switch.py} (100%) create mode 100644 homeassistant/components/workday/__init__.py rename homeassistant/components/{binary_sensor/workday.py => workday/binary_sensor.py} (100%) create mode 100644 homeassistant/components/worldclock/__init__.py rename homeassistant/components/{sensor/worldclock.py => worldclock/sensor.py} (100%) create mode 100644 homeassistant/components/wsdot/__init__.py rename homeassistant/components/{sensor/wsdot.py => wsdot/sensor.py} (100%) create mode 100644 homeassistant/components/wunderground/__init__.py rename homeassistant/components/{sensor/wunderground.py => wunderground/sensor.py} (100%) rename homeassistant/components/{device_tracker/xiaomi.py => xiaomi/device_tracker.py} (100%) create mode 100644 homeassistant/components/yamaha/__init__.py rename homeassistant/components/{media_player/yamaha.py => yamaha/media_player.py} (100%) create mode 100644 homeassistant/components/yr/__init__.py rename homeassistant/components/{sensor/yr.py => yr/sensor.py} (100%) create mode 100644 homeassistant/components/yweather/__init__.py rename homeassistant/components/{sensor/yweather.py => yweather/sensor.py} (100%) rename homeassistant/components/{weather/yweather.py => yweather/weather.py} (100%) create mode 100644 tests/components/api_streams/__init__.py rename tests/components/{sensor/test_api_streams.py => api_streams/test_sensor.py} (75%) create mode 100644 tests/components/asuswrt/__init__.py rename tests/components/{device_tracker/test_asuswrt.py => asuswrt/test_device_tracker.py} (100%) create mode 100644 tests/components/aurora/__init__.py rename tests/components/{binary_sensor/test_aurora.py => aurora/test_binary_sensor.py} (97%) create mode 100644 tests/components/automatic/__init__.py rename tests/components/{device_tracker/test_automatic.py => automatic/test_device_tracker.py} (95%) create mode 100644 tests/components/awair/__init__.py rename tests/components/{sensor/test_awair.py => awair/test_sensor.py} (98%) create mode 100644 tests/components/bayesian/__init__.py rename tests/components/{binary_sensor/test_bayesian.py => bayesian/test_binary_sensor.py} (99%) create mode 100644 tests/components/blackbird/__init__.py rename tests/components/{media_player/test_blackbird.py => blackbird/test_media_player.py} (99%) create mode 100644 tests/components/bom/__init__.py rename tests/components/{sensor/test_bom.py => bom/test_sensor.py} (98%) create mode 100644 tests/components/caldav/__init__.py rename tests/components/{calendar/test_caldav.py => caldav/test_calendar.py} (99%) rename tests/components/{sensor/test_canary.py => canary/test_sensor.py} (98%) create mode 100644 tests/components/coinmarketcap/__init__.py rename tests/components/{sensor/test_coinmarketcap.py => coinmarketcap/test_sensor.py} (100%) create mode 100644 tests/components/command_line/__init__.py rename tests/components/{binary_sensor/test_command_line.py => command_line/test_binary_sensor.py} (96%) rename tests/components/{cover/test_command_line.py => command_line/test_cover.py} (97%) rename tests/components/{sensor/test_command_line.py => command_line/test_sensor.py} (96%) rename tests/components/{switch/test_command_line.py => command_line/test_switch.py} (99%) create mode 100644 tests/components/darksky/__init__.py rename tests/components/{sensor/test_darksky.py => darksky/test_sensor.py} (98%) rename tests/components/{weather/test_darksky.py => darksky/test_weather.py} (100%) create mode 100644 tests/components/directv/__init__.py rename tests/components/{media_player/test_directv.py => directv/test_media_player.py} (99%) create mode 100644 tests/components/dsmr/__init__.py rename tests/components/{sensor/test_dsmr.py => dsmr/test_sensor.py} (99%) create mode 100644 tests/components/dte_energy_bridge/__init__.py rename tests/components/{sensor/test_dte_energy_bridge.py => dte_energy_bridge/test_sensor.py} (100%) rename tests/components/{climate/test_dyson.py => dyson/test_climate.py} (99%) rename tests/components/{fan/test_dyson.py => dyson/test_fan.py} (98%) rename tests/components/{sensor/test_dyson.py => dyson/test_sensor.py} (99%) rename tests/components/{vacuum/test_dyson.py => dyson/test_vacuum.py} (98%) create mode 100644 tests/components/ee_brightbox/__init__.py rename tests/components/{device_tracker/test_ee_brightbox.py => ee_brightbox/test_device_tracker.py} (100%) create mode 100644 tests/components/efergy/__init__.py rename tests/components/{sensor/test_efergy.py => efergy/test_sensor.py} (100%) create mode 100644 tests/components/entur_public_transport/__init__.py rename tests/components/{sensor/test_entur_public_transport.py => entur_public_transport/test_sensor.py} (94%) create mode 100644 tests/components/everlights/__init__.py rename tests/components/{light/test_everlights.py => everlights/test_light.py} (89%) create mode 100644 tests/components/facebox/__init__.py rename tests/components/{image_processing/test_facebox.py => facebox/test_image_processing.py} (97%) create mode 100644 tests/components/fail2ban/__init__.py rename tests/components/{sensor/test_fail2ban.py => fail2ban/test_sensor.py} (92%) create mode 100644 tests/components/fido/__init__.py rename tests/components/{sensor/test_fido.py => fido/test_sensor.py} (98%) create mode 100644 tests/components/file/__init__.py rename tests/components/{sensor/test_file.py => file/test_sensor.py} (94%) create mode 100644 tests/components/filesize/__init__.py rename tests/components/{sensor/test_filesize.py => filesize/test_sensor.py} (96%) create mode 100644 tests/components/filter/__init__.py rename tests/components/{sensor/test_filter.py => filter/test_sensor.py} (99%) create mode 100644 tests/components/flux/__init__.py rename tests/components/{switch/test_flux.py => flux/test_switch.py} (97%) create mode 100644 tests/components/folder/__init__.py rename tests/components/{sensor/test_folder.py => folder/test_sensor.py} (96%) create mode 100644 tests/components/foobot/__init__.py rename tests/components/{sensor/test_foobot.py => foobot/test_sensor.py} (98%) create mode 100644 tests/components/generic/__init__.py rename tests/components/{camera/test_generic.py => generic/test_camera.py} (100%) create mode 100644 tests/components/geo_json_events/__init__.py rename tests/components/{geo_location/test_geo_json_events.py => geo_json_events/test_geo_location.py} (99%) create mode 100644 tests/components/geo_rss_events/__init__.py rename tests/components/{sensor/test_geo_rss_events.py => geo_rss_events/test_sensor.py} (99%) create mode 100644 tests/components/google_pubsub/__init__.py create mode 100644 tests/components/google_wifi/__init__.py rename tests/components/{sensor/test_google_wifi.py => google_wifi/test_sensor.py} (99%) create mode 100644 tests/components/hddtemp/__init__.py rename tests/components/{sensor/test_hddtemp.py => hddtemp/test_sensor.py} (100%) create mode 100644 tests/components/history_stats/__init__.py rename tests/components/{sensor/test_history_stats.py => history_stats/test_sensor.py} (99%) create mode 100644 tests/components/homekit_controller/__init__.py create mode 100644 tests/components/honeywell/__init__.py rename tests/components/{climate/test_honeywell.py => honeywell/test_climate.py} (96%) create mode 100644 tests/components/hydroquebec/__init__.py rename tests/components/{sensor/test_hydroquebec.py => hydroquebec/test_sensor.py} (97%) create mode 100644 tests/components/imap_email_content/__init__.py rename tests/components/{sensor/test_imap_email_content.py => imap_email_content/test_sensor.py} (98%) create mode 100644 tests/components/integration/__init__.py rename tests/components/{sensor/test_integration.py => integration/test_sensor.py} (100%) create mode 100644 tests/components/islamic_prayer_times/__init__.py rename tests/components/{sensor/test_islamic_prayer_times.py => islamic_prayer_times/test_sensor.py} (97%) create mode 100644 tests/components/jewish_calendar/__init__.py rename tests/components/{sensor/test_jewish_calendar.py => jewish_calendar/test_sensor.py} (99%) rename tests/components/{light/test_litejet.py => litejet/test_light.py} (100%) rename tests/components/{scene/test_litejet.py => litejet/test_scene.py} (100%) rename tests/components/{switch/test_litejet.py => litejet/test_switch.py} (100%) create mode 100644 tests/components/local_file/__init__.py rename tests/components/{camera/test_local_file.py => local_file/test_camera.py} (96%) create mode 100644 tests/components/london_air/__init__.py rename tests/components/{sensor/test_london_air.py => london_air/test_sensor.py} (95%) create mode 100644 tests/components/manual/__init__.py create mode 100644 tests/components/manual_mqtt/__init__.py rename tests/components/{climate/test_melissa.py => melissa/test_climate.py} (95%) create mode 100644 tests/components/meraki/__init__.py rename tests/components/{device_tracker/test_meraki.py => meraki/test_device_tracker.py} (97%) create mode 100644 tests/components/mfi/__init__.py rename tests/components/{sensor/test_mfi.py => mfi/test_sensor.py} (98%) rename tests/components/{switch/test_mfi.py => mfi/test_switch.py} (95%) create mode 100644 tests/components/mhz19/__init__.py rename tests/components/{sensor/test_mhz19.py => mhz19/test_sensor.py} (98%) create mode 100644 tests/components/microsoft_face_detect/__init__.py rename tests/components/{image_processing/test_microsoft_face_detect.py => microsoft_face_detect/test_image_processing.py} (98%) create mode 100644 tests/components/microsoft_face_identify/__init__.py rename tests/components/{image_processing/test_microsoft_face_identify.py => microsoft_face_identify/test_image_processing.py} (98%) create mode 100644 tests/components/min_max/__init__.py rename tests/components/{sensor/test_min_max.py => min_max/test_sensor.py} (100%) create mode 100644 tests/components/monoprice/__init__.py rename tests/components/{media_player/test_monoprice.py => monoprice/test_media_player.py} (99%) create mode 100644 tests/components/moon/__init__.py rename tests/components/{sensor/test_moon.py => moon/test_sensor.py} (92%) create mode 100644 tests/components/mqtt_json/__init__.py rename tests/components/{device_tracker/test_mqtt_json.py => mqtt_json/test_device_tracker.py} (98%) create mode 100644 tests/components/mqtt_room/__init__.py rename tests/components/{sensor/test_mqtt_room.py => mqtt_room/test_sensor.py} (100%) create mode 100644 tests/components/nsw_fuel_station/__init__.py rename tests/components/{sensor/test_nsw_fuel_station.py => nsw_fuel_station/test_sensor.py} (100%) create mode 100644 tests/components/nsw_rural_fire_service_feed/__init__.py rename tests/components/{geo_location/test_nsw_rural_fire_service_feed.py => nsw_rural_fire_service_feed/test_geo_location.py} (99%) rename tests/components/{climate/test_nuheat.py => nuheat/test_climate.py} (97%) create mode 100644 tests/components/nx584/__init__.py rename tests/components/{binary_sensor/test_nx584.py => nx584/test_binary_sensor.py} (95%) create mode 100644 tests/components/openalpr_cloud/__init__.py rename tests/components/{image_processing/test_openalpr_cloud.py => openalpr_cloud/test_image_processing.py} (98%) create mode 100644 tests/components/openalpr_local/__init__.py rename tests/components/{image_processing/test_openalpr_local.py => openalpr_local/test_image_processing.py} (98%) create mode 100644 tests/components/openhardwaremonitor/__init__.py rename tests/components/{sensor/test_openhardwaremonitor.py => openhardwaremonitor/test_sensor.py} (100%) create mode 100644 tests/components/push/__init__.py rename tests/components/{camera/test_push.py => push/test_camera.py} (100%) create mode 100644 tests/components/radarr/__init__.py rename tests/components/{sensor/test_radarr.py => radarr/test_sensor.py} (99%) create mode 100644 tests/components/random/__init__.py rename tests/components/{binary_sensor/test_random.py => random/test_binary_sensor.py} (100%) rename tests/components/{sensor/test_random.py => random/test_sensor.py} (100%) create mode 100644 tests/components/rest/__init__.py rename tests/components/{binary_sensor/test_rest.py => rest/test_binary_sensor.py} (99%) rename tests/components/{sensor/test_rest.py => rest/test_sensor.py} (98%) rename tests/components/{switch/test_rest.py => rest/test_switch.py} (99%) rename tests/components/{binary_sensor/test_rflink.py => rflink/test_binary_sensor.py} (100%) rename tests/components/{cover/test_rflink.py => rflink/test_cover.py} (100%) rename tests/components/{light/test_rflink.py => rflink/test_light.py} (100%) rename tests/components/{sensor/test_rflink.py => rflink/test_sensor.py} (100%) rename tests/components/{switch/test_rflink.py => rflink/test_switch.py} (100%) rename tests/components/{binary_sensor/test_ring.py => ring/test_binary_sensor.py} (97%) rename tests/components/{sensor/test_ring.py => ring/test_sensor.py} (98%) create mode 100644 tests/components/rmvtransport/__init__.py rename tests/components/{sensor/test_rmvtransport.py => rmvtransport/test_sensor.py} (100%) create mode 100644 tests/components/samsungtv/__init__.py rename tests/components/{media_player/test_samsungtv.py => samsungtv/test_media_player.py} (96%) create mode 100644 tests/components/season/__init__.py rename tests/components/{sensor/test_season.py => season/test_sensor.py} (99%) create mode 100644 tests/components/sigfox/__init__.py rename tests/components/{sensor/test_sigfox.py => sigfox/test_sensor.py} (97%) create mode 100644 tests/components/simulated/__init__.py rename tests/components/{sensor/test_simulated.py => simulated/test_sensor.py} (96%) rename tests/components/{binary_sensor/test_sleepiq.py => sleepiq/test_binary_sensor.py} (96%) rename tests/components/{sensor/test_sleepiq.py => sleepiq/test_sensor.py} (96%) create mode 100644 tests/components/sonarr/__init__.py rename tests/components/{sensor/test_sonarr.py => sonarr/test_sensor.py} (99%) create mode 100644 tests/components/soundtouch/__init__.py rename tests/components/{media_player/test_soundtouch.py => soundtouch/test_media_player.py} (99%) create mode 100644 tests/components/sql/__init__.py rename tests/components/{sensor/test_sql.py => sql/test_sensor.py} (96%) create mode 100644 tests/components/srp_energy/__init__.py rename tests/components/{sensor/test_srp_energy.py => srp_energy/test_sensor.py} (100%) create mode 100644 tests/components/startca/__init__.py rename tests/components/{sensor/test_startca.py => startca/test_sensor.py} (99%) create mode 100644 tests/components/statistics/__init__.py rename tests/components/{sensor/test_statistics.py => statistics/test_sensor.py} (97%) create mode 100644 tests/components/tcp/__init__.py rename tests/components/{binary_sensor/test_tcp.py => tcp/test_binary_sensor.py} (82%) rename tests/components/{sensor/test_tcp.py => tcp/test_sensor.py} (91%) create mode 100644 tests/components/teksavvy/__init__.py rename tests/components/{sensor/test_teksavvy.py => teksavvy/test_sensor.py} (99%) create mode 100644 tests/components/template/__init__.py rename tests/components/{binary_sensor/test_template.py => template/test_binary_sensor.py} (99%) rename tests/components/{cover/test_template.py => template/test_cover.py} (100%) rename tests/components/{fan/test_template.py => template/test_fan.py} (100%) rename tests/components/{light/test_template.py => template/test_light.py} (100%) rename tests/components/{lock/test_template.py => template/test_lock.py} (100%) rename tests/components/{sensor/test_template.py => template/test_sensor.py} (100%) rename tests/components/{switch/test_template.py => template/test_switch.py} (100%) create mode 100644 tests/components/threshold/__init__.py rename tests/components/{binary_sensor/test_threshold.py => threshold/test_binary_sensor.py} (100%) create mode 100644 tests/components/time_date/__init__.py rename tests/components/{sensor/test_time_date.py => time_date/test_sensor.py} (98%) create mode 100644 tests/components/tod/__init__.py rename tests/components/{binary_sensor/test_tod.py => tod/test_binary_sensor.py} (89%) create mode 100644 tests/components/tomato/__init__.py rename tests/components/{device_tracker/test_tomato.py => tomato/test_device_tracker.py} (98%) rename tests/components/{device_tracker/test_tplink.py => tplink/test_device_tracker.py} (96%) create mode 100644 tests/components/transport_nsw/__init__.py rename tests/components/{sensor/test_transport_nsw.py => transport_nsw/test_sensor.py} (100%) create mode 100644 tests/components/trend/__init__.py rename tests/components/{binary_sensor/test_trend.py => trend/test_binary_sensor.py} (100%) create mode 100644 tests/components/uk_transport/__init__.py rename tests/components/{sensor/test_uk_transport.py => uk_transport/test_sensor.py} (98%) rename tests/components/{device_tracker/test_unifi.py => unifi/test_device_tracker.py} (97%) create mode 100644 tests/components/unifi_direct/__init__.py rename tests/components/{device_tracker/test_unifi_direct.py => unifi_direct/test_device_tracker.py} (96%) create mode 100644 tests/components/universal/__init__.py rename tests/components/{media_player/test_universal.py => universal/test_media_player.py} (99%) create mode 100644 tests/components/upc_connect/__init__.py rename tests/components/{device_tracker/test_upc_connect.py => upc_connect/test_device_tracker.py} (98%) create mode 100644 tests/components/uptime/__init__.py rename tests/components/{sensor/test_uptime.py => uptime/test_sensor.py} (98%) create mode 100644 tests/components/usgs_earthquakes_feed/__init__.py rename tests/components/{geo_location/test_usgs_earthquakes_feed.py => usgs_earthquakes_feed/test_geo_location.py} (97%) create mode 100644 tests/components/uvc/__init__.py rename tests/components/{camera/test_uvc.py => uvc/test_camera.py} (99%) create mode 100644 tests/components/version/__init__.py rename tests/components/{sensor/test_version.py => version/test_sensor.py} (100%) rename tests/components/{binary_sensor/test_vultr.py => vultr/test_binary_sensor.py} (98%) rename tests/components/{sensor/test_vultr.py => vultr/test_sensor.py} (99%) rename tests/components/{switch/test_vultr.py => vultr/test_switch.py} (99%) rename tests/components/{switch/test_wake_on_lan.py => wake_on_lan/test_switch.py} (100%) create mode 100644 tests/components/workday/__init__.py rename tests/components/{binary_sensor/test_workday.py => workday/test_binary_sensor.py} (98%) create mode 100644 tests/components/worldclock/__init__.py rename tests/components/{sensor/test_worldclock.py => worldclock/test_sensor.py} (100%) create mode 100644 tests/components/wsdot/__init__.py rename tests/components/{sensor/test_wsdot.py => wsdot/test_sensor.py} (95%) create mode 100644 tests/components/wunderground/__init__.py rename tests/components/{sensor/test_wunderground.py => wunderground/test_sensor.py} (98%) create mode 100644 tests/components/xiaomi/__init__.py rename tests/components/{device_tracker/test_xiaomi.py => xiaomi/test_device_tracker.py} (96%) create mode 100644 tests/components/yamaha/__init__.py rename tests/components/{media_player/test_yamaha.py => yamaha/test_media_player.py} (97%) create mode 100644 tests/components/yr/__init__.py rename tests/components/{sensor/test_yr.py => yr/test_sensor.py} (95%) create mode 100644 tests/components/yweather/__init__.py rename tests/components/{sensor/test_yweather.py => yweather/test_sensor.py} (100%) rename tests/components/{weather/test_yweather.py => yweather/test_weather.py} (100%) diff --git a/.coveragerc b/.coveragerc index 7c5ca1f3a0b..e3ad5ff206e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -45,16 +45,16 @@ omit = homeassistant/components/iss/binary_sensor.py homeassistant/components/mystrom/binary_sensor.py homeassistant/components/ping/binary_sensor.py - homeassistant/components/binary_sensor/rest.py + homeassistant/components/rest/binary_sensor.py homeassistant/components/tapsaff/binary_sensor.py homeassistant/components/uptimerobot/binary_sensor.py homeassistant/components/blink/* homeassistant/components/bloomsky/* homeassistant/components/bmw_connected_drive/* homeassistant/components/browser/* - homeassistant/components/calendar/caldav.py + homeassistant/components/caldav/calendar.py homeassistant/components/todoist/calendar.py - homeassistant/components/camera/bloomsky.py + homeassistant/components/bloomsky/camera.py homeassistant/components/canary/camera.py homeassistant/components/familyhub/camera.py homeassistant/components/ffmpeg/camera.py @@ -75,9 +75,9 @@ omit = homeassistant/components/eq3btsmart/climate.py homeassistant/components/flexit/climate.py homeassistant/components/heatmiser/climate.py - homeassistant/components/climate/homematic.py - homeassistant/components/climate/honeywell.py - homeassistant/components/climate/knx.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 @@ -93,18 +93,18 @@ omit = homeassistant/components/brunt/cover.py homeassistant/components/garadget/cover.py homeassistant/components/gogogate2/cover.py - homeassistant/components/cover/homematic.py - homeassistant/components/cover/knx.py + homeassistant/components/homematic/cover.py + homeassistant/components/knx/cover.py homeassistant/components/myq/cover.py homeassistant/components/opengarage/cover.py - homeassistant/components/cover/rpi_gpio.py - homeassistant/components/cover/scsgate.py + homeassistant/components/rpi_gpio/cover.py + homeassistant/components/scsgate/cover.py homeassistant/components/daikin/* homeassistant/components/danfoss_air/* homeassistant/components/actiontec/device_tracker.py homeassistant/components/aruba/device_tracker.py - homeassistant/components/device_tracker/asuswrt.py - homeassistant/components/device_tracker/automatic.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 @@ -132,11 +132,11 @@ omit = homeassistant/components/snmp/device_tracker.py homeassistant/components/swisscom/device_tracker.py homeassistant/components/synology_srm/device_tracker.py - homeassistant/components/device_tracker/tado.py + homeassistant/components/tado/device_tracker.py homeassistant/components/thomson/device_tracker.py homeassistant/components/tile/device_tracker.py - homeassistant/components/device_tracker/tomato.py - homeassistant/components/device_tracker/tplink.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 @@ -170,7 +170,7 @@ omit = homeassistant/components/esphome/switch.py homeassistant/components/eufy/* homeassistant/components/evohome/* - homeassistant/components/fan/wemo.py + homeassistant/components/wemo/fan.py homeassistant/components/fastdotcom/* homeassistant/components/fibaro/* homeassistant/components/folder_watcher/* @@ -227,11 +227,11 @@ omit = homeassistant/components/blinkt/light.py homeassistant/components/decora_wifi/light.py homeassistant/components/decora/light.py - homeassistant/components/light/everlights.py + homeassistant/components/everlights/light.py homeassistant/components/flux_led/light.py homeassistant/components/futurenow/light.py homeassistant/components/greenwave/light.py - homeassistant/components/light/hue.py + homeassistant/components/hue/light.py homeassistant/components/hyperion/light.py homeassistant/components/iglo/light.py homeassistant/components/lifx_legacy/light.py @@ -246,8 +246,8 @@ omit = homeassistant/components/rpi_gpio_pwm/light.py homeassistant/components/sensehat/light.py homeassistant/components/tikteck/light.py - homeassistant/components/light/tplink.py - homeassistant/components/light/tradfri.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 @@ -280,7 +280,7 @@ omit = homeassistant/components/cmus/media_player.py homeassistant/components/denon/media_player.py homeassistant/components/denonavr/media_player.py - homeassistant/components/media_player/directv.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 @@ -299,7 +299,7 @@ omit = homeassistant/components/mpchc/media_player.py homeassistant/components/mpd/media_player.py homeassistant/components/nad/media_player.py - homeassistant/components/media_player/nadtcp.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 @@ -321,7 +321,7 @@ omit = homeassistant/components/volumio/media_player.py homeassistant/components/xiaomi_tv/media_player.py homeassistant/components/yamaha_musiccast/media_player.py - homeassistant/components/media_player/yamaha.py + homeassistant/components/yamaha/media_player.py homeassistant/components/ziggo_mediabox_xl/media_player.py homeassistant/components/meteo_france/* homeassistant/components/mobile_app/* @@ -348,7 +348,7 @@ omit = homeassistant/components/notify/gntp.py homeassistant/components/notify/group.py homeassistant/components/notify/hipchat.py - homeassistant/components/notify/homematic.py + homeassistant/components/homematic/notify.py homeassistant/components/notify/kodi.py homeassistant/components/notify/lannouncer.py homeassistant/components/notify/llamalab_automate.py @@ -402,8 +402,8 @@ omit = homeassistant/components/raspyrfm/* homeassistant/components/reddit/* homeassistant/components/remember_the_milk/__init__.py - homeassistant/components/remote/harmony.py - homeassistant/components/remote/itach.py + homeassistant/components/harmony/remote.py + homeassistant/components/itach/remote.py homeassistant/components/rfxtrx/* homeassistant/components/roku/* homeassistant/components/route53/* @@ -415,6 +415,7 @@ 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 @@ -426,7 +427,7 @@ omit = homeassistant/components/blockchain/sensor.py homeassistant/components/bme280/sensor.py homeassistant/components/bme680/sensor.py - homeassistant/components/sensor/bom.py + homeassistant/components/bom/sensor.py homeassistant/components/broadlink/sensor.py homeassistant/components/brottsplatskartan/sensor.py homeassistant/components/buienradar/sensor.py @@ -443,8 +444,8 @@ omit = homeassistant/components/dht/sensor.py homeassistant/components/discogs/sensor.py homeassistant/components/dnsip/sensor.py - homeassistant/components/sensor/domain_expiry.py - homeassistant/components/sensor/dte_energy_bridge.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 @@ -456,13 +457,13 @@ omit = homeassistant/components/envirophat/sensor.py homeassistant/components/etherscan/sensor.py homeassistant/components/fedex/sensor.py - homeassistant/components/sensor/filesize.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/sensor/folder.py - homeassistant/components/sensor/foobot.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 @@ -480,7 +481,7 @@ omit = homeassistant/components/hp_ilo/sensor.py homeassistant/components/htu21d/sensor.py homeassistant/components/iliad_italy/sensor.py - homeassistant/components/sensor/imap_email_content.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 @@ -499,16 +500,16 @@ omit = homeassistant/components/mitemp_bt/sensor.py homeassistant/components/modem_callerid/sensor.py homeassistant/components/mopar/sensor.py - homeassistant/components/sensor/mqtt_room.py + homeassistant/components/mqtt_room/sensor.py homeassistant/components/mvglive/sensor.py homeassistant/components/nederlandse_spoorwegen/sensor.py homeassistant/components/netatmo_public/sensor.py - homeassistant/components/sensor/netdata_public.py + homeassistant/components/netdata_public/sensor.py homeassistant/components/netdata/sensor.py homeassistant/components/neurio_energy/sensor.py homeassistant/components/nmbs/sensor.py homeassistant/components/noaa_tides/sensor.py - homeassistant/components/sensor/nsw_fuel_station.py + homeassistant/components/nsw_fuel_station/sensor.py homeassistant/components/nut/sensor.py homeassistant/components/nzbget/sensor.py homeassistant/components/ohmconnect/sensor.py @@ -529,7 +530,7 @@ omit = homeassistant/components/pyload/sensor.py homeassistant/components/qbittorrent/sensor.py homeassistant/components/qnap/sensor.py - homeassistant/components/sensor/radarr.py + homeassistant/components/radarr/sensor.py homeassistant/components/rainbird/sensor.py homeassistant/components/recollect_waste/sensor.py homeassistant/components/rejseplanen/sensor.py @@ -544,17 +545,17 @@ omit = homeassistant/components/seventeentrack/sensor.py homeassistant/components/shodan/sensor.py homeassistant/components/sht31/sensor.py - homeassistant/components/sensor/sigfox.py - homeassistant/components/sensor/simulated.py + homeassistant/components/sigfox/sensor.py + homeassistant/components/simulated/sensor.py homeassistant/components/skybeacon/sensor.py homeassistant/components/sma/sensor.py homeassistant/components/snmp/sensor.py homeassistant/components/sochain/sensor.py homeassistant/components/socialblade/sensor.py homeassistant/components/solaredge/sensor.py - homeassistant/components/sensor/sonarr.py + homeassistant/components/sonarr/sensor.py homeassistant/components/spotcrime/sensor.py - homeassistant/components/sensor/srp_energy.py + homeassistant/components/srp_energy/sensor.py homeassistant/components/starlingbank/sensor.py homeassistant/components/steam_online/sensor.py homeassistant/components/supervisord/sensor.py @@ -569,7 +570,7 @@ omit = homeassistant/components/ted5000/sensor.py homeassistant/components/temper/sensor.py homeassistant/components/thermoworks_smoke/sensor.py - homeassistant/components/sensor/time_date.py + homeassistant/components/time_date/sensor.py homeassistant/components/torque/sensor.py homeassistant/components/trafikverket_weatherstation/sensor.py homeassistant/components/travisci/sensor.py @@ -617,14 +618,14 @@ omit = homeassistant/components/pulseaudio_loopback/switch.py homeassistant/components/rainbird/switch.py homeassistant/components/recswitch/switch.py - homeassistant/components/switch/rest.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/switch/tplink.py + homeassistant/components/tplink/switch.py homeassistant/components/vesync/switch.py homeassistant/components/tado/* homeassistant/components/tahoma/* @@ -661,7 +662,7 @@ omit = homeassistant/components/watson_iot/* homeassistant/components/bom/weather.py homeassistant/components/buienradar/weather.py - homeassistant/components/weather/darksky.py + homeassistant/components/darksky/weather.py homeassistant/components/met/weather.py homeassistant/components/metoffice/weather.py homeassistant/components/openweathermap/weather.py diff --git a/CODEOWNERS b/CODEOWNERS index c70aec85fc7..9a4f38f60d0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,7 +18,7 @@ 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_*.py @home-assistant/core +homeassistant/components/input_*/* @home-assistant/core homeassistant/components/introduction/* @home-assistant/core homeassistant/components/logger/* @home-assistant/core homeassistant/components/lovelace/* @home-assistant/core @@ -49,9 +49,9 @@ 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/binary_sensor/threshold.py @fabaff +homeassistant/components/threshold/binary_sensor.py @fabaff homeassistant/components/uptimerobot/binary_sensor.py @ludeeus -homeassistant/components/camera/push.py @dgomes +homeassistant/components/push/camera.py @dgomes homeassistant/components/yi/camera.py @bachya homeassistant/components/coolmaster/climate.py @OnFreund homeassistant/components/ephember/climate.py @ttroy50 @@ -60,9 +60,9 @@ 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/cover/template.py @PhracturedBlue -homeassistant/components/device_tracker/asuswrt.py @kennedyshead -homeassistant/components/device_tracker/automatic.py @armills +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 @@ -81,7 +81,7 @@ 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/media_player/monoprice.py @etsinko +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 @@ -105,10 +105,10 @@ 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/sensor/darksky.py @fabaff +homeassistant/components/darksky/sensor.py @fabaff homeassistant/components/discogs/sensor.py @thibmaek -homeassistant/components/sensor/file.py @fabaff -homeassistant/components/sensor/filter.py @dgomes +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 @@ -118,17 +118,17 @@ 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/sensor/integration.py @dgomes +homeassistant/components/integration/sensor.py @dgomes homeassistant/components/irish_rail_transport/sensor.py @ttroy50 -homeassistant/components/sensor/jewish_calendar.py @tsvi +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/sensor/min_max.py @fabaff -homeassistant/components/sensor/moon.py @fabaff +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/sensor/nsw_fuel_station.py @nickw444 +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 @@ -140,22 +140,22 @@ 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/sensor/sql.py @dgomes -homeassistant/components/sensor/statistics.py @fabaff -homeassistant/components/sensor/swiss*.py @fabaff +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/sensor/time_date.py @fabaff +homeassistant/components/time_date/sensor.py @fabaff homeassistant/components/uber/sensor.py @robbiet480 -homeassistant/components/sensor/version.py @fabaff +homeassistant/components/version/sensor.py @fabaff homeassistant/components/waqi/sensor.py @andrey-git -homeassistant/components/sensor/worldclock.py @fabaff +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/weather/darksky.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 @@ -164,12 +164,12 @@ homeassistant/components/openweathermap/weather.py @fabaff homeassistant/components/ambient_station/* @bachya homeassistant/components/arduino/* @fabaff homeassistant/components/axis/* @kane610 -homeassistant/components/*/arest.py @fabaff +homeassistant/components/arest/* @fabaff # B homeassistant/components/blink/* @fronzbot homeassistant/components/bmw_connected_drive/* @ChristianKuehnel -homeassistant/components/*/broadlink.py @danielhiversen +homeassistant/components/broadlink/* @danielhiversen # C homeassistant/components/cloudflare/* @ludeeus @@ -186,10 +186,10 @@ homeassistant/components/ecovacs/* @OverloadUT homeassistant/components/edp_redy/* @abmantis homeassistant/components/eight_sleep/* @mezz64 homeassistant/components/egardia/* @jeroenterheerdt -homeassistant/components/esphome/*.py @OttoWinter +homeassistant/components/esphome/* @OttoWinter # F -homeassistant/components/freebox/*.py @snoof85 +homeassistant/components/freebox/* @snoof85 homeassistant/components/foursquare/* @robbiet480 # G @@ -203,7 +203,7 @@ homeassistant/components/homekit/* @cdce8p homeassistant/components/huawei_lte/* @scop # I -homeassistant/components/influx/* @fabaff +homeassistant/components/influxdb/* @fabaff homeassistant/components/ios/* @robbiet480 homeassistant/components/ipma/* @dgomes @@ -219,12 +219,10 @@ homeassistant/components/luftdaten/* @fabaff homeassistant/components/matrix/* @tinloaf homeassistant/components/melissa/* @kennedyshead homeassistant/components/mobile_app/* @robbiet480 -homeassistant/components/*/melissa.py @kennedyshead -homeassistant/components/*/mystrom.py @fabaff +homeassistant/components/mystrom/* @fabaff # N homeassistant/components/ness_alarm/* @nickw444 -homeassistant/components/*/ness_alarm.py @nickw444 homeassistant/components/nissan_leaf/* @filcole homeassistant/components/no_ip/* @fabaff @@ -241,7 +239,7 @@ homeassistant/components/qwikswitch/* @kellerza # R homeassistant/components/rainmachine/* @bachya homeassistant/components/rfxtrx/* @danielhiversen -homeassistant/components/*/random.py @fabaff +homeassistant/components/random/* @fabaff # S homeassistant/components/shiftr/* @fabaff @@ -253,7 +251,7 @@ homeassistant/components/spider/* @peternijssen # T homeassistant/components/tahoma/* @philklei -homeassistant/components/tellduslive/*.py @fredrike +homeassistant/components/tellduslive/* @fredrike homeassistant/components/tesla/* @zabuldon homeassistant/components/thethingsnetwork/* @fabaff homeassistant/components/tibber/* @danielhiversen diff --git a/homeassistant/components/api_streams/__init__.py b/homeassistant/components/api_streams/__init__.py new file mode 100644 index 00000000000..dba43061313 --- /dev/null +++ b/homeassistant/components/api_streams/__init__.py @@ -0,0 +1 @@ +"""The api_streams component.""" diff --git a/homeassistant/components/sensor/api_streams.py b/homeassistant/components/api_streams/sensor.py similarity index 100% rename from homeassistant/components/sensor/api_streams.py rename to homeassistant/components/api_streams/sensor.py diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/asuswrt/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/asuswrt.py rename to homeassistant/components/asuswrt/device_tracker.py diff --git a/homeassistant/components/aurora/__init__.py b/homeassistant/components/aurora/__init__.py new file mode 100644 index 00000000000..2b3caa06843 --- /dev/null +++ b/homeassistant/components/aurora/__init__.py @@ -0,0 +1 @@ +"""The aurora component.""" diff --git a/homeassistant/components/binary_sensor/aurora.py b/homeassistant/components/aurora/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/aurora.py rename to homeassistant/components/aurora/binary_sensor.py diff --git a/homeassistant/components/automatic/__init__.py b/homeassistant/components/automatic/__init__.py new file mode 100644 index 00000000000..8a1cae16f1e --- /dev/null +++ b/homeassistant/components/automatic/__init__.py @@ -0,0 +1 @@ +"""The automatic component.""" diff --git a/homeassistant/components/device_tracker/automatic.py b/homeassistant/components/automatic/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/automatic.py rename to homeassistant/components/automatic/device_tracker.py diff --git a/homeassistant/components/awair/__init__.py b/homeassistant/components/awair/__init__.py new file mode 100644 index 00000000000..c9a08cb40b5 --- /dev/null +++ b/homeassistant/components/awair/__init__.py @@ -0,0 +1 @@ +"""The awair component.""" diff --git a/homeassistant/components/sensor/awair.py b/homeassistant/components/awair/sensor.py similarity index 100% rename from homeassistant/components/sensor/awair.py rename to homeassistant/components/awair/sensor.py diff --git a/homeassistant/components/bayesian/__init__.py b/homeassistant/components/bayesian/__init__.py new file mode 100644 index 00000000000..971ff8427ac --- /dev/null +++ b/homeassistant/components/bayesian/__init__.py @@ -0,0 +1 @@ +"""The bayesian component.""" diff --git a/homeassistant/components/binary_sensor/bayesian.py b/homeassistant/components/bayesian/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/bayesian.py rename to homeassistant/components/bayesian/binary_sensor.py diff --git a/homeassistant/components/blackbird/__init__.py b/homeassistant/components/blackbird/__init__.py new file mode 100644 index 00000000000..b901bda0469 --- /dev/null +++ b/homeassistant/components/blackbird/__init__.py @@ -0,0 +1 @@ +"""The blackbird component.""" diff --git a/homeassistant/components/media_player/blackbird.py b/homeassistant/components/blackbird/media_player.py similarity index 100% rename from homeassistant/components/media_player/blackbird.py rename to homeassistant/components/blackbird/media_player.py diff --git a/homeassistant/components/sensor/bom.py b/homeassistant/components/bom/sensor.py similarity index 100% rename from homeassistant/components/sensor/bom.py rename to homeassistant/components/bom/sensor.py diff --git a/homeassistant/components/bom/weather.py b/homeassistant/components/bom/weather.py index b05d5fc594d..b35e7928ec1 100644 --- a/homeassistant/components/bom/weather.py +++ b/homeassistant/components/bom/weather.py @@ -4,7 +4,7 @@ import logging import voluptuous as vol # Reuse data and API logic from the sensor implementation -from homeassistant.components.sensor.bom import ( +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 ( diff --git a/homeassistant/components/caldav/__init__.py b/homeassistant/components/caldav/__init__.py new file mode 100644 index 00000000000..6fe9a8d4d19 --- /dev/null +++ b/homeassistant/components/caldav/__init__.py @@ -0,0 +1 @@ +"""The caldav component.""" diff --git a/homeassistant/components/calendar/caldav.py b/homeassistant/components/caldav/calendar.py similarity index 100% rename from homeassistant/components/calendar/caldav.py rename to homeassistant/components/caldav/calendar.py diff --git a/homeassistant/components/sensor/canary.py b/homeassistant/components/canary/sensor.py similarity index 100% rename from homeassistant/components/sensor/canary.py rename to homeassistant/components/canary/sensor.py diff --git a/homeassistant/components/coinmarketcap/__init__.py b/homeassistant/components/coinmarketcap/__init__.py new file mode 100644 index 00000000000..0cdb5a16a4a --- /dev/null +++ b/homeassistant/components/coinmarketcap/__init__.py @@ -0,0 +1 @@ +"""The coinmarketcap component.""" diff --git a/homeassistant/components/sensor/coinmarketcap.py b/homeassistant/components/coinmarketcap/sensor.py similarity index 100% rename from homeassistant/components/sensor/coinmarketcap.py rename to homeassistant/components/coinmarketcap/sensor.py diff --git a/homeassistant/components/command_line/__init__.py b/homeassistant/components/command_line/__init__.py new file mode 100644 index 00000000000..fe0640d3efa --- /dev/null +++ b/homeassistant/components/command_line/__init__.py @@ -0,0 +1 @@ +"""The command_line component.""" diff --git a/homeassistant/components/binary_sensor/command_line.py b/homeassistant/components/command_line/binary_sensor.py similarity index 98% rename from homeassistant/components/binary_sensor/command_line.py rename to homeassistant/components/command_line/binary_sensor.py index a3f1595787d..af9b3d48dea 100644 --- a/homeassistant/components/binary_sensor/command_line.py +++ b/homeassistant/components/command_line/binary_sensor.py @@ -12,7 +12,7 @@ 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.sensor.command_line import CommandSensorData +from homeassistant.components.command_line.sensor import CommandSensorData from homeassistant.const import ( CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_COMMAND, CONF_DEVICE_CLASS) diff --git a/homeassistant/components/cover/command_line.py b/homeassistant/components/command_line/cover.py similarity index 100% rename from homeassistant/components/cover/command_line.py rename to homeassistant/components/command_line/cover.py diff --git a/homeassistant/components/sensor/command_line.py b/homeassistant/components/command_line/sensor.py similarity index 100% rename from homeassistant/components/sensor/command_line.py rename to homeassistant/components/command_line/sensor.py diff --git a/homeassistant/components/switch/command_line.py b/homeassistant/components/command_line/switch.py similarity index 100% rename from homeassistant/components/switch/command_line.py rename to homeassistant/components/command_line/switch.py diff --git a/homeassistant/components/darksky/__init__.py b/homeassistant/components/darksky/__init__.py new file mode 100644 index 00000000000..90a5d06dc0e --- /dev/null +++ b/homeassistant/components/darksky/__init__.py @@ -0,0 +1 @@ +"""The darksky component.""" diff --git a/homeassistant/components/sensor/darksky.py b/homeassistant/components/darksky/sensor.py similarity index 100% rename from homeassistant/components/sensor/darksky.py rename to homeassistant/components/darksky/sensor.py diff --git a/homeassistant/components/weather/darksky.py b/homeassistant/components/darksky/weather.py similarity index 100% rename from homeassistant/components/weather/darksky.py rename to homeassistant/components/darksky/weather.py diff --git a/homeassistant/components/demo/image_processing.py b/homeassistant/components/demo/image_processing.py index 42ba7b4c05f..71ec2dccbc6 100644 --- a/homeassistant/components/demo/image_processing.py +++ b/homeassistant/components/demo/image_processing.py @@ -8,7 +8,7 @@ from homeassistant.components.image_processing import ( ImageProcessingFaceEntity, ATTR_CONFIDENCE, ATTR_NAME, ATTR_AGE, ATTR_GENDER ) -from homeassistant.components.image_processing.openalpr_local import ( +from homeassistant.components.openalpr_local.image_processing import ( ImageProcessingAlprEntity) diff --git a/homeassistant/components/directv/__init__.py b/homeassistant/components/directv/__init__.py new file mode 100644 index 00000000000..5934e1b6c51 --- /dev/null +++ b/homeassistant/components/directv/__init__.py @@ -0,0 +1 @@ +"""The directv component.""" diff --git a/homeassistant/components/media_player/directv.py b/homeassistant/components/directv/media_player.py similarity index 100% rename from homeassistant/components/media_player/directv.py rename to homeassistant/components/directv/media_player.py diff --git a/homeassistant/components/dsmr/__init__.py b/homeassistant/components/dsmr/__init__.py new file mode 100644 index 00000000000..107e221a465 --- /dev/null +++ b/homeassistant/components/dsmr/__init__.py @@ -0,0 +1 @@ +"""The dsmr component.""" diff --git a/homeassistant/components/sensor/dsmr.py b/homeassistant/components/dsmr/sensor.py similarity index 100% rename from homeassistant/components/sensor/dsmr.py rename to homeassistant/components/dsmr/sensor.py diff --git a/homeassistant/components/dte_energy_bridge/__init__.py b/homeassistant/components/dte_energy_bridge/__init__.py new file mode 100644 index 00000000000..2525d047bce --- /dev/null +++ b/homeassistant/components/dte_energy_bridge/__init__.py @@ -0,0 +1 @@ +"""The dte_energy_bridge component.""" diff --git a/homeassistant/components/sensor/dte_energy_bridge.py b/homeassistant/components/dte_energy_bridge/sensor.py similarity index 100% rename from homeassistant/components/sensor/dte_energy_bridge.py rename to homeassistant/components/dte_energy_bridge/sensor.py diff --git a/homeassistant/components/dwd_weather_warnings/sensor.py b/homeassistant/components/dwd_weather_warnings/sensor.py index 4b7e07d4f3a..9e61c9e3196 100644 --- a/homeassistant/components/dwd_weather_warnings/sensor.py +++ b/homeassistant/components/dwd_weather_warnings/sensor.py @@ -25,7 +25,7 @@ from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_NAME, CONF_MONITORED_CONDITIONS) from homeassistant.util import Throttle import homeassistant.util.dt as dt_util -from homeassistant.components.sensor.rest import RestData +from homeassistant.components.rest.sensor import RestData _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/climate/dyson.py b/homeassistant/components/dyson/climate.py similarity index 100% rename from homeassistant/components/climate/dyson.py rename to homeassistant/components/dyson/climate.py diff --git a/homeassistant/components/fan/dyson.py b/homeassistant/components/dyson/fan.py similarity index 100% rename from homeassistant/components/fan/dyson.py rename to homeassistant/components/dyson/fan.py diff --git a/homeassistant/components/sensor/dyson.py b/homeassistant/components/dyson/sensor.py similarity index 100% rename from homeassistant/components/sensor/dyson.py rename to homeassistant/components/dyson/sensor.py diff --git a/homeassistant/components/vacuum/dyson.py b/homeassistant/components/dyson/vacuum.py similarity index 100% rename from homeassistant/components/vacuum/dyson.py rename to homeassistant/components/dyson/vacuum.py diff --git a/homeassistant/components/ee_brightbox/__init__.py b/homeassistant/components/ee_brightbox/__init__.py new file mode 100644 index 00000000000..bec0886ae07 --- /dev/null +++ b/homeassistant/components/ee_brightbox/__init__.py @@ -0,0 +1 @@ +"""The ee_brightbox component.""" diff --git a/homeassistant/components/device_tracker/ee_brightbox.py b/homeassistant/components/ee_brightbox/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/ee_brightbox.py rename to homeassistant/components/ee_brightbox/device_tracker.py diff --git a/homeassistant/components/efergy/__init__.py b/homeassistant/components/efergy/__init__.py new file mode 100644 index 00000000000..8ceeb1585a4 --- /dev/null +++ b/homeassistant/components/efergy/__init__.py @@ -0,0 +1 @@ +"""The efergy component.""" diff --git a/homeassistant/components/sensor/efergy.py b/homeassistant/components/efergy/sensor.py similarity index 100% rename from homeassistant/components/sensor/efergy.py rename to homeassistant/components/efergy/sensor.py diff --git a/homeassistant/components/entur_public_transport/__init__.py b/homeassistant/components/entur_public_transport/__init__.py new file mode 100644 index 00000000000..7d8e0a18d0b --- /dev/null +++ b/homeassistant/components/entur_public_transport/__init__.py @@ -0,0 +1 @@ +"""The entur_public_transport component.""" diff --git a/homeassistant/components/sensor/entur_public_transport.py b/homeassistant/components/entur_public_transport/sensor.py similarity index 100% rename from homeassistant/components/sensor/entur_public_transport.py rename to homeassistant/components/entur_public_transport/sensor.py diff --git a/homeassistant/components/everlights/__init__.py b/homeassistant/components/everlights/__init__.py new file mode 100644 index 00000000000..0b45bdd430b --- /dev/null +++ b/homeassistant/components/everlights/__init__.py @@ -0,0 +1 @@ +"""The everlights component.""" diff --git a/homeassistant/components/light/everlights.py b/homeassistant/components/everlights/light.py similarity index 100% rename from homeassistant/components/light/everlights.py rename to homeassistant/components/everlights/light.py diff --git a/homeassistant/components/facebox/__init__.py b/homeassistant/components/facebox/__init__.py new file mode 100644 index 00000000000..9e5b6afb10b --- /dev/null +++ b/homeassistant/components/facebox/__init__.py @@ -0,0 +1 @@ +"""The facebox component.""" diff --git a/homeassistant/components/image_processing/facebox.py b/homeassistant/components/facebox/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/facebox.py rename to homeassistant/components/facebox/image_processing.py diff --git a/homeassistant/components/fail2ban/__init__.py b/homeassistant/components/fail2ban/__init__.py new file mode 100644 index 00000000000..cb2716e581d --- /dev/null +++ b/homeassistant/components/fail2ban/__init__.py @@ -0,0 +1 @@ +"""The fail2ban component.""" diff --git a/homeassistant/components/sensor/fail2ban.py b/homeassistant/components/fail2ban/sensor.py similarity index 100% rename from homeassistant/components/sensor/fail2ban.py rename to homeassistant/components/fail2ban/sensor.py diff --git a/homeassistant/components/fido/__init__.py b/homeassistant/components/fido/__init__.py new file mode 100644 index 00000000000..d950d39ef70 --- /dev/null +++ b/homeassistant/components/fido/__init__.py @@ -0,0 +1 @@ +"""The fido component.""" diff --git a/homeassistant/components/sensor/fido.py b/homeassistant/components/fido/sensor.py similarity index 100% rename from homeassistant/components/sensor/fido.py rename to homeassistant/components/fido/sensor.py diff --git a/homeassistant/components/file/__init__.py b/homeassistant/components/file/__init__.py new file mode 100644 index 00000000000..ed31fa957dd --- /dev/null +++ b/homeassistant/components/file/__init__.py @@ -0,0 +1 @@ +"""The file component.""" diff --git a/homeassistant/components/sensor/file.py b/homeassistant/components/file/sensor.py similarity index 100% rename from homeassistant/components/sensor/file.py rename to homeassistant/components/file/sensor.py diff --git a/homeassistant/components/filesize/__init__.py b/homeassistant/components/filesize/__init__.py new file mode 100644 index 00000000000..c532274997c --- /dev/null +++ b/homeassistant/components/filesize/__init__.py @@ -0,0 +1 @@ +"""The filesize component.""" diff --git a/homeassistant/components/sensor/filesize.py b/homeassistant/components/filesize/sensor.py similarity index 100% rename from homeassistant/components/sensor/filesize.py rename to homeassistant/components/filesize/sensor.py diff --git a/homeassistant/components/filter/__init__.py b/homeassistant/components/filter/__init__.py new file mode 100644 index 00000000000..ebdcec75abf --- /dev/null +++ b/homeassistant/components/filter/__init__.py @@ -0,0 +1 @@ +"""The filter component.""" diff --git a/homeassistant/components/sensor/filter.py b/homeassistant/components/filter/sensor.py similarity index 100% rename from homeassistant/components/sensor/filter.py rename to homeassistant/components/filter/sensor.py diff --git a/homeassistant/components/flux/__init__.py b/homeassistant/components/flux/__init__.py new file mode 100644 index 00000000000..c9eeda06fa8 --- /dev/null +++ b/homeassistant/components/flux/__init__.py @@ -0,0 +1 @@ +"""The flux component.""" diff --git a/homeassistant/components/switch/flux.py b/homeassistant/components/flux/switch.py similarity index 100% rename from homeassistant/components/switch/flux.py rename to homeassistant/components/flux/switch.py diff --git a/homeassistant/components/folder/__init__.py b/homeassistant/components/folder/__init__.py new file mode 100644 index 00000000000..0b95217fd2a --- /dev/null +++ b/homeassistant/components/folder/__init__.py @@ -0,0 +1 @@ +"""The folder component.""" diff --git a/homeassistant/components/sensor/folder.py b/homeassistant/components/folder/sensor.py similarity index 100% rename from homeassistant/components/sensor/folder.py rename to homeassistant/components/folder/sensor.py diff --git a/homeassistant/components/foobot/__init__.py b/homeassistant/components/foobot/__init__.py new file mode 100644 index 00000000000..92edde9a5e1 --- /dev/null +++ b/homeassistant/components/foobot/__init__.py @@ -0,0 +1 @@ +"""The foobot component.""" diff --git a/homeassistant/components/sensor/foobot.py b/homeassistant/components/foobot/sensor.py similarity index 100% rename from homeassistant/components/sensor/foobot.py rename to homeassistant/components/foobot/sensor.py diff --git a/homeassistant/components/generic/__init__.py b/homeassistant/components/generic/__init__.py new file mode 100644 index 00000000000..28f79fc91e6 --- /dev/null +++ b/homeassistant/components/generic/__init__.py @@ -0,0 +1 @@ +"""The generic component.""" diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/generic/camera.py similarity index 100% rename from homeassistant/components/camera/generic.py rename to homeassistant/components/generic/camera.py diff --git a/homeassistant/components/geo_json_events/__init__.py b/homeassistant/components/geo_json_events/__init__.py new file mode 100644 index 00000000000..0bc612b6e8b --- /dev/null +++ b/homeassistant/components/geo_json_events/__init__.py @@ -0,0 +1 @@ +"""The geo_json_events component.""" diff --git a/homeassistant/components/geo_location/geo_json_events.py b/homeassistant/components/geo_json_events/geo_location.py similarity index 100% rename from homeassistant/components/geo_location/geo_json_events.py rename to homeassistant/components/geo_json_events/geo_location.py diff --git a/homeassistant/components/geo_rss_events/__init__.py b/homeassistant/components/geo_rss_events/__init__.py new file mode 100644 index 00000000000..3a1a9072960 --- /dev/null +++ b/homeassistant/components/geo_rss_events/__init__.py @@ -0,0 +1 @@ +"""The geo_rss_events component.""" diff --git a/homeassistant/components/sensor/geo_rss_events.py b/homeassistant/components/geo_rss_events/sensor.py similarity index 100% rename from homeassistant/components/sensor/geo_rss_events.py rename to homeassistant/components/geo_rss_events/sensor.py diff --git a/homeassistant/components/google_wifi/__init__.py b/homeassistant/components/google_wifi/__init__.py new file mode 100644 index 00000000000..a12bd9d4b8c --- /dev/null +++ b/homeassistant/components/google_wifi/__init__.py @@ -0,0 +1 @@ +"""The google_wifi component.""" diff --git a/homeassistant/components/sensor/google_wifi.py b/homeassistant/components/google_wifi/sensor.py similarity index 100% rename from homeassistant/components/sensor/google_wifi.py rename to homeassistant/components/google_wifi/sensor.py diff --git a/homeassistant/components/hddtemp/__init__.py b/homeassistant/components/hddtemp/__init__.py new file mode 100644 index 00000000000..121238df9fe --- /dev/null +++ b/homeassistant/components/hddtemp/__init__.py @@ -0,0 +1 @@ +"""The hddtemp component.""" diff --git a/homeassistant/components/sensor/hddtemp.py b/homeassistant/components/hddtemp/sensor.py similarity index 100% rename from homeassistant/components/sensor/hddtemp.py rename to homeassistant/components/hddtemp/sensor.py diff --git a/homeassistant/components/history_stats/__init__.py b/homeassistant/components/history_stats/__init__.py new file mode 100644 index 00000000000..3c5385be6ad --- /dev/null +++ b/homeassistant/components/history_stats/__init__.py @@ -0,0 +1 @@ +"""The history_stats component.""" diff --git a/homeassistant/components/sensor/history_stats.py b/homeassistant/components/history_stats/sensor.py similarity index 100% rename from homeassistant/components/sensor/history_stats.py rename to homeassistant/components/history_stats/sensor.py diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py new file mode 100644 index 00000000000..59a90711e57 --- /dev/null +++ b/homeassistant/components/honeywell/__init__.py @@ -0,0 +1 @@ +"""Support for Honeywell Round Connected and Honeywell Evohome thermostats.""" diff --git a/homeassistant/components/climate/honeywell.py b/homeassistant/components/honeywell/climate.py similarity index 100% rename from homeassistant/components/climate/honeywell.py rename to homeassistant/components/honeywell/climate.py diff --git a/homeassistant/components/hydroquebec/__init__.py b/homeassistant/components/hydroquebec/__init__.py new file mode 100644 index 00000000000..08a12f7955e --- /dev/null +++ b/homeassistant/components/hydroquebec/__init__.py @@ -0,0 +1 @@ +"""The hydroquebec component.""" diff --git a/homeassistant/components/sensor/hydroquebec.py b/homeassistant/components/hydroquebec/sensor.py similarity index 100% rename from homeassistant/components/sensor/hydroquebec.py rename to homeassistant/components/hydroquebec/sensor.py diff --git a/homeassistant/components/imap_email_content/__init__.py b/homeassistant/components/imap_email_content/__init__.py new file mode 100644 index 00000000000..263f57a3a9d --- /dev/null +++ b/homeassistant/components/imap_email_content/__init__.py @@ -0,0 +1 @@ +"""The imap_email_content component.""" diff --git a/homeassistant/components/sensor/imap_email_content.py b/homeassistant/components/imap_email_content/sensor.py similarity index 100% rename from homeassistant/components/sensor/imap_email_content.py rename to homeassistant/components/imap_email_content/sensor.py diff --git a/homeassistant/components/integration/__init__.py b/homeassistant/components/integration/__init__.py new file mode 100644 index 00000000000..4eea25fefe1 --- /dev/null +++ b/homeassistant/components/integration/__init__.py @@ -0,0 +1 @@ +"""The integration component.""" diff --git a/homeassistant/components/sensor/integration.py b/homeassistant/components/integration/sensor.py similarity index 100% rename from homeassistant/components/sensor/integration.py rename to homeassistant/components/integration/sensor.py diff --git a/homeassistant/components/islamic_prayer_times/__init__.py b/homeassistant/components/islamic_prayer_times/__init__.py new file mode 100644 index 00000000000..642c31118bd --- /dev/null +++ b/homeassistant/components/islamic_prayer_times/__init__.py @@ -0,0 +1 @@ +"""The islamic_prayer_times component.""" diff --git a/homeassistant/components/sensor/islamic_prayer_times.py b/homeassistant/components/islamic_prayer_times/sensor.py similarity index 100% rename from homeassistant/components/sensor/islamic_prayer_times.py rename to homeassistant/components/islamic_prayer_times/sensor.py diff --git a/homeassistant/components/jewish_calendar/__init__.py b/homeassistant/components/jewish_calendar/__init__.py new file mode 100644 index 00000000000..93a60e363e1 --- /dev/null +++ b/homeassistant/components/jewish_calendar/__init__.py @@ -0,0 +1 @@ +"""The jewish_calendar component.""" diff --git a/homeassistant/components/sensor/jewish_calendar.py b/homeassistant/components/jewish_calendar/sensor.py similarity index 100% rename from homeassistant/components/sensor/jewish_calendar.py rename to homeassistant/components/jewish_calendar/sensor.py diff --git a/homeassistant/components/light/litejet.py b/homeassistant/components/litejet/light.py similarity index 100% rename from homeassistant/components/light/litejet.py rename to homeassistant/components/litejet/light.py diff --git a/homeassistant/components/scene/litejet.py b/homeassistant/components/litejet/scene.py similarity index 100% rename from homeassistant/components/scene/litejet.py rename to homeassistant/components/litejet/scene.py diff --git a/homeassistant/components/switch/litejet.py b/homeassistant/components/litejet/switch.py similarity index 100% rename from homeassistant/components/switch/litejet.py rename to homeassistant/components/litejet/switch.py diff --git a/homeassistant/components/local_file/__init__.py b/homeassistant/components/local_file/__init__.py new file mode 100644 index 00000000000..4ad752bbc54 --- /dev/null +++ b/homeassistant/components/local_file/__init__.py @@ -0,0 +1 @@ +"""The local_file component.""" diff --git a/homeassistant/components/camera/local_file.py b/homeassistant/components/local_file/camera.py similarity index 100% rename from homeassistant/components/camera/local_file.py rename to homeassistant/components/local_file/camera.py diff --git a/homeassistant/components/london_air/__init__.py b/homeassistant/components/london_air/__init__.py new file mode 100644 index 00000000000..0f93dc83e27 --- /dev/null +++ b/homeassistant/components/london_air/__init__.py @@ -0,0 +1 @@ +"""The london_air component.""" diff --git a/homeassistant/components/sensor/london_air.py b/homeassistant/components/london_air/sensor.py similarity index 100% rename from homeassistant/components/sensor/london_air.py rename to homeassistant/components/london_air/sensor.py diff --git a/homeassistant/components/manual/__init__.py b/homeassistant/components/manual/__init__.py new file mode 100644 index 00000000000..64f6961e3ea --- /dev/null +++ b/homeassistant/components/manual/__init__.py @@ -0,0 +1 @@ +"""The manual component.""" diff --git a/homeassistant/components/manual_mqtt/__init__.py b/homeassistant/components/manual_mqtt/__init__.py new file mode 100644 index 00000000000..bdd0d5c09e4 --- /dev/null +++ b/homeassistant/components/manual_mqtt/__init__.py @@ -0,0 +1 @@ +"""The manual_mqtt component.""" diff --git a/homeassistant/components/climate/melissa.py b/homeassistant/components/melissa/climate.py similarity index 100% rename from homeassistant/components/climate/melissa.py rename to homeassistant/components/melissa/climate.py diff --git a/homeassistant/components/meraki/__init__.py b/homeassistant/components/meraki/__init__.py new file mode 100644 index 00000000000..ad9cf4abcaf --- /dev/null +++ b/homeassistant/components/meraki/__init__.py @@ -0,0 +1 @@ +"""The meraki component.""" diff --git a/homeassistant/components/device_tracker/meraki.py b/homeassistant/components/meraki/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/meraki.py rename to homeassistant/components/meraki/device_tracker.py diff --git a/homeassistant/components/mfi/__init__.py b/homeassistant/components/mfi/__init__.py new file mode 100644 index 00000000000..de354dfbc37 --- /dev/null +++ b/homeassistant/components/mfi/__init__.py @@ -0,0 +1 @@ +"""The mfi component.""" diff --git a/homeassistant/components/sensor/mfi.py b/homeassistant/components/mfi/sensor.py similarity index 100% rename from homeassistant/components/sensor/mfi.py rename to homeassistant/components/mfi/sensor.py diff --git a/homeassistant/components/switch/mfi.py b/homeassistant/components/mfi/switch.py similarity index 100% rename from homeassistant/components/switch/mfi.py rename to homeassistant/components/mfi/switch.py diff --git a/homeassistant/components/mhz19/__init__.py b/homeassistant/components/mhz19/__init__.py new file mode 100644 index 00000000000..5fa9bbb69e8 --- /dev/null +++ b/homeassistant/components/mhz19/__init__.py @@ -0,0 +1 @@ +"""The mhz19 component.""" diff --git a/homeassistant/components/sensor/mhz19.py b/homeassistant/components/mhz19/sensor.py similarity index 100% rename from homeassistant/components/sensor/mhz19.py rename to homeassistant/components/mhz19/sensor.py diff --git a/homeassistant/components/microsoft_face_detect/__init__.py b/homeassistant/components/microsoft_face_detect/__init__.py new file mode 100644 index 00000000000..897a367a101 --- /dev/null +++ b/homeassistant/components/microsoft_face_detect/__init__.py @@ -0,0 +1 @@ +"""The microsoft_face_detect component.""" diff --git a/homeassistant/components/image_processing/microsoft_face_detect.py b/homeassistant/components/microsoft_face_detect/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/microsoft_face_detect.py rename to homeassistant/components/microsoft_face_detect/image_processing.py diff --git a/homeassistant/components/microsoft_face_identify/__init__.py b/homeassistant/components/microsoft_face_identify/__init__.py new file mode 100644 index 00000000000..cd402d0fef1 --- /dev/null +++ b/homeassistant/components/microsoft_face_identify/__init__.py @@ -0,0 +1 @@ +"""The microsoft_face_identify component.""" diff --git a/homeassistant/components/image_processing/microsoft_face_identify.py b/homeassistant/components/microsoft_face_identify/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/microsoft_face_identify.py rename to homeassistant/components/microsoft_face_identify/image_processing.py diff --git a/homeassistant/components/min_max/__init__.py b/homeassistant/components/min_max/__init__.py new file mode 100644 index 00000000000..d13d963ff47 --- /dev/null +++ b/homeassistant/components/min_max/__init__.py @@ -0,0 +1 @@ +"""The min_max component.""" diff --git a/homeassistant/components/sensor/min_max.py b/homeassistant/components/min_max/sensor.py similarity index 100% rename from homeassistant/components/sensor/min_max.py rename to homeassistant/components/min_max/sensor.py diff --git a/homeassistant/components/monoprice/__init__.py b/homeassistant/components/monoprice/__init__.py new file mode 100644 index 00000000000..d968e270390 --- /dev/null +++ b/homeassistant/components/monoprice/__init__.py @@ -0,0 +1 @@ +"""The monoprice component.""" diff --git a/homeassistant/components/media_player/monoprice.py b/homeassistant/components/monoprice/media_player.py similarity index 100% rename from homeassistant/components/media_player/monoprice.py rename to homeassistant/components/monoprice/media_player.py diff --git a/homeassistant/components/moon/__init__.py b/homeassistant/components/moon/__init__.py new file mode 100644 index 00000000000..f7049608dda --- /dev/null +++ b/homeassistant/components/moon/__init__.py @@ -0,0 +1 @@ +"""The moon component.""" diff --git a/homeassistant/components/sensor/moon.py b/homeassistant/components/moon/sensor.py similarity index 100% rename from homeassistant/components/sensor/moon.py rename to homeassistant/components/moon/sensor.py diff --git a/homeassistant/components/mqtt_json/__init__.py b/homeassistant/components/mqtt_json/__init__.py new file mode 100644 index 00000000000..49014ea0f96 --- /dev/null +++ b/homeassistant/components/mqtt_json/__init__.py @@ -0,0 +1 @@ +"""The mqtt_json component.""" diff --git a/homeassistant/components/device_tracker/mqtt_json.py b/homeassistant/components/mqtt_json/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/mqtt_json.py rename to homeassistant/components/mqtt_json/device_tracker.py diff --git a/homeassistant/components/mqtt_room/__init__.py b/homeassistant/components/mqtt_room/__init__.py new file mode 100644 index 00000000000..77526ded34f --- /dev/null +++ b/homeassistant/components/mqtt_room/__init__.py @@ -0,0 +1 @@ +"""The mqtt_room component.""" diff --git a/homeassistant/components/sensor/mqtt_room.py b/homeassistant/components/mqtt_room/sensor.py similarity index 100% rename from homeassistant/components/sensor/mqtt_room.py rename to homeassistant/components/mqtt_room/sensor.py diff --git a/homeassistant/components/nsw_fuel_station/__init__.py b/homeassistant/components/nsw_fuel_station/__init__.py new file mode 100644 index 00000000000..88ff1e779be --- /dev/null +++ b/homeassistant/components/nsw_fuel_station/__init__.py @@ -0,0 +1 @@ +"""The nsw_fuel_station component.""" diff --git a/homeassistant/components/sensor/nsw_fuel_station.py b/homeassistant/components/nsw_fuel_station/sensor.py similarity index 100% rename from homeassistant/components/sensor/nsw_fuel_station.py rename to homeassistant/components/nsw_fuel_station/sensor.py diff --git a/homeassistant/components/nsw_rural_fire_service_feed/__init__.py b/homeassistant/components/nsw_rural_fire_service_feed/__init__.py new file mode 100644 index 00000000000..b54959f80ce --- /dev/null +++ b/homeassistant/components/nsw_rural_fire_service_feed/__init__.py @@ -0,0 +1 @@ +"""The nsw_rural_fire_service_feed component.""" diff --git a/homeassistant/components/geo_location/nsw_rural_fire_service_feed.py b/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py similarity index 100% rename from homeassistant/components/geo_location/nsw_rural_fire_service_feed.py rename to homeassistant/components/nsw_rural_fire_service_feed/geo_location.py diff --git a/homeassistant/components/climate/nuheat.py b/homeassistant/components/nuheat/climate.py similarity index 100% rename from homeassistant/components/climate/nuheat.py rename to homeassistant/components/nuheat/climate.py diff --git a/homeassistant/components/nx584/__init__.py b/homeassistant/components/nx584/__init__.py new file mode 100644 index 00000000000..95bd0b4e9b4 --- /dev/null +++ b/homeassistant/components/nx584/__init__.py @@ -0,0 +1 @@ +"""Support for NX584 alarm control panels.""" diff --git a/homeassistant/components/binary_sensor/nx584.py b/homeassistant/components/nx584/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/nx584.py rename to homeassistant/components/nx584/binary_sensor.py diff --git a/homeassistant/components/openalpr_cloud/__init__.py b/homeassistant/components/openalpr_cloud/__init__.py new file mode 100644 index 00000000000..a8a104ed3be --- /dev/null +++ b/homeassistant/components/openalpr_cloud/__init__.py @@ -0,0 +1 @@ +"""The openalpr_cloud component.""" diff --git a/homeassistant/components/image_processing/openalpr_cloud.py b/homeassistant/components/openalpr_cloud/image_processing.py similarity index 98% rename from homeassistant/components/image_processing/openalpr_cloud.py rename to homeassistant/components/openalpr_cloud/image_processing.py index dea55b76025..c983a945548 100644 --- a/homeassistant/components/image_processing/openalpr_cloud.py +++ b/homeassistant/components/openalpr_cloud/image_processing.py @@ -17,7 +17,7 @@ from homeassistant.core import split_entity_id from homeassistant.const import CONF_API_KEY from homeassistant.components.image_processing import ( PLATFORM_SCHEMA, CONF_CONFIDENCE, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) -from homeassistant.components.image_processing.openalpr_local import ( +from homeassistant.components.openalpr_local.image_processing import ( ImageProcessingAlprEntity) from homeassistant.helpers.aiohttp_client import async_get_clientsession diff --git a/homeassistant/components/openalpr_local/__init__.py b/homeassistant/components/openalpr_local/__init__.py new file mode 100644 index 00000000000..436f15baeeb --- /dev/null +++ b/homeassistant/components/openalpr_local/__init__.py @@ -0,0 +1 @@ +"""The openalpr_local component.""" diff --git a/homeassistant/components/image_processing/openalpr_local.py b/homeassistant/components/openalpr_local/image_processing.py similarity index 100% rename from homeassistant/components/image_processing/openalpr_local.py rename to homeassistant/components/openalpr_local/image_processing.py diff --git a/homeassistant/components/openhardwaremonitor/__init__.py b/homeassistant/components/openhardwaremonitor/__init__.py new file mode 100644 index 00000000000..498bb3f11cd --- /dev/null +++ b/homeassistant/components/openhardwaremonitor/__init__.py @@ -0,0 +1 @@ +"""The openhardwaremonitor component.""" diff --git a/homeassistant/components/sensor/openhardwaremonitor.py b/homeassistant/components/openhardwaremonitor/sensor.py similarity index 100% rename from homeassistant/components/sensor/openhardwaremonitor.py rename to homeassistant/components/openhardwaremonitor/sensor.py diff --git a/homeassistant/components/push/__init__.py b/homeassistant/components/push/__init__.py new file mode 100644 index 00000000000..81ace3bf398 --- /dev/null +++ b/homeassistant/components/push/__init__.py @@ -0,0 +1 @@ +"""The push component.""" diff --git a/homeassistant/components/camera/push.py b/homeassistant/components/push/camera.py similarity index 100% rename from homeassistant/components/camera/push.py rename to homeassistant/components/push/camera.py diff --git a/homeassistant/components/pvoutput/sensor.py b/homeassistant/components/pvoutput/sensor.py index a12093099c4..dbcd38af3cc 100644 --- a/homeassistant/components/pvoutput/sensor.py +++ b/homeassistant/components/pvoutput/sensor.py @@ -13,7 +13,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.components.sensor.rest import RestData +from homeassistant.components.rest.sensor import RestData from homeassistant.const import ( ATTR_TEMPERATURE, CONF_API_KEY, CONF_NAME, ATTR_DATE, ATTR_TIME, ATTR_VOLTAGE) diff --git a/homeassistant/components/radarr/__init__.py b/homeassistant/components/radarr/__init__.py new file mode 100644 index 00000000000..24377725bfc --- /dev/null +++ b/homeassistant/components/radarr/__init__.py @@ -0,0 +1 @@ +"""The radarr component.""" diff --git a/homeassistant/components/sensor/radarr.py b/homeassistant/components/radarr/sensor.py similarity index 100% rename from homeassistant/components/sensor/radarr.py rename to homeassistant/components/radarr/sensor.py diff --git a/homeassistant/components/random/__init__.py b/homeassistant/components/random/__init__.py new file mode 100644 index 00000000000..01bde80b0c3 --- /dev/null +++ b/homeassistant/components/random/__init__.py @@ -0,0 +1 @@ +"""The random component.""" diff --git a/homeassistant/components/binary_sensor/random.py b/homeassistant/components/random/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/random.py rename to homeassistant/components/random/binary_sensor.py diff --git a/homeassistant/components/sensor/random.py b/homeassistant/components/random/sensor.py similarity index 100% rename from homeassistant/components/sensor/random.py rename to homeassistant/components/random/sensor.py diff --git a/homeassistant/components/rest/__init__.py b/homeassistant/components/rest/__init__.py new file mode 100644 index 00000000000..fcdf39e8398 --- /dev/null +++ b/homeassistant/components/rest/__init__.py @@ -0,0 +1 @@ +"""The rest component.""" diff --git a/homeassistant/components/binary_sensor/rest.py b/homeassistant/components/rest/binary_sensor.py similarity index 98% rename from homeassistant/components/binary_sensor/rest.py rename to homeassistant/components/rest/binary_sensor.py index 304ed701148..1599d5bce3e 100644 --- a/homeassistant/components/binary_sensor/rest.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -11,7 +11,7 @@ from requests.auth import HTTPBasicAuth, HTTPDigestAuth from homeassistant.components.binary_sensor import ( BinarySensorDevice, DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA) -from homeassistant.components.sensor.rest import RestData +from homeassistant.components.rest.sensor import RestData from homeassistant.const import ( CONF_PAYLOAD, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_METHOD, CONF_RESOURCE, CONF_VERIFY_SSL, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT, diff --git a/homeassistant/components/sensor/rest.py b/homeassistant/components/rest/sensor.py similarity index 100% rename from homeassistant/components/sensor/rest.py rename to homeassistant/components/rest/sensor.py diff --git a/homeassistant/components/switch/rest.py b/homeassistant/components/rest/switch.py similarity index 100% rename from homeassistant/components/switch/rest.py rename to homeassistant/components/rest/switch.py diff --git a/homeassistant/components/binary_sensor/rflink.py b/homeassistant/components/rflink/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/rflink.py rename to homeassistant/components/rflink/binary_sensor.py diff --git a/homeassistant/components/cover/rflink.py b/homeassistant/components/rflink/cover.py similarity index 100% rename from homeassistant/components/cover/rflink.py rename to homeassistant/components/rflink/cover.py diff --git a/homeassistant/components/light/rflink.py b/homeassistant/components/rflink/light.py similarity index 100% rename from homeassistant/components/light/rflink.py rename to homeassistant/components/rflink/light.py diff --git a/homeassistant/components/sensor/rflink.py b/homeassistant/components/rflink/sensor.py similarity index 100% rename from homeassistant/components/sensor/rflink.py rename to homeassistant/components/rflink/sensor.py diff --git a/homeassistant/components/switch/rflink.py b/homeassistant/components/rflink/switch.py similarity index 100% rename from homeassistant/components/switch/rflink.py rename to homeassistant/components/rflink/switch.py diff --git a/homeassistant/components/binary_sensor/ring.py b/homeassistant/components/ring/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/ring.py rename to homeassistant/components/ring/binary_sensor.py diff --git a/homeassistant/components/sensor/ring.py b/homeassistant/components/ring/sensor.py similarity index 100% rename from homeassistant/components/sensor/ring.py rename to homeassistant/components/ring/sensor.py diff --git a/homeassistant/components/rmvtransport/__init__.py b/homeassistant/components/rmvtransport/__init__.py new file mode 100644 index 00000000000..abaee33e926 --- /dev/null +++ b/homeassistant/components/rmvtransport/__init__.py @@ -0,0 +1 @@ +"""The rmvtransport component.""" diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/rmvtransport/sensor.py similarity index 100% rename from homeassistant/components/sensor/rmvtransport.py rename to homeassistant/components/rmvtransport/sensor.py diff --git a/homeassistant/components/samsungtv/__init__.py b/homeassistant/components/samsungtv/__init__.py new file mode 100644 index 00000000000..e43ea1ba984 --- /dev/null +++ b/homeassistant/components/samsungtv/__init__.py @@ -0,0 +1 @@ +"""The samsungtv component.""" diff --git a/homeassistant/components/media_player/samsungtv.py b/homeassistant/components/samsungtv/media_player.py similarity index 100% rename from homeassistant/components/media_player/samsungtv.py rename to homeassistant/components/samsungtv/media_player.py diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 70dfae392be..a6d16852df3 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -10,7 +10,7 @@ import voluptuous as vol from requests.auth import HTTPBasicAuth, HTTPDigestAuth from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.components.sensor.rest import RestData +from homeassistant.components.rest.sensor import RestData from homeassistant.const import ( CONF_NAME, CONF_RESOURCE, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE, CONF_VERIFY_SSL, CONF_USERNAME, CONF_HEADERS, diff --git a/homeassistant/components/season/__init__.py b/homeassistant/components/season/__init__.py new file mode 100644 index 00000000000..095a4704b12 --- /dev/null +++ b/homeassistant/components/season/__init__.py @@ -0,0 +1 @@ +"""The season component.""" diff --git a/homeassistant/components/sensor/season.py b/homeassistant/components/season/sensor.py similarity index 100% rename from homeassistant/components/sensor/season.py rename to homeassistant/components/season/sensor.py diff --git a/homeassistant/components/sigfox/__init__.py b/homeassistant/components/sigfox/__init__.py new file mode 100644 index 00000000000..ead4ea997f9 --- /dev/null +++ b/homeassistant/components/sigfox/__init__.py @@ -0,0 +1 @@ +"""The sigfox component.""" diff --git a/homeassistant/components/sensor/sigfox.py b/homeassistant/components/sigfox/sensor.py similarity index 100% rename from homeassistant/components/sensor/sigfox.py rename to homeassistant/components/sigfox/sensor.py diff --git a/homeassistant/components/simulated/__init__.py b/homeassistant/components/simulated/__init__.py new file mode 100644 index 00000000000..35c6d106d03 --- /dev/null +++ b/homeassistant/components/simulated/__init__.py @@ -0,0 +1 @@ +"""The simulated component.""" diff --git a/homeassistant/components/sensor/simulated.py b/homeassistant/components/simulated/sensor.py similarity index 100% rename from homeassistant/components/sensor/simulated.py rename to homeassistant/components/simulated/sensor.py diff --git a/homeassistant/components/binary_sensor/sleepiq.py b/homeassistant/components/sleepiq/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/sleepiq.py rename to homeassistant/components/sleepiq/binary_sensor.py diff --git a/homeassistant/components/sensor/sleepiq.py b/homeassistant/components/sleepiq/sensor.py similarity index 100% rename from homeassistant/components/sensor/sleepiq.py rename to homeassistant/components/sleepiq/sensor.py diff --git a/homeassistant/components/sonarr/__init__.py b/homeassistant/components/sonarr/__init__.py new file mode 100644 index 00000000000..63c194cc969 --- /dev/null +++ b/homeassistant/components/sonarr/__init__.py @@ -0,0 +1 @@ +"""The sonarr component.""" diff --git a/homeassistant/components/sensor/sonarr.py b/homeassistant/components/sonarr/sensor.py similarity index 100% rename from homeassistant/components/sensor/sonarr.py rename to homeassistant/components/sonarr/sensor.py diff --git a/homeassistant/components/soundtouch/__init__.py b/homeassistant/components/soundtouch/__init__.py new file mode 100644 index 00000000000..6cd3c88fefc --- /dev/null +++ b/homeassistant/components/soundtouch/__init__.py @@ -0,0 +1 @@ +"""The soundtouch component.""" diff --git a/homeassistant/components/media_player/soundtouch.py b/homeassistant/components/soundtouch/media_player.py similarity index 100% rename from homeassistant/components/media_player/soundtouch.py rename to homeassistant/components/soundtouch/media_player.py diff --git a/homeassistant/components/sql/__init__.py b/homeassistant/components/sql/__init__.py new file mode 100644 index 00000000000..ae354f85adb --- /dev/null +++ b/homeassistant/components/sql/__init__.py @@ -0,0 +1 @@ +"""The sql component.""" diff --git a/homeassistant/components/sensor/sql.py b/homeassistant/components/sql/sensor.py similarity index 100% rename from homeassistant/components/sensor/sql.py rename to homeassistant/components/sql/sensor.py diff --git a/homeassistant/components/srp_energy/__init__.py b/homeassistant/components/srp_energy/__init__.py new file mode 100644 index 00000000000..71e04d7b8c9 --- /dev/null +++ b/homeassistant/components/srp_energy/__init__.py @@ -0,0 +1 @@ +"""The srp_energy component.""" diff --git a/homeassistant/components/sensor/srp_energy.py b/homeassistant/components/srp_energy/sensor.py similarity index 100% rename from homeassistant/components/sensor/srp_energy.py rename to homeassistant/components/srp_energy/sensor.py diff --git a/homeassistant/components/startca/__init__.py b/homeassistant/components/startca/__init__.py new file mode 100644 index 00000000000..aca4a424a36 --- /dev/null +++ b/homeassistant/components/startca/__init__.py @@ -0,0 +1 @@ +"""The startca component.""" diff --git a/homeassistant/components/sensor/startca.py b/homeassistant/components/startca/sensor.py similarity index 100% rename from homeassistant/components/sensor/startca.py rename to homeassistant/components/startca/sensor.py diff --git a/homeassistant/components/statistics/__init__.py b/homeassistant/components/statistics/__init__.py new file mode 100644 index 00000000000..3f0f03b4909 --- /dev/null +++ b/homeassistant/components/statistics/__init__.py @@ -0,0 +1 @@ +"""The statistics component.""" diff --git a/homeassistant/components/sensor/statistics.py b/homeassistant/components/statistics/sensor.py similarity index 100% rename from homeassistant/components/sensor/statistics.py rename to homeassistant/components/statistics/sensor.py diff --git a/homeassistant/components/tcp/__init__.py b/homeassistant/components/tcp/__init__.py new file mode 100644 index 00000000000..614f637a71a --- /dev/null +++ b/homeassistant/components/tcp/__init__.py @@ -0,0 +1 @@ +"""The tcp component.""" diff --git a/homeassistant/components/binary_sensor/tcp.py b/homeassistant/components/tcp/binary_sensor.py similarity index 94% rename from homeassistant/components/binary_sensor/tcp.py rename to homeassistant/components/tcp/binary_sensor.py index 764b6829c91..4a12febd871 100644 --- a/homeassistant/components/binary_sensor/tcp.py +++ b/homeassistant/components/tcp/binary_sensor.py @@ -7,7 +7,7 @@ https://home-assistant.io/components/binary_sensor.tcp/ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.sensor.tcp import ( +from homeassistant.components.tcp.sensor import ( TcpSensor, CONF_VALUE_ON, PLATFORM_SCHEMA) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sensor/tcp.py b/homeassistant/components/tcp/sensor.py similarity index 100% rename from homeassistant/components/sensor/tcp.py rename to homeassistant/components/tcp/sensor.py diff --git a/homeassistant/components/teksavvy/__init__.py b/homeassistant/components/teksavvy/__init__.py new file mode 100644 index 00000000000..ee0dcd1c810 --- /dev/null +++ b/homeassistant/components/teksavvy/__init__.py @@ -0,0 +1 @@ +"""The teksavvy component.""" diff --git a/homeassistant/components/sensor/teksavvy.py b/homeassistant/components/teksavvy/sensor.py similarity index 100% rename from homeassistant/components/sensor/teksavvy.py rename to homeassistant/components/teksavvy/sensor.py diff --git a/homeassistant/components/template/__init__.py b/homeassistant/components/template/__init__.py new file mode 100644 index 00000000000..0c205a0196c --- /dev/null +++ b/homeassistant/components/template/__init__.py @@ -0,0 +1 @@ +"""The template component.""" diff --git a/homeassistant/components/binary_sensor/template.py b/homeassistant/components/template/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/template.py rename to homeassistant/components/template/binary_sensor.py diff --git a/homeassistant/components/cover/template.py b/homeassistant/components/template/cover.py similarity index 100% rename from homeassistant/components/cover/template.py rename to homeassistant/components/template/cover.py diff --git a/homeassistant/components/fan/template.py b/homeassistant/components/template/fan.py similarity index 100% rename from homeassistant/components/fan/template.py rename to homeassistant/components/template/fan.py diff --git a/homeassistant/components/light/template.py b/homeassistant/components/template/light.py similarity index 100% rename from homeassistant/components/light/template.py rename to homeassistant/components/template/light.py diff --git a/homeassistant/components/lock/template.py b/homeassistant/components/template/lock.py similarity index 100% rename from homeassistant/components/lock/template.py rename to homeassistant/components/template/lock.py diff --git a/homeassistant/components/sensor/template.py b/homeassistant/components/template/sensor.py similarity index 100% rename from homeassistant/components/sensor/template.py rename to homeassistant/components/template/sensor.py diff --git a/homeassistant/components/switch/template.py b/homeassistant/components/template/switch.py similarity index 100% rename from homeassistant/components/switch/template.py rename to homeassistant/components/template/switch.py diff --git a/homeassistant/components/threshold/__init__.py b/homeassistant/components/threshold/__init__.py new file mode 100644 index 00000000000..98ebdcd8418 --- /dev/null +++ b/homeassistant/components/threshold/__init__.py @@ -0,0 +1 @@ +"""The threshold component.""" diff --git a/homeassistant/components/binary_sensor/threshold.py b/homeassistant/components/threshold/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/threshold.py rename to homeassistant/components/threshold/binary_sensor.py diff --git a/homeassistant/components/time_date/__init__.py b/homeassistant/components/time_date/__init__.py new file mode 100644 index 00000000000..25e6fa14f39 --- /dev/null +++ b/homeassistant/components/time_date/__init__.py @@ -0,0 +1 @@ +"""The time_date component.""" diff --git a/homeassistant/components/sensor/time_date.py b/homeassistant/components/time_date/sensor.py similarity index 100% rename from homeassistant/components/sensor/time_date.py rename to homeassistant/components/time_date/sensor.py diff --git a/homeassistant/components/tod/__init__.py b/homeassistant/components/tod/__init__.py new file mode 100644 index 00000000000..fa15326becb --- /dev/null +++ b/homeassistant/components/tod/__init__.py @@ -0,0 +1 @@ +"""The tod component.""" diff --git a/homeassistant/components/binary_sensor/tod.py b/homeassistant/components/tod/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/tod.py rename to homeassistant/components/tod/binary_sensor.py diff --git a/homeassistant/components/tomato/__init__.py b/homeassistant/components/tomato/__init__.py new file mode 100644 index 00000000000..e8a67f7e3bc --- /dev/null +++ b/homeassistant/components/tomato/__init__.py @@ -0,0 +1 @@ +"""The tomato component.""" diff --git a/homeassistant/components/device_tracker/tomato.py b/homeassistant/components/tomato/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/tomato.py rename to homeassistant/components/tomato/device_tracker.py diff --git a/homeassistant/components/device_tracker/tplink.py b/homeassistant/components/tplink/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/tplink.py rename to homeassistant/components/tplink/device_tracker.py diff --git a/homeassistant/components/transport_nsw/__init__.py b/homeassistant/components/transport_nsw/__init__.py new file mode 100644 index 00000000000..679c2f3944c --- /dev/null +++ b/homeassistant/components/transport_nsw/__init__.py @@ -0,0 +1 @@ +"""The transport_nsw component.""" diff --git a/homeassistant/components/sensor/transport_nsw.py b/homeassistant/components/transport_nsw/sensor.py similarity index 100% rename from homeassistant/components/sensor/transport_nsw.py rename to homeassistant/components/transport_nsw/sensor.py diff --git a/homeassistant/components/trend/__init__.py b/homeassistant/components/trend/__init__.py new file mode 100644 index 00000000000..77159060263 --- /dev/null +++ b/homeassistant/components/trend/__init__.py @@ -0,0 +1 @@ +"""A sensor that monitors trends in other components.""" diff --git a/homeassistant/components/binary_sensor/trend.py b/homeassistant/components/trend/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/trend.py rename to homeassistant/components/trend/binary_sensor.py diff --git a/homeassistant/components/uk_transport/__init__.py b/homeassistant/components/uk_transport/__init__.py new file mode 100644 index 00000000000..b02a6bf3f64 --- /dev/null +++ b/homeassistant/components/uk_transport/__init__.py @@ -0,0 +1 @@ +"""The uk_transport component.""" diff --git a/homeassistant/components/sensor/uk_transport.py b/homeassistant/components/uk_transport/sensor.py similarity index 100% rename from homeassistant/components/sensor/uk_transport.py rename to homeassistant/components/uk_transport/sensor.py diff --git a/homeassistant/components/device_tracker/unifi.py b/homeassistant/components/unifi/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/unifi.py rename to homeassistant/components/unifi/device_tracker.py diff --git a/homeassistant/components/unifi_direct/__init__.py b/homeassistant/components/unifi_direct/__init__.py new file mode 100644 index 00000000000..a73c6be43d4 --- /dev/null +++ b/homeassistant/components/unifi_direct/__init__.py @@ -0,0 +1 @@ +"""The unifi_direct component.""" diff --git a/homeassistant/components/device_tracker/unifi_direct.py b/homeassistant/components/unifi_direct/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/unifi_direct.py rename to homeassistant/components/unifi_direct/device_tracker.py diff --git a/homeassistant/components/universal/__init__.py b/homeassistant/components/universal/__init__.py new file mode 100644 index 00000000000..b21cd96ad94 --- /dev/null +++ b/homeassistant/components/universal/__init__.py @@ -0,0 +1 @@ +"""The universal component.""" diff --git a/homeassistant/components/media_player/universal.py b/homeassistant/components/universal/media_player.py similarity index 100% rename from homeassistant/components/media_player/universal.py rename to homeassistant/components/universal/media_player.py diff --git a/homeassistant/components/upc_connect/__init__.py b/homeassistant/components/upc_connect/__init__.py new file mode 100644 index 00000000000..1793d6a3856 --- /dev/null +++ b/homeassistant/components/upc_connect/__init__.py @@ -0,0 +1 @@ +"""The upc_connect component.""" diff --git a/homeassistant/components/device_tracker/upc_connect.py b/homeassistant/components/upc_connect/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/upc_connect.py rename to homeassistant/components/upc_connect/device_tracker.py diff --git a/homeassistant/components/uptime/__init__.py b/homeassistant/components/uptime/__init__.py new file mode 100644 index 00000000000..99abc91cdf1 --- /dev/null +++ b/homeassistant/components/uptime/__init__.py @@ -0,0 +1 @@ +"""The uptime component.""" diff --git a/homeassistant/components/sensor/uptime.py b/homeassistant/components/uptime/sensor.py similarity index 100% rename from homeassistant/components/sensor/uptime.py rename to homeassistant/components/uptime/sensor.py diff --git a/homeassistant/components/usgs_earthquakes_feed/__init__.py b/homeassistant/components/usgs_earthquakes_feed/__init__.py new file mode 100644 index 00000000000..a05751e10b7 --- /dev/null +++ b/homeassistant/components/usgs_earthquakes_feed/__init__.py @@ -0,0 +1 @@ +"""The usgs_earthquakes_feed component.""" diff --git a/homeassistant/components/geo_location/usgs_earthquakes_feed.py b/homeassistant/components/usgs_earthquakes_feed/geo_location.py similarity index 100% rename from homeassistant/components/geo_location/usgs_earthquakes_feed.py rename to homeassistant/components/usgs_earthquakes_feed/geo_location.py diff --git a/homeassistant/components/uvc/__init__.py b/homeassistant/components/uvc/__init__.py new file mode 100644 index 00000000000..0d2f64eb0ae --- /dev/null +++ b/homeassistant/components/uvc/__init__.py @@ -0,0 +1 @@ +"""The uvc component.""" diff --git a/homeassistant/components/camera/uvc.py b/homeassistant/components/uvc/camera.py similarity index 100% rename from homeassistant/components/camera/uvc.py rename to homeassistant/components/uvc/camera.py diff --git a/homeassistant/components/version/__init__.py b/homeassistant/components/version/__init__.py new file mode 100644 index 00000000000..eb257007f7c --- /dev/null +++ b/homeassistant/components/version/__init__.py @@ -0,0 +1 @@ +"""The version component.""" diff --git a/homeassistant/components/sensor/version.py b/homeassistant/components/version/sensor.py similarity index 100% rename from homeassistant/components/sensor/version.py rename to homeassistant/components/version/sensor.py diff --git a/homeassistant/components/binary_sensor/vultr.py b/homeassistant/components/vultr/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/vultr.py rename to homeassistant/components/vultr/binary_sensor.py diff --git a/homeassistant/components/sensor/vultr.py b/homeassistant/components/vultr/sensor.py similarity index 100% rename from homeassistant/components/sensor/vultr.py rename to homeassistant/components/vultr/sensor.py diff --git a/homeassistant/components/switch/vultr.py b/homeassistant/components/vultr/switch.py similarity index 100% rename from homeassistant/components/switch/vultr.py rename to homeassistant/components/vultr/switch.py diff --git a/homeassistant/components/switch/wake_on_lan.py b/homeassistant/components/wake_on_lan/switch.py similarity index 100% rename from homeassistant/components/switch/wake_on_lan.py rename to homeassistant/components/wake_on_lan/switch.py diff --git a/homeassistant/components/workday/__init__.py b/homeassistant/components/workday/__init__.py new file mode 100644 index 00000000000..8daef2b3518 --- /dev/null +++ b/homeassistant/components/workday/__init__.py @@ -0,0 +1 @@ +"""Sensor to indicate whether the current day is a workday.""" diff --git a/homeassistant/components/binary_sensor/workday.py b/homeassistant/components/workday/binary_sensor.py similarity index 100% rename from homeassistant/components/binary_sensor/workday.py rename to homeassistant/components/workday/binary_sensor.py diff --git a/homeassistant/components/worldclock/__init__.py b/homeassistant/components/worldclock/__init__.py new file mode 100644 index 00000000000..978eaac8968 --- /dev/null +++ b/homeassistant/components/worldclock/__init__.py @@ -0,0 +1 @@ +"""The worldclock component.""" diff --git a/homeassistant/components/sensor/worldclock.py b/homeassistant/components/worldclock/sensor.py similarity index 100% rename from homeassistant/components/sensor/worldclock.py rename to homeassistant/components/worldclock/sensor.py diff --git a/homeassistant/components/wsdot/__init__.py b/homeassistant/components/wsdot/__init__.py new file mode 100644 index 00000000000..9135f042c62 --- /dev/null +++ b/homeassistant/components/wsdot/__init__.py @@ -0,0 +1 @@ +"""The wsdot component.""" diff --git a/homeassistant/components/sensor/wsdot.py b/homeassistant/components/wsdot/sensor.py similarity index 100% rename from homeassistant/components/sensor/wsdot.py rename to homeassistant/components/wsdot/sensor.py diff --git a/homeassistant/components/wunderground/__init__.py b/homeassistant/components/wunderground/__init__.py new file mode 100644 index 00000000000..faed41fdbea --- /dev/null +++ b/homeassistant/components/wunderground/__init__.py @@ -0,0 +1 @@ +"""The wunderground component.""" diff --git a/homeassistant/components/sensor/wunderground.py b/homeassistant/components/wunderground/sensor.py similarity index 100% rename from homeassistant/components/sensor/wunderground.py rename to homeassistant/components/wunderground/sensor.py diff --git a/homeassistant/components/device_tracker/xiaomi.py b/homeassistant/components/xiaomi/device_tracker.py similarity index 100% rename from homeassistant/components/device_tracker/xiaomi.py rename to homeassistant/components/xiaomi/device_tracker.py diff --git a/homeassistant/components/yamaha/__init__.py b/homeassistant/components/yamaha/__init__.py new file mode 100644 index 00000000000..92a34517ec6 --- /dev/null +++ b/homeassistant/components/yamaha/__init__.py @@ -0,0 +1 @@ +"""The yamaha component.""" diff --git a/homeassistant/components/media_player/yamaha.py b/homeassistant/components/yamaha/media_player.py similarity index 100% rename from homeassistant/components/media_player/yamaha.py rename to homeassistant/components/yamaha/media_player.py diff --git a/homeassistant/components/yr/__init__.py b/homeassistant/components/yr/__init__.py new file mode 100644 index 00000000000..8d33bd56d43 --- /dev/null +++ b/homeassistant/components/yr/__init__.py @@ -0,0 +1 @@ +"""The yr component.""" diff --git a/homeassistant/components/sensor/yr.py b/homeassistant/components/yr/sensor.py similarity index 100% rename from homeassistant/components/sensor/yr.py rename to homeassistant/components/yr/sensor.py diff --git a/homeassistant/components/yweather/__init__.py b/homeassistant/components/yweather/__init__.py new file mode 100644 index 00000000000..0d5012f4c5d --- /dev/null +++ b/homeassistant/components/yweather/__init__.py @@ -0,0 +1 @@ +"""The yweather component.""" diff --git a/homeassistant/components/sensor/yweather.py b/homeassistant/components/yweather/sensor.py similarity index 100% rename from homeassistant/components/sensor/yweather.py rename to homeassistant/components/yweather/sensor.py diff --git a/homeassistant/components/weather/yweather.py b/homeassistant/components/yweather/weather.py similarity index 100% rename from homeassistant/components/weather/yweather.py rename to homeassistant/components/yweather/weather.py diff --git a/requirements_all.txt b/requirements_all.txt index 27a011c8875..f344e1d4670 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -57,13 +57,13 @@ PyNaCl==1.3.0 # homeassistant.auth.mfa_modules.totp PyQRCode==1.2.1 -# homeassistant.components.sensor.rmvtransport +# homeassistant.components.rmvtransport.sensor PyRMVtransport==0.1.3 # homeassistant.components.switchbot.switch # PySwitchbot==0.5 -# homeassistant.components.sensor.transport_nsw +# homeassistant.components.transport_nsw.sensor PyTransportNSW==0.1.1 # homeassistant.components.xiaomi_aqara @@ -102,7 +102,7 @@ aioambient==0.1.3 # homeassistant.components.asuswrt aioasuswrt==1.1.21 -# homeassistant.components.device_tracker.automatic +# homeassistant.components.automatic.device_tracker aioautomatic==0.6.5 # homeassistant.components.dnsip.sensor @@ -268,7 +268,7 @@ btsmarthub_devicelist==0.1.3 # homeassistant.components.buienradar.weather buienradar==0.91 -# homeassistant.components.calendar.caldav +# homeassistant.components.caldav.calendar caldav==0.5.0 # homeassistant.components.cisco_mobility_express.device_tracker @@ -286,7 +286,7 @@ co2signal==0.4.2 # homeassistant.components.coinbase coinbase==2.1.0 -# homeassistant.components.sensor.coinmarketcap +# homeassistant.components.coinmarketcap.sensor coinmarketcap==5.0.3 # homeassistant.scripts.check_config @@ -328,8 +328,8 @@ datapoint==0.4.3 # homeassistant.components.ihc # homeassistant.components.namecheapdns -# homeassistant.components.device_tracker.upc_connect # homeassistant.components.ohmconnect.sensor +# homeassistant.components.upc_connect.device_tracker defusedxml==0.5.0 # homeassistant.components.deluge.sensor @@ -339,7 +339,7 @@ deluge-client==1.4.0 # homeassistant.components.denonavr.media_player denonavr==0.7.8 -# homeassistant.components.media_player.directv +# homeassistant.components.directv.media_player directpy==0.5 # homeassistant.components.discogs.sensor @@ -360,7 +360,7 @@ doorbirdpy==2.0.6 # homeassistant.components.dovado dovado==0.4.1 -# homeassistant.components.sensor.dsmr +# homeassistant.components.dsmr.sensor dsmr_parser==0.12 # homeassistant.components.dweet @@ -376,7 +376,7 @@ ecoaliface==0.4.0 # homeassistant.components.edp_redy edp_redy==0.0.3 -# homeassistant.components.device_tracker.ee_brightbox +# homeassistant.components.ee_brightbox.device_tracker eebrightbox==0.0.4 # homeassistant.components.eliqonline.sensor @@ -391,7 +391,7 @@ emulated_roku==0.1.8 # homeassistant.components.enocean enocean==0.40 -# homeassistant.components.sensor.entur_public_transport +# homeassistant.components.entur_public_transport.sensor enturclient==0.1.3 # homeassistant.components.envirophat.sensor @@ -400,7 +400,7 @@ enturclient==0.1.3 # homeassistant.components.enphase_envoy.sensor envoy_reader==0.3 -# homeassistant.components.sensor.season +# homeassistant.components.season.sensor ephem==3.7.6.0 # homeassistant.components.epson.media_player @@ -413,7 +413,7 @@ eternalegypt==0.0.5 # evdev==0.6.1 # homeassistant.components.evohome -# homeassistant.components.climate.honeywell +# homeassistant.components.honeywell.climate evohomeclient==0.2.8 # homeassistant.components.dlib_face_detect.image_processing @@ -444,7 +444,7 @@ fixerio==1.0.0a0 # homeassistant.components.flux_led.light flux_led==0.22 -# homeassistant.components.sensor.foobot +# homeassistant.components.foobot.sensor foobot_async==0.3.1 # homeassistant.components.notify.free_mobile @@ -467,12 +467,12 @@ gearbest_parser==1.0.7 # homeassistant.components.geizhals.sensor geizhals==0.0.9 -# homeassistant.components.geo_location.geo_json_events -# homeassistant.components.geo_location.nsw_rural_fire_service_feed -# homeassistant.components.geo_location.usgs_earthquakes_feed +# homeassistant.components.geo_json_events.geo_location +# homeassistant.components.nsw_rural_fire_service_feed.geo_location +# homeassistant.components.usgs_earthquakes_feed.geo_location geojson_client==0.3 -# homeassistant.components.sensor.geo_rss_events +# homeassistant.components.geo_rss_events.sensor georss_client==0.5 # homeassistant.components.gitter.sensor @@ -526,7 +526,7 @@ hass-nabucasa==0.7 # homeassistant.components.mqtt.server hbmqtt==0.9.4 -# homeassistant.components.sensor.jewish_calendar +# homeassistant.components.jewish_calendar.sensor hdate==0.8.7 # homeassistant.components.heatmiser.climate @@ -547,7 +547,7 @@ hlk-sw16==0.0.7 # homeassistant.components.pi_hole.sensor hole==0.3.0 -# homeassistant.components.binary_sensor.workday +# homeassistant.components.workday.binary_sensor holidays==0.9.9 # homeassistant.components.frontend @@ -636,7 +636,7 @@ libpyfoscam==1.0 # homeassistant.components.mikrotik.device_tracker librouteros==2.2.0 -# homeassistant.components.media_player.soundtouch +# homeassistant.components.soundtouch.media_player libsoundtouch==0.7.2 # homeassistant.components.lifx_legacy.light @@ -700,8 +700,8 @@ messagebird==1.2.0 # homeassistant.components.meteo_france meteofrance==0.3.4 -# homeassistant.components.sensor.mfi -# homeassistant.components.switch.mfi +# homeassistant.components.mfi.sensor +# homeassistant.components.mfi.switch mficlient==0.3.0 # homeassistant.components.miflora.sensor @@ -755,16 +755,16 @@ niluclient==0.1.2 # homeassistant.components.nederlandse_spoorwegen.sensor nsapi==2.7.4 -# homeassistant.components.sensor.nsw_fuel_station +# homeassistant.components.nsw_fuel_station.sensor nsw-fuel-api-client==1.0.10 # homeassistant.components.nuheat nuheat==0.3.0 -# homeassistant.components.binary_sensor.trend # homeassistant.components.opencv.image_processing # homeassistant.components.pollen.sensor # homeassistant.components.tensorflow.image_processing +# homeassistant.components.trend.binary_sensor numpy==1.16.2 # homeassistant.components.google @@ -815,8 +815,8 @@ pencompy==0.0.3 # homeassistant.components.aruba.device_tracker # homeassistant.components.cisco_ios.device_tracker -# homeassistant.components.device_tracker.unifi_direct # homeassistant.components.pandora.media_player +# homeassistant.components.unifi_direct.device_tracker pexpect==4.6.0 # homeassistant.components.rpi_pfio @@ -846,7 +846,7 @@ plexapi==3.0.6 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 -# homeassistant.components.sensor.mhz19 +# homeassistant.components.mhz19.sensor # homeassistant.components.serial_pm.sensor pmsensor==0.4 @@ -859,7 +859,7 @@ postnl_api==1.0.2 # homeassistant.components.reddit.sensor praw==6.1.1 -# homeassistant.components.sensor.islamic_prayer_times +# homeassistant.components.islamic_prayer_times.sensor prayer_times_calculator==0.0.3 # homeassistant.components.prezzibenzina.sensor @@ -961,7 +961,7 @@ pyatv==0.3.12 # homeassistant.components.bbox.sensor pybbox==0.0.5-alpha -# homeassistant.components.media_player.blackbird +# homeassistant.components.blackbird.media_player pyblackbird==0.5 # homeassistant.components.bluetooth_tracker.device_tracker @@ -1036,10 +1036,10 @@ pyenvisalink==3.8 # homeassistant.components.ephember.climate pyephember==0.2.0 -# homeassistant.components.light.everlights +# homeassistant.components.everlights.light pyeverlights==0.1.0 -# homeassistant.components.sensor.fido +# homeassistant.components.fido.sensor pyfido==2.1.1 # homeassistant.components.flexit.climate @@ -1073,7 +1073,7 @@ pygtfs==0.1.5 # homeassistant.components.gtt.sensor pygtt==1.1.2 -# homeassistant.components.sensor.version +# homeassistant.components.version.sensor pyhaversion==2.0.3 # homeassistant.components.hikvision.binary_sensor @@ -1088,7 +1088,7 @@ pyhomematic==0.1.58 # homeassistant.components.homeworks pyhomeworks==0.0.6 -# homeassistant.components.sensor.hydroquebec +# homeassistant.components.hydroquebec.sensor pyhydroquebec==2.2.2 # homeassistant.components.ialarm.alarm_control_panel @@ -1161,7 +1161,7 @@ pymochad==0.2.0 # homeassistant.components.modbus pymodbus==1.5.2 -# homeassistant.components.media_player.monoprice +# homeassistant.components.monoprice.media_player pymonoprice==0.3 # homeassistant.components.yamaha_musiccast.media_player @@ -1191,7 +1191,8 @@ pynuki==1.3.2 # homeassistant.components.nut.sensor pynut2==2.1.2 -# homeassistant.components.binary_sensor.nx584 +# homeassistant.components.nx584.alarm_control_panel +# homeassistant.components.nx584.binary_sensor pynx584==0.4 # homeassistant.components.openuv @@ -1326,8 +1327,8 @@ python-etherscan-api==0.0.3 # homeassistant.components.familyhub.camera python-family-hub-local==0.0.2 -# homeassistant.components.sensor.darksky -# homeassistant.components.weather.darksky +# homeassistant.components.darksky.sensor +# homeassistant.components.darksky.weather python-forecastio==1.4.0 # homeassistant.components.gc100 @@ -1413,7 +1414,7 @@ python-whois==0.7.1 # homeassistant.components.wink python-wink==1.10.3 -# homeassistant.components.sensor.awair +# homeassistant.components.awair.sensor python_awair==0.0.3 # homeassistant.components.swiss_public_transport.sensor @@ -1443,7 +1444,7 @@ pytrafikverket==0.1.5.9 # homeassistant.components.ubee.device_tracker pyubee==0.2 -# homeassistant.components.device_tracker.unifi +# homeassistant.components.unifi.device_tracker pyunifi==2.16 # homeassistant.components.uptimerobot.binary_sensor @@ -1542,10 +1543,10 @@ russound==0.1.9 # homeassistant.components.russound_rio.media_player russound_rio==0.1.4 -# homeassistant.components.media_player.yamaha +# homeassistant.components.yamaha.media_player rxv==0.6.0 -# homeassistant.components.media_player.samsungtv +# homeassistant.components.samsungtv.media_player samsungctl[websocket]==0.7.1 # homeassistant.components.satel_integra @@ -1617,7 +1618,7 @@ socialbladeclient==0.2 # homeassistant.components.solaredge.sensor solaredge==0.0.2 -# homeassistant.components.climate.honeywell +# homeassistant.components.honeywell.climate somecomfort==0.5.2 # homeassistant.components.speedtestdotnet @@ -1633,10 +1634,10 @@ spotcrime==1.0.3 spotipy-homeassistant==2.4.4.dev1 # homeassistant.components.recorder -# homeassistant.components.sensor.sql +# homeassistant.components.sql.sensor sqlalchemy==1.2.18 -# homeassistant.components.sensor.srp_energy +# homeassistant.components.srp_energy.sensor srpenergy==1.0.5 # homeassistant.components.starlingbank.sensor @@ -1714,7 +1715,7 @@ total_connect_client==0.22 # homeassistant.components.tplink_lte tp-connected==0.0.4 -# homeassistant.components.device_tracker.tplink +# homeassistant.components.tplink.device_tracker tplink==0.2.1 # homeassistant.components.transmission @@ -1738,7 +1739,7 @@ upsmychoice==1.0.6 # homeassistant.components.uscis.sensor uscisstatus==0.1.1 -# homeassistant.components.camera.uvc +# homeassistant.components.uvc.camera uvcclient==0.11.0 # homeassistant.components.venstar.climate @@ -1760,9 +1761,9 @@ vtjp==0.1.14 vultr==0.1.2 # homeassistant.components.wake_on_lan -# homeassistant.components.media_player.samsungtv # homeassistant.components.panasonic_viera.media_player -# homeassistant.components.switch.wake_on_lan +# homeassistant.components.samsungtv.media_player +# homeassistant.components.wake_on_lan.switch wakeonlan==1.1.6 # homeassistant.components.waqi.sensor @@ -1799,17 +1800,17 @@ xfinity-gateway==0.0.4 xknx==0.10.0 # homeassistant.components.bluesound.media_player -# homeassistant.components.sensor.startca -# homeassistant.components.sensor.yr +# homeassistant.components.startca.sensor # homeassistant.components.ted5000.sensor +# homeassistant.components.yr.sensor # homeassistant.components.zestimate.sensor xmltodict==0.11.0 # homeassistant.components.xs1 xs1-api-client==2.3.5 -# homeassistant.components.sensor.yweather -# homeassistant.components.weather.yweather +# homeassistant.components.yweather.sensor +# homeassistant.components.yweather.weather yahooweather==0.10 # homeassistant.components.yale_smart_alarm.alarm_control_panel diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ce48d51199e..fd6c620f57a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -25,10 +25,10 @@ HAP-python==2.4.2 # homeassistant.components.owntracks PyNaCl==1.3.0 -# homeassistant.components.sensor.rmvtransport +# homeassistant.components.rmvtransport.sensor PyRMVtransport==0.1.3 -# homeassistant.components.sensor.transport_nsw +# homeassistant.components.transport_nsw.sensor PyTransportNSW==0.1.1 # homeassistant.components.notify.yessssms @@ -37,7 +37,7 @@ YesssSMS==0.2.3 # homeassistant.components.ambient_station aioambient==0.1.3 -# homeassistant.components.device_tracker.automatic +# homeassistant.components.automatic.device_tracker aioautomatic==0.6.5 # homeassistant.components.emulated_hue @@ -59,52 +59,52 @@ av==6.1.2 # homeassistant.components.zha bellows-homeassistant==0.7.1 -# homeassistant.components.calendar.caldav +# homeassistant.components.caldav.calendar caldav==0.5.0 -# homeassistant.components.sensor.coinmarketcap +# homeassistant.components.coinmarketcap.sensor coinmarketcap==5.0.3 # homeassistant.components.ihc # homeassistant.components.namecheapdns -# homeassistant.components.device_tracker.upc_connect # homeassistant.components.ohmconnect.sensor +# homeassistant.components.upc_connect.device_tracker defusedxml==0.5.0 -# homeassistant.components.sensor.dsmr +# homeassistant.components.dsmr.sensor dsmr_parser==0.12 -# homeassistant.components.device_tracker.ee_brightbox +# homeassistant.components.ee_brightbox.device_tracker eebrightbox==0.0.4 # homeassistant.components.emulated_roku emulated_roku==0.1.8 -# homeassistant.components.sensor.entur_public_transport +# homeassistant.components.entur_public_transport.sensor enturclient==0.1.3 -# homeassistant.components.sensor.season +# homeassistant.components.season.sensor ephem==3.7.6.0 # homeassistant.components.evohome -# homeassistant.components.climate.honeywell +# homeassistant.components.honeywell.climate evohomeclient==0.2.8 # homeassistant.components.feedreader feedparser-homeassistant==5.2.2.dev1 -# homeassistant.components.sensor.foobot +# homeassistant.components.foobot.sensor foobot_async==0.3.1 # homeassistant.components.google.tts gTTS-token==1.1.3 -# homeassistant.components.geo_location.geo_json_events -# homeassistant.components.geo_location.nsw_rural_fire_service_feed -# homeassistant.components.geo_location.usgs_earthquakes_feed +# homeassistant.components.geo_json_events.geo_location +# homeassistant.components.nsw_rural_fire_service_feed.geo_location +# homeassistant.components.usgs_earthquakes_feed.geo_location geojson_client==0.3 -# homeassistant.components.sensor.geo_rss_events +# homeassistant.components.geo_rss_events.sensor georss_client==0.5 # homeassistant.components.ffmpeg @@ -119,10 +119,10 @@ hass-nabucasa==0.7 # homeassistant.components.mqtt.server hbmqtt==0.9.4 -# homeassistant.components.sensor.jewish_calendar +# homeassistant.components.jewish_calendar.sensor hdate==0.8.7 -# homeassistant.components.binary_sensor.workday +# homeassistant.components.workday.binary_sensor holidays==0.9.9 # homeassistant.components.frontend @@ -144,7 +144,7 @@ jsonpath==0.75 # homeassistant.components.dyson libpurecoollink==0.4.2 -# homeassistant.components.media_player.soundtouch +# homeassistant.components.soundtouch.media_player libsoundtouch==0.7.2 # homeassistant.components.luftdaten @@ -153,14 +153,14 @@ luftdaten==0.3.4 # homeassistant.components.mythicbeastsdns mbddns==0.1.2 -# homeassistant.components.sensor.mfi -# homeassistant.components.switch.mfi +# homeassistant.components.mfi.sensor +# homeassistant.components.mfi.switch mficlient==0.3.0 -# homeassistant.components.binary_sensor.trend # homeassistant.components.opencv.image_processing # homeassistant.components.pollen.sensor # homeassistant.components.tensorflow.image_processing +# homeassistant.components.trend.binary_sensor numpy==1.16.2 # homeassistant.components.mqtt @@ -169,14 +169,14 @@ paho-mqtt==1.4.0 # homeassistant.components.aruba.device_tracker # homeassistant.components.cisco_ios.device_tracker -# homeassistant.components.device_tracker.unifi_direct # homeassistant.components.pandora.media_player +# homeassistant.components.unifi_direct.device_tracker pexpect==4.6.0 # homeassistant.components.pilight pilight==0.1.1 -# homeassistant.components.sensor.mhz19 +# homeassistant.components.mhz19.sensor # homeassistant.components.serial_pm.sensor pmsensor==0.4 @@ -193,7 +193,7 @@ py-canary==0.5.0 # homeassistant.components.tplink pyHS100==0.3.4 -# homeassistant.components.media_player.blackbird +# homeassistant.components.blackbird.media_player pyblackbird==0.5 # homeassistant.components.deconz @@ -208,10 +208,11 @@ pyhomematic==0.1.58 # homeassistant.components.litejet pylitejet==0.1 -# homeassistant.components.media_player.monoprice +# homeassistant.components.monoprice.media_player pymonoprice==0.3 -# homeassistant.components.binary_sensor.nx584 +# homeassistant.components.nx584.alarm_control_panel +# homeassistant.components.nx584.binary_sensor pynx584==0.4 # homeassistant.components.openuv @@ -240,20 +241,20 @@ pysonos==0.0.8 # homeassistant.components.spc pyspcwebgw==0.4.0 -# homeassistant.components.sensor.darksky -# homeassistant.components.weather.darksky +# homeassistant.components.darksky.sensor +# homeassistant.components.darksky.weather python-forecastio==1.4.0 # homeassistant.components.nest python-nest==4.1.0 -# homeassistant.components.sensor.awair +# homeassistant.components.awair.sensor python_awair==0.0.3 # homeassistant.components.tradfri pytradfri[async]==6.0.1 -# homeassistant.components.device_tracker.unifi +# homeassistant.components.unifi.device_tracker pyunifi==2.16 # homeassistant.components.notify.html5 @@ -271,7 +272,7 @@ rflink==0.0.37 # homeassistant.components.ring ring_doorbell==0.2.2 -# homeassistant.components.media_player.yamaha +# homeassistant.components.yamaha.media_player rxv==0.6.0 # homeassistant.components.simplisafe @@ -283,14 +284,14 @@ sleepyq==0.6 # homeassistant.components.smhi smhi-pkg==1.0.10 -# homeassistant.components.climate.honeywell +# homeassistant.components.honeywell.climate somecomfort==0.5.2 # homeassistant.components.recorder -# homeassistant.components.sensor.sql +# homeassistant.components.sql.sensor sqlalchemy==1.2.18 -# homeassistant.components.sensor.srp_energy +# homeassistant.components.srp_energy.sensor srpenergy==1.0.5 # homeassistant.components.statsd @@ -299,7 +300,7 @@ statsd==3.2.1 # homeassistant.components.toon toonapilib==3.2.2 -# homeassistant.components.camera.uvc +# homeassistant.components.uvc.camera uvcclient==0.11.0 # homeassistant.components.verisure @@ -309,9 +310,9 @@ vsure==1.5.2 vultr==0.1.2 # homeassistant.components.wake_on_lan -# homeassistant.components.media_player.samsungtv # homeassistant.components.panasonic_viera.media_player -# homeassistant.components.switch.wake_on_lan +# homeassistant.components.samsungtv.media_player +# homeassistant.components.wake_on_lan.switch wakeonlan==1.1.6 # homeassistant.components.zha diff --git a/tests/components/api_streams/__init__.py b/tests/components/api_streams/__init__.py new file mode 100644 index 00000000000..b1d0cf13569 --- /dev/null +++ b/tests/components/api_streams/__init__.py @@ -0,0 +1 @@ +"""Tests for the api_streams component.""" diff --git a/tests/components/sensor/test_api_streams.py b/tests/components/api_streams/test_sensor.py similarity index 75% rename from tests/components/sensor/test_api_streams.py rename to tests/components/api_streams/test_sensor.py index e7978ce0fb8..48ab7e1c3f9 100644 --- a/tests/components/sensor/test_api_streams.py +++ b/tests/components/api_streams/test_sensor.py @@ -1,18 +1,19 @@ """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 -@asyncio.coroutine -def test_api_streams(hass): +@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): - yield from async_setup_component(hass, 'sensor', { + await async_setup_component(hass, 'sensor', { 'sensor': { 'platform': 'api_streams', } @@ -22,31 +23,31 @@ def test_api_streams(hass): assert state.state == '0' log.debug('STREAM 1 ATTACHED') - yield from hass.async_block_till_done() + await asyncio.sleep(0.1) state = hass.states.get('sensor.connected_clients') assert state.state == '1' log.debug('STREAM 1 ATTACHED') - yield from hass.async_block_till_done() + await asyncio.sleep(0.1) state = hass.states.get('sensor.connected_clients') assert state.state == '2' log.debug('STREAM 1 RESPONSE CLOSED') - yield from hass.async_block_till_done() + await asyncio.sleep(0.1) state = hass.states.get('sensor.connected_clients') assert state.state == '1' -@asyncio.coroutine -def test_websocket_api(hass): +@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): - yield from async_setup_component(hass, 'sensor', { + await async_setup_component(hass, 'sensor', { 'sensor': { 'platform': 'api_streams', } @@ -56,19 +57,19 @@ def test_websocket_api(hass): assert state.state == '0' log.debug('WS %s: %s', id(log), 'Connected') - yield from hass.async_block_till_done() + await asyncio.sleep(0.1) state = hass.states.get('sensor.connected_clients') assert state.state == '1' log.debug('WS %s: %s', id(log), 'Connected') - yield from hass.async_block_till_done() + 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') - yield from hass.async_block_till_done() + await asyncio.sleep(0.1) state = hass.states.get('sensor.connected_clients') assert state.state == '1' diff --git a/tests/components/asuswrt/__init__.py b/tests/components/asuswrt/__init__.py new file mode 100644 index 00000000000..4635400b481 --- /dev/null +++ b/tests/components/asuswrt/__init__.py @@ -0,0 +1 @@ +"""Tests for the asuswrt component.""" diff --git a/tests/components/device_tracker/test_asuswrt.py b/tests/components/asuswrt/test_device_tracker.py similarity index 100% rename from tests/components/device_tracker/test_asuswrt.py rename to tests/components/asuswrt/test_device_tracker.py diff --git a/tests/components/aurora/__init__.py b/tests/components/aurora/__init__.py new file mode 100644 index 00000000000..4ce9649eff9 --- /dev/null +++ b/tests/components/aurora/__init__.py @@ -0,0 +1 @@ +"""The tests for the Aurora sensor platform.""" diff --git a/tests/components/binary_sensor/test_aurora.py b/tests/components/aurora/test_binary_sensor.py similarity index 97% rename from tests/components/binary_sensor/test_aurora.py rename to tests/components/aurora/test_binary_sensor.py index 109da8e8a43..48f02f3c9f2 100644 --- a/tests/components/binary_sensor/test_aurora.py +++ b/tests/components/aurora/test_binary_sensor.py @@ -4,7 +4,7 @@ import unittest import requests_mock -from homeassistant.components.binary_sensor import aurora +from homeassistant.components.aurora import binary_sensor as aurora from tests.common import load_fixture, get_test_home_assistant diff --git a/tests/components/automatic/__init__.py b/tests/components/automatic/__init__.py new file mode 100644 index 00000000000..4f7f83b97b5 --- /dev/null +++ b/tests/components/automatic/__init__.py @@ -0,0 +1 @@ +"""Tests for the automatic component.""" diff --git a/tests/components/device_tracker/test_automatic.py b/tests/components/automatic/test_device_tracker.py similarity index 95% rename from tests/components/device_tracker/test_automatic.py rename to tests/components/automatic/test_device_tracker.py index 84ea84cdcad..03b631b4689 100644 --- a/tests/components/device_tracker/test_automatic.py +++ b/tests/components/automatic/test_device_tracker.py @@ -6,7 +6,7 @@ from unittest.mock import patch, MagicMock import aioautomatic from homeassistant.setup import async_setup_component -from homeassistant.components.device_tracker.automatic import ( +from homeassistant.components.automatic.device_tracker import ( async_setup_scanner) _LOGGER = logging.getLogger(__name__) @@ -17,7 +17,7 @@ _LOGGER = logging.getLogger(__name__) @patch('json.dump') @patch('os.makedirs') @patch('os.path.isfile', return_value=True) -@patch('homeassistant.components.device_tracker.automatic.open', create=True) +@patch('homeassistant.components.automatic.device_tracker.open', create=True) def test_invalid_credentials( mock_open, mock_isfile, mock_makedirs, mock_json_dump, mock_json_load, mock_create_session, hass): @@ -52,7 +52,7 @@ def test_invalid_credentials( @patch('json.dump') @patch('os.makedirs') @patch('os.path.isfile', return_value=True) -@patch('homeassistant.components.device_tracker.automatic.open', create=True) +@patch('homeassistant.components.automatic.device_tracker.open', create=True) def test_valid_credentials( mock_open, mock_isfile, mock_makedirs, mock_json_dump, mock_json_load, mock_ws_connect, mock_create_session, hass): diff --git a/tests/components/awair/__init__.py b/tests/components/awair/__init__.py new file mode 100644 index 00000000000..5331ae5492a --- /dev/null +++ b/tests/components/awair/__init__.py @@ -0,0 +1 @@ +"""Tests for the awair component.""" diff --git a/tests/components/sensor/test_awair.py b/tests/components/awair/test_sensor.py similarity index 98% rename from tests/components/sensor/test_awair.py rename to tests/components/awair/test_sensor.py index e00cc816518..d251e8fdce8 100644 --- a/tests/components/sensor/test_awair.py +++ b/tests/components/awair/test_sensor.py @@ -7,7 +7,7 @@ import logging from unittest.mock import patch from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.components.sensor.awair import ( +from homeassistant.components.awair.sensor import ( ATTR_LAST_API_UPDATE, ATTR_TIMESTAMP, DEVICE_CLASS_CARBON_DIOXIDE, @@ -54,7 +54,7 @@ def alter_time(retval): patch_one = patch("homeassistant.util.dt.utcnow", return_value=retval) patch_two = patch("homeassistant.util.utcnow", return_value=retval) patch_three = patch( - "homeassistant.components.sensor.awair.dt.utcnow", return_value=retval + "homeassistant.components.awair.sensor.dt.utcnow", return_value=retval ) with patch_one, patch_two, patch_three: diff --git a/tests/components/bayesian/__init__.py b/tests/components/bayesian/__init__.py new file mode 100644 index 00000000000..d3850a2e520 --- /dev/null +++ b/tests/components/bayesian/__init__.py @@ -0,0 +1 @@ +"""Tests for bayesian component.""" diff --git a/tests/components/binary_sensor/test_bayesian.py b/tests/components/bayesian/test_binary_sensor.py similarity index 99% rename from tests/components/binary_sensor/test_bayesian.py rename to tests/components/bayesian/test_binary_sensor.py index b52459ec47d..4b10470fc89 100644 --- a/tests/components/binary_sensor/test_bayesian.py +++ b/tests/components/bayesian/test_binary_sensor.py @@ -2,7 +2,7 @@ import unittest from homeassistant.setup import setup_component -from homeassistant.components.binary_sensor import bayesian +from homeassistant.components.bayesian import binary_sensor as bayesian from tests.common import get_test_home_assistant diff --git a/tests/components/blackbird/__init__.py b/tests/components/blackbird/__init__.py new file mode 100644 index 00000000000..5f6031695f4 --- /dev/null +++ b/tests/components/blackbird/__init__.py @@ -0,0 +1 @@ +"""Tests for the blackbird component.""" diff --git a/tests/components/media_player/test_blackbird.py b/tests/components/blackbird/test_media_player.py similarity index 99% rename from tests/components/media_player/test_blackbird.py rename to tests/components/blackbird/test_media_player.py index 6ab3b69b558..3430a232808 100644 --- a/tests/components/media_player/test_blackbird.py +++ b/tests/components/blackbird/test_media_player.py @@ -9,7 +9,7 @@ from homeassistant.components.media_player.const import ( from homeassistant.const import STATE_ON, STATE_OFF import tests.common -from homeassistant.components.media_player.blackbird import ( +from homeassistant.components.blackbird.media_player import ( DATA_BLACKBIRD, PLATFORM_SCHEMA, SERVICE_SETALLZONES, setup_platform) import pytest diff --git a/tests/components/bom/__init__.py b/tests/components/bom/__init__.py new file mode 100644 index 00000000000..a129618ef2c --- /dev/null +++ b/tests/components/bom/__init__.py @@ -0,0 +1 @@ +"""Tests for the bom component.""" diff --git a/tests/components/sensor/test_bom.py b/tests/components/bom/test_sensor.py similarity index 98% rename from tests/components/sensor/test_bom.py rename to tests/components/bom/test_sensor.py index fc2722f9742..d5c197d9eea 100644 --- a/tests/components/sensor/test_bom.py +++ b/tests/components/bom/test_sensor.py @@ -8,7 +8,7 @@ from urllib.parse import urlparse import requests from homeassistant.components import sensor -from homeassistant.components.sensor.bom import BOMCurrentData +from homeassistant.components.bom.sensor import BOMCurrentData from homeassistant.setup import setup_component from tests.common import ( assert_setup_component, get_test_home_assistant, load_fixture) diff --git a/tests/components/caldav/__init__.py b/tests/components/caldav/__init__.py new file mode 100644 index 00000000000..385d11b2f4e --- /dev/null +++ b/tests/components/caldav/__init__.py @@ -0,0 +1 @@ +"""Tests for the caldav component.""" diff --git a/tests/components/calendar/test_caldav.py b/tests/components/caldav/test_calendar.py similarity index 99% rename from tests/components/calendar/test_caldav.py rename to tests/components/caldav/test_calendar.py index 45de0052ce6..ceca68c1f8a 100644 --- a/tests/components/calendar/test_caldav.py +++ b/tests/components/caldav/test_calendar.py @@ -6,7 +6,7 @@ import unittest from unittest.mock import (patch, Mock, MagicMock) import homeassistant.components.calendar as calendar_base -import homeassistant.components.calendar.caldav as caldav +import homeassistant.components.caldav.calendar as caldav from caldav.objects import Event from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON from homeassistant.util import dt diff --git a/tests/components/sensor/test_canary.py b/tests/components/canary/test_sensor.py similarity index 98% rename from tests/components/sensor/test_canary.py rename to tests/components/canary/test_sensor.py index dde8f4e0f94..c785a98e0d8 100644 --- a/tests/components/sensor/test_canary.py +++ b/tests/components/canary/test_sensor.py @@ -4,8 +4,8 @@ import unittest from unittest.mock import Mock from homeassistant.components.canary import DATA_CANARY -from homeassistant.components.sensor import canary -from homeassistant.components.sensor.canary import CanarySensor, \ +from homeassistant.components.canary import sensor as canary +from homeassistant.components.canary.sensor import CanarySensor, \ SENSOR_TYPES, ATTR_AIR_QUALITY, STATE_AIR_QUALITY_NORMAL, \ STATE_AIR_QUALITY_ABNORMAL, STATE_AIR_QUALITY_VERY_ABNORMAL from tests.common import (get_test_home_assistant) diff --git a/tests/components/coinmarketcap/__init__.py b/tests/components/coinmarketcap/__init__.py new file mode 100644 index 00000000000..9e9b871bbe2 --- /dev/null +++ b/tests/components/coinmarketcap/__init__.py @@ -0,0 +1 @@ +"""Tests for the coinmarketcap component.""" diff --git a/tests/components/sensor/test_coinmarketcap.py b/tests/components/coinmarketcap/test_sensor.py similarity index 100% rename from tests/components/sensor/test_coinmarketcap.py rename to tests/components/coinmarketcap/test_sensor.py diff --git a/tests/components/command_line/__init__.py b/tests/components/command_line/__init__.py new file mode 100644 index 00000000000..d79b3e27db3 --- /dev/null +++ b/tests/components/command_line/__init__.py @@ -0,0 +1 @@ +"""Tests for command_line component.""" diff --git a/tests/components/binary_sensor/test_command_line.py b/tests/components/command_line/test_binary_sensor.py similarity index 96% rename from tests/components/binary_sensor/test_command_line.py rename to tests/components/command_line/test_binary_sensor.py index 2a6b35ea5e0..be93d6561ec 100644 --- a/tests/components/binary_sensor/test_command_line.py +++ b/tests/components/command_line/test_binary_sensor.py @@ -2,7 +2,7 @@ import unittest from homeassistant.const import (STATE_ON, STATE_OFF) -from homeassistant.components.binary_sensor import command_line +from homeassistant.components.command_line import binary_sensor as command_line from homeassistant.helpers import template from tests.common import get_test_home_assistant diff --git a/tests/components/cover/test_command_line.py b/tests/components/command_line/test_cover.py similarity index 97% rename from tests/components/cover/test_command_line.py rename to tests/components/command_line/test_cover.py index 0e03539d58c..b583e1a83a6 100644 --- a/tests/components/cover/test_command_line.py +++ b/tests/components/command_line/test_cover.py @@ -6,7 +6,7 @@ from unittest import mock import pytest from homeassistant.components.cover import DOMAIN -import homeassistant.components.cover.command_line as cmd_rs +import homeassistant.components.command_line.cover as cmd_rs from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER, SERVICE_STOP_COVER) diff --git a/tests/components/sensor/test_command_line.py b/tests/components/command_line/test_sensor.py similarity index 96% rename from tests/components/sensor/test_command_line.py rename to tests/components/command_line/test_sensor.py index cacd7a4156d..40bb44a68cc 100644 --- a/tests/components/sensor/test_command_line.py +++ b/tests/components/command_line/test_sensor.py @@ -3,7 +3,7 @@ import unittest from unittest.mock import patch from homeassistant.helpers.template import Template -from homeassistant.components.sensor import command_line +from homeassistant.components.command_line import sensor as command_line from tests.common import get_test_home_assistant @@ -95,7 +95,7 @@ class TestCommandSensorSensor(unittest.TestCase): assert 'value_three' == \ self.sensor.device_state_attributes['key_three'] - @patch('homeassistant.components.sensor.command_line._LOGGER') + @patch('homeassistant.components.command_line.sensor._LOGGER') def test_update_with_json_attrs_no_data(self, mock_logger): """Test attributes when no JSON result fetched.""" data = command_line.CommandSensorData( @@ -108,7 +108,7 @@ class TestCommandSensorSensor(unittest.TestCase): assert {} == self.sensor.device_state_attributes assert mock_logger.warning.called - @patch('homeassistant.components.sensor.command_line._LOGGER') + @patch('homeassistant.components.command_line.sensor._LOGGER') def test_update_with_json_attrs_not_dict(self, mock_logger): """Test attributes get extracted from a JSON result.""" data = command_line.CommandSensorData( @@ -121,7 +121,7 @@ class TestCommandSensorSensor(unittest.TestCase): assert {} == self.sensor.device_state_attributes assert mock_logger.warning.called - @patch('homeassistant.components.sensor.command_line._LOGGER') + @patch('homeassistant.components.command_line.sensor._LOGGER') def test_update_with_json_attrs_bad_JSON(self, mock_logger): """Test attributes get extracted from a JSON result.""" data = command_line.CommandSensorData( diff --git a/tests/components/switch/test_command_line.py b/tests/components/command_line/test_switch.py similarity index 99% rename from tests/components/switch/test_command_line.py rename to tests/components/command_line/test_switch.py index 06618e248ce..0cc9f27d07c 100644 --- a/tests/components/switch/test_command_line.py +++ b/tests/components/command_line/test_switch.py @@ -7,7 +7,7 @@ import unittest from homeassistant.setup import setup_component from homeassistant.const import STATE_ON, STATE_OFF import homeassistant.components.switch as switch -import homeassistant.components.switch.command_line as command_line +import homeassistant.components.command_line.switch as command_line from tests.common import get_test_home_assistant from tests.components.switch import common diff --git a/tests/components/darksky/__init__.py b/tests/components/darksky/__init__.py new file mode 100644 index 00000000000..b58d250e975 --- /dev/null +++ b/tests/components/darksky/__init__.py @@ -0,0 +1 @@ +"""Tests for the darksky component.""" diff --git a/tests/components/sensor/test_darksky.py b/tests/components/darksky/test_sensor.py similarity index 98% rename from tests/components/sensor/test_darksky.py rename to tests/components/darksky/test_sensor.py index 58ce932020a..25e54fac823 100644 --- a/tests/components/sensor/test_darksky.py +++ b/tests/components/darksky/test_sensor.py @@ -9,7 +9,7 @@ import requests_mock import forecastio -from homeassistant.components.sensor import darksky +from homeassistant.components.darksky import sensor as darksky from homeassistant.setup import setup_component from tests.common import (load_fixture, get_test_home_assistant, diff --git a/tests/components/weather/test_darksky.py b/tests/components/darksky/test_weather.py similarity index 100% rename from tests/components/weather/test_darksky.py rename to tests/components/darksky/test_weather.py diff --git a/tests/components/directv/__init__.py b/tests/components/directv/__init__.py new file mode 100644 index 00000000000..9a32215e53d --- /dev/null +++ b/tests/components/directv/__init__.py @@ -0,0 +1 @@ +"""Tests for the directv component.""" diff --git a/tests/components/media_player/test_directv.py b/tests/components/directv/test_media_player.py similarity index 99% rename from tests/components/media_player/test_directv.py rename to tests/components/directv/test_media_player.py index 5918946fe6c..6a7bd0ce06f 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/directv/test_media_player.py @@ -13,7 +13,7 @@ from homeassistant.components.media_player.const import ( SERVICE_PLAY_MEDIA, SUPPORT_PAUSE, SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA, SUPPORT_STOP, SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_PLAY) -from homeassistant.components.media_player.directv import ( +from homeassistant.components.directv.media_player import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, ATTR_MEDIA_START_TIME, DEFAULT_DEVICE, DEFAULT_PORT) from homeassistant.const import ( diff --git a/tests/components/dsmr/__init__.py b/tests/components/dsmr/__init__.py new file mode 100644 index 00000000000..cab1d0fe2e7 --- /dev/null +++ b/tests/components/dsmr/__init__.py @@ -0,0 +1 @@ +"""Tests for the dsmr component.""" diff --git a/tests/components/sensor/test_dsmr.py b/tests/components/dsmr/test_sensor.py similarity index 99% rename from tests/components/sensor/test_dsmr.py rename to tests/components/dsmr/test_sensor.py index c2ea61e5bb4..366d2163818 100644 --- a/tests/components/sensor/test_dsmr.py +++ b/tests/components/dsmr/test_sensor.py @@ -12,7 +12,7 @@ from unittest.mock import Mock import asynctest from homeassistant.bootstrap import async_setup_component -from homeassistant.components.sensor.dsmr import DerivativeDSMREntity +from homeassistant.components.dsmr.sensor import DerivativeDSMREntity import pytest from tests.common import assert_setup_component diff --git a/tests/components/dte_energy_bridge/__init__.py b/tests/components/dte_energy_bridge/__init__.py new file mode 100644 index 00000000000..615944bda88 --- /dev/null +++ b/tests/components/dte_energy_bridge/__init__.py @@ -0,0 +1 @@ +"""Tests for the dte_energy_bridge component.""" diff --git a/tests/components/sensor/test_dte_energy_bridge.py b/tests/components/dte_energy_bridge/test_sensor.py similarity index 100% rename from tests/components/sensor/test_dte_energy_bridge.py rename to tests/components/dte_energy_bridge/test_sensor.py diff --git a/tests/components/climate/test_dyson.py b/tests/components/dyson/test_climate.py similarity index 99% rename from tests/components/climate/test_dyson.py rename to tests/components/dyson/test_climate.py index e2cfffe6458..43ce6344ec4 100644 --- a/tests/components/climate/test_dyson.py +++ b/tests/components/dyson/test_climate.py @@ -6,7 +6,7 @@ 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.climate import dyson +from homeassistant.components.dyson import climate as dyson from homeassistant.components import dyson as dyson_parent from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE from homeassistant.setup import setup_component diff --git a/tests/components/fan/test_dyson.py b/tests/components/dyson/test_fan.py similarity index 98% rename from tests/components/fan/test_dyson.py rename to tests/components/dyson/test_fan.py index aacab700f05..a04116f10f2 100644 --- a/tests/components/fan/test_dyson.py +++ b/tests/components/dyson/test_fan.py @@ -4,8 +4,8 @@ from unittest import mock from homeassistant.setup import setup_component from homeassistant.components import dyson as dyson_parent -from homeassistant.components.dyson import DYSON_DEVICES -from homeassistant.components.fan import (dyson, ATTR_SPEED, ATTR_SPEED_LIST, +from homeassistant.components.dyson import DYSON_DEVICES, fan as dyson +from homeassistant.components.fan import (ATTR_SPEED, ATTR_SPEED_LIST, ATTR_OSCILLATING) from tests.common import get_test_home_assistant from libpurecoollink.const import FanSpeed, FanMode, NightMode, Oscillation diff --git a/tests/components/sensor/test_dyson.py b/tests/components/dyson/test_sensor.py similarity index 99% rename from tests/components/sensor/test_dyson.py rename to tests/components/dyson/test_sensor.py index d4609c2b2c5..3218038c7e3 100644 --- a/tests/components/sensor/test_dyson.py +++ b/tests/components/dyson/test_sensor.py @@ -4,7 +4,7 @@ from unittest import mock from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, \ STATE_OFF -from homeassistant.components.sensor import dyson +from homeassistant.components.dyson import sensor as dyson from tests.common import get_test_home_assistant from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink diff --git a/tests/components/vacuum/test_dyson.py b/tests/components/dyson/test_vacuum.py similarity index 98% rename from tests/components/vacuum/test_dyson.py rename to tests/components/dyson/test_vacuum.py index 0bdaa619c7b..05ad8cf0db7 100644 --- a/tests/components/vacuum/test_dyson.py +++ b/tests/components/dyson/test_vacuum.py @@ -5,8 +5,8 @@ from unittest import mock from libpurecoollink.dyson_360_eye import Dyson360Eye from libpurecoollink.const import Dyson360EyeMode, PowerMode -from homeassistant.components.vacuum import dyson -from homeassistant.components.vacuum.dyson import Dyson360EyeDevice +from homeassistant.components.dyson import vacuum as dyson +from homeassistant.components.dyson.vacuum import Dyson360EyeDevice from tests.common import get_test_home_assistant diff --git a/tests/components/ee_brightbox/__init__.py b/tests/components/ee_brightbox/__init__.py new file mode 100644 index 00000000000..03abf6af02a --- /dev/null +++ b/tests/components/ee_brightbox/__init__.py @@ -0,0 +1 @@ +"""Tests for the ee_brightbox component.""" diff --git a/tests/components/device_tracker/test_ee_brightbox.py b/tests/components/ee_brightbox/test_device_tracker.py similarity index 100% rename from tests/components/device_tracker/test_ee_brightbox.py rename to tests/components/ee_brightbox/test_device_tracker.py diff --git a/tests/components/efergy/__init__.py b/tests/components/efergy/__init__.py new file mode 100644 index 00000000000..242d36fb932 --- /dev/null +++ b/tests/components/efergy/__init__.py @@ -0,0 +1 @@ +"""Tests for the efergy component.""" diff --git a/tests/components/sensor/test_efergy.py b/tests/components/efergy/test_sensor.py similarity index 100% rename from tests/components/sensor/test_efergy.py rename to tests/components/efergy/test_sensor.py diff --git a/tests/components/entur_public_transport/__init__.py b/tests/components/entur_public_transport/__init__.py new file mode 100644 index 00000000000..015a5e9103c --- /dev/null +++ b/tests/components/entur_public_transport/__init__.py @@ -0,0 +1 @@ +"""Tests for the entur_public_transport component.""" diff --git a/tests/components/sensor/test_entur_public_transport.py b/tests/components/entur_public_transport/test_sensor.py similarity index 94% rename from tests/components/sensor/test_entur_public_transport.py rename to tests/components/entur_public_transport/test_sensor.py index 20b50ce9ddd..98e72d19e81 100644 --- a/tests/components/sensor/test_entur_public_transport.py +++ b/tests/components/entur_public_transport/test_sensor.py @@ -7,7 +7,7 @@ from enturclient.api import RESOURCE from enturclient.consts import ATTR_EXPECTED_AT, ATTR_ROUTE, ATTR_STOP_ID import requests_mock -from homeassistant.components.sensor.entur_public_transport import ( +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 @@ -40,7 +40,7 @@ class TestEnturPublicTransportSensor(unittest.TestCase): @requests_mock.Mocker() @patch( - 'homeassistant.components.sensor.entur_public_transport.dt_util.now', + '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.""" diff --git a/tests/components/everlights/__init__.py b/tests/components/everlights/__init__.py new file mode 100644 index 00000000000..3d1e3fbb91e --- /dev/null +++ b/tests/components/everlights/__init__.py @@ -0,0 +1 @@ +"""Tests for the everlights component.""" diff --git a/tests/components/light/test_everlights.py b/tests/components/everlights/test_light.py similarity index 89% rename from tests/components/light/test_everlights.py rename to tests/components/everlights/test_light.py index 026e7927c8d..26480f779a3 100644 --- a/tests/components/light/test_everlights.py +++ b/tests/components/everlights/test_light.py @@ -1,5 +1,5 @@ """The tests for the everlights component.""" -from homeassistant.components.light import everlights +from homeassistant.components.everlights import light as everlights def test_color_rgb_to_int(): diff --git a/tests/components/facebox/__init__.py b/tests/components/facebox/__init__.py new file mode 100644 index 00000000000..fbbb6640e40 --- /dev/null +++ b/tests/components/facebox/__init__.py @@ -0,0 +1 @@ +"""Tests for the facebox component.""" diff --git a/tests/components/image_processing/test_facebox.py b/tests/components/facebox/test_image_processing.py similarity index 97% rename from tests/components/image_processing/test_facebox.py rename to tests/components/facebox/test_image_processing.py index 62e47a07c08..a4e7890f3fa 100644 --- a/tests/components/image_processing/test_facebox.py +++ b/tests/components/facebox/test_image_processing.py @@ -12,7 +12,7 @@ from homeassistant.const import ( HTTP_BAD_REQUEST, HTTP_OK, HTTP_UNAUTHORIZED, STATE_UNKNOWN) from homeassistant.setup import async_setup_component import homeassistant.components.image_processing as ip -import homeassistant.components.image_processing.facebox as fb +import homeassistant.components.facebox.image_processing as fb MOCK_IP = '192.168.0.1' MOCK_PORT = '8080' @@ -72,8 +72,8 @@ VALID_CONFIG = { @pytest.fixture def mock_healthybox(): """Mock fb.check_box_health.""" - check_box_health = 'homeassistant.components.image_processing.' \ - 'facebox.check_box_health' + check_box_health = 'homeassistant.components.facebox.image_processing.' \ + 'check_box_health' with patch(check_box_health, return_value=MOCK_BOX_ID) as _mock_healthybox: yield _mock_healthybox @@ -81,7 +81,7 @@ def mock_healthybox(): @pytest.fixture def mock_isfile(): """Mock os.path.isfile.""" - with patch('homeassistant.components.image_processing.facebox.cv.isfile', + with patch('homeassistant.components.facebox.image_processing.cv.isfile', return_value=True) as _mock_isfile: yield _mock_isfile @@ -98,7 +98,7 @@ def mock_image(): def mock_open_file(): """Mock open.""" mopen = mock_open() - with patch('homeassistant.components.image_processing.facebox.open', + with patch('homeassistant.components.facebox.image_processing.open', mopen, create=True) as _mock_open: yield _mock_open diff --git a/tests/components/fail2ban/__init__.py b/tests/components/fail2ban/__init__.py new file mode 100644 index 00000000000..ed1aef1e833 --- /dev/null +++ b/tests/components/fail2ban/__init__.py @@ -0,0 +1 @@ +"""Tests for the fail2ban component.""" diff --git a/tests/components/sensor/test_fail2ban.py b/tests/components/fail2ban/test_sensor.py similarity index 92% rename from tests/components/sensor/test_fail2ban.py rename to tests/components/fail2ban/test_sensor.py index f9be2cbf985..1958dd683e2 100644 --- a/tests/components/sensor/test_fail2ban.py +++ b/tests/components/fail2ban/test_sensor.py @@ -5,7 +5,7 @@ from unittest.mock import Mock, patch from mock_open import MockOpen from homeassistant.setup import setup_component -from homeassistant.components.sensor.fail2ban import ( +from homeassistant.components.fail2ban.sensor import ( BanSensor, BanLogParser, STATE_CURRENT_BANS, STATE_ALL_BANS ) @@ -78,7 +78,7 @@ class TestBanSensor(unittest.TestCase): } } mock_fh = MockOpen() - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() @@ -94,7 +94,7 @@ class TestBanSensor(unittest.TestCase): } } mock_fh = MockOpen() - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() @@ -106,7 +106,7 @@ class TestBanSensor(unittest.TestCase): sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('single_ban')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() @@ -122,7 +122,7 @@ class TestBanSensor(unittest.TestCase): sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('ipv6_ban')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() @@ -139,7 +139,7 @@ class TestBanSensor(unittest.TestCase): sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('multi_ban')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() @@ -155,7 +155,7 @@ class TestBanSensor(unittest.TestCase): sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('unban_all')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() @@ -170,7 +170,7 @@ class TestBanSensor(unittest.TestCase): sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('unban_one')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() @@ -188,7 +188,7 @@ class TestBanSensor(unittest.TestCase): assert sensor1.name == 'fail2ban jail_one' assert sensor2.name == 'fail2ban jail_two' mock_fh = MockOpen(read_data=fake_log('multi_jail')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor1.update() sensor2.update() @@ -208,7 +208,7 @@ class TestBanSensor(unittest.TestCase): sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('single_ban')) - with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, + with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() assert sensor.state == '111.111.111.111' diff --git a/tests/components/fido/__init__.py b/tests/components/fido/__init__.py new file mode 100644 index 00000000000..e54f0f39fb2 --- /dev/null +++ b/tests/components/fido/__init__.py @@ -0,0 +1 @@ +"""Tests for the fido component.""" diff --git a/tests/components/sensor/test_fido.py b/tests/components/fido/test_sensor.py similarity index 98% rename from tests/components/sensor/test_fido.py rename to tests/components/fido/test_sensor.py index c0bbadc043b..98a5fba4b46 100644 --- a/tests/components/sensor/test_fido.py +++ b/tests/components/fido/test_sensor.py @@ -5,7 +5,7 @@ import sys from unittest.mock import MagicMock from homeassistant.bootstrap import async_setup_component -from homeassistant.components.sensor import fido +from homeassistant.components.fido import sensor as fido from tests.common import assert_setup_component diff --git a/tests/components/file/__init__.py b/tests/components/file/__init__.py new file mode 100644 index 00000000000..60027c0bfa7 --- /dev/null +++ b/tests/components/file/__init__.py @@ -0,0 +1 @@ +"""Tests for the file component.""" diff --git a/tests/components/sensor/test_file.py b/tests/components/file/test_sensor.py similarity index 94% rename from tests/components/sensor/test_file.py rename to tests/components/file/test_sensor.py index 3ac37b989c7..65def0b5443 100644 --- a/tests/components/sensor/test_file.py +++ b/tests/components/file/test_sensor.py @@ -39,7 +39,7 @@ class TestFileSensor(unittest.TestCase): } m_open = MockOpen(read_data='43\n45\n21') - with patch('homeassistant.components.sensor.file.open', m_open, + with patch('homeassistant.components.file.sensor.open', m_open, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() @@ -64,7 +64,7 @@ class TestFileSensor(unittest.TestCase): '{"temperature": 26, "humidity": 36}' m_open = MockOpen(read_data=data) - with patch('homeassistant.components.sensor.file.open', m_open, + with patch('homeassistant.components.file.sensor.open', m_open, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() @@ -85,7 +85,7 @@ class TestFileSensor(unittest.TestCase): } m_open = MockOpen(read_data='') - with patch('homeassistant.components.sensor.file.open', m_open, + with patch('homeassistant.components.file.sensor.open', m_open, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() diff --git a/tests/components/filesize/__init__.py b/tests/components/filesize/__init__.py new file mode 100644 index 00000000000..02876267482 --- /dev/null +++ b/tests/components/filesize/__init__.py @@ -0,0 +1 @@ +"""Tests for the filesize component.""" diff --git a/tests/components/sensor/test_filesize.py b/tests/components/filesize/test_sensor.py similarity index 96% rename from tests/components/sensor/test_filesize.py rename to tests/components/filesize/test_sensor.py index b8b5c67da7d..dbc78bbd29b 100644 --- a/tests/components/sensor/test_filesize.py +++ b/tests/components/filesize/test_sensor.py @@ -2,7 +2,7 @@ import unittest import os -from homeassistant.components.sensor.filesize import CONF_FILE_PATHS +from homeassistant.components.filesize.sensor import CONF_FILE_PATHS from homeassistant.setup import setup_component from tests.common import get_test_home_assistant diff --git a/tests/components/filter/__init__.py b/tests/components/filter/__init__.py new file mode 100644 index 00000000000..f69eb7fac1f --- /dev/null +++ b/tests/components/filter/__init__.py @@ -0,0 +1 @@ +"""Tests for the filter component.""" diff --git a/tests/components/sensor/test_filter.py b/tests/components/filter/test_sensor.py similarity index 99% rename from tests/components/sensor/test_filter.py rename to tests/components/filter/test_sensor.py index 29308f2a83d..d14eba405a8 100644 --- a/tests/components/sensor/test_filter.py +++ b/tests/components/filter/test_sensor.py @@ -3,7 +3,7 @@ from datetime import timedelta import unittest from unittest.mock import patch -from homeassistant.components.sensor.filter import ( +from homeassistant.components.filter.sensor import ( LowPassFilter, OutlierFilter, ThrottleFilter, TimeSMAFilter, RangeFilter, TimeThrottleFilter) import homeassistant.util.dt as dt_util diff --git a/tests/components/flux/__init__.py b/tests/components/flux/__init__.py new file mode 100644 index 00000000000..69feef62c52 --- /dev/null +++ b/tests/components/flux/__init__.py @@ -0,0 +1 @@ +"""Tests for the flux component.""" diff --git a/tests/components/switch/test_flux.py b/tests/components/flux/test_switch.py similarity index 97% rename from tests/components/switch/test_flux.py rename to tests/components/flux/test_switch.py index 84db2fd0df0..c43f1071e33 100644 --- a/tests/components/switch/test_flux.py +++ b/tests/components/flux/test_switch.py @@ -181,7 +181,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -227,7 +227,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -319,7 +319,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -369,7 +369,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -420,7 +420,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -519,7 +519,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -569,7 +569,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -616,7 +616,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -665,7 +665,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -727,7 +727,7 @@ class TestSwitchFlux(unittest.TestCase): print('sunset {}'.format(sunset_time)) return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -779,7 +779,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): @@ -823,7 +823,7 @@ class TestSwitchFlux(unittest.TestCase): return sunrise_time return sunset_time - with patch('homeassistant.components.switch.flux.dt_utcnow', + with patch('homeassistant.components.flux.switch.dt_utcnow', return_value=test_time), \ patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): diff --git a/tests/components/folder/__init__.py b/tests/components/folder/__init__.py new file mode 100644 index 00000000000..9d5477c09ac --- /dev/null +++ b/tests/components/folder/__init__.py @@ -0,0 +1 @@ +"""Tests for the folder component.""" diff --git a/tests/components/sensor/test_folder.py b/tests/components/folder/test_sensor.py similarity index 96% rename from tests/components/sensor/test_folder.py rename to tests/components/folder/test_sensor.py index e4f97fbaa46..8e445dd4f53 100644 --- a/tests/components/sensor/test_folder.py +++ b/tests/components/folder/test_sensor.py @@ -2,7 +2,7 @@ import unittest import os -from homeassistant.components.sensor.folder import CONF_FOLDER_PATHS +from homeassistant.components.folder.sensor import CONF_FOLDER_PATHS from homeassistant.setup import setup_component from tests.common import get_test_home_assistant diff --git a/tests/components/foobot/__init__.py b/tests/components/foobot/__init__.py new file mode 100644 index 00000000000..88d916d997f --- /dev/null +++ b/tests/components/foobot/__init__.py @@ -0,0 +1 @@ +"""Tests for the foobot component.""" diff --git a/tests/components/sensor/test_foobot.py b/tests/components/foobot/test_sensor.py similarity index 98% rename from tests/components/sensor/test_foobot.py rename to tests/components/foobot/test_sensor.py index 71f2d0481bf..7187fd1fea9 100644 --- a/tests/components/sensor/test_foobot.py +++ b/tests/components/foobot/test_sensor.py @@ -7,7 +7,7 @@ import pytest import homeassistant.components.sensor as sensor -from homeassistant.components.sensor import foobot +from homeassistant.components.foobot import sensor as foobot from homeassistant.const import (TEMP_CELSIUS) from homeassistant.exceptions import PlatformNotReady from homeassistant.setup import async_setup_component diff --git a/tests/components/generic/__init__.py b/tests/components/generic/__init__.py new file mode 100644 index 00000000000..1477d9d9a2f --- /dev/null +++ b/tests/components/generic/__init__.py @@ -0,0 +1 @@ +"""Tests for the generic component.""" diff --git a/tests/components/camera/test_generic.py b/tests/components/generic/test_camera.py similarity index 100% rename from tests/components/camera/test_generic.py rename to tests/components/generic/test_camera.py diff --git a/tests/components/geo_json_events/__init__.py b/tests/components/geo_json_events/__init__.py new file mode 100644 index 00000000000..09a76738530 --- /dev/null +++ b/tests/components/geo_json_events/__init__.py @@ -0,0 +1 @@ +"""Tests for the geo_json_events component.""" diff --git a/tests/components/geo_location/test_geo_json_events.py b/tests/components/geo_json_events/test_geo_location.py similarity index 99% rename from tests/components/geo_location/test_geo_json_events.py rename to tests/components/geo_json_events/test_geo_location.py index 46d1ed630c4..7004b78c148 100644 --- a/tests/components/geo_location/test_geo_json_events.py +++ b/tests/components/geo_json_events/test_geo_location.py @@ -3,7 +3,7 @@ from asynctest.mock import patch, MagicMock, call from homeassistant.components import geo_location from homeassistant.components.geo_location import ATTR_SOURCE -from homeassistant.components.geo_location.geo_json_events import \ +from homeassistant.components.geo_json_events.geo_location import \ SCAN_INTERVAL, ATTR_EXTERNAL_ID, SIGNAL_DELETE_ENTITY, SIGNAL_UPDATE_ENTITY from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_START, \ CONF_RADIUS, ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_FRIENDLY_NAME, \ diff --git a/tests/components/geo_rss_events/__init__.py b/tests/components/geo_rss_events/__init__.py new file mode 100644 index 00000000000..dd10a455699 --- /dev/null +++ b/tests/components/geo_rss_events/__init__.py @@ -0,0 +1 @@ +"""Tests for the geo_rss_events component.""" diff --git a/tests/components/sensor/test_geo_rss_events.py b/tests/components/geo_rss_events/test_sensor.py similarity index 99% rename from tests/components/sensor/test_geo_rss_events.py rename to tests/components/geo_rss_events/test_sensor.py index fae5016cf91..f6101b13ea6 100644 --- a/tests/components/sensor/test_geo_rss_events.py +++ b/tests/components/geo_rss_events/test_sensor.py @@ -9,7 +9,7 @@ from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME, \ from homeassistant.setup import setup_component from tests.common import get_test_home_assistant, \ assert_setup_component, fire_time_changed -import homeassistant.components.sensor.geo_rss_events as geo_rss_events +import homeassistant.components.geo_rss_events.sensor as geo_rss_events import homeassistant.util.dt as dt_util URL = 'http://geo.rss.local/geo_rss_events.xml' diff --git a/tests/components/google_pubsub/__init__.py b/tests/components/google_pubsub/__init__.py new file mode 100644 index 00000000000..2387617705b --- /dev/null +++ b/tests/components/google_pubsub/__init__.py @@ -0,0 +1 @@ +"""Tests for google_pubsub component.""" diff --git a/tests/components/google_wifi/__init__.py b/tests/components/google_wifi/__init__.py new file mode 100644 index 00000000000..8b95a49ecef --- /dev/null +++ b/tests/components/google_wifi/__init__.py @@ -0,0 +1 @@ +"""Tests for the google_wifi component.""" diff --git a/tests/components/sensor/test_google_wifi.py b/tests/components/google_wifi/test_sensor.py similarity index 99% rename from tests/components/sensor/test_google_wifi.py rename to tests/components/google_wifi/test_sensor.py index 989cd13c5d6..ee0cf3b0658 100644 --- a/tests/components/sensor/test_google_wifi.py +++ b/tests/components/google_wifi/test_sensor.py @@ -7,7 +7,7 @@ import requests_mock from homeassistant import core as ha from homeassistant.setup import setup_component -import homeassistant.components.sensor.google_wifi as google_wifi +import homeassistant.components.google_wifi.sensor as google_wifi from homeassistant.const import STATE_UNKNOWN from homeassistant.util import dt as dt_util diff --git a/tests/components/hddtemp/__init__.py b/tests/components/hddtemp/__init__.py new file mode 100644 index 00000000000..11d53678a39 --- /dev/null +++ b/tests/components/hddtemp/__init__.py @@ -0,0 +1 @@ +"""Tests for the hddtemp component.""" diff --git a/tests/components/sensor/test_hddtemp.py b/tests/components/hddtemp/test_sensor.py similarity index 100% rename from tests/components/sensor/test_hddtemp.py rename to tests/components/hddtemp/test_sensor.py diff --git a/tests/components/history_stats/__init__.py b/tests/components/history_stats/__init__.py new file mode 100644 index 00000000000..34018f171bb --- /dev/null +++ b/tests/components/history_stats/__init__.py @@ -0,0 +1 @@ +"""Tests for the history_stats component.""" diff --git a/tests/components/sensor/test_history_stats.py b/tests/components/history_stats/test_sensor.py similarity index 99% rename from tests/components/sensor/test_history_stats.py rename to tests/components/history_stats/test_sensor.py index eda0034c922..05a2d585d16 100644 --- a/tests/components/sensor/test_history_stats.py +++ b/tests/components/history_stats/test_sensor.py @@ -9,7 +9,7 @@ from homeassistant.helpers import template from homeassistant.const import STATE_UNKNOWN from homeassistant.setup import setup_component -from homeassistant.components.sensor.history_stats import HistoryStatsSensor +from homeassistant.components.history_stats.sensor import HistoryStatsSensor import homeassistant.core as ha from homeassistant.helpers.template import Template import homeassistant.util.dt as dt_util diff --git a/tests/components/homekit_controller/__init__.py b/tests/components/homekit_controller/__init__.py new file mode 100644 index 00000000000..364f8d7beeb --- /dev/null +++ b/tests/components/homekit_controller/__init__.py @@ -0,0 +1 @@ +"""Tests for homekit_controller component.""" diff --git a/tests/components/honeywell/__init__.py b/tests/components/honeywell/__init__.py new file mode 100644 index 00000000000..7c6b4ca78c6 --- /dev/null +++ b/tests/components/honeywell/__init__.py @@ -0,0 +1 @@ +"""Tests for honeywell component.""" diff --git a/tests/components/climate/test_honeywell.py b/tests/components/honeywell/test_climate.py similarity index 96% rename from tests/components/climate/test_honeywell.py rename to tests/components/honeywell/test_climate.py index b01b5b35c35..e8e0c0a2929 100644 --- a/tests/components/climate/test_honeywell.py +++ b/tests/components/honeywell/test_climate.py @@ -11,7 +11,7 @@ from homeassistant.const import ( from homeassistant.components.climate.const import ( ATTR_FAN_MODE, ATTR_OPERATION_MODE, ATTR_FAN_LIST, ATTR_OPERATION_LIST) -import homeassistant.components.climate.honeywell as honeywell +import homeassistant.components.honeywell.climate as honeywell import pytest @@ -19,8 +19,8 @@ class TestHoneywell(unittest.TestCase): """A test class for Honeywell themostats.""" @mock.patch('somecomfort.SomeComfort') - @mock.patch('homeassistant.components.climate.' - 'honeywell.HoneywellUSThermostat') + @mock.patch('homeassistant.components.honeywell.' + 'climate.HoneywellUSThermostat') def test_setup_us(self, mock_ht, mock_sc): """Test for the US setup.""" config = { @@ -105,8 +105,8 @@ class TestHoneywell(unittest.TestCase): assert not add_entities.called @mock.patch('somecomfort.SomeComfort') - @mock.patch('homeassistant.components.climate.' - 'honeywell.HoneywellUSThermostat') + @mock.patch('homeassistant.components.honeywell.' + 'climate.HoneywellUSThermostat') def _test_us_filtered_devices(self, mock_ht, mock_sc, loc=None, dev=None): """Test for US filtered thermostats.""" config = { @@ -171,7 +171,7 @@ class TestHoneywell(unittest.TestCase): assert [mock.sentinel.loc2dev1] == devices @mock.patch('evohomeclient.EvohomeClient') - @mock.patch('homeassistant.components.climate.honeywell.' + @mock.patch('homeassistant.components.honeywell.climate.' 'RoundThermostat') def test_eu_setup_full_config(self, mock_round, mock_evo): """Test the EU setup with complete configuration.""" @@ -198,7 +198,7 @@ class TestHoneywell(unittest.TestCase): assert 2 == add_entities.call_count @mock.patch('evohomeclient.EvohomeClient') - @mock.patch('homeassistant.components.climate.honeywell.' + @mock.patch('homeassistant.components.honeywell.climate.' 'RoundThermostat') def test_eu_setup_partial_config(self, mock_round, mock_evo): """Test the EU setup with partial configuration.""" @@ -222,7 +222,7 @@ class TestHoneywell(unittest.TestCase): ]) @mock.patch('evohomeclient.EvohomeClient') - @mock.patch('homeassistant.components.climate.honeywell.' + @mock.patch('homeassistant.components.honeywell.climate.' 'RoundThermostat') def test_eu_setup_bad_temp(self, mock_round, mock_evo): """Test the EU setup with invalid temperature.""" @@ -237,7 +237,7 @@ class TestHoneywell(unittest.TestCase): honeywell.PLATFORM_SCHEMA(config) @mock.patch('evohomeclient.EvohomeClient') - @mock.patch('homeassistant.components.climate.honeywell.' + @mock.patch('homeassistant.components.honeywell.climate.' 'RoundThermostat') def test_eu_setup_error(self, mock_round, mock_evo): """Test the EU setup with errors.""" diff --git a/tests/components/hydroquebec/__init__.py b/tests/components/hydroquebec/__init__.py new file mode 100644 index 00000000000..1342395d265 --- /dev/null +++ b/tests/components/hydroquebec/__init__.py @@ -0,0 +1 @@ +"""Tests for the hydroquebec component.""" diff --git a/tests/components/sensor/test_hydroquebec.py b/tests/components/hydroquebec/test_sensor.py similarity index 97% rename from tests/components/sensor/test_hydroquebec.py rename to tests/components/hydroquebec/test_sensor.py index eed5ab4a6a6..e7883bb9853 100644 --- a/tests/components/sensor/test_hydroquebec.py +++ b/tests/components/hydroquebec/test_sensor.py @@ -5,7 +5,7 @@ import sys from unittest.mock import MagicMock from homeassistant.bootstrap import async_setup_component -from homeassistant.components.sensor import hydroquebec +from homeassistant.components.hydroquebec import sensor as hydroquebec from tests.common import assert_setup_component diff --git a/tests/components/imap_email_content/__init__.py b/tests/components/imap_email_content/__init__.py new file mode 100644 index 00000000000..2c7e5692366 --- /dev/null +++ b/tests/components/imap_email_content/__init__.py @@ -0,0 +1 @@ +"""Tests for the imap_email_content component.""" diff --git a/tests/components/sensor/test_imap_email_content.py b/tests/components/imap_email_content/test_sensor.py similarity index 98% rename from tests/components/sensor/test_imap_email_content.py rename to tests/components/imap_email_content/test_sensor.py index a0cfb783d0b..2afb3c39341 100644 --- a/tests/components/sensor/test_imap_email_content.py +++ b/tests/components/imap_email_content/test_sensor.py @@ -8,7 +8,8 @@ import unittest from homeassistant.helpers.template import Template from homeassistant.helpers.event import track_state_change -from homeassistant.components.sensor import imap_email_content +from homeassistant.components.imap_email_content \ + import sensor as imap_email_content from tests.common import get_test_home_assistant diff --git a/tests/components/integration/__init__.py b/tests/components/integration/__init__.py new file mode 100644 index 00000000000..5dd7e83b062 --- /dev/null +++ b/tests/components/integration/__init__.py @@ -0,0 +1 @@ +"""Tests for the integration component.""" diff --git a/tests/components/sensor/test_integration.py b/tests/components/integration/test_sensor.py similarity index 100% rename from tests/components/sensor/test_integration.py rename to tests/components/integration/test_sensor.py diff --git a/tests/components/islamic_prayer_times/__init__.py b/tests/components/islamic_prayer_times/__init__.py new file mode 100644 index 00000000000..4a2f0002516 --- /dev/null +++ b/tests/components/islamic_prayer_times/__init__.py @@ -0,0 +1 @@ +"""Tests for the islamic_prayer_times component.""" diff --git a/tests/components/sensor/test_islamic_prayer_times.py b/tests/components/islamic_prayer_times/test_sensor.py similarity index 97% rename from tests/components/sensor/test_islamic_prayer_times.py rename to tests/components/islamic_prayer_times/test_sensor.py index 2c132ac5a8f..5310fb6befb 100644 --- a/tests/components/sensor/test_islamic_prayer_times.py +++ b/tests/components/islamic_prayer_times/test_sensor.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from unittest.mock import patch from homeassistant.setup import async_setup_component -from homeassistant.components.sensor.islamic_prayer_times import \ +from homeassistant.components.islamic_prayer_times.sensor import \ IslamicPrayerTimesData from tests.common import MockDependency import homeassistant.util.dt as dt_util @@ -154,7 +154,7 @@ async def test_islamic_prayer_times_sensor_update(hass): future = midnight_dt + timedelta(days=1, minutes=1) with patch( - 'homeassistant.components.sensor.islamic_prayer_times' + 'homeassistant.components.islamic_prayer_times.sensor' '.dt_util.utcnow', return_value=future): async_fire_time_changed(hass, future) diff --git a/tests/components/jewish_calendar/__init__.py b/tests/components/jewish_calendar/__init__.py new file mode 100644 index 00000000000..d6928c189e8 --- /dev/null +++ b/tests/components/jewish_calendar/__init__.py @@ -0,0 +1 @@ +"""Tests for the jewish_calendar component.""" diff --git a/tests/components/sensor/test_jewish_calendar.py b/tests/components/jewish_calendar/test_sensor.py similarity index 99% rename from tests/components/sensor/test_jewish_calendar.py rename to tests/components/jewish_calendar/test_sensor.py index 7243874a41d..6a7f9249fe1 100644 --- a/tests/components/sensor/test_jewish_calendar.py +++ b/tests/components/jewish_calendar/test_sensor.py @@ -9,7 +9,7 @@ import pytest from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.util.dt import get_time_zone, set_default_time_zone from homeassistant.setup import setup_component -from homeassistant.components.sensor.jewish_calendar import ( +from homeassistant.components.jewish_calendar.sensor import ( JewishCalSensor, CANDLE_LIGHT_DEFAULT) from tests.common import get_test_home_assistant diff --git a/tests/components/light/test_litejet.py b/tests/components/litejet/test_light.py similarity index 100% rename from tests/components/light/test_litejet.py rename to tests/components/litejet/test_light.py diff --git a/tests/components/scene/test_litejet.py b/tests/components/litejet/test_scene.py similarity index 100% rename from tests/components/scene/test_litejet.py rename to tests/components/litejet/test_scene.py diff --git a/tests/components/switch/test_litejet.py b/tests/components/litejet/test_switch.py similarity index 100% rename from tests/components/switch/test_litejet.py rename to tests/components/litejet/test_switch.py diff --git a/tests/components/local_file/__init__.py b/tests/components/local_file/__init__.py new file mode 100644 index 00000000000..1a76dca2377 --- /dev/null +++ b/tests/components/local_file/__init__.py @@ -0,0 +1 @@ +"""Tests for the local_file component.""" diff --git a/tests/components/camera/test_local_file.py b/tests/components/local_file/test_camera.py similarity index 96% rename from tests/components/camera/test_local_file.py rename to tests/components/local_file/test_camera.py index f2dbb294136..3d70e3f77a7 100644 --- a/tests/components/camera/test_local_file.py +++ b/tests/components/local_file/test_camera.py @@ -3,7 +3,7 @@ import asyncio from unittest import mock from homeassistant.components.camera import DOMAIN -from homeassistant.components.camera.local_file import ( +from homeassistant.components.local_file.camera import ( SERVICE_UPDATE_FILE_PATH) from homeassistant.setup import async_setup_component @@ -28,7 +28,7 @@ def test_loading_file(hass, hass_client): m_open = mock.mock_open(read_data=b'hello') with mock.patch( - 'homeassistant.components.camera.local_file.open', + 'homeassistant.components.local_file.camera.open', m_open, create=True ): resp = yield from client.get('/api/camera_proxy/camera.config_test') @@ -87,7 +87,7 @@ def test_camera_content_type(hass, hass_client): image = 'hello' m_open = mock.mock_open(read_data=image.encode()) - with mock.patch('homeassistant.components.camera.local_file.open', + with mock.patch('homeassistant.components.local_file.camera.open', m_open, create=True): resp_1 = yield from client.get('/api/camera_proxy/camera.test_jpg') resp_2 = yield from client.get('/api/camera_proxy/camera.test_png') diff --git a/tests/components/london_air/__init__.py b/tests/components/london_air/__init__.py new file mode 100644 index 00000000000..a7d3e8bb1db --- /dev/null +++ b/tests/components/london_air/__init__.py @@ -0,0 +1 @@ +"""Tests for the london_air component.""" diff --git a/tests/components/sensor/test_london_air.py b/tests/components/london_air/test_sensor.py similarity index 95% rename from tests/components/sensor/test_london_air.py rename to tests/components/london_air/test_sensor.py index e6a11b6724c..d30cc39d808 100644 --- a/tests/components/sensor/test_london_air.py +++ b/tests/components/london_air/test_sensor.py @@ -2,7 +2,7 @@ import unittest import requests_mock -from homeassistant.components.sensor.london_air import ( +from homeassistant.components.london_air.sensor import ( CONF_LOCATIONS, URL) from homeassistant.setup import setup_component from tests.common import load_fixture, get_test_home_assistant diff --git a/tests/components/manual/__init__.py b/tests/components/manual/__init__.py new file mode 100644 index 00000000000..ac0fc57ac6e --- /dev/null +++ b/tests/components/manual/__init__.py @@ -0,0 +1 @@ +"""Tests for manual component.""" diff --git a/tests/components/manual_mqtt/__init__.py b/tests/components/manual_mqtt/__init__.py new file mode 100644 index 00000000000..e1b3f3c4e38 --- /dev/null +++ b/tests/components/manual_mqtt/__init__.py @@ -0,0 +1 @@ +"""Tests for manual_mqtt component.""" diff --git a/tests/components/climate/test_melissa.py b/tests/components/melissa/test_climate.py similarity index 95% rename from tests/components/climate/test_melissa.py rename to tests/components/melissa/test_climate.py index 6b77981a914..b6dc1a8de4f 100644 --- a/tests/components/climate/test_melissa.py +++ b/tests/components/melissa/test_climate.py @@ -2,9 +2,9 @@ from unittest.mock import Mock, patch import json -from homeassistant.components.climate.melissa import MelissaClimate +from homeassistant.components.melissa.climate import MelissaClimate -from homeassistant.components.climate import melissa +from homeassistant.components.melissa import climate as melissa from homeassistant.components.climate.const import ( SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, SUPPORT_ON_OFF, SUPPORT_FAN_MODE, STATE_HEAT, STATE_FAN_ONLY, STATE_DRY, @@ -56,7 +56,7 @@ def melissa_mock(): async def test_setup_platform(hass): """Test setup_platform.""" - with patch("homeassistant.components.climate.melissa.MelissaClimate" + with patch("homeassistant.components.melissa.climate.MelissaClimate" ) as mocked_thermostat: api = melissa_mock() device = (await api.async_fetch_devices())[_SERIAL] @@ -319,9 +319,9 @@ async def test_send(hass): async def test_update(hass): """Test update.""" - with patch('homeassistant.components.melissa'): - with patch('homeassistant.components.climate.melissa._LOGGER.warning' - ) as mocked_warning: + with patch('homeassistant.components.melissa.climate._LOGGER.warning' + ) as mocked_warning: + with patch('homeassistant.components.melissa'): api = melissa_mock() device = (await api.async_fetch_devices())[_SERIAL] thermostat = MelissaClimate(api, _SERIAL, device) @@ -374,9 +374,9 @@ async def test_melissa_fan_to_hass(hass): async def test_hass_mode_to_melissa(hass): """Test for hass operations to melssa.""" - with patch('homeassistant.components.melissa'): - with patch('homeassistant.components.climate.melissa._LOGGER.warning' - ) as mocked_warning: + with patch('homeassistant.components.melissa.climate._LOGGER.warning' + ) as mocked_warning: + with patch('homeassistant.components.melissa'): api = melissa_mock() device = (await api.async_fetch_devices())[_SERIAL] thermostat = MelissaClimate(api, _SERIAL, device) @@ -391,9 +391,10 @@ async def test_hass_mode_to_melissa(hass): async def test_hass_fan_to_melissa(hass): """Test for translate melissa states to hass.""" - with patch('homeassistant.components.melissa'): - with patch('homeassistant.components.climate.melissa._LOGGER.warning' - ) as mocked_warning: + with patch( + 'homeassistant.components.melissa.climate._LOGGER.warning' + ) as mocked_warning: + with patch('homeassistant.components.melissa'): api = melissa_mock() device = (await api.async_fetch_devices())[_SERIAL] thermostat = MelissaClimate(api, _SERIAL, device) diff --git a/tests/components/meraki/__init__.py b/tests/components/meraki/__init__.py new file mode 100644 index 00000000000..e0ab288f466 --- /dev/null +++ b/tests/components/meraki/__init__.py @@ -0,0 +1 @@ +"""Tests for the meraki component.""" diff --git a/tests/components/device_tracker/test_meraki.py b/tests/components/meraki/test_device_tracker.py similarity index 97% rename from tests/components/device_tracker/test_meraki.py rename to tests/components/meraki/test_device_tracker.py index 02b24c4c476..f4ae96f3ed2 100644 --- a/tests/components/device_tracker/test_meraki.py +++ b/tests/components/meraki/test_device_tracker.py @@ -4,12 +4,12 @@ import json import pytest -from homeassistant.components.device_tracker.meraki import ( +from homeassistant.components.meraki.device_tracker import ( CONF_VALIDATOR, CONF_SECRET) from homeassistant.setup import async_setup_component import homeassistant.components.device_tracker as device_tracker from homeassistant.const import CONF_PLATFORM -from homeassistant.components.device_tracker.meraki import URL +from homeassistant.components.meraki.device_tracker import URL @pytest.fixture diff --git a/tests/components/mfi/__init__.py b/tests/components/mfi/__init__.py new file mode 100644 index 00000000000..e0c3ebbec33 --- /dev/null +++ b/tests/components/mfi/__init__.py @@ -0,0 +1 @@ +"""Tests for the mfi component.""" diff --git a/tests/components/sensor/test_mfi.py b/tests/components/mfi/test_sensor.py similarity index 98% rename from tests/components/sensor/test_mfi.py rename to tests/components/mfi/test_sensor.py index d30618a330d..58566f7bcb2 100644 --- a/tests/components/sensor/test_mfi.py +++ b/tests/components/mfi/test_sensor.py @@ -6,7 +6,7 @@ import requests from homeassistant.setup import setup_component import homeassistant.components.sensor as sensor -import homeassistant.components.sensor.mfi as mfi +import homeassistant.components.mfi.sensor as mfi from homeassistant.const import TEMP_CELSIUS from tests.common import get_test_home_assistant @@ -104,7 +104,7 @@ class TestMfiSensorSetup(unittest.TestCase): ) @mock.patch('mficlient.client.MFiClient') - @mock.patch('homeassistant.components.sensor.mfi.MfiSensor') + @mock.patch('homeassistant.components.mfi.sensor.MfiSensor') def test_setup_adds_proper_devices(self, mock_sensor, mock_client): """Test if setup adds devices.""" ports = {i: mock.MagicMock(model=model) diff --git a/tests/components/switch/test_mfi.py b/tests/components/mfi/test_switch.py similarity index 95% rename from tests/components/switch/test_mfi.py rename to tests/components/mfi/test_switch.py index 222efee0e46..07e8c5c6912 100644 --- a/tests/components/switch/test_mfi.py +++ b/tests/components/mfi/test_switch.py @@ -4,8 +4,8 @@ import unittest.mock as mock from homeassistant.setup import setup_component import homeassistant.components.switch as switch -import homeassistant.components.switch.mfi as mfi -from tests.components.sensor import test_mfi as test_mfi_sensor +import homeassistant.components.mfi.switch as mfi +from tests.components.mfi import test_sensor as test_mfi_sensor from tests.common import get_test_home_assistant @@ -29,7 +29,7 @@ class TestMfiSwitchSetup(test_mfi_sensor.TestMfiSensorSetup): } @mock.patch('mficlient.client.MFiClient') - @mock.patch('homeassistant.components.switch.mfi.MfiSwitch') + @mock.patch('homeassistant.components.mfi.switch.MfiSwitch') def test_setup_adds_proper_devices(self, mock_switch, mock_client): """Test if setup adds devices.""" ports = {i: mock.MagicMock(model=model) diff --git a/tests/components/mhz19/__init__.py b/tests/components/mhz19/__init__.py new file mode 100644 index 00000000000..a35660a3726 --- /dev/null +++ b/tests/components/mhz19/__init__.py @@ -0,0 +1 @@ +"""Tests for the mhz19 component.""" diff --git a/tests/components/sensor/test_mhz19.py b/tests/components/mhz19/test_sensor.py similarity index 98% rename from tests/components/sensor/test_mhz19.py rename to tests/components/mhz19/test_sensor.py index 003cc199954..a5ca5ec10d0 100644 --- a/tests/components/sensor/test_mhz19.py +++ b/tests/components/mhz19/test_sensor.py @@ -4,7 +4,7 @@ from unittest.mock import patch, DEFAULT, Mock from homeassistant.setup import setup_component from homeassistant.components.sensor import DOMAIN -import homeassistant.components.sensor.mhz19 as mhz19 +import homeassistant.components.mhz19.sensor as mhz19 from homeassistant.const import TEMP_FAHRENHEIT from tests.common import get_test_home_assistant, assert_setup_component diff --git a/tests/components/microsoft_face_detect/__init__.py b/tests/components/microsoft_face_detect/__init__.py new file mode 100644 index 00000000000..df079d019eb --- /dev/null +++ b/tests/components/microsoft_face_detect/__init__.py @@ -0,0 +1 @@ +"""Tests for the microsoft_face_detect component.""" diff --git a/tests/components/image_processing/test_microsoft_face_detect.py b/tests/components/microsoft_face_detect/test_image_processing.py similarity index 98% rename from tests/components/image_processing/test_microsoft_face_detect.py rename to tests/components/microsoft_face_detect/test_image_processing.py index 9e65386a3c6..a6599fb4032 100644 --- a/tests/components/image_processing/test_microsoft_face_detect.py +++ b/tests/components/microsoft_face_detect/test_image_processing.py @@ -105,7 +105,7 @@ class TestMicrosoftFaceDetect: """Stop everything that was started.""" self.hass.stop() - @patch('homeassistant.components.image_processing.microsoft_face_detect.' + @patch('homeassistant.components.microsoft_face_detect.image_processing.' 'MicrosoftFaceDetectEntity.should_poll', new_callable=PropertyMock(return_value=False)) def test_ms_detect_process_image(self, poll_mock, aioclient_mock): diff --git a/tests/components/microsoft_face_identify/__init__.py b/tests/components/microsoft_face_identify/__init__.py new file mode 100644 index 00000000000..0b31c83b942 --- /dev/null +++ b/tests/components/microsoft_face_identify/__init__.py @@ -0,0 +1 @@ +"""Tests for the microsoft_face_identify component.""" diff --git a/tests/components/image_processing/test_microsoft_face_identify.py b/tests/components/microsoft_face_identify/test_image_processing.py similarity index 98% rename from tests/components/image_processing/test_microsoft_face_identify.py rename to tests/components/microsoft_face_identify/test_image_processing.py index c24e758da97..ccee60eed48 100644 --- a/tests/components/image_processing/test_microsoft_face_identify.py +++ b/tests/components/microsoft_face_identify/test_image_processing.py @@ -106,7 +106,7 @@ class TestMicrosoftFaceIdentify: """Stop everything that was started.""" self.hass.stop() - @patch('homeassistant.components.image_processing.microsoft_face_identify.' + @patch('homeassistant.components.microsoft_face_identify.image_processing.' 'MicrosoftFaceIdentifyEntity.should_poll', new_callable=PropertyMock(return_value=False)) def test_ms_identify_process_image(self, poll_mock, aioclient_mock): diff --git a/tests/components/min_max/__init__.py b/tests/components/min_max/__init__.py new file mode 100644 index 00000000000..3767049b537 --- /dev/null +++ b/tests/components/min_max/__init__.py @@ -0,0 +1 @@ +"""Tests for the min_max component.""" diff --git a/tests/components/sensor/test_min_max.py b/tests/components/min_max/test_sensor.py similarity index 100% rename from tests/components/sensor/test_min_max.py rename to tests/components/min_max/test_sensor.py diff --git a/tests/components/monoprice/__init__.py b/tests/components/monoprice/__init__.py new file mode 100644 index 00000000000..3b2ac0426b9 --- /dev/null +++ b/tests/components/monoprice/__init__.py @@ -0,0 +1 @@ +"""Tests for the monoprice component.""" diff --git a/tests/components/media_player/test_monoprice.py b/tests/components/monoprice/test_media_player.py similarity index 99% rename from tests/components/media_player/test_monoprice.py rename to tests/components/monoprice/test_media_player.py index d6374cf9dd7..eeccd6fbf78 100644 --- a/tests/components/media_player/test_monoprice.py +++ b/tests/components/monoprice/test_media_player.py @@ -10,7 +10,7 @@ from homeassistant.components.media_player.const import ( from homeassistant.const import STATE_ON, STATE_OFF import tests.common -from homeassistant.components.media_player.monoprice import ( +from homeassistant.components.monoprice.media_player import ( DATA_MONOPRICE, PLATFORM_SCHEMA, SERVICE_SNAPSHOT, SERVICE_RESTORE, setup_platform) import pytest diff --git a/tests/components/moon/__init__.py b/tests/components/moon/__init__.py new file mode 100644 index 00000000000..13afa4696d2 --- /dev/null +++ b/tests/components/moon/__init__.py @@ -0,0 +1 @@ +"""Tests for the moon component.""" diff --git a/tests/components/sensor/test_moon.py b/tests/components/moon/test_sensor.py similarity index 92% rename from tests/components/sensor/test_moon.py rename to tests/components/moon/test_sensor.py index 14c93678a0f..6dd177fe1d0 100644 --- a/tests/components/sensor/test_moon.py +++ b/tests/components/moon/test_sensor.py @@ -23,7 +23,7 @@ class TestMoonSensor(unittest.TestCase): """Stop everything that was started.""" self.hass.stop() - @patch('homeassistant.components.sensor.moon.dt_util.utcnow', + @patch('homeassistant.components.moon.sensor.dt_util.utcnow', return_value=DAY1) def test_moon_day1(self, mock_request): """Test the Moon sensor.""" @@ -39,7 +39,7 @@ class TestMoonSensor(unittest.TestCase): state = self.hass.states.get('sensor.moon_day1') assert state.state == 'waxing_crescent' - @patch('homeassistant.components.sensor.moon.dt_util.utcnow', + @patch('homeassistant.components.moon.sensor.dt_util.utcnow', return_value=DAY2) def test_moon_day2(self, mock_request): """Test the Moon sensor.""" diff --git a/tests/components/mqtt_json/__init__.py b/tests/components/mqtt_json/__init__.py new file mode 100644 index 00000000000..75eea75e604 --- /dev/null +++ b/tests/components/mqtt_json/__init__.py @@ -0,0 +1 @@ +"""Tests for the mqtt_json component.""" diff --git a/tests/components/device_tracker/test_mqtt_json.py b/tests/components/mqtt_json/test_device_tracker.py similarity index 98% rename from tests/components/device_tracker/test_mqtt_json.py rename to tests/components/mqtt_json/test_device_tracker.py index 252d40338fc..ea87be42bd6 100644 --- a/tests/components/device_tracker/test_mqtt_json.py +++ b/tests/components/mqtt_json/test_device_tracker.py @@ -39,7 +39,7 @@ async def test_ensure_device_tracker_platform_validation(hass): """Check that Qos was added by validation.""" assert 'qos' in config - with patch('homeassistant.components.device_tracker.mqtt_json.' + with patch('homeassistant.components.mqtt_json.device_tracker.' 'async_setup_scanner', autospec=True, side_effect=mock_setup_scanner) as mock_sp: diff --git a/tests/components/mqtt_room/__init__.py b/tests/components/mqtt_room/__init__.py new file mode 100644 index 00000000000..2fcfed6c813 --- /dev/null +++ b/tests/components/mqtt_room/__init__.py @@ -0,0 +1 @@ +"""Tests for the mqtt_room component.""" diff --git a/tests/components/sensor/test_mqtt_room.py b/tests/components/mqtt_room/test_sensor.py similarity index 100% rename from tests/components/sensor/test_mqtt_room.py rename to tests/components/mqtt_room/test_sensor.py diff --git a/tests/components/nsw_fuel_station/__init__.py b/tests/components/nsw_fuel_station/__init__.py new file mode 100644 index 00000000000..965a21529ec --- /dev/null +++ b/tests/components/nsw_fuel_station/__init__.py @@ -0,0 +1 @@ +"""Tests for the nsw_fuel_station component.""" diff --git a/tests/components/sensor/test_nsw_fuel_station.py b/tests/components/nsw_fuel_station/test_sensor.py similarity index 100% rename from tests/components/sensor/test_nsw_fuel_station.py rename to tests/components/nsw_fuel_station/test_sensor.py diff --git a/tests/components/nsw_rural_fire_service_feed/__init__.py b/tests/components/nsw_rural_fire_service_feed/__init__.py new file mode 100644 index 00000000000..691fd2f01ac --- /dev/null +++ b/tests/components/nsw_rural_fire_service_feed/__init__.py @@ -0,0 +1 @@ +"""Tests for the nsw_rural_fire_service_feed component.""" diff --git a/tests/components/geo_location/test_nsw_rural_fire_service_feed.py b/tests/components/nsw_rural_fire_service_feed/test_geo_location.py similarity index 99% rename from tests/components/geo_location/test_nsw_rural_fire_service_feed.py rename to tests/components/nsw_rural_fire_service_feed/test_geo_location.py index 3254fd570ce..facc0604058 100644 --- a/tests/components/geo_location/test_nsw_rural_fire_service_feed.py +++ b/tests/components/nsw_rural_fire_service_feed/test_geo_location.py @@ -4,7 +4,7 @@ from asynctest.mock import patch, MagicMock, call from homeassistant.components import geo_location from homeassistant.components.geo_location import ATTR_SOURCE -from homeassistant.components.geo_location.nsw_rural_fire_service_feed import \ +from homeassistant.components.nsw_rural_fire_service_feed.geo_location import \ ATTR_EXTERNAL_ID, SCAN_INTERVAL, ATTR_CATEGORY, ATTR_FIRE, ATTR_LOCATION, \ ATTR_COUNCIL_AREA, ATTR_STATUS, ATTR_TYPE, ATTR_SIZE, \ ATTR_RESPONSIBLE_AGENCY, ATTR_PUBLICATION_DATE diff --git a/tests/components/climate/test_nuheat.py b/tests/components/nuheat/test_climate.py similarity index 97% rename from tests/components/climate/test_nuheat.py rename to tests/components/nuheat/test_climate.py index 19919d25954..6a697e5cb0e 100644 --- a/tests/components/climate/test_nuheat.py +++ b/tests/components/nuheat/test_climate.py @@ -9,7 +9,7 @@ from homeassistant.components.climate.const import ( SUPPORT_TARGET_TEMPERATURE, STATE_HEAT, STATE_IDLE) -import homeassistant.components.climate.nuheat as nuheat +import homeassistant.components.nuheat.climate as nuheat from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT SCHEDULE_HOLD = 3 @@ -57,7 +57,7 @@ class TestNuHeat(unittest.TestCase): """Stop hass.""" self.hass.stop() - @patch("homeassistant.components.climate.nuheat.NuHeatThermostat") + @patch("homeassistant.components.nuheat.climate.NuHeatThermostat") def test_setup_platform(self, mocked_thermostat): """Test setup_platform.""" mocked_thermostat.return_value = self.thermostat @@ -73,7 +73,7 @@ class TestNuHeat(unittest.TestCase): nuheat.setup_platform(self.hass, config, add_entities, discovery_info) add_entities.assert_called_once_with(thermostats, True) - @patch("homeassistant.components.climate.nuheat.NuHeatThermostat") + @patch("homeassistant.components.nuheat.climate.NuHeatThermostat") def test_resume_program_service(self, mocked_thermostat): """Test resume program service.""" mocked_thermostat.return_value = self.thermostat diff --git a/tests/components/nx584/__init__.py b/tests/components/nx584/__init__.py new file mode 100644 index 00000000000..7c2ce89e7b7 --- /dev/null +++ b/tests/components/nx584/__init__.py @@ -0,0 +1 @@ +"""Tests for nx584 component.""" diff --git a/tests/components/binary_sensor/test_nx584.py b/tests/components/nx584/test_binary_sensor.py similarity index 95% rename from tests/components/binary_sensor/test_nx584.py rename to tests/components/nx584/test_binary_sensor.py index 3239ddbc436..53516885f30 100644 --- a/tests/components/binary_sensor/test_nx584.py +++ b/tests/components/nx584/test_binary_sensor.py @@ -5,7 +5,7 @@ from unittest import mock from nx584 import client as nx584_client -from homeassistant.components.binary_sensor import nx584 +from homeassistant.components.nx584 import binary_sensor as nx584 from homeassistant.setup import setup_component from tests.common import get_test_home_assistant @@ -42,8 +42,8 @@ class TestNX584SensorSetup(unittest.TestCase): self.hass.stop() self._mock_client.stop() - @mock.patch('homeassistant.components.binary_sensor.nx584.NX584Watcher') - @mock.patch('homeassistant.components.binary_sensor.nx584.NX584ZoneSensor') + @mock.patch('homeassistant.components.nx584.binary_sensor.NX584Watcher') + @mock.patch('homeassistant.components.nx584.binary_sensor.NX584ZoneSensor') def test_setup_defaults(self, mock_nx, mock_watcher): """Test the setup with no configuration.""" add_entities = mock.MagicMock() @@ -61,8 +61,8 @@ class TestNX584SensorSetup(unittest.TestCase): assert nx584_client.Client.call_args == \ mock.call('http://localhost:5007') - @mock.patch('homeassistant.components.binary_sensor.nx584.NX584Watcher') - @mock.patch('homeassistant.components.binary_sensor.nx584.NX584ZoneSensor') + @mock.patch('homeassistant.components.nx584.binary_sensor.NX584Watcher') + @mock.patch('homeassistant.components.nx584.binary_sensor.NX584ZoneSensor') def test_setup_full_config(self, mock_nx, mock_watcher): """Test the setup with full configuration.""" config = { diff --git a/tests/components/openalpr_cloud/__init__.py b/tests/components/openalpr_cloud/__init__.py new file mode 100644 index 00000000000..21e2605966b --- /dev/null +++ b/tests/components/openalpr_cloud/__init__.py @@ -0,0 +1 @@ +"""Tests for the openalpr_cloud component.""" diff --git a/tests/components/image_processing/test_openalpr_cloud.py b/tests/components/openalpr_cloud/test_image_processing.py similarity index 98% rename from tests/components/image_processing/test_openalpr_cloud.py rename to tests/components/openalpr_cloud/test_image_processing.py index 8a71db7fb7b..d7be9effc8a 100644 --- a/tests/components/image_processing/test_openalpr_cloud.py +++ b/tests/components/openalpr_cloud/test_image_processing.py @@ -5,7 +5,7 @@ from unittest.mock import patch, PropertyMock from homeassistant.core import callback from homeassistant.setup import setup_component from homeassistant.components import camera, image_processing as ip -from homeassistant.components.image_processing.openalpr_cloud import ( +from homeassistant.components.openalpr_cloud.image_processing import ( OPENALPR_API_URL) from tests.common import ( @@ -126,7 +126,7 @@ class TestOpenAlprCloud: }, } - with patch('homeassistant.components.image_processing.openalpr_cloud.' + with patch('homeassistant.components.openalpr_cloud.image_processing.' 'OpenAlprCloudEntity.should_poll', new_callable=PropertyMock(return_value=False)): setup_component(self.hass, ip.DOMAIN, config) diff --git a/tests/components/openalpr_local/__init__.py b/tests/components/openalpr_local/__init__.py new file mode 100644 index 00000000000..36b7703491f --- /dev/null +++ b/tests/components/openalpr_local/__init__.py @@ -0,0 +1 @@ +"""Tests for the openalpr_local component.""" diff --git a/tests/components/image_processing/test_openalpr_local.py b/tests/components/openalpr_local/test_image_processing.py similarity index 98% rename from tests/components/image_processing/test_openalpr_local.py rename to tests/components/openalpr_local/test_image_processing.py index 6d860da3313..5a5a2604c6c 100644 --- a/tests/components/image_processing/test_openalpr_local.py +++ b/tests/components/openalpr_local/test_image_processing.py @@ -118,7 +118,7 @@ class TestOpenAlprLocal: }, } - with patch('homeassistant.components.image_processing.openalpr_local.' + with patch('homeassistant.components.openalpr_local.image_processing.' 'OpenAlprLocalEntity.should_poll', new_callable=PropertyMock(return_value=False)): setup_component(self.hass, ip.DOMAIN, config) diff --git a/tests/components/openhardwaremonitor/__init__.py b/tests/components/openhardwaremonitor/__init__.py new file mode 100644 index 00000000000..9a27cc3254f --- /dev/null +++ b/tests/components/openhardwaremonitor/__init__.py @@ -0,0 +1 @@ +"""Tests for the openhardwaremonitor component.""" diff --git a/tests/components/sensor/test_openhardwaremonitor.py b/tests/components/openhardwaremonitor/test_sensor.py similarity index 100% rename from tests/components/sensor/test_openhardwaremonitor.py rename to tests/components/openhardwaremonitor/test_sensor.py diff --git a/tests/components/push/__init__.py b/tests/components/push/__init__.py new file mode 100644 index 00000000000..1ef6ee48b29 --- /dev/null +++ b/tests/components/push/__init__.py @@ -0,0 +1 @@ +"""Tests for the push component.""" diff --git a/tests/components/camera/test_push.py b/tests/components/push/test_camera.py similarity index 100% rename from tests/components/camera/test_push.py rename to tests/components/push/test_camera.py diff --git a/tests/components/radarr/__init__.py b/tests/components/radarr/__init__.py new file mode 100644 index 00000000000..13cc76db384 --- /dev/null +++ b/tests/components/radarr/__init__.py @@ -0,0 +1 @@ +"""Tests for the radarr component.""" diff --git a/tests/components/sensor/test_radarr.py b/tests/components/radarr/test_sensor.py similarity index 99% rename from tests/components/sensor/test_radarr.py rename to tests/components/radarr/test_sensor.py index 5ac23f76251..3263cb07a0d 100644 --- a/tests/components/sensor/test_radarr.py +++ b/tests/components/radarr/test_sensor.py @@ -3,7 +3,7 @@ import unittest import pytest -from homeassistant.components.sensor import radarr +import homeassistant.components.radarr.sensor as radarr from tests.common import get_test_home_assistant diff --git a/tests/components/random/__init__.py b/tests/components/random/__init__.py new file mode 100644 index 00000000000..a9137dde998 --- /dev/null +++ b/tests/components/random/__init__.py @@ -0,0 +1 @@ +"""Tests for random component.""" diff --git a/tests/components/binary_sensor/test_random.py b/tests/components/random/test_binary_sensor.py similarity index 100% rename from tests/components/binary_sensor/test_random.py rename to tests/components/random/test_binary_sensor.py diff --git a/tests/components/sensor/test_random.py b/tests/components/random/test_sensor.py similarity index 100% rename from tests/components/sensor/test_random.py rename to tests/components/random/test_sensor.py diff --git a/tests/components/rest/__init__.py b/tests/components/rest/__init__.py new file mode 100644 index 00000000000..b2dc9f1839f --- /dev/null +++ b/tests/components/rest/__init__.py @@ -0,0 +1 @@ +"""Tests for rest component.""" diff --git a/tests/components/binary_sensor/test_rest.py b/tests/components/rest/test_binary_sensor.py similarity index 99% rename from tests/components/binary_sensor/test_rest.py rename to tests/components/rest/test_binary_sensor.py index befeca07251..3a91edcbcb6 100644 --- a/tests/components/binary_sensor/test_rest.py +++ b/tests/components/rest/test_binary_sensor.py @@ -10,7 +10,7 @@ import requests_mock from homeassistant.exceptions import PlatformNotReady from homeassistant.setup import setup_component import homeassistant.components.binary_sensor as binary_sensor -import homeassistant.components.binary_sensor.rest as rest +import homeassistant.components.rest.binary_sensor as rest from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.helpers import template diff --git a/tests/components/sensor/test_rest.py b/tests/components/rest/test_sensor.py similarity index 98% rename from tests/components/sensor/test_rest.py rename to tests/components/rest/test_sensor.py index 343cc696763..bb957b5b68d 100644 --- a/tests/components/sensor/test_rest.py +++ b/tests/components/rest/test_sensor.py @@ -10,7 +10,7 @@ import requests_mock from homeassistant.exceptions import PlatformNotReady from homeassistant.setup import setup_component import homeassistant.components.sensor as sensor -import homeassistant.components.sensor.rest as rest +import homeassistant.components.rest.sensor as rest from homeassistant.helpers.config_validation import template from tests.common import get_test_home_assistant, assert_setup_component @@ -214,7 +214,7 @@ class TestRestSensor(unittest.TestCase): assert 'some_json_value' == \ self.sensor.device_state_attributes['key'] - @patch('homeassistant.components.sensor.rest._LOGGER') + @patch('homeassistant.components.rest.sensor._LOGGER') def test_update_with_json_attrs_no_data(self, mock_logger): """Test attributes when no JSON result fetched.""" self.rest.update = Mock('rest.RestData.update', @@ -227,7 +227,7 @@ class TestRestSensor(unittest.TestCase): assert {} == self.sensor.device_state_attributes assert mock_logger.warning.called - @patch('homeassistant.components.sensor.rest._LOGGER') + @patch('homeassistant.components.rest.sensor._LOGGER') def test_update_with_json_attrs_not_dict(self, mock_logger): """Test attributes get extracted from a JSON result.""" self.rest.update = Mock('rest.RestData.update', @@ -241,7 +241,7 @@ class TestRestSensor(unittest.TestCase): assert {} == self.sensor.device_state_attributes assert mock_logger.warning.called - @patch('homeassistant.components.sensor.rest._LOGGER') + @patch('homeassistant.components.rest.sensor._LOGGER') def test_update_with_json_attrs_bad_JSON(self, mock_logger): """Test attributes get extracted from a JSON result.""" self.rest.update = Mock('rest.RestData.update', diff --git a/tests/components/switch/test_rest.py b/tests/components/rest/test_switch.py similarity index 99% rename from tests/components/switch/test_rest.py rename to tests/components/rest/test_switch.py index 56f3f0eebc5..11cf2334ae8 100644 --- a/tests/components/switch/test_rest.py +++ b/tests/components/rest/test_switch.py @@ -3,7 +3,7 @@ import asyncio import aiohttp -import homeassistant.components.switch.rest as rest +import homeassistant.components.rest.switch as rest from homeassistant.setup import setup_component from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.helpers.template import Template diff --git a/tests/components/binary_sensor/test_rflink.py b/tests/components/rflink/test_binary_sensor.py similarity index 100% rename from tests/components/binary_sensor/test_rflink.py rename to tests/components/rflink/test_binary_sensor.py diff --git a/tests/components/cover/test_rflink.py b/tests/components/rflink/test_cover.py similarity index 100% rename from tests/components/cover/test_rflink.py rename to tests/components/rflink/test_cover.py diff --git a/tests/components/light/test_rflink.py b/tests/components/rflink/test_light.py similarity index 100% rename from tests/components/light/test_rflink.py rename to tests/components/rflink/test_light.py diff --git a/tests/components/sensor/test_rflink.py b/tests/components/rflink/test_sensor.py similarity index 100% rename from tests/components/sensor/test_rflink.py rename to tests/components/rflink/test_sensor.py diff --git a/tests/components/switch/test_rflink.py b/tests/components/rflink/test_switch.py similarity index 100% rename from tests/components/switch/test_rflink.py rename to tests/components/rflink/test_switch.py diff --git a/tests/components/binary_sensor/test_ring.py b/tests/components/ring/test_binary_sensor.py similarity index 97% rename from tests/components/binary_sensor/test_ring.py rename to tests/components/ring/test_binary_sensor.py index 924ed01f9e8..84ff9672850 100644 --- a/tests/components/binary_sensor/test_ring.py +++ b/tests/components/ring/test_binary_sensor.py @@ -3,7 +3,7 @@ import os import unittest import requests_mock -from homeassistant.components.binary_sensor import ring +from homeassistant.components.ring import binary_sensor as ring from homeassistant.components import ring as base_ring from tests.components.ring.test_init import ATTRIBUTION, VALID_CONFIG diff --git a/tests/components/sensor/test_ring.py b/tests/components/ring/test_sensor.py similarity index 98% rename from tests/components/sensor/test_ring.py rename to tests/components/ring/test_sensor.py index c54f22af8dc..872e647aced 100644 --- a/tests/components/sensor/test_ring.py +++ b/tests/components/ring/test_sensor.py @@ -3,7 +3,7 @@ import os import unittest import requests_mock -from homeassistant.components.sensor import ring +import homeassistant.components.ring.sensor as ring from homeassistant.components import ring as base_ring from tests.components.ring.test_init import ATTRIBUTION, VALID_CONFIG diff --git a/tests/components/rmvtransport/__init__.py b/tests/components/rmvtransport/__init__.py new file mode 100644 index 00000000000..db55bd32248 --- /dev/null +++ b/tests/components/rmvtransport/__init__.py @@ -0,0 +1 @@ +"""Tests for the rmvtransport component.""" diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/rmvtransport/test_sensor.py similarity index 100% rename from tests/components/sensor/test_rmvtransport.py rename to tests/components/rmvtransport/test_sensor.py diff --git a/tests/components/samsungtv/__init__.py b/tests/components/samsungtv/__init__.py new file mode 100644 index 00000000000..4ad1622c6ca --- /dev/null +++ b/tests/components/samsungtv/__init__.py @@ -0,0 +1 @@ +"""Tests for the samsungtv component.""" diff --git a/tests/components/media_player/test_samsungtv.py b/tests/components/samsungtv/test_media_player.py similarity index 96% rename from tests/components/media_player/test_samsungtv.py rename to tests/components/samsungtv/test_media_player.py index a74f476981a..b175a722036 100644 --- a/tests/components/media_player/test_samsungtv.py +++ b/tests/components/samsungtv/test_media_player.py @@ -10,7 +10,7 @@ import pytest import tests.common from homeassistant.components.media_player.const import SUPPORT_TURN_ON, \ MEDIA_TYPE_CHANNEL, MEDIA_TYPE_URL -from homeassistant.components.media_player.samsungtv import setup_platform, \ +from homeassistant.components.samsungtv.media_player import setup_platform, \ CONF_TIMEOUT, SamsungTVDevice, SUPPORT_SAMSUNGTV from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, STATE_ON, \ CONF_MAC, STATE_OFF @@ -71,7 +71,7 @@ class TestSamsungTv(unittest.TestCase): def test_setup(self, samsung_mock, wol_mock): """Testing setup of platform.""" with mock.patch( - 'homeassistant.components.media_player.samsungtv.socket'): + 'homeassistant.components.samsungtv.media_player.socket'): add_entities = mock.Mock() setup_platform( self.hass, WORKING_CONFIG, add_entities) @@ -81,7 +81,7 @@ class TestSamsungTv(unittest.TestCase): def test_setup_discovery(self, samsung_mock, wol_mock): """Testing setup of platform with discovery.""" with mock.patch( - 'homeassistant.components.media_player.samsungtv.socket'): + 'homeassistant.components.samsungtv.media_player.socket'): add_entities = mock.Mock() setup_platform(self.hass, {}, add_entities, discovery_info=DISCOVERY_INFO) @@ -89,11 +89,11 @@ class TestSamsungTv(unittest.TestCase): @MockDependency('samsungctl') @MockDependency('wakeonlan') @mock.patch( - 'homeassistant.components.media_player.samsungtv._LOGGER.warning') + 'homeassistant.components.samsungtv.media_player._LOGGER.warning') def test_setup_none(self, samsung_mock, wol_mock, mocked_warn): """Testing setup of platform with no data.""" with mock.patch( - 'homeassistant.components.media_player.samsungtv.socket'): + 'homeassistant.components.samsungtv.media_player.socket'): add_entities = mock.Mock() setup_platform(self.hass, {}, add_entities, discovery_info=None) @@ -214,7 +214,7 @@ class TestSamsungTv(unittest.TestCase): self.device.send_key.assert_called_once_with('KEY_POWEROFF') @mock.patch( - 'homeassistant.components.media_player.samsungtv._LOGGER.debug') + 'homeassistant.components.samsungtv.media_player._LOGGER.debug') def test_turn_off_os_error(self, mocked_debug): """Test for turn_off with OSError.""" _remote = mock.Mock() diff --git a/tests/components/season/__init__.py b/tests/components/season/__init__.py new file mode 100644 index 00000000000..91002d8d918 --- /dev/null +++ b/tests/components/season/__init__.py @@ -0,0 +1 @@ +"""Tests for the season component.""" diff --git a/tests/components/sensor/test_season.py b/tests/components/season/test_sensor.py similarity index 99% rename from tests/components/sensor/test_season.py rename to tests/components/season/test_sensor.py index 20432857aa3..eab4d708630 100644 --- a/tests/components/sensor/test_season.py +++ b/tests/components/season/test_sensor.py @@ -4,7 +4,7 @@ import unittest from datetime import datetime from homeassistant.setup import setup_component -import homeassistant.components.sensor.season as season +import homeassistant.components.season.sensor as season from tests.common import get_test_home_assistant diff --git a/tests/components/sigfox/__init__.py b/tests/components/sigfox/__init__.py new file mode 100644 index 00000000000..f0054389136 --- /dev/null +++ b/tests/components/sigfox/__init__.py @@ -0,0 +1 @@ +"""Tests for the sigfox component.""" diff --git a/tests/components/sensor/test_sigfox.py b/tests/components/sigfox/test_sensor.py similarity index 97% rename from tests/components/sensor/test_sigfox.py rename to tests/components/sigfox/test_sensor.py index c785d272f55..17706e263f3 100644 --- a/tests/components/sensor/test_sigfox.py +++ b/tests/components/sigfox/test_sensor.py @@ -3,7 +3,7 @@ import re import requests_mock import unittest -from homeassistant.components.sensor.sigfox import ( +from homeassistant.components.sigfox.sensor import ( API_URL, CONF_API_LOGIN, CONF_API_PASSWORD) from homeassistant.setup import setup_component from tests.common import get_test_home_assistant diff --git a/tests/components/simulated/__init__.py b/tests/components/simulated/__init__.py new file mode 100644 index 00000000000..501fbab603a --- /dev/null +++ b/tests/components/simulated/__init__.py @@ -0,0 +1 @@ +"""Tests for the simulated component.""" diff --git a/tests/components/sensor/test_simulated.py b/tests/components/simulated/test_sensor.py similarity index 96% rename from tests/components/sensor/test_simulated.py rename to tests/components/simulated/test_sensor.py index c51e281d123..f97fa7b070f 100644 --- a/tests/components/sensor/test_simulated.py +++ b/tests/components/simulated/test_sensor.py @@ -3,7 +3,7 @@ import unittest from tests.common import get_test_home_assistant -from homeassistant.components.sensor.simulated import ( +from homeassistant.components.simulated.sensor import ( CONF_AMP, CONF_FWHM, CONF_MEAN, CONF_PERIOD, CONF_PHASE, CONF_SEED, CONF_UNIT, CONF_RELATIVE_TO_EPOCH, DEFAULT_AMP, DEFAULT_FWHM, DEFAULT_MEAN, DEFAULT_NAME, DEFAULT_PHASE, DEFAULT_SEED, DEFAULT_RELATIVE_TO_EPOCH) diff --git a/tests/components/binary_sensor/test_sleepiq.py b/tests/components/sleepiq/test_binary_sensor.py similarity index 96% rename from tests/components/binary_sensor/test_sleepiq.py rename to tests/components/sleepiq/test_binary_sensor.py index 524093d76b3..66748f1379c 100644 --- a/tests/components/binary_sensor/test_sleepiq.py +++ b/tests/components/sleepiq/test_binary_sensor.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock import requests_mock from homeassistant.setup import setup_component -from homeassistant.components.binary_sensor import sleepiq +from homeassistant.components.sleepiq import binary_sensor as sleepiq from tests.components.sleepiq.test_init import mock_responses from tests.common import get_test_home_assistant diff --git a/tests/components/sensor/test_sleepiq.py b/tests/components/sleepiq/test_sensor.py similarity index 96% rename from tests/components/sensor/test_sleepiq.py rename to tests/components/sleepiq/test_sensor.py index c667a6a2cdf..8b5c039011f 100644 --- a/tests/components/sensor/test_sleepiq.py +++ b/tests/components/sleepiq/test_sensor.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock import requests_mock from homeassistant.setup import setup_component -from homeassistant.components.sensor import sleepiq +import homeassistant.components.sleepiq.sensor as sleepiq from tests.components.sleepiq.test_init import mock_responses from tests.common import get_test_home_assistant diff --git a/tests/components/sonarr/__init__.py b/tests/components/sonarr/__init__.py new file mode 100644 index 00000000000..573575cedc0 --- /dev/null +++ b/tests/components/sonarr/__init__.py @@ -0,0 +1 @@ +"""Tests for the sonarr component.""" diff --git a/tests/components/sensor/test_sonarr.py b/tests/components/sonarr/test_sensor.py similarity index 99% rename from tests/components/sensor/test_sonarr.py rename to tests/components/sonarr/test_sensor.py index e8c7c17d006..1d2933a795b 100644 --- a/tests/components/sensor/test_sonarr.py +++ b/tests/components/sonarr/test_sensor.py @@ -5,7 +5,7 @@ from datetime import datetime import pytest -from homeassistant.components.sensor import sonarr +import homeassistant.components.sonarr.sensor as sonarr from tests.common import get_test_home_assistant diff --git a/tests/components/soundtouch/__init__.py b/tests/components/soundtouch/__init__.py new file mode 100644 index 00000000000..8cd8088b9d9 --- /dev/null +++ b/tests/components/soundtouch/__init__.py @@ -0,0 +1 @@ +"""Tests for the soundtouch component.""" diff --git a/tests/components/media_player/test_soundtouch.py b/tests/components/soundtouch/test_media_player.py similarity index 99% rename from tests/components/media_player/test_soundtouch.py rename to tests/components/soundtouch/test_media_player.py index 2a763bfc706..87f41b11d95 100644 --- a/tests/components/media_player/test_soundtouch.py +++ b/tests/components/soundtouch/test_media_player.py @@ -5,7 +5,7 @@ from unittest import mock from libsoundtouch.device import SoundTouchDevice as STD, Status, Volume, \ Preset, Config -from homeassistant.components.media_player import soundtouch +from homeassistant.components.soundtouch import media_player as soundtouch from homeassistant.const import ( STATE_OFF, STATE_PAUSED, STATE_PLAYING) from tests.common import get_test_home_assistant diff --git a/tests/components/sql/__init__.py b/tests/components/sql/__init__.py new file mode 100644 index 00000000000..5981fdbd24e --- /dev/null +++ b/tests/components/sql/__init__.py @@ -0,0 +1 @@ +"""Tests for the sql component.""" diff --git a/tests/components/sensor/test_sql.py b/tests/components/sql/test_sensor.py similarity index 96% rename from tests/components/sensor/test_sql.py rename to tests/components/sql/test_sensor.py index c966af653d2..3e8af25299d 100644 --- a/tests/components/sensor/test_sql.py +++ b/tests/components/sql/test_sensor.py @@ -3,7 +3,7 @@ import unittest import pytest import voluptuous as vol -from homeassistant.components.sensor.sql import validate_sql_select +from homeassistant.components.sql.sensor import validate_sql_select from homeassistant.setup import setup_component from homeassistant.const import STATE_UNKNOWN diff --git a/tests/components/srp_energy/__init__.py b/tests/components/srp_energy/__init__.py new file mode 100644 index 00000000000..2a278cf1d38 --- /dev/null +++ b/tests/components/srp_energy/__init__.py @@ -0,0 +1 @@ +"""Tests for the srp_energy component.""" diff --git a/tests/components/sensor/test_srp_energy.py b/tests/components/srp_energy/test_sensor.py similarity index 100% rename from tests/components/sensor/test_srp_energy.py rename to tests/components/srp_energy/test_sensor.py diff --git a/tests/components/startca/__init__.py b/tests/components/startca/__init__.py new file mode 100644 index 00000000000..f2eaf7079b4 --- /dev/null +++ b/tests/components/startca/__init__.py @@ -0,0 +1 @@ +"""Tests for the startca component.""" diff --git a/tests/components/sensor/test_startca.py b/tests/components/startca/test_sensor.py similarity index 99% rename from tests/components/sensor/test_startca.py rename to tests/components/startca/test_sensor.py index eb5ca2203e0..79e1d3cab13 100644 --- a/tests/components/sensor/test_startca.py +++ b/tests/components/startca/test_sensor.py @@ -1,7 +1,7 @@ """Tests for the Start.ca sensor platform.""" import asyncio from homeassistant.bootstrap import async_setup_component -from homeassistant.components.sensor.startca import StartcaData +from homeassistant.components.startca.sensor import StartcaData from homeassistant.helpers.aiohttp_client import async_get_clientsession diff --git a/tests/components/statistics/__init__.py b/tests/components/statistics/__init__.py new file mode 100644 index 00000000000..fdd623ec02c --- /dev/null +++ b/tests/components/statistics/__init__.py @@ -0,0 +1 @@ +"""Tests for the statistics component.""" diff --git a/tests/components/sensor/test_statistics.py b/tests/components/statistics/test_sensor.py similarity index 97% rename from tests/components/sensor/test_statistics.py rename to tests/components/statistics/test_sensor.py index 1bd3ee73616..580b11f5ccd 100644 --- a/tests/components/sensor/test_statistics.py +++ b/tests/components/statistics/test_sensor.py @@ -5,7 +5,7 @@ import statistics import pytest from homeassistant.setup import setup_component -from homeassistant.components.sensor.statistics import StatisticsSensor +from homeassistant.components.statistics.sensor import StatisticsSensor from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, STATE_UNKNOWN) from homeassistant.util import dt as dt_util @@ -165,7 +165,7 @@ class TestStatisticsSensor(unittest.TestCase): def mock_now(): return mock_data['return_time'] - with patch('homeassistant.components.sensor.statistics.dt_util.utcnow', + with patch('homeassistant.components.statistics.sensor.dt_util.utcnow', new=mock_now): assert setup_component(self.hass, 'sensor', { 'sensor': { @@ -201,7 +201,7 @@ class TestStatisticsSensor(unittest.TestCase): def mock_now(): return mock_data['return_time'] - with patch('homeassistant.components.sensor.statistics.dt_util.utcnow', + with patch('homeassistant.components.statistics.sensor.dt_util.utcnow', new=mock_now): assert setup_component(self.hass, 'sensor', { 'sensor': { @@ -284,7 +284,7 @@ class TestStatisticsSensor(unittest.TestCase): # enable the recorder init_recorder_component(self.hass) - with patch('homeassistant.components.sensor.statistics.dt_util.utcnow', + with patch('homeassistant.components.statistics.sensor.dt_util.utcnow', new=mock_now), \ patch.object(StatisticsSensor, '_purge_old', mock_purge): # store some values diff --git a/tests/components/tcp/__init__.py b/tests/components/tcp/__init__.py new file mode 100644 index 00000000000..56410765ce6 --- /dev/null +++ b/tests/components/tcp/__init__.py @@ -0,0 +1 @@ +"""Tests for tcp component.""" diff --git a/tests/components/binary_sensor/test_tcp.py b/tests/components/tcp/test_binary_sensor.py similarity index 82% rename from tests/components/binary_sensor/test_tcp.py rename to tests/components/tcp/test_binary_sensor.py index cb21d7d2c71..aeddd8d117d 100644 --- a/tests/components/binary_sensor/test_tcp.py +++ b/tests/components/tcp/test_binary_sensor.py @@ -1,12 +1,13 @@ """The tests for the TCP binary sensor platform.""" import unittest -from unittest.mock import patch, Mock +from unittest.mock import Mock, patch +from homeassistant.components.tcp import binary_sensor as bin_tcp +import homeassistant.components.tcp.sensor as tcp from homeassistant.setup import setup_component -from homeassistant.components.binary_sensor import tcp as bin_tcp -from homeassistant.components.sensor import tcp -from tests.common import (get_test_home_assistant, assert_setup_component) -from tests.components.sensor import test_tcp + +from tests.common import assert_setup_component, get_test_home_assistant +import tests.components.tcp.test_sensor as test_tcp class TestTCPBinarySensor(unittest.TestCase): @@ -36,7 +37,7 @@ class TestTCPBinarySensor(unittest.TestCase): } }) - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_setup_platform_devices(self, mock_update): """Check the supplied config and call add_entities with sensor.""" add_entities = Mock() @@ -46,7 +47,7 @@ class TestTCPBinarySensor(unittest.TestCase): assert isinstance( add_entities.call_args[0][0][0], bin_tcp.TcpBinarySensor) - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_is_on_true(self, mock_update): """Check the return that _state is value_on.""" sensor = bin_tcp.TcpBinarySensor( @@ -55,7 +56,7 @@ class TestTCPBinarySensor(unittest.TestCase): print(sensor._state) assert sensor.is_on - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_is_on_false(self, mock_update): """Check the return that _state is not the same as value_on.""" sensor = bin_tcp.TcpBinarySensor( diff --git a/tests/components/sensor/test_tcp.py b/tests/components/tcp/test_sensor.py similarity index 91% rename from tests/components/sensor/test_tcp.py rename to tests/components/tcp/test_sensor.py index e89e8db861a..f926dfdf7ef 100644 --- a/tests/components/sensor/test_tcp.py +++ b/tests/components/tcp/test_sensor.py @@ -1,15 +1,16 @@ """The tests for the TCP sensor platform.""" +from copy import copy import socket import unittest -from copy import copy +from unittest.mock import Mock, patch from uuid import uuid4 -from unittest.mock import patch, Mock -from tests.common import (get_test_home_assistant, assert_setup_component) -from homeassistant.setup import setup_component -from homeassistant.components.sensor import tcp +import homeassistant.components.tcp.sensor as tcp from homeassistant.helpers.entity import Entity from homeassistant.helpers.template import Template +from homeassistant.setup import setup_component + +from tests.common import assert_setup_component, get_test_home_assistant TEST_CONFIG = { 'sensor': { @@ -46,7 +47,7 @@ class TestTCPSensor(unittest.TestCase): """Stop everything that was started.""" self.hass.stop() - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_setup_platform_valid_config(self, mock_update): """Check a valid configuration and call add_entities with sensor.""" with assert_setup_component(0, 'sensor'): @@ -67,13 +68,13 @@ class TestTCPSensor(unittest.TestCase): } }) - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_name(self, mock_update): """Return the name if set in the configuration.""" sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) assert sensor.name == TEST_CONFIG['sensor'][tcp.CONF_NAME] - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_name_not_set(self, mock_update): """Return the superclass name property if not set in configuration.""" config = copy(TEST_CONFIG['sensor']) @@ -82,7 +83,7 @@ class TestTCPSensor(unittest.TestCase): sensor = tcp.TcpSensor(self.hass, config) assert sensor.name == entity.name - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_state(self, mock_update): """Return the contents of _state.""" sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) @@ -90,14 +91,14 @@ class TestTCPSensor(unittest.TestCase): sensor._state = uuid assert sensor.state == uuid - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_unit_of_measurement(self, mock_update): """Return the configured unit of measurement.""" sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) assert sensor.unit_of_measurement == \ TEST_CONFIG['sensor'][tcp.CONF_UNIT_OF_MEASUREMENT] - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_config_valid_keys(self, *args): """Store valid keys in _config.""" sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) @@ -111,7 +112,7 @@ class TestTCPSensor(unittest.TestCase): with assert_setup_component(0, 'sensor'): assert setup_component(self.hass, 'sensor', TEST_CONFIG) - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_config_invalid_keys(self, mock_update): """Shouldn't store invalid keys in _config.""" config = copy(TEST_CONFIG['sensor']) @@ -135,7 +136,7 @@ class TestTCPSensor(unittest.TestCase): with assert_setup_component(0, 'sensor'): assert setup_component(self.hass, 'sensor', {'tcp': config}) - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_config_uses_defaults(self, mock_update): """Check if defaults were set.""" config = copy(TEST_CONFIG['sensor']) @@ -173,7 +174,7 @@ class TestTCPSensor(unittest.TestCase): with assert_setup_component(0, 'sensor'): assert setup_component(self.hass, 'sensor', {'tcp': config}) - @patch('homeassistant.components.sensor.tcp.TcpSensor.update') + @patch('homeassistant.components.tcp.sensor.TcpSensor.update') def test_init_calls_update(self, mock_update): """Call update() method during __init__().""" tcp.TcpSensor(self.hass, TEST_CONFIG) @@ -192,7 +193,7 @@ class TestTCPSensor(unittest.TestCase): @patch('socket.socket.connect', side_effect=socket.error()) def test_update_returns_if_connecting_fails(self, *args): """Return if connecting to host fails.""" - with patch('homeassistant.components.sensor.tcp.TcpSensor.update'): + with patch('homeassistant.components.tcp.sensor.TcpSensor.update'): sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) assert sensor.update() is None @@ -200,7 +201,7 @@ class TestTCPSensor(unittest.TestCase): @patch('socket.socket.send', side_effect=socket.error()) def test_update_returns_if_sending_fails(self, *args): """Return if sending fails.""" - with patch('homeassistant.components.sensor.tcp.TcpSensor.update'): + with patch('homeassistant.components.tcp.sensor.TcpSensor.update'): sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) assert sensor.update() is None @@ -209,7 +210,7 @@ class TestTCPSensor(unittest.TestCase): @patch('select.select', return_value=(False, False, False)) def test_update_returns_if_select_fails(self, *args): """Return if select fails to return a socket.""" - with patch('homeassistant.components.sensor.tcp.TcpSensor.update'): + with patch('homeassistant.components.tcp.sensor.TcpSensor.update'): sensor = tcp.TcpSensor(self.hass, TEST_CONFIG['sensor']) assert sensor.update() is None diff --git a/tests/components/teksavvy/__init__.py b/tests/components/teksavvy/__init__.py new file mode 100644 index 00000000000..8c8a0fc82ca --- /dev/null +++ b/tests/components/teksavvy/__init__.py @@ -0,0 +1 @@ +"""Tests for the teksavvy component.""" diff --git a/tests/components/sensor/test_teksavvy.py b/tests/components/teksavvy/test_sensor.py similarity index 99% rename from tests/components/sensor/test_teksavvy.py rename to tests/components/teksavvy/test_sensor.py index 2c493d04050..366f0278354 100644 --- a/tests/components/sensor/test_teksavvy.py +++ b/tests/components/teksavvy/test_sensor.py @@ -1,7 +1,7 @@ """Tests for the TekSavvy sensor platform.""" import asyncio from homeassistant.bootstrap import async_setup_component -from homeassistant.components.sensor.teksavvy import TekSavvyData +from homeassistant.components.teksavvy.sensor import TekSavvyData from homeassistant.helpers.aiohttp_client import async_get_clientsession diff --git a/tests/components/template/__init__.py b/tests/components/template/__init__.py new file mode 100644 index 00000000000..a2ce6bb1a77 --- /dev/null +++ b/tests/components/template/__init__.py @@ -0,0 +1 @@ +"""Tests for template component.""" diff --git a/tests/components/binary_sensor/test_template.py b/tests/components/template/test_binary_sensor.py similarity index 99% rename from tests/components/binary_sensor/test_template.py rename to tests/components/template/test_binary_sensor.py index a1f97398616..d0bd1609a91 100644 --- a/tests/components/binary_sensor/test_template.py +++ b/tests/components/template/test_binary_sensor.py @@ -5,7 +5,7 @@ from unittest import mock from homeassistant.const import MATCH_ALL, EVENT_HOMEASSISTANT_START from homeassistant import setup -from homeassistant.components.binary_sensor import template +from homeassistant.components.template import binary_sensor as template from homeassistant.exceptions import TemplateError from homeassistant.helpers import template as template_hlpr from homeassistant.util.async_ import run_callback_threadsafe @@ -159,7 +159,7 @@ class TestBinarySensorTemplate(unittest.TestCase): state = self.hass.states.get('binary_sensor.test_template_sensor') assert state.attributes['entity_picture'] == '/local/sensor.png' - @mock.patch('homeassistant.components.binary_sensor.template.' + @mock.patch('homeassistant.components.template.binary_sensor.' 'BinarySensorTemplate._async_render') def test_match_all(self, _async_render): """Test MATCH_ALL in template.""" diff --git a/tests/components/cover/test_template.py b/tests/components/template/test_cover.py similarity index 100% rename from tests/components/cover/test_template.py rename to tests/components/template/test_cover.py diff --git a/tests/components/fan/test_template.py b/tests/components/template/test_fan.py similarity index 100% rename from tests/components/fan/test_template.py rename to tests/components/template/test_fan.py diff --git a/tests/components/light/test_template.py b/tests/components/template/test_light.py similarity index 100% rename from tests/components/light/test_template.py rename to tests/components/template/test_light.py diff --git a/tests/components/lock/test_template.py b/tests/components/template/test_lock.py similarity index 100% rename from tests/components/lock/test_template.py rename to tests/components/template/test_lock.py diff --git a/tests/components/sensor/test_template.py b/tests/components/template/test_sensor.py similarity index 100% rename from tests/components/sensor/test_template.py rename to tests/components/template/test_sensor.py diff --git a/tests/components/switch/test_template.py b/tests/components/template/test_switch.py similarity index 100% rename from tests/components/switch/test_template.py rename to tests/components/template/test_switch.py diff --git a/tests/components/threshold/__init__.py b/tests/components/threshold/__init__.py new file mode 100644 index 00000000000..7abfd4046a0 --- /dev/null +++ b/tests/components/threshold/__init__.py @@ -0,0 +1 @@ +"""Tests for threshold component.""" diff --git a/tests/components/binary_sensor/test_threshold.py b/tests/components/threshold/test_binary_sensor.py similarity index 100% rename from tests/components/binary_sensor/test_threshold.py rename to tests/components/threshold/test_binary_sensor.py diff --git a/tests/components/time_date/__init__.py b/tests/components/time_date/__init__.py new file mode 100644 index 00000000000..22734c19bbb --- /dev/null +++ b/tests/components/time_date/__init__.py @@ -0,0 +1 @@ +"""Tests for the time_date component.""" diff --git a/tests/components/sensor/test_time_date.py b/tests/components/time_date/test_sensor.py similarity index 98% rename from tests/components/sensor/test_time_date.py rename to tests/components/time_date/test_sensor.py index 98368e997d5..b2a22439c1f 100644 --- a/tests/components/sensor/test_time_date.py +++ b/tests/components/time_date/test_sensor.py @@ -2,7 +2,7 @@ import unittest from unittest.mock import patch -from homeassistant.components.sensor import time_date as time_date +import homeassistant.components.time_date.sensor as time_date import homeassistant.util.dt as dt_util from tests.common import get_test_home_assistant diff --git a/tests/components/tod/__init__.py b/tests/components/tod/__init__.py new file mode 100644 index 00000000000..627b9b3cf47 --- /dev/null +++ b/tests/components/tod/__init__.py @@ -0,0 +1 @@ +"""Tests for tod component.""" diff --git a/tests/components/binary_sensor/test_tod.py b/tests/components/tod/test_binary_sensor.py similarity index 89% rename from tests/components/binary_sensor/test_tod.py rename to tests/components/tod/test_binary_sensor.py index 7af6ef95bfa..07d1a9e14cb 100644 --- a/tests/components/binary_sensor/test_tod.py +++ b/tests/components/tod/test_binary_sensor.py @@ -78,7 +78,7 @@ class TestBinarySensorTod(unittest.TestCase): } ] } - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=test_time): setup_component(self.hass, 'binary_sensor', config) @@ -100,7 +100,7 @@ class TestBinarySensorTod(unittest.TestCase): }, ] } - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=test_time): setup_component(self.hass, 'binary_sensor', config) @@ -122,7 +122,7 @@ class TestBinarySensorTod(unittest.TestCase): }, ] } - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=test_time): setup_component(self.hass, 'binary_sensor', config) @@ -131,7 +131,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=test_time + timedelta(hours=1)): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -155,7 +155,7 @@ class TestBinarySensorTod(unittest.TestCase): } ] } - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=test_time): setup_component(self.hass, 'binary_sensor', config) @@ -178,7 +178,7 @@ class TestBinarySensorTod(unittest.TestCase): } ] } - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=test_time): setup_component(self.hass, 'binary_sensor', config) @@ -188,7 +188,7 @@ class TestBinarySensorTod(unittest.TestCase): switchover_time = self.hass.config.time_zone.localize( datetime(2019, 1, 11, 4, 59, 0)).astimezone(pytz.UTC) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=switchover_time): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -197,7 +197,7 @@ class TestBinarySensorTod(unittest.TestCase): state = self.hass.states.get('binary_sensor.night') assert state.state == STATE_ON - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=switchover_time + timedelta( minutes=1, seconds=1)): @@ -229,7 +229,7 @@ class TestBinarySensorTod(unittest.TestCase): } entity_id = 'binary_sensor.day' testtime = sunrise + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -238,7 +238,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -249,7 +249,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunrise + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -262,7 +262,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -275,7 +275,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -288,7 +288,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -319,7 +319,7 @@ class TestBinarySensorTod(unittest.TestCase): } entity_id = 'binary_sensor.night' testtime = sunset + timedelta(minutes=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -328,7 +328,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunset - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -339,7 +339,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunset + timedelta(minutes=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -350,7 +350,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunrise + timedelta(minutes=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -361,7 +361,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunrise - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -374,7 +374,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise + timedelta(minutes=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -408,7 +408,7 @@ class TestBinarySensorTod(unittest.TestCase): ] } testtime = after + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -417,7 +417,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = after - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { ha.ATTR_NOW: testtime}) @@ -427,7 +427,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = before + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { ha.ATTR_NOW: testtime}) @@ -437,7 +437,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = before - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { ha.ATTR_NOW: testtime}) @@ -447,7 +447,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = before + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { ha.ATTR_NOW: testtime}) @@ -475,7 +475,7 @@ class TestBinarySensorTod(unittest.TestCase): ] } testtime = after + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -484,7 +484,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = after - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { ha.ATTR_NOW: testtime}) @@ -516,7 +516,7 @@ class TestBinarySensorTod(unittest.TestCase): } entity_id = 'binary_sensor.day' testtime = test_time - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -525,7 +525,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -536,7 +536,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -547,7 +547,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunrise + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -560,7 +560,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -573,7 +573,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -586,7 +586,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -620,7 +620,7 @@ class TestBinarySensorTod(unittest.TestCase): } entity_id = 'binary_sensor.day' testtime = test_time - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -629,7 +629,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -640,7 +640,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -651,7 +651,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunrise + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -664,7 +664,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -677,7 +677,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -690,7 +690,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -724,7 +724,7 @@ class TestBinarySensorTod(unittest.TestCase): } entity_id = 'binary_sensor.day' testtime = sunrise + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) @@ -733,7 +733,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_OFF testtime = sunrise - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -744,7 +744,7 @@ class TestBinarySensorTod(unittest.TestCase): assert state.state == STATE_ON testtime = sunrise + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -757,7 +757,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=-1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -770,7 +770,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -783,7 +783,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass.block_till_done() testtime = sunset + timedelta(seconds=1) - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -798,7 +798,7 @@ class TestBinarySensorTod(unittest.TestCase): self.hass, 'sunrise', dt_util.as_utc(test_time)) + timedelta(hours=-1, minutes=-30)) testtime = sunrise - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): self.hass.bus.fire(ha.EVENT_TIME_CHANGED, { @@ -828,7 +828,7 @@ class TestBinarySensorTod(unittest.TestCase): # Internally the entity_id = 'binary_sensor.day' testtime = test_time - with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow', + with patch('homeassistant.components.tod.binary_sensor.dt_util.utcnow', return_value=testtime): setup_component(self.hass, 'binary_sensor', config) diff --git a/tests/components/tomato/__init__.py b/tests/components/tomato/__init__.py new file mode 100644 index 00000000000..ac6fe559490 --- /dev/null +++ b/tests/components/tomato/__init__.py @@ -0,0 +1 @@ +"""Tests for the tomato component.""" diff --git a/tests/components/device_tracker/test_tomato.py b/tests/components/tomato/test_device_tracker.py similarity index 98% rename from tests/components/device_tracker/test_tomato.py rename to tests/components/tomato/test_device_tracker.py index 0c20350a845..0d865b2d6dc 100644 --- a/tests/components/device_tracker/test_tomato.py +++ b/tests/components/tomato/test_device_tracker.py @@ -5,7 +5,8 @@ import requests import requests_mock import voluptuous as vol -from homeassistant.components.device_tracker import DOMAIN, tomato as tomato +from homeassistant.components.device_tracker import DOMAIN +import homeassistant.components.tomato.device_tracker as tomato from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT, CONF_SSL, CONF_PLATFORM, CONF_VERIFY_SSL) @@ -39,8 +40,8 @@ def mock_session_response(*args, **kwargs): @pytest.fixture def mock_exception_logger(): """Mock pyunifi.""" - with mock.patch('homeassistant.components.device_tracker' - '.tomato._LOGGER.exception') as mock_exception_logger: + with mock.patch('homeassistant.components.tomato.device_tracker' + '._LOGGER.exception') as mock_exception_logger: yield mock_exception_logger diff --git a/tests/components/device_tracker/test_tplink.py b/tests/components/tplink/test_device_tracker.py similarity index 96% rename from tests/components/device_tracker/test_tplink.py rename to tests/components/tplink/test_device_tracker.py index 8f226f449b0..f1d60d46762 100644 --- a/tests/components/device_tracker/test_tplink.py +++ b/tests/components/tplink/test_device_tracker.py @@ -4,7 +4,7 @@ import os import pytest from homeassistant.components import device_tracker -from homeassistant.components.device_tracker.tplink import Tplink4DeviceScanner +from homeassistant.components.tplink.device_tracker import Tplink4DeviceScanner from homeassistant.const import (CONF_PLATFORM, CONF_PASSWORD, CONF_USERNAME, CONF_HOST) import requests_mock diff --git a/tests/components/transport_nsw/__init__.py b/tests/components/transport_nsw/__init__.py new file mode 100644 index 00000000000..978a4df0b89 --- /dev/null +++ b/tests/components/transport_nsw/__init__.py @@ -0,0 +1 @@ +"""Tests for the transport_nsw component.""" diff --git a/tests/components/sensor/test_transport_nsw.py b/tests/components/transport_nsw/test_sensor.py similarity index 100% rename from tests/components/sensor/test_transport_nsw.py rename to tests/components/transport_nsw/test_sensor.py diff --git a/tests/components/trend/__init__.py b/tests/components/trend/__init__.py new file mode 100644 index 00000000000..612560a18c0 --- /dev/null +++ b/tests/components/trend/__init__.py @@ -0,0 +1 @@ +"""Tests for trend component.""" diff --git a/tests/components/binary_sensor/test_trend.py b/tests/components/trend/test_binary_sensor.py similarity index 100% rename from tests/components/binary_sensor/test_trend.py rename to tests/components/trend/test_binary_sensor.py diff --git a/tests/components/uk_transport/__init__.py b/tests/components/uk_transport/__init__.py new file mode 100644 index 00000000000..2e114fc5932 --- /dev/null +++ b/tests/components/uk_transport/__init__.py @@ -0,0 +1 @@ +"""Tests for the uk_transport component.""" diff --git a/tests/components/sensor/test_uk_transport.py b/tests/components/uk_transport/test_sensor.py similarity index 98% rename from tests/components/sensor/test_uk_transport.py rename to tests/components/uk_transport/test_sensor.py index 65e6b7f0f38..50730d89404 100644 --- a/tests/components/sensor/test_uk_transport.py +++ b/tests/components/uk_transport/test_sensor.py @@ -4,7 +4,7 @@ import re import requests_mock import unittest -from homeassistant.components.sensor.uk_transport import ( +from homeassistant.components.uk_transport.sensor import ( UkTransportSensor, ATTR_ATCOCODE, ATTR_LOCALITY, ATTR_STOP_NAME, ATTR_NEXT_BUSES, ATTR_STATION_CODE, ATTR_CALLING_AT, ATTR_NEXT_TRAINS, diff --git a/tests/components/device_tracker/test_unifi.py b/tests/components/unifi/test_device_tracker.py similarity index 97% rename from tests/components/device_tracker/test_unifi.py rename to tests/components/unifi/test_device_tracker.py index 7a791b334aa..0fb0751c5b6 100644 --- a/tests/components/device_tracker/test_unifi.py +++ b/tests/components/unifi/test_device_tracker.py @@ -8,7 +8,8 @@ import pytest import voluptuous as vol import homeassistant.util.dt as dt_util -from homeassistant.components.device_tracker import DOMAIN, unifi as unifi +from homeassistant.components.device_tracker import DOMAIN +import homeassistant.components.unifi.device_tracker as unifi from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PLATFORM, CONF_VERIFY_SSL, CONF_MONITORED_CONDITIONS) @@ -25,7 +26,7 @@ def mock_ctrl(): @pytest.fixture def mock_scanner(): """Mock UnifyScanner.""" - with mock.patch('homeassistant.components.device_tracker.unifi' + with mock.patch('homeassistant.components.unifi.device_tracker' '.UnifiScanner') as scanner: yield scanner diff --git a/tests/components/unifi_direct/__init__.py b/tests/components/unifi_direct/__init__.py new file mode 100644 index 00000000000..7f8d0fa29f7 --- /dev/null +++ b/tests/components/unifi_direct/__init__.py @@ -0,0 +1 @@ +"""Tests for the unifi_direct component.""" diff --git a/tests/components/device_tracker/test_unifi_direct.py b/tests/components/unifi_direct/test_device_tracker.py similarity index 96% rename from tests/components/device_tracker/test_unifi_direct.py rename to tests/components/unifi_direct/test_device_tracker.py index 1b1dc1a7cb5..ba40a09aa59 100644 --- a/tests/components/device_tracker/test_unifi_direct.py +++ b/tests/components/unifi_direct/test_device_tracker.py @@ -11,7 +11,7 @@ from homeassistant.components import device_tracker from homeassistant.components.device_tracker import ( CONF_CONSIDER_HOME, CONF_TRACK_NEW, CONF_AWAY_HIDE, CONF_NEW_DEVICE_DEFAULTS) -from homeassistant.components.device_tracker.unifi_direct import ( +from homeassistant.components.unifi_direct.device_tracker import ( DOMAIN, CONF_PORT, PLATFORM_SCHEMA, _response_to_json, get_scanner) from homeassistant.const import (CONF_PLATFORM, CONF_PASSWORD, CONF_USERNAME, CONF_HOST) @@ -19,8 +19,8 @@ from homeassistant.const import (CONF_PLATFORM, CONF_PASSWORD, CONF_USERNAME, from tests.common import ( assert_setup_component, mock_component, load_fixture) -scanner_path = 'homeassistant.components.device_tracker.' + \ - 'unifi_direct.UnifiDeviceScanner' +scanner_path = 'homeassistant.components.unifi_direct.device_tracker.' + \ + 'UnifiDeviceScanner' @pytest.fixture(autouse=True) diff --git a/tests/components/universal/__init__.py b/tests/components/universal/__init__.py new file mode 100644 index 00000000000..9a814402b9c --- /dev/null +++ b/tests/components/universal/__init__.py @@ -0,0 +1 @@ +"""Tests for the universal component.""" diff --git a/tests/components/media_player/test_universal.py b/tests/components/universal/test_media_player.py similarity index 99% rename from tests/components/media_player/test_universal.py rename to tests/components/universal/test_media_player.py index ee86735a063..e773c560510 100644 --- a/tests/components/media_player/test_universal.py +++ b/tests/components/universal/test_media_player.py @@ -10,7 +10,7 @@ import homeassistant.components.switch as switch import homeassistant.components.input_number as input_number import homeassistant.components.input_select as input_select import homeassistant.components.media_player as media_player -import homeassistant.components.media_player.universal as universal +import homeassistant.components.universal.media_player as universal from homeassistant.util.async_ import run_coroutine_threadsafe from tests.common import mock_service, get_test_home_assistant diff --git a/tests/components/upc_connect/__init__.py b/tests/components/upc_connect/__init__.py new file mode 100644 index 00000000000..d491190d111 --- /dev/null +++ b/tests/components/upc_connect/__init__.py @@ -0,0 +1 @@ +"""Tests for the upc_connect component.""" diff --git a/tests/components/device_tracker/test_upc_connect.py b/tests/components/upc_connect/test_device_tracker.py similarity index 98% rename from tests/components/device_tracker/test_upc_connect.py rename to tests/components/upc_connect/test_device_tracker.py index 677a6d1f310..97167ad0801 100644 --- a/tests/components/device_tracker/test_upc_connect.py +++ b/tests/components/upc_connect/test_device_tracker.py @@ -5,7 +5,7 @@ from asynctest import patch import pytest from homeassistant.components.device_tracker import DOMAIN -import homeassistant.components.device_tracker.upc_connect as platform +import homeassistant.components.upc_connect.device_tracker as platform from homeassistant.const import CONF_HOST, CONF_PLATFORM from homeassistant.setup import async_setup_component @@ -65,7 +65,7 @@ async def test_setup_platform_timeout_webservice(hass, caplog, aioclient_mock): assert 'Error setting up platform' in caplog.text -@patch('homeassistant.components.device_tracker.upc_connect.' +@patch('homeassistant.components.upc_connect.device_tracker.' 'UPCDeviceScanner.async_scan_devices', return_value=async_scan_devices_mock) async def test_setup_platform(scan_mock, hass, aioclient_mock): diff --git a/tests/components/uptime/__init__.py b/tests/components/uptime/__init__.py new file mode 100644 index 00000000000..f098fd12eb9 --- /dev/null +++ b/tests/components/uptime/__init__.py @@ -0,0 +1 @@ +"""Tests for the uptime component.""" diff --git a/tests/components/sensor/test_uptime.py b/tests/components/uptime/test_sensor.py similarity index 98% rename from tests/components/sensor/test_uptime.py rename to tests/components/uptime/test_sensor.py index 00552dd9e49..2f024b36c45 100644 --- a/tests/components/sensor/test_uptime.py +++ b/tests/components/uptime/test_sensor.py @@ -5,7 +5,7 @@ from datetime import timedelta from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.setup import setup_component -from homeassistant.components.sensor.uptime import UptimeSensor +from homeassistant.components.uptime.sensor import UptimeSensor from tests.common import get_test_home_assistant diff --git a/tests/components/usgs_earthquakes_feed/__init__.py b/tests/components/usgs_earthquakes_feed/__init__.py new file mode 100644 index 00000000000..8c5f1609ae4 --- /dev/null +++ b/tests/components/usgs_earthquakes_feed/__init__.py @@ -0,0 +1 @@ +"""Tests for the usgs_earthquakes_feed component.""" diff --git a/tests/components/geo_location/test_usgs_earthquakes_feed.py b/tests/components/usgs_earthquakes_feed/test_geo_location.py similarity index 97% rename from tests/components/geo_location/test_usgs_earthquakes_feed.py rename to tests/components/usgs_earthquakes_feed/test_geo_location.py index f0383c221c4..b2bc0bc4c25 100644 --- a/tests/components/geo_location/test_usgs_earthquakes_feed.py +++ b/tests/components/usgs_earthquakes_feed/test_geo_location.py @@ -4,11 +4,10 @@ from unittest.mock import patch, MagicMock, call from homeassistant.components import geo_location from homeassistant.components.geo_location import ATTR_SOURCE -from homeassistant.components.geo_location\ - .usgs_earthquakes_feed import \ - ATTR_ALERT, ATTR_EXTERNAL_ID, SCAN_INTERVAL, ATTR_PLACE, \ - ATTR_MAGNITUDE, ATTR_STATUS, ATTR_TYPE, \ - ATTR_TIME, ATTR_UPDATED, CONF_FEED_TYPE +from homeassistant.components.usgs_earthquakes_feed.geo_location import ( + ATTR_ALERT, ATTR_EXTERNAL_ID, SCAN_INTERVAL, ATTR_PLACE, + ATTR_MAGNITUDE, ATTR_STATUS, ATTR_TYPE, + ATTR_TIME, ATTR_UPDATED, CONF_FEED_TYPE) 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 diff --git a/tests/components/uvc/__init__.py b/tests/components/uvc/__init__.py new file mode 100644 index 00000000000..6ea965a14cb --- /dev/null +++ b/tests/components/uvc/__init__.py @@ -0,0 +1 @@ +"""Tests for the uvc component.""" diff --git a/tests/components/camera/test_uvc.py b/tests/components/uvc/test_camera.py similarity index 99% rename from tests/components/camera/test_uvc.py rename to tests/components/uvc/test_camera.py index 476e612eb06..29982c250fb 100644 --- a/tests/components/camera/test_uvc.py +++ b/tests/components/uvc/test_camera.py @@ -9,7 +9,7 @@ from uvcclient import nvr from homeassistant.exceptions import PlatformNotReady from homeassistant.setup import setup_component -from homeassistant.components.camera import uvc +from homeassistant.components.uvc import camera as uvc from tests.common import get_test_home_assistant import pytest diff --git a/tests/components/version/__init__.py b/tests/components/version/__init__.py new file mode 100644 index 00000000000..c282c31945c --- /dev/null +++ b/tests/components/version/__init__.py @@ -0,0 +1 @@ +"""Tests for the version component.""" diff --git a/tests/components/sensor/test_version.py b/tests/components/version/test_sensor.py similarity index 100% rename from tests/components/sensor/test_version.py rename to tests/components/version/test_sensor.py diff --git a/tests/components/binary_sensor/test_vultr.py b/tests/components/vultr/test_binary_sensor.py similarity index 98% rename from tests/components/binary_sensor/test_vultr.py rename to tests/components/vultr/test_binary_sensor.py index 3dcba033d73..fba002af53a 100644 --- a/tests/components/binary_sensor/test_vultr.py +++ b/tests/components/vultr/test_binary_sensor.py @@ -7,7 +7,7 @@ import requests_mock import pytest import voluptuous as vol -from homeassistant.components.binary_sensor import vultr +from homeassistant.components.vultr import binary_sensor as vultr from homeassistant.components import vultr as base_vultr from homeassistant.components.vultr import ( ATTR_ALLOWED_BANDWIDTH, ATTR_AUTO_BACKUPS, ATTR_IPV4_ADDRESS, diff --git a/tests/components/sensor/test_vultr.py b/tests/components/vultr/test_sensor.py similarity index 99% rename from tests/components/sensor/test_vultr.py rename to tests/components/vultr/test_sensor.py index 333e4938ba4..2ecc682bfb1 100644 --- a/tests/components/sensor/test_vultr.py +++ b/tests/components/vultr/test_sensor.py @@ -7,7 +7,7 @@ import pytest import requests_mock import voluptuous as vol -from homeassistant.components.sensor import vultr +import homeassistant.components.vultr.sensor as vultr from homeassistant.components import vultr as base_vultr from homeassistant.components.vultr import CONF_SUBSCRIPTION from homeassistant.const import ( diff --git a/tests/components/switch/test_vultr.py b/tests/components/vultr/test_switch.py similarity index 99% rename from tests/components/switch/test_vultr.py rename to tests/components/vultr/test_switch.py index f5e94e3e1b1..9b95b9a164c 100644 --- a/tests/components/switch/test_vultr.py +++ b/tests/components/vultr/test_switch.py @@ -7,7 +7,7 @@ import requests_mock import pytest import voluptuous as vol -from homeassistant.components.switch import vultr +from homeassistant.components.vultr import switch as vultr from homeassistant.components import vultr as base_vultr from homeassistant.components.vultr import ( ATTR_ALLOWED_BANDWIDTH, ATTR_AUTO_BACKUPS, ATTR_IPV4_ADDRESS, diff --git a/tests/components/switch/test_wake_on_lan.py b/tests/components/wake_on_lan/test_switch.py similarity index 100% rename from tests/components/switch/test_wake_on_lan.py rename to tests/components/wake_on_lan/test_switch.py diff --git a/tests/components/workday/__init__.py b/tests/components/workday/__init__.py new file mode 100644 index 00000000000..b731fce2edc --- /dev/null +++ b/tests/components/workday/__init__.py @@ -0,0 +1 @@ +"""Tests the HASS workday binary sensor.""" diff --git a/tests/components/binary_sensor/test_workday.py b/tests/components/workday/test_binary_sensor.py similarity index 98% rename from tests/components/binary_sensor/test_workday.py rename to tests/components/workday/test_binary_sensor.py index 5aa5a5dad5c..6895032bc94 100644 --- a/tests/components/binary_sensor/test_workday.py +++ b/tests/components/workday/test_binary_sensor.py @@ -2,14 +2,14 @@ from datetime import date from unittest.mock import patch -from homeassistant.components.binary_sensor.workday import day_to_string +from homeassistant.components.workday.binary_sensor import day_to_string from homeassistant.setup import setup_component from tests.common import ( get_test_home_assistant, assert_setup_component) -FUNCTION_PATH = 'homeassistant.components.binary_sensor.workday.get_date' +FUNCTION_PATH = 'homeassistant.components.workday.binary_sensor.get_date' class TestWorkdaySetup: diff --git a/tests/components/worldclock/__init__.py b/tests/components/worldclock/__init__.py new file mode 100644 index 00000000000..49ef84bd2fe --- /dev/null +++ b/tests/components/worldclock/__init__.py @@ -0,0 +1 @@ +"""Tests for the worldclock component.""" diff --git a/tests/components/sensor/test_worldclock.py b/tests/components/worldclock/test_sensor.py similarity index 100% rename from tests/components/sensor/test_worldclock.py rename to tests/components/worldclock/test_sensor.py diff --git a/tests/components/wsdot/__init__.py b/tests/components/wsdot/__init__.py new file mode 100644 index 00000000000..2f28c29ae8d --- /dev/null +++ b/tests/components/wsdot/__init__.py @@ -0,0 +1 @@ +"""Tests for the wsdot component.""" diff --git a/tests/components/sensor/test_wsdot.py b/tests/components/wsdot/test_sensor.py similarity index 95% rename from tests/components/sensor/test_wsdot.py rename to tests/components/wsdot/test_sensor.py index b90529693b6..2538bf303ee 100644 --- a/tests/components/sensor/test_wsdot.py +++ b/tests/components/wsdot/test_sensor.py @@ -6,8 +6,8 @@ import unittest import requests_mock from tests.common import get_test_home_assistant, load_fixture -from homeassistant.components.sensor import wsdot -from homeassistant.components.sensor.wsdot import ( +import homeassistant.components.wsdot.sensor as wsdot +from homeassistant.components.wsdot.sensor import ( ATTR_DESCRIPTION, ATTR_TIME_UPDATED, CONF_API_KEY, CONF_ID, CONF_NAME, CONF_TRAVEL_TIMES, RESOURCE, SCAN_INTERVAL) from homeassistant.setup import setup_component diff --git a/tests/components/wunderground/__init__.py b/tests/components/wunderground/__init__.py new file mode 100644 index 00000000000..d3f839a35f6 --- /dev/null +++ b/tests/components/wunderground/__init__.py @@ -0,0 +1 @@ +"""Tests for the wunderground component.""" diff --git a/tests/components/sensor/test_wunderground.py b/tests/components/wunderground/test_sensor.py similarity index 98% rename from tests/components/sensor/test_wunderground.py rename to tests/components/wunderground/test_sensor.py index 3f490b4ab12..02d8842dd69 100644 --- a/tests/components/sensor/test_wunderground.py +++ b/tests/components/wunderground/test_sensor.py @@ -4,7 +4,7 @@ import aiohttp from pytest import raises -from homeassistant.components.sensor import wunderground +import homeassistant.components.wunderground.sensor as wunderground from homeassistant.const import TEMP_CELSIUS, LENGTH_INCHES, STATE_UNKNOWN from homeassistant.exceptions import PlatformNotReady from homeassistant.setup import async_setup_component diff --git a/tests/components/xiaomi/__init__.py b/tests/components/xiaomi/__init__.py new file mode 100644 index 00000000000..46404fc6f32 --- /dev/null +++ b/tests/components/xiaomi/__init__.py @@ -0,0 +1 @@ +"""Tests for the xiaomi component.""" diff --git a/tests/components/device_tracker/test_xiaomi.py b/tests/components/xiaomi/test_device_tracker.py similarity index 96% rename from tests/components/device_tracker/test_xiaomi.py rename to tests/components/xiaomi/test_device_tracker.py index 7b141159256..57a794c2f3d 100644 --- a/tests/components/device_tracker/test_xiaomi.py +++ b/tests/components/xiaomi/test_device_tracker.py @@ -4,8 +4,9 @@ from asynctest import mock, patch import requests -from homeassistant.components.device_tracker import DOMAIN, xiaomi as xiaomi -from homeassistant.components.device_tracker.xiaomi import get_scanner +from homeassistant.components.device_tracker import DOMAIN +import homeassistant.components.xiaomi.device_tracker as xiaomi +from homeassistant.components.xiaomi.device_tracker import get_scanner from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PLATFORM) @@ -150,7 +151,7 @@ def mocked_requests(*args, **kwargs): @patch( - 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', + 'homeassistant.components.xiaomi.device_tracker.XiaomiDeviceScanner', return_value=mock.MagicMock()) async def test_config(xiaomi_mock, hass): """Testing minimal configuration.""" @@ -172,7 +173,7 @@ async def test_config(xiaomi_mock, hass): @patch( - 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', + 'homeassistant.components.xiaomi.device_tracker.XiaomiDeviceScanner', return_value=mock.MagicMock()) async def test_config_full(xiaomi_mock, hass): """Testing full configuration.""" diff --git a/tests/components/yamaha/__init__.py b/tests/components/yamaha/__init__.py new file mode 100644 index 00000000000..0df69c55380 --- /dev/null +++ b/tests/components/yamaha/__init__.py @@ -0,0 +1 @@ +"""Tests for the yamaha component.""" diff --git a/tests/components/media_player/test_yamaha.py b/tests/components/yamaha/test_media_player.py similarity index 97% rename from tests/components/media_player/test_yamaha.py rename to tests/components/yamaha/test_media_player.py index 13d5a785e7f..8056cdd2f80 100644 --- a/tests/components/media_player/test_yamaha.py +++ b/tests/components/yamaha/test_media_player.py @@ -4,7 +4,7 @@ from unittest.mock import patch, MagicMock from homeassistant.setup import setup_component import homeassistant.components.media_player as mp -from homeassistant.components.media_player import yamaha +from homeassistant.components.yamaha import media_player as yamaha from tests.common import get_test_home_assistant diff --git a/tests/components/yr/__init__.py b/tests/components/yr/__init__.py new file mode 100644 index 00000000000..d85c8ab9758 --- /dev/null +++ b/tests/components/yr/__init__.py @@ -0,0 +1 @@ +"""Tests for the yr component.""" diff --git a/tests/components/sensor/test_yr.py b/tests/components/yr/test_sensor.py similarity index 95% rename from tests/components/sensor/test_yr.py rename to tests/components/yr/test_sensor.py index 4c2118f43f3..d0b4cc44dd2 100644 --- a/tests/components/sensor/test_yr.py +++ b/tests/components/yr/test_sensor.py @@ -20,7 +20,7 @@ def test_default_setup(hass, aioclient_mock): config = {'platform': 'yr', 'elevation': 0} hass.allow_pool = True - with patch('homeassistant.components.sensor.yr.dt_util.utcnow', + with patch('homeassistant.components.yr.sensor.dt_util.utcnow', return_value=NOW), assert_setup_component(1): yield from async_setup_component(hass, 'sensor', {'sensor': config}) @@ -46,7 +46,7 @@ def test_custom_setup(hass, aioclient_mock): 'fog', 'windSpeed']} hass.allow_pool = True - with patch('homeassistant.components.sensor.yr.dt_util.utcnow', + with patch('homeassistant.components.yr.sensor.dt_util.utcnow', return_value=NOW), assert_setup_component(1): yield from async_setup_component(hass, 'sensor', {'sensor': config}) @@ -88,7 +88,7 @@ def test_forecast_setup(hass, aioclient_mock): 'fog', 'windSpeed']} hass.allow_pool = True - with patch('homeassistant.components.sensor.yr.dt_util.utcnow', + with patch('homeassistant.components.yr.sensor.dt_util.utcnow', return_value=NOW), assert_setup_component(1): yield from async_setup_component(hass, 'sensor', {'sensor': config}) diff --git a/tests/components/yweather/__init__.py b/tests/components/yweather/__init__.py new file mode 100644 index 00000000000..6ae50651310 --- /dev/null +++ b/tests/components/yweather/__init__.py @@ -0,0 +1 @@ +"""Tests for the yweather component.""" diff --git a/tests/components/sensor/test_yweather.py b/tests/components/yweather/test_sensor.py similarity index 100% rename from tests/components/sensor/test_yweather.py rename to tests/components/yweather/test_sensor.py diff --git a/tests/components/weather/test_yweather.py b/tests/components/yweather/test_weather.py similarity index 100% rename from tests/components/weather/test_yweather.py rename to tests/components/yweather/test_weather.py From 12df14b87bc0865498a86f96e6ed2deec7de1ca7 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 19 Mar 2019 07:46:04 +0000 Subject: [PATCH 076/290] Change ATTR_BATTERY_STATE_UNPLUGGED for ios component (#22152) * AH - Updated Text Description of ATTR_BATTERY_STATE_UNPLUGGED * AH - updated device_tracker ios test to use new battery state description --- homeassistant/components/ios/__init__.py | 2 +- tests/components/device_tracker/test_init.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ios/__init__.py b/homeassistant/components/ios/__init__.py index 737216af5c9..cc8bd62293a 100644 --- a/homeassistant/components/ios/__init__.py +++ b/homeassistant/components/ios/__init__.py @@ -76,7 +76,7 @@ PERMISSIONS = [ATTR_LOCATION_PERMISSION, ATTR_NOTIFICATIONS_PERMISSION] ATTR_BATTERY_STATE = 'state' ATTR_BATTERY_LEVEL = 'level' -ATTR_BATTERY_STATE_UNPLUGGED = 'Unplugged' +ATTR_BATTERY_STATE_UNPLUGGED = 'Not Charging' ATTR_BATTERY_STATE_CHARGING = 'Charging' ATTR_BATTERY_STATE_FULL = 'Full' ATTR_BATTERY_STATE_UNKNOWN = 'Unknown' diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 31f2d5865b3..63f1c60327a 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -626,7 +626,7 @@ def test_see_schema_allowing_ios_calls(): device_tracker.SERVICE_SEE_PAYLOAD_SCHEMA({ 'dev_id': 'Test', "battery": 35, - "battery_status": 'Unplugged', + "battery_status": 'Not Charging', "gps": [10.0, 10.0], "gps_accuracy": 300, "hostname": 'beer', From 188293770e79db2432949c5ede7693490c652208 Mon Sep 17 00:00:00 2001 From: uchagani Date: Tue, 19 Mar 2019 03:51:42 -0400 Subject: [PATCH 077/290] bump total_connect_client to 0.24 (#22166) --- 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 3b0725658d4..ba8155fde93 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -18,7 +18,7 @@ from homeassistant.const import ( 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 f344e1d4670..ca2872512d8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1710,7 +1710,7 @@ todoist-python==7.0.17 toonapilib==3.2.2 # homeassistant.components.totalconnect.alarm_control_panel -total_connect_client==0.22 +total_connect_client==0.24 # homeassistant.components.tplink_lte tp-connected==0.0.4 From c2aa06d0d46df9ffdaa2cb47adea189486ccf963 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 19 Mar 2019 07:58:56 +0000 Subject: [PATCH 078/290] Add some tests for Koogeek LS1 (#22141) --- tests/components/homekit_controller/common.py | 76 ++++-- .../specific_devices/koogeek_ls1.json | 244 ++++++++++++++++++ .../specific_devices/test_koogeek_ls1.py | 85 ++++++ 3 files changed, 390 insertions(+), 15 deletions(-) create mode 100644 tests/components/homekit_controller/specific_devices/koogeek_ls1.json create mode 100644 tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py diff --git a/tests/components/homekit_controller/common.py b/tests/components/homekit_controller/common.py index 0447de97929..2c60dc168d0 100644 --- a/tests/components/homekit_controller/common.py +++ b/tests/components/homekit_controller/common.py @@ -1,4 +1,5 @@ """Code to support homekit_controller tests.""" +import json from datetime import timedelta from unittest import mock @@ -147,6 +148,41 @@ class FakeService(AbstractService): return char +def setup_accessories_from_file(path): + """Load an collection of accessory defs from JSON data.""" + with open(path, 'r') as accessories_data: + accessories_json = json.load(accessories_data) + + accessories = [] + + for accessory_data in accessories_json: + accessory = Accessory('Name', 'Mfr', 'Model', '0001', '0.1') + accessory.services = [] + accessory.aid = accessory_data['aid'] + for service_data in accessory_data['services']: + service = FakeService('public.hap.service.accessory-information') + service.type = service_data['type'] + service.iid = service_data['iid'] + + for char_data in service_data['characteristics']: + char = FakeCharacteristic(1, '23', None) + char.type = char_data['type'] + char.iid = char_data['iid'] + char.perms = char_data['perms'] + char.format = char_data['format'] + if 'description' in char_data: + char.description = char_data['description'] + if 'value' in char_data: + char.value = char_data['value'] + service.characteristics.append(char) + + accessory.services.append(service) + + accessories.append(accessory) + + return accessories + + async def setup_platform(hass): """Load the platform but with a fake Controller API.""" config = { @@ -161,6 +197,30 @@ async def setup_platform(hass): return fake_controller +async def setup_test_accessories(hass, accessories, capitalize=False): + """Load a fake homekit accessory based on a homekit accessory model. + + If capitalize is True, property names will be in upper case. + """ + fake_controller = await setup_platform(hass) + pairing = fake_controller.add(accessories) + + discovery_info = { + 'host': '127.0.0.1', + 'port': 8080, + 'properties': { + ('MD' if capitalize else 'md'): 'TestDevice', + ('ID' if capitalize else 'id'): '00:00:00:00:00:00', + ('C#' if capitalize else 'c#'): 1, + } + } + + fire_service_discovered(hass, SERVICE_HOMEKIT, discovery_info) + await hass.async_block_till_done() + + return pairing + + async def setup_test_component(hass, services, capitalize=False, suffix=None): """Load a fake homekit accessory based on a homekit accessory model. @@ -177,24 +237,10 @@ async def setup_test_component(hass, services, capitalize=False, suffix=None): assert domain, 'Cannot map test homekit services to homeassistant domain' - fake_controller = await setup_platform(hass) - accessory = Accessory('TestDevice', 'example.com', 'Test', '0001', '0.1') accessory.services.extend(services) - pairing = fake_controller.add([accessory]) - discovery_info = { - 'host': '127.0.0.1', - 'port': 8080, - 'properties': { - ('MD' if capitalize else 'md'): 'TestDevice', - ('ID' if capitalize else 'id'): '00:00:00:00:00:00', - ('C#' if capitalize else 'c#'): 1, - } - } - - fire_service_discovered(hass, SERVICE_HOMEKIT, discovery_info) - await hass.async_block_till_done() + pairing = await setup_test_accessories(hass, [accessory], capitalize) entity = 'testdevice' if suffix is None else 'testdevice_{}'.format(suffix) return Helper(hass, '.'.join((domain, entity)), pairing, accessory) diff --git a/tests/components/homekit_controller/specific_devices/koogeek_ls1.json b/tests/components/homekit_controller/specific_devices/koogeek_ls1.json new file mode 100644 index 00000000000..9b05ce76639 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/koogeek_ls1.json @@ -0,0 +1,244 @@ +[ + { + "aid": 1, + "services": [ + { + "characteristics": [ + { + "format": "string", + "iid": 2, + "maxLen": 64, + "perms": [ + "pr" + ], + "type": "23", + "value": "Koogeek-LS1-20833F" + }, + { + "format": "string", + "iid": 3, + "maxLen": 64, + "perms": [ + "pr" + ], + "type": "20", + "value": "Koogeek" + }, + { + "format": "string", + "iid": 4, + "maxLen": 64, + "perms": [ + "pr" + ], + "type": "21", + "value": "LS1" + }, + { + "format": "string", + "iid": 5, + "maxLen": 64, + "perms": [ + "pr" + ], + "type": "30", + "value": "AAAA011111111111" + }, + { + "format": "bool", + "iid": 6, + "perms": [ + "pw" + ], + "type": "14" + }, + { + "format": "string", + "iid": 23, + "perms": [ + "pr" + ], + "type": "52", + "value": "2.2.15" + } + ], + "iid": 1, + "type": "3E" + }, + { + "characteristics": [ + { + "ev": false, + "format": "bool", + "iid": 8, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "25", + "value": false + }, + { + "ev": false, + "format": "float", + "iid": 9, + "maxValue": 359, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "13", + "unit": "arcdegrees", + "value": 44 + }, + { + "ev": false, + "format": "float", + "iid": 10, + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "2F", + "unit": "percentage", + "value": 0 + }, + { + "ev": false, + "format": "int", + "iid": 11, + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "8", + "unit": "percentage", + "value": 100 + }, + { + "format": "string", + "iid": 12, + "maxLen": 64, + "perms": [ + "pr" + ], + "type": "23", + "value": "Light Strip" + } + ], + "iid": 7, + "primary": true, + "type": "43" + }, + { + "characteristics": [ + { + "description": "TIMER_SETTINGS", + "format": "tlv8", + "iid": 14, + "perms": [ + "pr", + "pw" + ], + "type": "4aaaf942-0dec-11e5-b939-0800200c9a66", + "value": "AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + } + ], + "iid": 13, + "type": "4aaaf940-0dec-11e5-b939-0800200c9a66" + }, + { + "characteristics": [ + { + "description": "FW Upgrade supported types", + "format": "string", + "iid": 16, + "perms": [ + "pr", + "hd" + ], + "type": "151909D2-3802-11E4-916C-0800200C9A66", + "value": "url,data" + }, + { + "description": "FW Upgrade URL", + "format": "string", + "iid": 17, + "maxLen": 256, + "perms": [ + "pw", + "hd" + ], + "type": "151909D1-3802-11E4-916C-0800200C9A66" + }, + { + "description": "FW Upgrade Status", + "ev": false, + "format": "int", + "iid": 18, + "perms": [ + "pr", + "ev", + "hd" + ], + "type": "151909D6-3802-11E4-916C-0800200C9A66", + "value": 0 + }, + { + "description": "FW Upgrade Data", + "format": "data", + "iid": 19, + "perms": [ + "pw", + "hd" + ], + "type": "151909D7-3802-11E4-916C-0800200C9A66" + } + ], + "hidden": true, + "iid": 15, + "type": "151909D0-3802-11E4-916C-0800200C9A66" + }, + { + "characteristics": [ + { + "description": "Timezone", + "format": "int", + "iid": 21, + "perms": [ + "pr", + "pw" + ], + "type": "151909D5-3802-11E4-916C-0800200C9A66", + "value": 0 + }, + { + "description": "Time value since Epoch", + "format": "int", + "iid": 22, + "perms": [ + "pr", + "pw" + ], + "type": "151909D4-3802-11E4-916C-0800200C9A66", + "value": 1550348623 + } + ], + "iid": 20, + "type": "151909D3-3802-11E4-916C-0800200C9A66" + } + ] + } +] \ No newline at end of file diff --git a/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py b/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py new file mode 100644 index 00000000000..78528c1247f --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py @@ -0,0 +1,85 @@ +"""Make sure that existing Koogeek LS1 support isn't broken.""" + +import os +from datetime import timedelta +from unittest import mock + +import pytest + +from homekit.exceptions import AccessoryDisconnectedError, EncryptionError +import homeassistant.util.dt as dt_util +from homeassistant.components.light import SUPPORT_BRIGHTNESS, SUPPORT_COLOR +from tests.common import async_fire_time_changed +from tests.components.homekit_controller.common import ( + setup_accessories_from_file, setup_test_accessories, FakePairing, Helper +) + +LIGHT_ON = ('lightbulb', 'on') + + +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) + pairing = await setup_test_accessories(hass, accessories) + + entity_registry = await hass.helpers.entity_registry.async_get_registry() + + # Assert that the entity is correctly added to the entity registry + entity = entity_registry.async_get('light.testdevice') + assert entity.unique_id == 'homekit-AAAA011111111111-7' + + helper = Helper(hass, 'light.testdevice', pairing, accessories[0]) + state = await helper.poll_and_get_state() + + # Assert that the friendly name is detected correctly + assert state.attributes['friendly_name'] == 'TestDevice' + + # Assert that all optional features the LS1 supports are detected + assert state.attributes['supported_features'] == ( + SUPPORT_BRIGHTNESS | SUPPORT_COLOR + ) + + +@pytest.mark.parametrize('failure_cls', [ + AccessoryDisconnectedError, EncryptionError +]) +async def test_recover_from_failure(hass, utcnow, failure_cls): + """ + Test that entity actually recovers from a network connection drop. + + 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) + pairing = await setup_test_accessories(hass, accessories) + + helper = Helper(hass, 'light.testdevice', pairing, accessories[0]) + + # Set light state on fake device to off + helper.characteristics[LIGHT_ON].set_value(False) + + # Test that entity starts off in a known state + state = await helper.poll_and_get_state() + assert state.state == 'off' + + # Set light state on fake device to on + helper.characteristics[LIGHT_ON].set_value(True) + + # Test that entity remains in the same state if there is a network error + next_update = dt_util.utcnow() + timedelta(seconds=60) + with mock.patch.object(FakePairing, 'get_characteristics') as get_char: + get_char.side_effect = failure_cls('Disconnected') + + state = await helper.poll_and_get_state() + assert state.state == 'off' + + get_char.assert_called_with([(1, 8), (1, 9), (1, 10), (1, 11)]) + + # Test that entity changes state when network error goes away + next_update += timedelta(seconds=60) + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + + state = await helper.poll_and_get_state() + assert state.state == 'on' From bf4b7a82b4fa27b45375050afbd1588fe5cf92fd Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Tue, 19 Mar 2019 21:43:15 +0800 Subject: [PATCH 079/290] Move platforms patch (#22168) * move mold sensor * Get tests working * Move moon and season sensor strings. * Renamed sensor strings. --- homeassistant/components/mold_indicator/__init__.py | 1 + .../{sensor/mold_indicator.py => mold_indicator/sensor.py} | 0 .../moon.ar.json => moon/.translations/sensor.ar.json} | 0 .../moon.ca.json => moon/.translations/sensor.ca.json} | 0 .../moon.cs.json => moon/.translations/sensor.cs.json} | 0 .../moon.da.json => moon/.translations/sensor.da.json} | 0 .../moon.de.json => moon/.translations/sensor.de.json} | 0 .../moon.en.json => moon/.translations/sensor.en.json} | 0 .../moon.es-419.json => moon/.translations/sensor.es-419.json} | 0 .../moon.es.json => moon/.translations/sensor.es.json} | 0 .../moon.et.json => moon/.translations/sensor.et.json} | 0 .../moon.fi.json => moon/.translations/sensor.fi.json} | 0 .../moon.fr.json => moon/.translations/sensor.fr.json} | 0 .../moon.he.json => moon/.translations/sensor.he.json} | 0 .../moon.hu.json => moon/.translations/sensor.hu.json} | 0 .../moon.id.json => moon/.translations/sensor.id.json} | 0 .../moon.it.json => moon/.translations/sensor.it.json} | 0 .../moon.ko.json => moon/.translations/sensor.ko.json} | 0 .../moon.lb.json => moon/.translations/sensor.lb.json} | 0 .../moon.nl.json => moon/.translations/sensor.nl.json} | 0 .../moon.nn.json => moon/.translations/sensor.nn.json} | 0 .../moon.no.json => moon/.translations/sensor.no.json} | 0 .../moon.pl.json => moon/.translations/sensor.pl.json} | 0 .../moon.pt-BR.json => moon/.translations/sensor.pt-BR.json} | 0 .../moon.pt.json => moon/.translations/sensor.pt.json} | 0 .../moon.ro.json => moon/.translations/sensor.ro.json} | 0 .../moon.ru.json => moon/.translations/sensor.ru.json} | 0 .../moon.sl.json => moon/.translations/sensor.sl.json} | 0 .../moon.sv.json => moon/.translations/sensor.sv.json} | 0 .../moon.uk.json => moon/.translations/sensor.uk.json} | 0 .../.translations/sensor.zh-Hans.json} | 0 .../.translations/sensor.zh-Hant.json} | 0 .../{sensor/strings.moon.json => moon/strings.sensor.json} | 0 .../season.af.json => season/.translations/sensor.af.json} | 0 .../season.bg.json => season/.translations/sensor.bg.json} | 0 .../season.ca.json => season/.translations/sensor.ca.json} | 0 .../season.cs.json => season/.translations/sensor.cs.json} | 0 .../season.cy.json => season/.translations/sensor.cy.json} | 0 .../season.da.json => season/.translations/sensor.da.json} | 0 .../season.de.json => season/.translations/sensor.de.json} | 0 .../season.en.json => season/.translations/sensor.en.json} | 0 .../.translations/sensor.es-419.json} | 0 .../season.es.json => season/.translations/sensor.es.json} | 0 .../season.et.json => season/.translations/sensor.et.json} | 0 .../season.eu.json => season/.translations/sensor.eu.json} | 0 .../season.fi.json => season/.translations/sensor.fi.json} | 0 .../season.fr.json => season/.translations/sensor.fr.json} | 0 .../season.he.json => season/.translations/sensor.he.json} | 0 .../season.hu.json => season/.translations/sensor.hu.json} | 0 .../season.id.json => season/.translations/sensor.id.json} | 0 .../season.it.json => season/.translations/sensor.it.json} | 0 .../season.ja.json => season/.translations/sensor.ja.json} | 0 .../season.ko.json => season/.translations/sensor.ko.json} | 0 .../season.lb.json => season/.translations/sensor.lb.json} | 0 .../season.lv.json => season/.translations/sensor.lv.json} | 0 .../season.nl.json => season/.translations/sensor.nl.json} | 0 .../season.nn.json => season/.translations/sensor.nn.json} | 0 .../season.no.json => season/.translations/sensor.no.json} | 0 .../season.pl.json => season/.translations/sensor.pl.json} | 0 .../.translations/sensor.pt-BR.json} | 0 .../season.pt.json => season/.translations/sensor.pt.json} | 0 .../season.ro.json => season/.translations/sensor.ro.json} | 0 .../season.ru.json => season/.translations/sensor.ru.json} | 0 .../season.sl.json => season/.translations/sensor.sl.json} | 0 .../season.sv.json => season/.translations/sensor.sv.json} | 0 .../season.th.json => season/.translations/sensor.th.json} | 0 .../season.uk.json => season/.translations/sensor.uk.json} | 0 .../season.vi.json => season/.translations/sensor.vi.json} | 0 .../.translations/sensor.zh-Hans.json} | 0 .../.translations/sensor.zh-Hant.json} | 0 .../{sensor/strings.season.json => season/strings.sensor.json} | 0 tests/components/mold_indicator/__init__.py | 1 + .../test_mold_indicator.py => mold_indicator/test_sensor.py} | 2 +- 73 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/mold_indicator/__init__.py rename homeassistant/components/{sensor/mold_indicator.py => mold_indicator/sensor.py} (100%) rename homeassistant/components/{sensor/.translations/moon.ar.json => moon/.translations/sensor.ar.json} (100%) rename homeassistant/components/{sensor/.translations/moon.ca.json => moon/.translations/sensor.ca.json} (100%) rename homeassistant/components/{sensor/.translations/moon.cs.json => moon/.translations/sensor.cs.json} (100%) rename homeassistant/components/{sensor/.translations/moon.da.json => moon/.translations/sensor.da.json} (100%) rename homeassistant/components/{sensor/.translations/moon.de.json => moon/.translations/sensor.de.json} (100%) rename homeassistant/components/{sensor/.translations/moon.en.json => moon/.translations/sensor.en.json} (100%) rename homeassistant/components/{sensor/.translations/moon.es-419.json => moon/.translations/sensor.es-419.json} (100%) rename homeassistant/components/{sensor/.translations/moon.es.json => moon/.translations/sensor.es.json} (100%) rename homeassistant/components/{sensor/.translations/moon.et.json => moon/.translations/sensor.et.json} (100%) rename homeassistant/components/{sensor/.translations/moon.fi.json => moon/.translations/sensor.fi.json} (100%) rename homeassistant/components/{sensor/.translations/moon.fr.json => moon/.translations/sensor.fr.json} (100%) rename homeassistant/components/{sensor/.translations/moon.he.json => moon/.translations/sensor.he.json} (100%) rename homeassistant/components/{sensor/.translations/moon.hu.json => moon/.translations/sensor.hu.json} (100%) rename homeassistant/components/{sensor/.translations/moon.id.json => moon/.translations/sensor.id.json} (100%) rename homeassistant/components/{sensor/.translations/moon.it.json => moon/.translations/sensor.it.json} (100%) rename homeassistant/components/{sensor/.translations/moon.ko.json => moon/.translations/sensor.ko.json} (100%) rename homeassistant/components/{sensor/.translations/moon.lb.json => moon/.translations/sensor.lb.json} (100%) rename homeassistant/components/{sensor/.translations/moon.nl.json => moon/.translations/sensor.nl.json} (100%) rename homeassistant/components/{sensor/.translations/moon.nn.json => moon/.translations/sensor.nn.json} (100%) rename homeassistant/components/{sensor/.translations/moon.no.json => moon/.translations/sensor.no.json} (100%) rename homeassistant/components/{sensor/.translations/moon.pl.json => moon/.translations/sensor.pl.json} (100%) rename homeassistant/components/{sensor/.translations/moon.pt-BR.json => moon/.translations/sensor.pt-BR.json} (100%) rename homeassistant/components/{sensor/.translations/moon.pt.json => moon/.translations/sensor.pt.json} (100%) rename homeassistant/components/{sensor/.translations/moon.ro.json => moon/.translations/sensor.ro.json} (100%) rename homeassistant/components/{sensor/.translations/moon.ru.json => moon/.translations/sensor.ru.json} (100%) rename homeassistant/components/{sensor/.translations/moon.sl.json => moon/.translations/sensor.sl.json} (100%) rename homeassistant/components/{sensor/.translations/moon.sv.json => moon/.translations/sensor.sv.json} (100%) rename homeassistant/components/{sensor/.translations/moon.uk.json => moon/.translations/sensor.uk.json} (100%) rename homeassistant/components/{sensor/.translations/moon.zh-Hans.json => moon/.translations/sensor.zh-Hans.json} (100%) rename homeassistant/components/{sensor/.translations/moon.zh-Hant.json => moon/.translations/sensor.zh-Hant.json} (100%) rename homeassistant/components/{sensor/strings.moon.json => moon/strings.sensor.json} (100%) rename homeassistant/components/{sensor/.translations/season.af.json => season/.translations/sensor.af.json} (100%) rename homeassistant/components/{sensor/.translations/season.bg.json => season/.translations/sensor.bg.json} (100%) rename homeassistant/components/{sensor/.translations/season.ca.json => season/.translations/sensor.ca.json} (100%) rename homeassistant/components/{sensor/.translations/season.cs.json => season/.translations/sensor.cs.json} (100%) rename homeassistant/components/{sensor/.translations/season.cy.json => season/.translations/sensor.cy.json} (100%) rename homeassistant/components/{sensor/.translations/season.da.json => season/.translations/sensor.da.json} (100%) rename homeassistant/components/{sensor/.translations/season.de.json => season/.translations/sensor.de.json} (100%) rename homeassistant/components/{sensor/.translations/season.en.json => season/.translations/sensor.en.json} (100%) rename homeassistant/components/{sensor/.translations/season.es-419.json => season/.translations/sensor.es-419.json} (100%) rename homeassistant/components/{sensor/.translations/season.es.json => season/.translations/sensor.es.json} (100%) rename homeassistant/components/{sensor/.translations/season.et.json => season/.translations/sensor.et.json} (100%) rename homeassistant/components/{sensor/.translations/season.eu.json => season/.translations/sensor.eu.json} (100%) rename homeassistant/components/{sensor/.translations/season.fi.json => season/.translations/sensor.fi.json} (100%) rename homeassistant/components/{sensor/.translations/season.fr.json => season/.translations/sensor.fr.json} (100%) rename homeassistant/components/{sensor/.translations/season.he.json => season/.translations/sensor.he.json} (100%) rename homeassistant/components/{sensor/.translations/season.hu.json => season/.translations/sensor.hu.json} (100%) rename homeassistant/components/{sensor/.translations/season.id.json => season/.translations/sensor.id.json} (100%) rename homeassistant/components/{sensor/.translations/season.it.json => season/.translations/sensor.it.json} (100%) rename homeassistant/components/{sensor/.translations/season.ja.json => season/.translations/sensor.ja.json} (100%) rename homeassistant/components/{sensor/.translations/season.ko.json => season/.translations/sensor.ko.json} (100%) rename homeassistant/components/{sensor/.translations/season.lb.json => season/.translations/sensor.lb.json} (100%) rename homeassistant/components/{sensor/.translations/season.lv.json => season/.translations/sensor.lv.json} (100%) rename homeassistant/components/{sensor/.translations/season.nl.json => season/.translations/sensor.nl.json} (100%) rename homeassistant/components/{sensor/.translations/season.nn.json => season/.translations/sensor.nn.json} (100%) rename homeassistant/components/{sensor/.translations/season.no.json => season/.translations/sensor.no.json} (100%) rename homeassistant/components/{sensor/.translations/season.pl.json => season/.translations/sensor.pl.json} (100%) rename homeassistant/components/{sensor/.translations/season.pt-BR.json => season/.translations/sensor.pt-BR.json} (100%) rename homeassistant/components/{sensor/.translations/season.pt.json => season/.translations/sensor.pt.json} (100%) rename homeassistant/components/{sensor/.translations/season.ro.json => season/.translations/sensor.ro.json} (100%) rename homeassistant/components/{sensor/.translations/season.ru.json => season/.translations/sensor.ru.json} (100%) rename homeassistant/components/{sensor/.translations/season.sl.json => season/.translations/sensor.sl.json} (100%) rename homeassistant/components/{sensor/.translations/season.sv.json => season/.translations/sensor.sv.json} (100%) rename homeassistant/components/{sensor/.translations/season.th.json => season/.translations/sensor.th.json} (100%) rename homeassistant/components/{sensor/.translations/season.uk.json => season/.translations/sensor.uk.json} (100%) rename homeassistant/components/{sensor/.translations/season.vi.json => season/.translations/sensor.vi.json} (100%) rename homeassistant/components/{sensor/.translations/season.zh-Hans.json => season/.translations/sensor.zh-Hans.json} (100%) rename homeassistant/components/{sensor/.translations/season.zh-Hant.json => season/.translations/sensor.zh-Hant.json} (100%) rename homeassistant/components/{sensor/strings.season.json => season/strings.sensor.json} (100%) create mode 100644 tests/components/mold_indicator/__init__.py rename tests/components/{sensor/test_mold_indicator.py => mold_indicator/test_sensor.py} (99%) diff --git a/homeassistant/components/mold_indicator/__init__.py b/homeassistant/components/mold_indicator/__init__.py new file mode 100644 index 00000000000..adadf41b2b0 --- /dev/null +++ b/homeassistant/components/mold_indicator/__init__.py @@ -0,0 +1 @@ +"""Calculates mold growth indication from temperature and humidity.""" diff --git a/homeassistant/components/sensor/mold_indicator.py b/homeassistant/components/mold_indicator/sensor.py similarity index 100% rename from homeassistant/components/sensor/mold_indicator.py rename to homeassistant/components/mold_indicator/sensor.py diff --git a/homeassistant/components/sensor/.translations/moon.ar.json b/homeassistant/components/moon/.translations/sensor.ar.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.ar.json rename to homeassistant/components/moon/.translations/sensor.ar.json diff --git a/homeassistant/components/sensor/.translations/moon.ca.json b/homeassistant/components/moon/.translations/sensor.ca.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.ca.json rename to homeassistant/components/moon/.translations/sensor.ca.json diff --git a/homeassistant/components/sensor/.translations/moon.cs.json b/homeassistant/components/moon/.translations/sensor.cs.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.cs.json rename to homeassistant/components/moon/.translations/sensor.cs.json diff --git a/homeassistant/components/sensor/.translations/moon.da.json b/homeassistant/components/moon/.translations/sensor.da.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.da.json rename to homeassistant/components/moon/.translations/sensor.da.json diff --git a/homeassistant/components/sensor/.translations/moon.de.json b/homeassistant/components/moon/.translations/sensor.de.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.de.json rename to homeassistant/components/moon/.translations/sensor.de.json diff --git a/homeassistant/components/sensor/.translations/moon.en.json b/homeassistant/components/moon/.translations/sensor.en.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.en.json rename to homeassistant/components/moon/.translations/sensor.en.json diff --git a/homeassistant/components/sensor/.translations/moon.es-419.json b/homeassistant/components/moon/.translations/sensor.es-419.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.es-419.json rename to homeassistant/components/moon/.translations/sensor.es-419.json diff --git a/homeassistant/components/sensor/.translations/moon.es.json b/homeassistant/components/moon/.translations/sensor.es.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.es.json rename to homeassistant/components/moon/.translations/sensor.es.json diff --git a/homeassistant/components/sensor/.translations/moon.et.json b/homeassistant/components/moon/.translations/sensor.et.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.et.json rename to homeassistant/components/moon/.translations/sensor.et.json diff --git a/homeassistant/components/sensor/.translations/moon.fi.json b/homeassistant/components/moon/.translations/sensor.fi.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.fi.json rename to homeassistant/components/moon/.translations/sensor.fi.json diff --git a/homeassistant/components/sensor/.translations/moon.fr.json b/homeassistant/components/moon/.translations/sensor.fr.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.fr.json rename to homeassistant/components/moon/.translations/sensor.fr.json diff --git a/homeassistant/components/sensor/.translations/moon.he.json b/homeassistant/components/moon/.translations/sensor.he.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.he.json rename to homeassistant/components/moon/.translations/sensor.he.json diff --git a/homeassistant/components/sensor/.translations/moon.hu.json b/homeassistant/components/moon/.translations/sensor.hu.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.hu.json rename to homeassistant/components/moon/.translations/sensor.hu.json diff --git a/homeassistant/components/sensor/.translations/moon.id.json b/homeassistant/components/moon/.translations/sensor.id.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.id.json rename to homeassistant/components/moon/.translations/sensor.id.json diff --git a/homeassistant/components/sensor/.translations/moon.it.json b/homeassistant/components/moon/.translations/sensor.it.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.it.json rename to homeassistant/components/moon/.translations/sensor.it.json diff --git a/homeassistant/components/sensor/.translations/moon.ko.json b/homeassistant/components/moon/.translations/sensor.ko.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.ko.json rename to homeassistant/components/moon/.translations/sensor.ko.json diff --git a/homeassistant/components/sensor/.translations/moon.lb.json b/homeassistant/components/moon/.translations/sensor.lb.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.lb.json rename to homeassistant/components/moon/.translations/sensor.lb.json diff --git a/homeassistant/components/sensor/.translations/moon.nl.json b/homeassistant/components/moon/.translations/sensor.nl.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.nl.json rename to homeassistant/components/moon/.translations/sensor.nl.json diff --git a/homeassistant/components/sensor/.translations/moon.nn.json b/homeassistant/components/moon/.translations/sensor.nn.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.nn.json rename to homeassistant/components/moon/.translations/sensor.nn.json diff --git a/homeassistant/components/sensor/.translations/moon.no.json b/homeassistant/components/moon/.translations/sensor.no.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.no.json rename to homeassistant/components/moon/.translations/sensor.no.json diff --git a/homeassistant/components/sensor/.translations/moon.pl.json b/homeassistant/components/moon/.translations/sensor.pl.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.pl.json rename to homeassistant/components/moon/.translations/sensor.pl.json diff --git a/homeassistant/components/sensor/.translations/moon.pt-BR.json b/homeassistant/components/moon/.translations/sensor.pt-BR.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.pt-BR.json rename to homeassistant/components/moon/.translations/sensor.pt-BR.json diff --git a/homeassistant/components/sensor/.translations/moon.pt.json b/homeassistant/components/moon/.translations/sensor.pt.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.pt.json rename to homeassistant/components/moon/.translations/sensor.pt.json diff --git a/homeassistant/components/sensor/.translations/moon.ro.json b/homeassistant/components/moon/.translations/sensor.ro.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.ro.json rename to homeassistant/components/moon/.translations/sensor.ro.json diff --git a/homeassistant/components/sensor/.translations/moon.ru.json b/homeassistant/components/moon/.translations/sensor.ru.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.ru.json rename to homeassistant/components/moon/.translations/sensor.ru.json diff --git a/homeassistant/components/sensor/.translations/moon.sl.json b/homeassistant/components/moon/.translations/sensor.sl.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.sl.json rename to homeassistant/components/moon/.translations/sensor.sl.json diff --git a/homeassistant/components/sensor/.translations/moon.sv.json b/homeassistant/components/moon/.translations/sensor.sv.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.sv.json rename to homeassistant/components/moon/.translations/sensor.sv.json diff --git a/homeassistant/components/sensor/.translations/moon.uk.json b/homeassistant/components/moon/.translations/sensor.uk.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.uk.json rename to homeassistant/components/moon/.translations/sensor.uk.json diff --git a/homeassistant/components/sensor/.translations/moon.zh-Hans.json b/homeassistant/components/moon/.translations/sensor.zh-Hans.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.zh-Hans.json rename to homeassistant/components/moon/.translations/sensor.zh-Hans.json diff --git a/homeassistant/components/sensor/.translations/moon.zh-Hant.json b/homeassistant/components/moon/.translations/sensor.zh-Hant.json similarity index 100% rename from homeassistant/components/sensor/.translations/moon.zh-Hant.json rename to homeassistant/components/moon/.translations/sensor.zh-Hant.json diff --git a/homeassistant/components/sensor/strings.moon.json b/homeassistant/components/moon/strings.sensor.json similarity index 100% rename from homeassistant/components/sensor/strings.moon.json rename to homeassistant/components/moon/strings.sensor.json diff --git a/homeassistant/components/sensor/.translations/season.af.json b/homeassistant/components/season/.translations/sensor.af.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.af.json rename to homeassistant/components/season/.translations/sensor.af.json diff --git a/homeassistant/components/sensor/.translations/season.bg.json b/homeassistant/components/season/.translations/sensor.bg.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.bg.json rename to homeassistant/components/season/.translations/sensor.bg.json diff --git a/homeassistant/components/sensor/.translations/season.ca.json b/homeassistant/components/season/.translations/sensor.ca.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.ca.json rename to homeassistant/components/season/.translations/sensor.ca.json diff --git a/homeassistant/components/sensor/.translations/season.cs.json b/homeassistant/components/season/.translations/sensor.cs.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.cs.json rename to homeassistant/components/season/.translations/sensor.cs.json diff --git a/homeassistant/components/sensor/.translations/season.cy.json b/homeassistant/components/season/.translations/sensor.cy.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.cy.json rename to homeassistant/components/season/.translations/sensor.cy.json diff --git a/homeassistant/components/sensor/.translations/season.da.json b/homeassistant/components/season/.translations/sensor.da.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.da.json rename to homeassistant/components/season/.translations/sensor.da.json diff --git a/homeassistant/components/sensor/.translations/season.de.json b/homeassistant/components/season/.translations/sensor.de.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.de.json rename to homeassistant/components/season/.translations/sensor.de.json diff --git a/homeassistant/components/sensor/.translations/season.en.json b/homeassistant/components/season/.translations/sensor.en.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.en.json rename to homeassistant/components/season/.translations/sensor.en.json diff --git a/homeassistant/components/sensor/.translations/season.es-419.json b/homeassistant/components/season/.translations/sensor.es-419.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.es-419.json rename to homeassistant/components/season/.translations/sensor.es-419.json diff --git a/homeassistant/components/sensor/.translations/season.es.json b/homeassistant/components/season/.translations/sensor.es.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.es.json rename to homeassistant/components/season/.translations/sensor.es.json diff --git a/homeassistant/components/sensor/.translations/season.et.json b/homeassistant/components/season/.translations/sensor.et.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.et.json rename to homeassistant/components/season/.translations/sensor.et.json diff --git a/homeassistant/components/sensor/.translations/season.eu.json b/homeassistant/components/season/.translations/sensor.eu.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.eu.json rename to homeassistant/components/season/.translations/sensor.eu.json diff --git a/homeassistant/components/sensor/.translations/season.fi.json b/homeassistant/components/season/.translations/sensor.fi.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.fi.json rename to homeassistant/components/season/.translations/sensor.fi.json diff --git a/homeassistant/components/sensor/.translations/season.fr.json b/homeassistant/components/season/.translations/sensor.fr.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.fr.json rename to homeassistant/components/season/.translations/sensor.fr.json diff --git a/homeassistant/components/sensor/.translations/season.he.json b/homeassistant/components/season/.translations/sensor.he.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.he.json rename to homeassistant/components/season/.translations/sensor.he.json diff --git a/homeassistant/components/sensor/.translations/season.hu.json b/homeassistant/components/season/.translations/sensor.hu.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.hu.json rename to homeassistant/components/season/.translations/sensor.hu.json diff --git a/homeassistant/components/sensor/.translations/season.id.json b/homeassistant/components/season/.translations/sensor.id.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.id.json rename to homeassistant/components/season/.translations/sensor.id.json diff --git a/homeassistant/components/sensor/.translations/season.it.json b/homeassistant/components/season/.translations/sensor.it.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.it.json rename to homeassistant/components/season/.translations/sensor.it.json diff --git a/homeassistant/components/sensor/.translations/season.ja.json b/homeassistant/components/season/.translations/sensor.ja.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.ja.json rename to homeassistant/components/season/.translations/sensor.ja.json diff --git a/homeassistant/components/sensor/.translations/season.ko.json b/homeassistant/components/season/.translations/sensor.ko.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.ko.json rename to homeassistant/components/season/.translations/sensor.ko.json diff --git a/homeassistant/components/sensor/.translations/season.lb.json b/homeassistant/components/season/.translations/sensor.lb.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.lb.json rename to homeassistant/components/season/.translations/sensor.lb.json diff --git a/homeassistant/components/sensor/.translations/season.lv.json b/homeassistant/components/season/.translations/sensor.lv.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.lv.json rename to homeassistant/components/season/.translations/sensor.lv.json diff --git a/homeassistant/components/sensor/.translations/season.nl.json b/homeassistant/components/season/.translations/sensor.nl.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.nl.json rename to homeassistant/components/season/.translations/sensor.nl.json diff --git a/homeassistant/components/sensor/.translations/season.nn.json b/homeassistant/components/season/.translations/sensor.nn.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.nn.json rename to homeassistant/components/season/.translations/sensor.nn.json diff --git a/homeassistant/components/sensor/.translations/season.no.json b/homeassistant/components/season/.translations/sensor.no.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.no.json rename to homeassistant/components/season/.translations/sensor.no.json diff --git a/homeassistant/components/sensor/.translations/season.pl.json b/homeassistant/components/season/.translations/sensor.pl.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.pl.json rename to homeassistant/components/season/.translations/sensor.pl.json diff --git a/homeassistant/components/sensor/.translations/season.pt-BR.json b/homeassistant/components/season/.translations/sensor.pt-BR.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.pt-BR.json rename to homeassistant/components/season/.translations/sensor.pt-BR.json diff --git a/homeassistant/components/sensor/.translations/season.pt.json b/homeassistant/components/season/.translations/sensor.pt.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.pt.json rename to homeassistant/components/season/.translations/sensor.pt.json diff --git a/homeassistant/components/sensor/.translations/season.ro.json b/homeassistant/components/season/.translations/sensor.ro.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.ro.json rename to homeassistant/components/season/.translations/sensor.ro.json diff --git a/homeassistant/components/sensor/.translations/season.ru.json b/homeassistant/components/season/.translations/sensor.ru.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.ru.json rename to homeassistant/components/season/.translations/sensor.ru.json diff --git a/homeassistant/components/sensor/.translations/season.sl.json b/homeassistant/components/season/.translations/sensor.sl.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.sl.json rename to homeassistant/components/season/.translations/sensor.sl.json diff --git a/homeassistant/components/sensor/.translations/season.sv.json b/homeassistant/components/season/.translations/sensor.sv.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.sv.json rename to homeassistant/components/season/.translations/sensor.sv.json diff --git a/homeassistant/components/sensor/.translations/season.th.json b/homeassistant/components/season/.translations/sensor.th.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.th.json rename to homeassistant/components/season/.translations/sensor.th.json diff --git a/homeassistant/components/sensor/.translations/season.uk.json b/homeassistant/components/season/.translations/sensor.uk.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.uk.json rename to homeassistant/components/season/.translations/sensor.uk.json diff --git a/homeassistant/components/sensor/.translations/season.vi.json b/homeassistant/components/season/.translations/sensor.vi.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.vi.json rename to homeassistant/components/season/.translations/sensor.vi.json diff --git a/homeassistant/components/sensor/.translations/season.zh-Hans.json b/homeassistant/components/season/.translations/sensor.zh-Hans.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.zh-Hans.json rename to homeassistant/components/season/.translations/sensor.zh-Hans.json diff --git a/homeassistant/components/sensor/.translations/season.zh-Hant.json b/homeassistant/components/season/.translations/sensor.zh-Hant.json similarity index 100% rename from homeassistant/components/sensor/.translations/season.zh-Hant.json rename to homeassistant/components/season/.translations/sensor.zh-Hant.json diff --git a/homeassistant/components/sensor/strings.season.json b/homeassistant/components/season/strings.sensor.json similarity index 100% rename from homeassistant/components/sensor/strings.season.json rename to homeassistant/components/season/strings.sensor.json diff --git a/tests/components/mold_indicator/__init__.py b/tests/components/mold_indicator/__init__.py new file mode 100644 index 00000000000..aefb419b74c --- /dev/null +++ b/tests/components/mold_indicator/__init__.py @@ -0,0 +1 @@ +"""The tests for the MoldIndicator sensor.""" diff --git a/tests/components/sensor/test_mold_indicator.py b/tests/components/mold_indicator/test_sensor.py similarity index 99% rename from tests/components/sensor/test_mold_indicator.py rename to tests/components/mold_indicator/test_sensor.py index 8b39804cacd..fb6654ce7d9 100644 --- a/tests/components/sensor/test_mold_indicator.py +++ b/tests/components/mold_indicator/test_sensor.py @@ -3,7 +3,7 @@ import unittest from homeassistant.setup import setup_component import homeassistant.components.sensor as sensor -from homeassistant.components.sensor.mold_indicator import (ATTR_DEWPOINT, +from homeassistant.components.mold_indicator.sensor import (ATTR_DEWPOINT, ATTR_CRITICAL_TEMP) from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, STATE_UNKNOWN, TEMP_CELSIUS) From 1499485a71791bb37fbcbe0d2d8283264fc388d1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 19 Mar 2019 15:10:30 +0100 Subject: [PATCH 080/290] 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 9f6e678e417..ff1b2344ac8 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ from .const import ( 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 ca2872512d8..d4c5ac2c6cf 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.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 fd6c620f57a..206b2c15489 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 350904870e0f87d05af54788945a71708ee5f816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 19 Mar 2019 16:16:11 +0100 Subject: [PATCH 081/290] restore entity for switchbot (#22087) --- homeassistant/components/switchbot/switch.py | 31 ++++++++++++-------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/switchbot/switch.py b/homeassistant/components/switchbot/switch.py index a85357b525a..3db9b5fd226 100644 --- a/homeassistant/components/switchbot/switch.py +++ b/homeassistant/components/switchbot/switch.py @@ -1,9 +1,4 @@ -""" -Support for Switchbot. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.switchbot -""" +"""Support for Switchbot.""" import logging import voluptuous as vol @@ -11,6 +6,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA from homeassistant.const import CONF_NAME, CONF_MAC +from homeassistant.helpers.restore_state import RestoreEntity REQUIREMENTS = ['PySwitchbot==0.5'] @@ -18,10 +14,12 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Switchbot' -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_MAC): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, -}) +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_MAC): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + } +) def setup_platform(hass, config, add_entities, discovery_info=None): @@ -31,18 +29,27 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities([SwitchBot(mac_addr, name)]) -class SwitchBot(SwitchDevice): +class SwitchBot(SwitchDevice, RestoreEntity): """Representation of a Switchbot.""" def __init__(self, mac, name) -> None: """Initialize the Switchbot.""" # pylint: disable=import-error, no-member import switchbot - self._state = False + + self._state = None self._name = name self._mac = mac self._device = switchbot.Switchbot(mac=mac) + async def async_added_to_hass(self): + """Run when entity about to be added.""" + await super().async_added_to_hass() + state = await self.async_get_last_state() + if not state: + return + self._state = state.state + def turn_on(self, **kwargs) -> None: """Turn device on.""" if self._device.turn_on(): From 88669c65438f6ee1639d507cd278985f04762b2c Mon Sep 17 00:00:00 2001 From: Eliseo Martelli Date: Tue, 19 Mar 2019 16:16:29 +0100 Subject: [PATCH 082/290] removed iliad (#22175) --- .../components/iliad_italy/__init__.py | 1 - .../components/iliad_italy/sensor.py | 119 ------------------ requirements_all.txt | 3 - 3 files changed, 123 deletions(-) delete mode 100644 homeassistant/components/iliad_italy/__init__.py delete mode 100644 homeassistant/components/iliad_italy/sensor.py diff --git a/homeassistant/components/iliad_italy/__init__.py b/homeassistant/components/iliad_italy/__init__.py deleted file mode 100644 index c171befd196..00000000000 --- a/homeassistant/components/iliad_italy/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The iliad_italy component.""" diff --git a/homeassistant/components/iliad_italy/sensor.py b/homeassistant/components/iliad_italy/sensor.py deleted file mode 100644 index 1e1e5077e80..00000000000 --- a/homeassistant/components/iliad_italy/sensor.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -Sensor to get Iliad Italy personal data. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.iliad_italy/ -""" -from datetime import timedelta -import logging - -import voluptuous as vol - -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import ATTR_ATTRIBUTION, CONF_PASSWORD, CONF_USERNAME -from homeassistant.helpers.aiohttp_client import async_get_clientsession -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import Entity -import homeassistant.util.dt as dt_util - -REQUIREMENTS = ['aioiliad==0.1.1'] - -_LOGGER = logging.getLogger(__name__) - -ATTRIBUTION = "Data provided by Iliad Italy" - -ICON = 'mdi:phone' - -SCAN_INTERVAL = timedelta(minutes=10) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, -}) - - -async def async_setup_platform( - hass, conf, async_add_entities, discovery_info=None): - """Set up the Iliad Italy sensor platform.""" - from aioiliad import Iliad - iliad = Iliad(conf[CONF_USERNAME], conf[CONF_PASSWORD], - async_get_clientsession(hass), hass.loop) - await iliad.login() - - if not iliad.is_logged_in(): - _LOGGER.error("Check username and password") - return - - async_add_entities([IliadSensor(iliad)], True) - - -class IliadSensor(Entity): - """Representation of a Iliad Italy Sensor.""" - - def __init__(self, iliad): - """Initialize the Iliad Italy sensor.""" - from aioiliad.IliadData import IliadData - self._iliad = iliad - self._iliaddata = IliadData(self._iliad) - self._data = None - self._state = None - - @property - def name(self): - """Return the name of the sensor.""" - return "Iliad {}".format(self._data['info']['utente']) - - @property - def icon(self): - """Return the icon of the sensor.""" - return ICON - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def unit_of_measurement(self): - """Return the unit of measurement of the sensor.""" - return '€' - - @property - def device_state_attributes(self): - """Return the state attributes of the sensor.""" - attr = { - ATTR_ATTRIBUTION: ATTRIBUTION, - 'next_renewal': - dt_util.utc_from_timestamp( - self._data['info']['rinnovo']).isoformat(), - 'italy_sent_sms': self._data['italy']['sms'], - 'italy_over_plan_sms': self._data['italy']['sms_extra'], - 'italy_sent_mms': self._data['italy']['mms'], - 'italy_over_plan_mms': self._data['italy']['mms_extra'], - 'italy_calls_seconds': self._data['italy']['chiamate'], - 'italy_over_plan_calls': self._data['italy']['chiamate_extra'], - 'italy_data': self._data['italy']['internet'], - 'italy_data_max': self._data['italy']['internet_max'], - 'italy_data_over_plan': self._data['italy']['internet_over'], - - 'abroad_sent_sms': self._data['estero']['sms'], - 'abroad_over_plan_sms': self._data['estero']['sms_extra'], - 'abroad_sent_mms': self._data['estero']['mms'], - 'abroad_over_plan_mms': self._data['estero']['mms_extra'], - 'abroad_calls_seconds': self._data['estero']['chiamate'], - 'abroad_over_plan_calls': self._data['estero']['chiamate_extra'], - 'abroad_data': self._data['estero']['internet'], - 'abroad_data_max': self._data['estero']['internet_max'], - 'abroad_data_over_plan': self._data['estero']['internet_over'], - } - return attr - - async def async_update(self): - """Update device state.""" - await self._iliaddata.update() - self._data = { - 'italy': self._iliaddata.get_italy(), - 'estero': self._iliaddata.get_estero(), - 'info': self._iliaddata.get_general_info() - } - self._state = self._data['info']['credito'].replace('€', '') diff --git a/requirements_all.txt b/requirements_all.txt index d4c5ac2c6cf..baadfbc8510 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -127,9 +127,6 @@ aiohttp_cors==0.7.0 # homeassistant.components.hue aiohue==1.9.1 -# homeassistant.components.iliad_italy.sensor -aioiliad==0.1.1 - # homeassistant.components.imap.sensor aioimaplib==0.7.15 From 92dc26bab33d368a2f7507e259f4fefc81d925c9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 11:33:50 -0700 Subject: [PATCH 083/290] 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 444b4a9f855..3d05eb06e6c 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 6a561e570c5..d4816213f50 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -9,7 +9,6 @@ loaded before the EVENT_PLATFORM_DISCOVERED is fired. import json from datetime import timedelta import logging -import os import voluptuous as vol @@ -199,10 +198,6 @@ async def async_setup(hass, config): """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 d4566bc0b03..28d30a9167f 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 test_discover_duplicates(hass): 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 978b0b9d450..1b62c5244e4 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 08849fd3e855fe5ae0b310eb8139a234808f6c13 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 11:38:05 -0700 Subject: [PATCH 084/290] 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 96619face25..ee779589461 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 baadfbc8510..6db90e47b74 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==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 206b2c15489..606aa3aecd7 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 2b250a7ec8d03ee558371dcab235e878e8d36cc0 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 19 Mar 2019 20:01:58 +0100 Subject: [PATCH 085/290] Upgrade youtube_dl to 2019.03.18 (#22181) --- homeassistant/components/media_extractor/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_extractor/__init__.py b/homeassistant/components/media_extractor/__init__.py index b343c13ef9a..67dd06efab8 100644 --- a/homeassistant/components/media_extractor/__init__.py +++ b/homeassistant/components/media_extractor/__init__.py @@ -12,7 +12,7 @@ from homeassistant.const import ( ATTR_ENTITY_ID) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['youtube_dl==2019.03.09'] +REQUIREMENTS = ['youtube_dl==2019.03.18'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 6db90e47b74..5be5bfeea3e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1820,7 +1820,7 @@ yeelight==0.4.3 yeelightsunflower==0.0.10 # homeassistant.components.media_extractor -youtube_dl==2019.03.09 +youtube_dl==2019.03.18 # homeassistant.components.zengge.light zengge==0.2 From 6cb880608512799c0932bb074d9efe46a07d7b5d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 19 Mar 2019 20:02:56 +0100 Subject: [PATCH 086/290] Upgrade Sphinx to 1.8.5 (#22180) --- requirements_docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_docs.txt b/requirements_docs.txt index 1efe929d666..eca1abdb365 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -1,3 +1,3 @@ -Sphinx==1.8.4 +Sphinx==1.8.5 sphinx-autodoc-typehints==1.6.0 sphinx-autodoc-annotation==1.0.post1 \ No newline at end of file From 0344c761fc6de4c16d4dc4166f32cfb76e47dcc1 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 19 Mar 2019 19:04:20 +0000 Subject: [PATCH 087/290] Better handling of HomeKit accessory-information service (#22171) * HomeKit controller: Better handling of accessory-information service * Changes from review --- .../components/homekit_controller/__init__.py | 9 +++++---- homeassistant/components/homekit_controller/cover.py | 11 ----------- .../specific_devices/test_koogeek_ls1.py | 10 +++++----- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index ec38cf881d6..b16f82de7f6 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -9,6 +9,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import call_later +from .connection import get_accessory_information from .const import ( CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_ACCESSORIES, KNOWN_DEVICES @@ -204,11 +205,9 @@ class HomeKitEntity(Entity): def __init__(self, accessory, devinfo): """Initialise a generic HomeKit device.""" self._available = True - self._name = accessory.model self._accessory = accessory self._aid = devinfo['aid'] self._iid = devinfo['iid'] - self._address = "homekit-{}-{}".format(devinfo['serial'], self._iid) self._features = 0 self._chars = {} self.setup() @@ -232,6 +231,7 @@ class HomeKitEntity(Entity): for accessory in pairing_data.get('accessories', []): if accessory['aid'] != self._aid: continue + self._accessory_info = get_accessory_information(accessory) for service in accessory['services']: if service['iid'] != self._iid: continue @@ -304,12 +304,13 @@ class HomeKitEntity(Entity): @property def unique_id(self): """Return the ID of this device.""" - return self._address + serial = self._accessory_info['serial-number'] + return "homekit-{}-{}".format(serial, self._iid) @property def name(self): """Return the name of the device if any.""" - return self._name + return self._accessory_info.get('name') @property def available(self) -> bool: diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index ccb1939e141..4cd4c9ed251 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -74,21 +74,14 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice): CharacteristicsTypes.DOOR_STATE_CURRENT, CharacteristicsTypes.DOOR_STATE_TARGET, CharacteristicsTypes.OBSTRUCTION_DETECTED, - CharacteristicsTypes.NAME, ] - def _setup_name(self, char): - self._name = char['value'] - def _update_door_state_current(self, value): self._state = CURRENT_GARAGE_STATE_MAP[value] def _update_obstruction_detected(self, value): self._obstruction_detected = value - def _update_name(self, value): - self._name = value - @property def available(self): """Return True if entity is available.""" @@ -172,12 +165,8 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice): CharacteristicsTypes.HORIZONTAL_TILT_CURRENT, CharacteristicsTypes.HORIZONTAL_TILT_TARGET, CharacteristicsTypes.OBSTRUCTION_DETECTED, - CharacteristicsTypes.NAME, ] - def _setup_name(self, char): - self._name = char['value'] - def _update_position_state(self, value): self._state = CURRENT_WINDOW_STATE_MAP[value] 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 78528c1247f..7b7981cf6de 100644 --- a/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py +++ b/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py @@ -26,14 +26,14 @@ async def test_koogeek_ls1_setup(hass): entity_registry = await hass.helpers.entity_registry.async_get_registry() # Assert that the entity is correctly added to the entity registry - entity = entity_registry.async_get('light.testdevice') - assert entity.unique_id == 'homekit-AAAA011111111111-7' + entry = entity_registry.async_get('light.koogeek_ls1_20833f') + assert entry.unique_id == 'homekit-AAAA011111111111-7' - helper = Helper(hass, 'light.testdevice', pairing, accessories[0]) + helper = Helper(hass, 'light.koogeek_ls1_20833f', pairing, accessories[0]) state = await helper.poll_and_get_state() # Assert that the friendly name is detected correctly - assert state.attributes['friendly_name'] == 'TestDevice' + assert state.attributes['friendly_name'] == 'Koogeek-LS1-20833F' # Assert that all optional features the LS1 supports are detected assert state.attributes['supported_features'] == ( @@ -54,7 +54,7 @@ async def test_recover_from_failure(hass, utcnow, failure_cls): accessories = setup_accessories_from_file(profile_path) pairing = await setup_test_accessories(hass, accessories) - helper = Helper(hass, 'light.testdevice', pairing, accessories[0]) + helper = Helper(hass, 'light.koogeek_ls1_20833f', pairing, accessories[0]) # Set light state on fake device to off helper.characteristics[LIGHT_ON].set_value(False) From db07e45df89f23dbfe078364d0e361b39dd138e5 Mon Sep 17 00:00:00 2001 From: CV Date: Tue, 19 Mar 2019 22:37:12 +0100 Subject: [PATCH 088/290] Fix breaking on HTML email without images (#22143) * Fix breaking on HTML email without images If using html emails with no images the code breaks since it is not tested for empty (uninitialized) key images. * fixed long line * Implemented suggested better solution Better solution to allow data -> html email without images. * Protecting data -> without html key from crashing If the data key does not contain the html key, sending the email would crash this "script". Preventing this by returning an default empty array. --- homeassistant/components/notify/smtp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/notify/smtp.py b/homeassistant/components/notify/smtp.py index fc38647a065..e3a6f8b85b1 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/notify/smtp.py @@ -151,10 +151,10 @@ class MailNotificationService(BaseNotificationService): if data: if ATTR_HTML in data: msg = _build_html_msg( - message, data[ATTR_HTML], images=data.get(ATTR_IMAGES)) + message, data[ATTR_HTML], images=data.get(ATTR_IMAGES, [])) else: msg = _build_multipart_msg( - message, images=data.get(ATTR_IMAGES)) + message, images=data.get(ATTR_IMAGES, [])) else: msg = _build_text_msg(message) From 398281959af62dc9b91086bf300a95c1226a332d Mon Sep 17 00:00:00 2001 From: Matt Snyder Date: Wed, 20 Mar 2019 05:33:42 -0500 Subject: [PATCH 089/290] Update codeowners (#22198) --- CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 9a4f38f60d0..33853f1c08b 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 090/290] 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 3d05eb06e6c..d532d9cdb86 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -5,7 +5,7 @@ import os 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 01d8b5831ea6fd97dac1beea99844754c4520857 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 20 Mar 2019 07:45:09 -0700 Subject: [PATCH 091/290] 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 ee779589461..6e05299ec52 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 5be5bfeea3e..06ad5698223 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 606aa3aecd7..0795db63d52 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 092/290] 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 d0848222549..1e98118e09f 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -179,7 +179,7 @@ class ZhaEntity(RestoreEntity, entity.Entity): 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 093/290] 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 4eae738b0d3..0c8908a309f 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 @@ CONFIG_SCHEMA = vol.Schema({ 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 @@ async def async_setup(hass, config): 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 @@ class SpeedtestData: 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 4deb6550444..11bc9a37ca0 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 8eccdc7b3b7..cf65daa4395 100644 --- a/homeassistant/components/systemmonitor/sensor.py +++ b/homeassistant/components/systemmonitor/sensor.py @@ -12,7 +12,7 @@ from homeassistant.helpers.entity import Entity 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 06ad5698223..a61f39e2084 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 094/290] 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 6b547927af4..b505e075018 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.const import CONF_NAME, WEEKDAYS 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 @@ _LOGGER = logging.getLogger(__name__) # 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 a61f39e2084..ef5aa8f8cfc 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 0795db63d52..98ef1dbab43 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 095/290] 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 bd34c535b70..8cb899f1d28 100644 --- a/homeassistant/components/nanoleaf/light.py +++ b/homeassistant/components/nanoleaf/light.py @@ -195,6 +195,7 @@ class NanoleafLight(Light): 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 @@ class NanoleafLight(Light): 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 096/290] 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 | 16 +++++----- 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 | 12 +++---- 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 | 9 +++--- 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 | 7 +++-- 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 | 16 +++++----- 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 | 18 ++++------- 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 | 17 +++++----- .../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, 1834 insertions(+), 1725 deletions(-) diff --git a/homeassistant/components/abode/alarm_control_panel.py b/homeassistant/components/abode/alarm_control_panel.py index 838d09b73af..d7426e04166 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 47baef1d7e5..874723420ed 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 99613d07c47..d37644eccc3 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 03d6219ebce..c40159164dc 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 aabf5fbccdc..9e88acce41f 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 ce6634268e9..0f568a4ace2 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 fa6cb9323bf..ef6941c76d8 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 d5303a27cd2..3e3ce031855 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 6771e99cd77..3b935156d18 100644 --- a/homeassistant/components/ads/binary_sensor.py +++ b/homeassistant/components/ads/binary_sensor.py @@ -3,12 +3,13 @@ import logging 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 e5299821e39..c61fd813634 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 4db6ca7dbca..118a669a7ad 100644 --- a/homeassistant/components/ads/sensor.py +++ b/homeassistant/components/ads/sensor.py @@ -4,13 +4,13 @@ import logging 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 e3aee023f21..094b4552349 100644 --- a/homeassistant/components/ads/switch.py +++ b/homeassistant/components/ads/switch.py @@ -3,12 +3,13 @@ import logging 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 cf26e42b056..d7eced933dd 100644 --- a/homeassistant/components/alarmdecoder/alarm_control_panel.py +++ b/homeassistant/components/alarmdecoder/alarm_control_panel.py @@ -4,12 +4,13 @@ import logging 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 c5af6ea79cb..09e63b4d664 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 b2f697ea83f..88371dad17a 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 2defa032809..04a38901683 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 fa3222bf0e4..b394dc558e6 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 32885c2a83c..7ba3ea04bf5 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 4869dfffa6e..c721914c73c 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 3c1f03f0145..0bbd290b3ac 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 e33e22f3778..c058c44c503 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 e98ce7951b8..4d29493d64f 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 73a94acbcdd..0304c5747f7 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 03ac5bd2549..e00ce6ed13b 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.const import ( 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 2d80ded6861..25b500ac09d 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 9e061ba91bf..dc06a2127e9 100644 --- a/homeassistant/components/aqualogic/sensor.py +++ b/homeassistant/components/aqualogic/sensor.py @@ -4,12 +4,13 @@ import logging 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 @@ class AquaLogicSensor(Entity): 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 ee040fa1ba5..21e573f944b 100644 --- a/homeassistant/components/aqualogic/switch.py +++ b/homeassistant/components/aqualogic/switch.py @@ -3,11 +3,12 @@ import logging 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 @@ class AquaLogicSwitch(SwitchDevice): 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 931dfa1b15d..3557ed125c6 100644 --- a/homeassistant/components/arlo/alarm_control_panel.py +++ b/homeassistant/components/arlo/alarm_control_panel.py @@ -3,16 +3,16 @@ import logging 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 6f20ecdadcd..43ccabb7390 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -3,16 +3,16 @@ import logging 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 1c3cc933438..e08669eb80b 100644 --- a/homeassistant/components/arlo/sensor.py +++ b/homeassistant/components/arlo/sensor.py @@ -3,19 +3,18 @@ import logging 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 9da4bd1a21a..a3e7c3f4d61 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 4630c3730ca..f5c6dd4a42a 100644 --- a/homeassistant/components/asuswrt/device_tracker.py +++ b/homeassistant/components/asuswrt/device_tracker.py @@ -6,9 +6,10 @@ https://home-assistant.io/components/device_tracker.asuswrt/ """ 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 08e2ec27cb2..53d232862c6 100644 --- a/homeassistant/components/asuswrt/sensor.py +++ b/homeassistant/components/asuswrt/sensor.py @@ -7,7 +7,8 @@ https://home-assistant.io/components/sensor.asuswrt/ 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 1ad2d80cea8..3a69d41177d 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 7420bb04067..53a9d78bc60 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -3,9 +3,10 @@ from datetime import timedelta 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 ff355bbf87b..e112eaa2592 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 5a7b19ce4e3..94776deefb0 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -6,20 +6,20 @@ import logging 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 @@ _LOGGER = logging.getLogger(__name__) 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 b9bf4a5250f..75e645dff5f 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 fe0b95b1517..466b73caf5f 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 2e5c024d6e5..1da3080e3ff 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 c1fdf9252dd..0e97db9d7d4 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 1843f647df8..deab157292d 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 21121b069af..20e84e33e29 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 8a5eddaa86a..fe646dcd1c9 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 a01142c53ed..03c03f01b4a 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 b35e7928ec1..2444192d87d 100644 --- a/homeassistant/components/bom/weather.py +++ b/homeassistant/components/bom/weather.py @@ -3,14 +3,15 @@ import logging 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 eeb65ab3ce5..fa386a17705 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -8,19 +8,18 @@ import asyncio 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 585f9ac55b9..86dcb229a78 100644 --- a/homeassistant/components/buienradar/weather.py +++ b/homeassistant/components/buienradar/weather.py @@ -3,8 +3,6 @@ import logging 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 @@ 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 BrData + REQUIREMENTS = ['buienradar==0.91'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/calendar/demo.py b/homeassistant/components/calendar/demo.py index bf9d4abeb58..bd5724ca455 100644 --- a/homeassistant/components/calendar/demo.py +++ b/homeassistant/components/calendar/demo.py @@ -6,9 +6,10 @@ https://home-assistant.io/components/demo/ """ 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 f950edb5c6c..f9be3f47c35 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/camera/demo.py @@ -7,7 +7,7 @@ https://home-assistant.io/components/demo/ 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 b22a76fdb3b..61794224666 100644 --- a/homeassistant/components/canary/alarm_control_panel.py +++ b/homeassistant/components/canary/alarm_control_panel.py @@ -7,9 +7,11 @@ https://home-assistant.io/components/alarm_control_panel.canary/ 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 eb0c8f3fc6d..c54565d6fde 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -5,18 +5,19 @@ For more details about this platform, please refer to the documentation at 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 015c6b378e0..d24c00c9266 100644 --- a/homeassistant/components/canary/sensor.py +++ b/homeassistant/components/canary/sensor.py @@ -5,11 +5,12 @@ For more details about this platform, please refer to the documentation at 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 28373cc6c14..77332883a91 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -7,14 +7,13 @@ from typing import Optional, Tuple 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.dispatcher import ( 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 5b4775982a6..2dd31c1b20d 100644 --- a/homeassistant/components/climate/demo.py +++ b/homeassistant/components/climate/demo.py @@ -4,16 +4,16 @@ 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/ """ -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 da4f79ec1e6..af0a3eea6ab 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -9,23 +9,23 @@ import logging 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 a396dd276a5..88dcffcfd21 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 ac5de866cfb..edb96b8d279 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 af9b3d48dea..21ee1312e7a 100644 --- a/homeassistant/components/command_line/binary_sensor.py +++ b/homeassistant/components/command_line/binary_sensor.py @@ -4,18 +4,19 @@ 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/ """ -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 47ac9d3a4b2..175d90ff59c 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 ec9d9d0ff27..bb774ae7ef8 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 f9b9a2c4918..60421bcc125 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 640e267d066..c8a58e5d72a 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 f29dde4594c..e7e39968401 100644 --- a/homeassistant/components/config/zwave.py +++ b/homeassistant/components/config/zwave.py @@ -4,13 +4,14 @@ import logging 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 7082cb36726..bb2d692f249 100644 --- a/homeassistant/components/conversation/__init__.py +++ b/homeassistant/components/conversation/__init__.py @@ -6,17 +6,16 @@ import voluptuous as vol 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 21add0a6c62..1f31cecc996 100644 --- a/homeassistant/components/cover/demo.py +++ b/homeassistant/components/cover/demo.py @@ -4,11 +4,12 @@ Demo platform for the cover component. 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 0424c900747..aba57284da8 100644 --- a/homeassistant/components/cover/group.py +++ b/homeassistant/components/cover/group.py @@ -8,22 +8,22 @@ import logging 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 869c38869cb..c42f2785576 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -4,19 +4,20 @@ import re 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 3669dfac280..5a005e29989 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 4052a100540..723b0d08801 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 9902184e624..a5dc2a2eb09 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 ec85757be59..f5a7fd47f69 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 bf0799d1fa2..e728430f202 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 88df56cc629..d496a09161b 100644 --- a/homeassistant/components/digital_ocean/binary_sensor.py +++ b/homeassistant/components/digital_ocean/binary_sensor.py @@ -3,14 +3,15 @@ import logging 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 9b5ddda3408..bc4a6a29b42 100644 --- a/homeassistant/components/digital_ocean/switch.py +++ b/homeassistant/components/digital_ocean/switch.py @@ -3,13 +3,14 @@ import logging 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 e201837aaf6..272a3cb932a 100644 --- a/homeassistant/components/doorbird/camera.py +++ b/homeassistant/components/doorbird/camera.py @@ -7,9 +7,10 @@ import aiohttp 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 376713d4b27..ba6f96660d1 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 ea6ba2b455f..59827529ed3 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 eb0016ed298..56c4ee03a3a 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 09196a82bed..3e5c976b1f4 100644 --- a/homeassistant/components/dyson/climate.py +++ b/homeassistant/components/dyson/climate.py @@ -6,12 +6,13 @@ https://home-assistant.io/components/climate.dyson/ """ 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 ef517021178..743d301df42 100644 --- a/homeassistant/components/dyson/fan.py +++ b/homeassistant/components/dyson/fan.py @@ -7,11 +7,12 @@ import logging 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 53913d47b72..ed8987f75c2 100644 --- a/homeassistant/components/dyson/sensor.py +++ b/homeassistant/components/dyson/sensor.py @@ -6,10 +6,11 @@ https://home-assistant.io/components/sensor.dyson/ """ 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 3d6e23c20c8..7902cfa1585 100644 --- a/homeassistant/components/dyson/vacuum.py +++ b/homeassistant/components/dyson/vacuum.py @@ -6,13 +6,14 @@ https://home-assistant.io/components/vacuum.dyson/ """ 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 47ed2d6ebdf..ef8b39842d9 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 f113125194a..db8759a032a 100644 --- a/homeassistant/components/ecoal_boiler/switch.py +++ b/homeassistant/components/ecoal_boiler/switch.py @@ -3,8 +3,8 @@ import logging 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 9d2af730315..b9fe94f2bed 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 389ae77f35b..b8f9c031c29 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 ad4ce8fe728..0c92f80ccf6 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 e202a46f9f1..7fc60d5fb5d 100644 --- a/homeassistant/components/egardia/alarm_control_panel.py +++ b/homeassistant/components/egardia/alarm_control_panel.py @@ -4,14 +4,15 @@ import logging 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 74a048a86c0..d11894ae675 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 2a9cb19a327..a3ca27b570d 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 2bb03c8d4f2..a1ad93ec54a 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 63b38c1d321..e9155dd17b5 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 72f93b5419c..93e4aa66b23 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 3a282595d58..fe84ab3f251 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 c8583b1d8bf..1d08f4cf96d 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 63da6ea5376..da27a3ac4b1 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 7badd6ee5dc..740a2965865 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 cd42560288d..9dfb58a5e34 100644 --- a/homeassistant/components/emulated_roku/binding.py +++ b/homeassistant/components/emulated_roku/binding.py @@ -5,7 +5,7 @@ from homeassistant.const import ( 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 a4cc5864fc4..44874c6d5e8 100644 --- a/homeassistant/components/envisalink/alarm_control_panel.py +++ b/homeassistant/components/envisalink/alarm_control_panel.py @@ -3,16 +3,18 @@ import logging 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 26b54e16cc8..267bba8cd28 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.components.binary_sensor import BinarySensorDevice +from homeassistant.const import ATTR_LAST_TRIP_TIME 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.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 cc6a8b87232..67a601b02a2 100644 --- a/homeassistant/components/envisalink/sensor.py +++ b/homeassistant/components/envisalink/sensor.py @@ -3,11 +3,12 @@ import logging 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 4796df636ba..2db2f209fa5 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 14fce3fb4eb..d86c40e627e 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 49e2401545b..05f18cb014a 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 3b0889ede8e..c84c50010d9 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 typing import TYPE_CHECKING, List, Optional, Tuple -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 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 f776ea4683b..e4fb7ef82ba 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 4bf4b416b8d..e5a9d0cf446 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 955b82e37e3..eea34e07001 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -6,30 +6,18 @@ from requests.exceptions import HTTPError 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 840196c8bf0..e67fbef650e 100644 --- a/homeassistant/components/fan/demo.py +++ b/homeassistant/components/fan/demo.py @@ -4,11 +4,12 @@ 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/ """ -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 0f17179f918..37fc0815ddc 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 db9e73f3e1b..4272a3d6029 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -9,13 +9,12 @@ import logging 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 2c2d9c30a79..f71a5f3662e 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 aa34fcc36a9..0f5cc32bc96 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 5ee3e83b95f..600b566b36b 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 620f095b733..93f0cd5b63a 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 01452d8b394..20a37fd3c23 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.const import ( - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_ILLUMINANCE, 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 homeassistant.const import ( + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, + TEMP_CELSIUS, TEMP_FAHRENHEIT) +from homeassistant.helpers.entity import Entity + +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 04b8aba1cf4..024531f62c7 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 fb94f7f56f5..5418c1c61a7 100644 --- a/homeassistant/components/freebox/device_tracker.py +++ b/homeassistant/components/freebox/device_tracker.py @@ -3,7 +3,8 @@ from collections import namedtuple 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 49e68dc2c41..328665ab51c 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 c68c79f1e77..65578c57180 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -4,7 +4,8 @@ import logging 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 e8c20061b4e..e2c9be833ac 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -3,19 +3,19 @@ import logging 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 a1736fb9857..7309f8cc618 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -3,11 +3,11 @@ import logging 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 617c4902068..e227cdaef8a 100644 --- a/homeassistant/components/fritzbox/switch.py +++ b/homeassistant/components/fritzbox/switch.py @@ -3,12 +3,12 @@ import logging 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 27466f64cfb..ec69d1eec83 100644 --- a/homeassistant/components/gc100/binary_sensor.py +++ b/homeassistant/components/gc100/binary_sensor.py @@ -6,12 +6,13 @@ https://home-assistant.io/components/binary_sensor.gc100/ """ 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 2a8e7eaa847..94c824fa5d7 100644 --- a/homeassistant/components/gc100/switch.py +++ b/homeassistant/components/gc100/switch.py @@ -6,11 +6,12 @@ https://home-assistant.io/components/switch.gc100/ """ import voluptuous as vol -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.components.switch import PLATFORM_SCHEMA from homeassistant.const import DEVICE_DEFAULT_NAME +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import ToggleEntity + +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 523e125a737..d63280ce609 100644 --- a/homeassistant/components/geo_location/demo.py +++ b/homeassistant/components/geo_location/demo.py @@ -5,9 +5,10 @@ from math import cos, pi, radians, sin 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 51201240c1c..5e7b8291840 100644 --- a/homeassistant/components/geofency/device_tracker.py +++ b/homeassistant/components/geofency/device_tracker.py @@ -8,10 +8,10 @@ import logging 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 cc65c6d655d..9f71e7c4f20 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 462f5db3b9b..c024cde0c6c 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 7577ee0b017..4f37740da85 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 90d2dc04f89..c9496975272 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 4796744c170..2d36de8b769 100644 --- a/homeassistant/components/hangouts/__init__.py +++ b/homeassistant/components/hangouts/__init__.py @@ -4,12 +4,12 @@ import logging 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 ca0fdf986ee..38b238292b3 100644 --- a/homeassistant/components/hangouts/const.py +++ b/homeassistant/components/hangouts/const.py @@ -7,7 +7,7 @@ from homeassistant.components.notify import ( 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 c3b5450be05..de9af2e0775 100644 --- a/homeassistant/components/hangouts/notify.py +++ b/homeassistant/components/hangouts/notify.py @@ -3,12 +3,13 @@ import logging 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 553983a1f03..b2d2910e145 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.components.media_player.const import ( 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 ff423890ba5..639f545707e 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 dee27c5c710..a0973f4d8e9 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 45829cda087..dac7feb2927 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 2bec60f0ee4..3a2176c3eed 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 142c8c7ee94..e7b7d6b4597 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 c897e37f34b..fd4d3d69b50 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 b1bfc5ce23d..164a504fa34 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 61352c3bedc..9bc15aad75d 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 5d83ce6d984..7fcc5b4e833 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 8696d2b1f97..243b795e792 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -3,11 +3,11 @@ import logging 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 4cd4c9ed251..4db1246b992 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -5,11 +5,11 @@ from homeassistant.components.cover import ( 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 b5677c0e095..db8fd332c0c 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 6da5fa35655..a2aac5767bd 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 5af0016eb16..955a1a7927e 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 21f10e6243c..ba19413d411 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 1704411c9cc..7bf260a9bdc 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 e5eb292b4ff..146cad1bc4c 100644 --- a/homeassistant/components/homematic/climate.py +++ b/homeassistant/components/homematic/climate.py @@ -5,10 +5,10 @@ from homeassistant.components.climate import ClimateDevice 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 79a1afe9a0e..33b764dc31f 100644 --- a/homeassistant/components/homematic/cover.py +++ b/homeassistant/components/homematic/cover.py @@ -3,9 +3,10 @@ import logging 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 21b875742c4..c3601461173 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 5d857617fde..3c0ca040c5f 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 e6ef1a60e28..021560eee3c 100644 --- a/homeassistant/components/homematic/notify.py +++ b/homeassistant/components/homematic/notify.py @@ -8,14 +8,15 @@ import logging 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 8e3e55e1f7f..401d11f70c8 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 cfcd26891e0..393ad09b310 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 efa1ea1f46e..eb5855bb980 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 9445d6521cc..786a28a70a5 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 08c88bbb796..955f3e5baa7 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -4,10 +4,10 @@ import logging 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 fbda56f2805..c9a5df601e4 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 86c11dab70d..735e8789670 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 73c607683ba..f8b19b5bb1e 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 d6155998332..39758739400 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 74f50f87b25..62e72f0ade7 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 5a6261195da..101adcdeaaa 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 7f5d7f6aab7..ca41dff9834 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 bb7f2c2fee2..daac9fef748 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -9,12 +9,12 @@ from aiohttp.web_exceptions import ( 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 2eb30d47804..d61a0aa7e89 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 bfe7cbd5531..85a51d3649e 100644 --- a/homeassistant/components/hydrawise/binary_sensor.py +++ b/homeassistant/components/hydrawise/binary_sensor.py @@ -3,13 +3,14 @@ import logging 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 575686b92cd..fc15a54ed60 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -3,11 +3,12 @@ import logging 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 a6a8b9c54cf..dcbd5274a62 100644 --- a/homeassistant/components/hydrawise/switch.py +++ b/homeassistant/components/hydrawise/switch.py @@ -3,13 +3,14 @@ import logging 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 98a176b1e82..3f806173196 100644 --- a/homeassistant/components/ifttt/alarm_control_panel.py +++ b/homeassistant/components/ifttt/alarm_control_panel.py @@ -7,14 +7,14 @@ import voluptuous as vol 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 daaf471e318..102acd82551 100644 --- a/homeassistant/components/ihc/__init__.py +++ b/homeassistant/components/ihc/__init__.py @@ -5,13 +5,6 @@ import os.path 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 @@ from homeassistant.helpers import discovery 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 7e3371a834c..69e3e1685af 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 646be7597d0..ad6d0fb6511 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 930ac221629..fd1f2cee53a 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 d25b343446d..e2189492b8f 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 35229c2a805..9dbb4787df7 100644 --- a/homeassistant/components/influxdb/sensor.py +++ b/homeassistant/components/influxdb/sensor.py @@ -9,7 +9,6 @@ import logging 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 @@ import homeassistant.helpers.config_validation as cv 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 06eddb9a004..6f1e5675639 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 7de2e872489..1bb316152a9 100644 --- a/homeassistant/components/insteon/cover.py +++ b/homeassistant/components/insteon/cover.py @@ -5,7 +5,8 @@ import math 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 2b6097a4ba2..26a56d6df98 100644 --- a/homeassistant/components/insteon/fan.py +++ b/homeassistant/components/insteon/fan.py @@ -4,9 +4,10 @@ import logging 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 e8ffc226716..676c053325c 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 d895d972027..edea87e1f73 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 2a6b97a39d1..4fdcdb20bb2 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 5cd5db6169b..2955828aff5 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 59813ae0455..db9aafcdf4b 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 bdd97c74e6a..1e778eff5bd 100644 --- a/homeassistant/components/ipma/const.py +++ b/homeassistant/components/ipma/const.py @@ -11,4 +11,4 @@ ENTITY_ID_SENSOR_FORMAT = WEATHER_DOMAIN + ".ipma_{}" 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 013b99fbb15..ce95e71e8d4 100644 --- a/homeassistant/components/isy994/binary_sensor.py +++ b/homeassistant/components/isy994/binary_sensor.py @@ -4,14 +4,14 @@ import logging 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 22ea1629794..b40d6428f24 100644 --- a/homeassistant/components/isy994/cover.py +++ b/homeassistant/components/isy994/cover.py @@ -3,12 +3,12 @@ import logging 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 142eaedd66b..5a21a28fd8d 100644 --- a/homeassistant/components/isy994/fan.py +++ b/homeassistant/components/isy994/fan.py @@ -5,10 +5,10 @@ from typing import Callable 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 cc39a6d1a3b..0ac50a5f384 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 a2e8b1a1e56..92cb317ed20 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 2115c19f496..43c016ed4d1 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 96f17c80bef..5f0acd1b1e2 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 00b183fca46..6b55e539547 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 c84e5820f04..8ee21e24c5e 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -3,12 +3,12 @@ import voluptuous as vol 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 921b2936d97..e11e5449326 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -5,11 +5,12 @@ from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice 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 9423983f9f7..b2b287d1e87 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -5,12 +5,13 @@ from homeassistant.components.cover import ( 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 baba7edd21a..cf59f1fc135 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -3,7 +3,6 @@ from enum import Enum 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 @@ from homeassistant.core import callback 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 1e1d7f185f0..742252d1874 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 b1bb2bf3109..4bf186c28ff 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 abbb61e150d..7ddafe53be4 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 cef14fb74dc..e3beff39677 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 a47f81b9556..1fbfbea1861 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 eb3f5511346..a48d1a58619 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 1a4b495297e..dfb135e19f6 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 9903676d9f9..358bb056b00 100644 --- a/homeassistant/components/lametric/notify.py +++ b/homeassistant/components/lametric/notify.py @@ -5,11 +5,11 @@ from requests.exceptions import ConnectionError as RequestsConnectionError 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 6b995643443..e380c2bb4a1 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -3,12 +3,6 @@ import logging 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 @@ 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) + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pypck==0.5.9'] diff --git a/homeassistant/components/lcn/cover.py b/homeassistant/components/lcn/cover.py index 4b4542fd623..a32ff7c23f4 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 5f1008cbd57..00b78259354 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 e56c42de058..5e50d092ada 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 09f35d26718..7c375f4a598 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 19a9f7583ec..014ca9ae6c8 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -16,9 +16,6 @@ from homeassistant.components.light import ( 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.event import async_track_point_in_utc_time 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 980d8491744..d9affb03db3 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -6,10 +6,10 @@ https://home-assistant.io/components/demo/ """ 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 bf54d3ecf29..7e9d11d3a02 100644 --- a/homeassistant/components/light/group.py +++ b/homeassistant/components/light/group.py @@ -4,27 +4,28 @@ 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/ """ -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 de6247a2772..4b4f3313349 100644 --- a/homeassistant/components/light/switch.py +++ b/homeassistant/components/light/switch.py @@ -5,22 +5,18 @@ For more information about this platform, please refer to the documentation at 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 1dfbac37c88..f22533d2548 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 d6c00b7fddb..dfa93b4b151 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 a05681497de..19455917dbb 100644 --- a/homeassistant/components/linode/binary_sensor.py +++ b/homeassistant/components/linode/binary_sensor.py @@ -5,11 +5,12 @@ import voluptuous as vol 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 0cab2f4d0f2..e5f97ef756e 100644 --- a/homeassistant/components/linode/switch.py +++ b/homeassistant/components/linode/switch.py @@ -3,12 +3,13 @@ import logging 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 78090914b2c..9dbf7e74fe3 100644 --- a/homeassistant/components/locative/device_tracker.py +++ b/homeassistant/components/locative/device_tracker.py @@ -6,13 +6,13 @@ https://home-assistant.io/components/device_tracker.locative/ """ 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 a0cc45991c8..94a67fb87f6 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -4,8 +4,9 @@ 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/ """ -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 4f349dd986e..814475d04de 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 4830219091c..06d1701a9eb 100644 --- a/homeassistant/components/logi_circle/sensor.py +++ b/homeassistant/components/logi_circle/sensor.py @@ -3,18 +3,18 @@ import logging 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 398ec30a3f5..107673bac45 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 de62e5bfac2..0a88f3bd552 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 8a5e103db0d..2c3f5e0e0b8 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.lupusec import (LupusecDevice, - DOMAIN as LUPUSEC_DOMAIN) -from homeassistant.components.binary_sensor import (BinarySensorDevice, - DEVICE_CLASSES) +from homeassistant.components.binary_sensor import ( + DEVICE_CLASSES, BinarySensorDevice) + +from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice DEPENDENCIES = ['lupusec'] diff --git a/homeassistant/components/lupusec/switch.py b/homeassistant/components/lupusec/switch.py index 8a30d65fec3..0d86ea0a365 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 cc7a57a5522..da7f69095fc 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 c0b3b991147..5f3fd4787fd 100644 --- a/homeassistant/components/lutron/light.py +++ b/homeassistant/components/lutron/light.py @@ -3,8 +3,8 @@ import logging 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 f9002f2a839..a2d18c6d242 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 bfdb06be33c..b42c0d930bc 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 5e09dcc3c85..d970f5282ff 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 3bab781f3b6..d883da73c91 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 c6ca7bad3ac..2e7059a56fc 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 0ef0595187b..54c67091357 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 05137254fcc..b9f5bf0b100 100644 --- a/homeassistant/components/mailgun/notify.py +++ b/homeassistant/components/mailgun/notify.py @@ -3,14 +3,14 @@ import logging 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 8d5ab84f6d3..6221b95d879 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 170a3ba349c..c30ebc7d697 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 de455879d3d..070701f6322 100644 --- a/homeassistant/components/media_player/demo.py +++ b/homeassistant/components/media_player/demo.py @@ -4,16 +4,16 @@ Demo implementation of the media player. 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 b9eb28a61d7..0df294a148d 100644 --- a/homeassistant/components/melissa/climate.py +++ b/homeassistant/components/melissa/climate.py @@ -8,16 +8,15 @@ import logging 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 f0ef926793e..122b91cae44 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 849c9d9da10..b2b94c7622e 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 b4984bc3cad..a67dcdcdbd6 100644 --- a/homeassistant/components/metoffice/weather.py +++ b/homeassistant/components/metoffice/weather.py @@ -3,13 +3,13 @@ import logging 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 4e0ab74445d..0c10548452a 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -4,12 +4,12 @@ import logging 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 44daedac9c1..4d2b86903e7 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -6,11 +6,11 @@ import voluptuous as vol 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 3f8c68b25ff..10e11a9a656 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -4,8 +4,6 @@ import struct 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.const import ( 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 b7039a01da3..69c5e3e4838 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -3,8 +3,6 @@ import logging 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 import config_validation as cv 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 e4d468e2155..2c605fb4b0d 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -5,8 +5,8 @@ For more details about this component, please refer to the documentation at 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 requests.certs 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.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 -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 @@ class MqttDiscoveryUpdate(Entity): 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 c350b32b4ff..9498b597590 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -11,12 +11,6 @@ import voluptuous as vol 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 @@ import homeassistant.helpers.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_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 f2a93d06f8e..7532f305328 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -11,12 +11,6 @@ import voluptuous as vol 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 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect 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 ca41f3c4225..889e5533403 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -12,17 +12,17 @@ import voluptuous as vol 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 ae847437932..a9c23d27e11 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -10,20 +10,13 @@ import voluptuous as vol 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 @@ import homeassistant.helpers.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_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 37222cbe868..8116421ac10 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -13,12 +13,6 @@ from homeassistant.components.cover import ( 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 @@ import homeassistant.helpers.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_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 bf55d955ce1..0f22ed150ca 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -9,12 +9,13 @@ import logging 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 745e54d0ed7..cb87a208b4f 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -10,12 +10,13 @@ import logging 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 7c9f816eff7..b8bff6088d8 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -12,12 +12,6 @@ from homeassistant.components import fan, mqtt 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 @@ import homeassistant.helpers.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_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 d9adc37d79a..ee459d2174f 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -10,12 +10,6 @@ import voluptuous as vol 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 @@ import homeassistant.helpers.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_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 c6ef3344fcf..aa8d5e2a31e 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -12,12 +12,6 @@ from typing import Optional 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.event import async_track_point_in_utc_time 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 e4563fc377e..e159132d560 100644 --- a/homeassistant/components/mqtt/subscription.py +++ b/homeassistant/components/mqtt/subscription.py @@ -10,10 +10,11 @@ from typing import Any, Callable, Dict, Optional 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 de7da6b7249..4847afd80c9 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -9,12 +9,6 @@ import logging 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.dispatcher import async_dispatcher_connect 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 eb7e78b6254..efa00821c1b 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -9,11 +9,6 @@ import logging 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 @@ import homeassistant.helpers.config_validation as cv 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 67f12a14359..a2435d596be 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 c7d140e0c4c..42b27df6299 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 530aa8fc6f1..f8106c3e645 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 fcc72762b8d..ea60f9492e2 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 2f2f3904947..3575301ea97 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 ee3a0c213cb..f77b534980f 100644 --- a/homeassistant/components/ness_alarm/alarm_control_panel.py +++ b/homeassistant/components/ness_alarm/alarm_control_panel.py @@ -8,14 +8,14 @@ https://home-assistant.io/components/alarm_control_panel.ness_alarm/ 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 9f1479efd69..7b684f74aa1 100644 --- a/homeassistant/components/ness_alarm/binary_sensor.py +++ b/homeassistant/components/ness_alarm/binary_sensor.py @@ -7,12 +7,13 @@ https://home-assistant.io/components/binary_sensor.ness_alarm/ 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 1077fdb073e..aa56bfbf29d 100644 --- a/homeassistant/components/nest/binary_sensor.py +++ b/homeassistant/components/nest/binary_sensor.py @@ -3,10 +3,11 @@ from itertools import chain 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 88b6cbbbeb0..cd9a7cb71b6 100644 --- a/homeassistant/components/nest/climate.py +++ b/homeassistant/components/nest/climate.py @@ -3,20 +3,19 @@ import logging 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 bde3f681c2b..ecae83e303c 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 7986010ef64..a11ce6bddf7 100644 --- a/homeassistant/components/netatmo/binary_sensor.py +++ b/homeassistant/components/netatmo/binary_sensor.py @@ -4,11 +4,12 @@ import logging 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 57d30d6cbc9..513852cde23 100644 --- a/homeassistant/components/netatmo/camera.py +++ b/homeassistant/components/netatmo/camera.py @@ -4,11 +4,12 @@ import logging 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 2397405ec20..5c71cf1fc51 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 1ca7fceb911..95f6fcdcaf1 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 f6206f1f4ef..682f482b488 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 60b9a6630cd..e6d72103a6c 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 8fabfc3aefb..3f5403f0c13 100644 --- a/homeassistant/components/notify/apns.py +++ b/homeassistant/components/notify/apns.py @@ -9,13 +9,14 @@ import os 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 17df1ba8f5a..e605f82c3f1 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -4,19 +4,18 @@ 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/ """ -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 065898bcb85..9363576fc1a 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/notify/aws_sns.py @@ -4,18 +4,18 @@ 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/ """ -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 78e71bde97a..ed22147cfed 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/notify/aws_sqs.py @@ -4,17 +4,16 @@ 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/ """ -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 e83e0e9024f..a33e9432e92 100644 --- a/homeassistant/components/notify/ciscospark.py +++ b/homeassistant/components/notify/ciscospark.py @@ -8,11 +8,11 @@ import logging 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 6af2b455129..559232e2555 100644 --- a/homeassistant/components/notify/clickatell.py +++ b/homeassistant/components/notify/clickatell.py @@ -9,10 +9,10 @@ import logging 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 faf30ac7cc6..6f10ac70734 100644 --- a/homeassistant/components/notify/clicksend.py +++ b/homeassistant/components/notify/clicksend.py @@ -7,16 +7,16 @@ https://home-assistant.io/components/notify.clicksend/ 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 c60e02ece1a..1481fea1783 100644 --- a/homeassistant/components/notify/clicksend_tts.py +++ b/homeassistant/components/notify/clicksend_tts.py @@ -9,15 +9,15 @@ https://home-assistant.io/components/notify.clicksend_tts/ 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 4a7483d6e4d..6a81dace288 100644 --- a/homeassistant/components/notify/command_line.py +++ b/homeassistant/components/notify/command_line.py @@ -9,11 +9,11 @@ import subprocess 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 5b8e1f1688f..9cb61609017 100644 --- a/homeassistant/components/notify/demo.py +++ b/homeassistant/components/notify/demo.py @@ -4,7 +4,7 @@ Demo notification service. 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 e9f0d5cec28..f98c136b3f9 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/notify/discord.py @@ -8,10 +8,10 @@ import logging 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 b73f845ea17..b642a7b932b 100644 --- a/homeassistant/components/notify/facebook.py +++ b/homeassistant/components/notify/facebook.py @@ -11,11 +11,11 @@ from aiohttp.hdrs import CONTENT_TYPE 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 749a6d4b330..98f6da66e3d 100644 --- a/homeassistant/components/notify/file.py +++ b/homeassistant/components/notify/file.py @@ -9,11 +9,12 @@ import os 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 d26f629809f..16f38bbbb63 100644 --- a/homeassistant/components/notify/flock.py +++ b/homeassistant/components/notify/flock.py @@ -10,12 +10,12 @@ import logging 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 a27d0495193..fb7829105fc 100644 --- a/homeassistant/components/notify/free_mobile.py +++ b/homeassistant/components/notify/free_mobile.py @@ -8,11 +8,11 @@ import logging 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 1a2b65f958f..4560eb21f31 100644 --- a/homeassistant/components/notify/gntp.py +++ b/homeassistant/components/notify/gntp.py @@ -9,11 +9,12 @@ import os 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 5d25c2d815e..85d4d53e3be 100644 --- a/homeassistant/components/notify/group.py +++ b/homeassistant/components/notify/group.py @@ -8,13 +8,15 @@ import asyncio 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 344827c00b4..8ce7b8b120e 100644 --- a/homeassistant/components/notify/hipchat.py +++ b/homeassistant/components/notify/hipchat.py @@ -8,10 +8,10 @@ import logging 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 17f7e316357..f9bf36e61f0 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -5,9 +5,9 @@ For more details about this platform, please refer to the documentation at 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 @@ from aiohttp.hdrs import AUTHORIZATION 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 50d2246cd29..2dd33bf8990 100644 --- a/homeassistant/components/notify/kodi.py +++ b/homeassistant/components/notify/kodi.py @@ -10,14 +10,15 @@ import aiohttp 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 5677f38b06c..5c975e8422f 100644 --- a/homeassistant/components/notify/lannouncer.py +++ b/homeassistant/components/notify/lannouncer.py @@ -5,16 +5,16 @@ For more details about this platform, please refer to the documentation at 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 0ddcb450bcf..d3689dbbd81 100644 --- a/homeassistant/components/notify/llamalab_automate.py +++ b/homeassistant/components/notify/llamalab_automate.py @@ -5,14 +5,14 @@ For more details about this platform, please refer to the documentation at 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 095e3f98ff9..59c787bc026 100644 --- a/homeassistant/components/notify/mastodon.py +++ b/homeassistant/components/notify/mastodon.py @@ -8,11 +8,11 @@ import logging 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 fa747ccba88..c45d153d813 100644 --- a/homeassistant/components/notify/message_bird.py +++ b/homeassistant/components/notify/message_bird.py @@ -8,10 +8,10 @@ import logging 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 1fd22c5c42b..e9cd44d5d06 100644 --- a/homeassistant/components/notify/mycroft.py +++ b/homeassistant/components/notify/mycroft.py @@ -6,8 +6,7 @@ https://home-assistant.io/components/notify.mycroft/ """ 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 f99d97574b4..8127b4b3e96 100644 --- a/homeassistant/components/notify/nfandroidtv.py +++ b/homeassistant/components/notify/nfandroidtv.py @@ -4,21 +4,21 @@ 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/ """ -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 f0741766a70..27ce8d0fb7a 100644 --- a/homeassistant/components/notify/prowl.py +++ b/homeassistant/components/notify/prowl.py @@ -4,18 +4,19 @@ Prowl notification service. 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 a94cf4f1055..505d6f4e1c1 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/notify/pushbullet.py @@ -9,12 +9,13 @@ import mimetypes 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 a29cd587105..5db67177548 100644 --- a/homeassistant/components/notify/pushetta.py +++ b/homeassistant/components/notify/pushetta.py @@ -8,11 +8,12 @@ import logging 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 b249ca804b3..372ea361f13 100644 --- a/homeassistant/components/notify/pushover.py +++ b/homeassistant/components/notify/pushover.py @@ -8,12 +8,13 @@ import logging 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 94dc08a8113..9a3308ff21e 100644 --- a/homeassistant/components/notify/pushsafer.py +++ b/homeassistant/components/notify/pushsafer.py @@ -4,19 +4,20 @@ Pushsafer platform for notify component. 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 469319a0f3e..eec2ea4aa37 100644 --- a/homeassistant/components/notify/rest.py +++ b/homeassistant/components/notify/rest.py @@ -9,16 +9,16 @@ import logging 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 e9b481b1cf3..116c32993d8 100644 --- a/homeassistant/components/notify/rocketchat.py +++ b/homeassistant/components/notify/rocketchat.py @@ -8,11 +8,11 @@ import logging 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 e72dcbbed36..6bab566bfc7 100644 --- a/homeassistant/components/notify/sendgrid.py +++ b/homeassistant/components/notify/sendgrid.py @@ -8,12 +8,13 @@ import logging 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 9d5c58fc5b1..1d198b5adec 100644 --- a/homeassistant/components/notify/simplepush.py +++ b/homeassistant/components/notify/simplepush.py @@ -8,10 +8,11 @@ import logging import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_PASSWORD +import homeassistant.helpers.config_validation as cv + +from . import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['simplepush==1.1.4'] diff --git a/homeassistant/components/notify/slack.py b/homeassistant/components/notify/slack.py index 961f671203f..7aee58ed012 100644 --- a/homeassistant/components/notify/slack.py +++ b/homeassistant/components/notify/slack.py @@ -7,15 +7,15 @@ https://home-assistant.io/components/notify.slack/ 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 e3a6f8b85b1..995aae76cc6 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/notify/smtp.py @@ -4,25 +4,26 @@ Mail (SMTP) notification service. 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 f31e50a5886..f5f5b52ab67 100644 --- a/homeassistant/components/notify/stride.py +++ b/homeassistant/components/notify/stride.py @@ -8,10 +8,10 @@ import logging 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 922631b4045..023586ae532 100644 --- a/homeassistant/components/notify/synology_chat.py +++ b/homeassistant/components/notify/synology_chat.py @@ -4,17 +4,17 @@ 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/ """ -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 740148e28e5..4f7900b8d45 100644 --- a/homeassistant/components/notify/syslog.py +++ b/homeassistant/components/notify/syslog.py @@ -8,7 +8,7 @@ import logging 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 1dff82fa2cd..a6975f60fae 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/notify/telegram.py @@ -8,11 +8,12 @@ import logging 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 538a4fd9512..4826ab77612 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -11,8 +11,8 @@ import voluptuous as vol 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 e106c5ae6aa..165e743977d 100644 --- a/homeassistant/components/notify/twilio_sms.py +++ b/homeassistant/components/notify/twilio_sms.py @@ -10,8 +10,8 @@ import voluptuous as vol 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 d494952716e..43d977d26a6 100644 --- a/homeassistant/components/notify/twitter.py +++ b/homeassistant/components/notify/twitter.py @@ -4,21 +4,21 @@ Twitter platform for notify component. 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 462dd007d53..3827674316b 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/notify/xmpp.py @@ -14,13 +14,14 @@ import string 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 e16e384ca25..19efa2a53d8 100644 --- a/homeassistant/components/notify/yessssms.py +++ b/homeassistant/components/notify/yessssms.py @@ -8,11 +8,10 @@ import logging 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 f52d2c7b501..27e909f8f04 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -4,29 +4,22 @@ Support for NuHeat thermostats. 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 cb860177796..be3381f3bc8 100644 --- a/homeassistant/components/octoprint/binary_sensor.py +++ b/homeassistant/components/octoprint/binary_sensor.py @@ -3,10 +3,10 @@ import logging 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 2df307f02ef..f07d88d11da 100644 --- a/homeassistant/components/octoprint/sensor.py +++ b/homeassistant/components/octoprint/sensor.py @@ -3,11 +3,11 @@ import logging 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 b35998c807b..d0b60a25770 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 1a7c031638f..58ce49a9b02 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -3,14 +3,15 @@ import logging 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 070f847e5e5..5c64b8ab719 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 b790427b228..cfc82a75729 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 489a100a5e5..42780d57b3c 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 cb66278150a..bcdd0fec11f 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 b91cc387718..849e0858ade 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 be8698a47b1..f1214b62b0e 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -9,12 +9,11 @@ import logging 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 43cceaa671f..233539560f4 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 a50dffe42b9..4fd5ffea641 100644 --- a/homeassistant/components/point/alarm_control_panel.py +++ b/homeassistant/components/point/alarm_control_panel.py @@ -3,13 +3,13 @@ import logging 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 c29ce421682..2276d4e2fb5 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 90b83ba42e3..46399c82af4 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.const import ( 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 087d3f89f80..edefb5e4709 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -6,8 +6,9 @@ https://home-assistant.io/components/ps4/ """ 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 d000ed1f7e7..482c7383d89 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -5,11 +5,11 @@ import logging 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 e2b8e97f425..60b656a469d 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -9,20 +9,18 @@ import socket 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 2fe14773d3a..17021f7a9e9 100644 --- a/homeassistant/components/qwikswitch/binary_sensor.py +++ b/homeassistant/components/qwikswitch/binary_sensor.py @@ -7,9 +7,10 @@ https://home-assistant.io/components/binary_sensor.qwikswitch/ 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 413358d9cee..46a0a88483b 100644 --- a/homeassistant/components/qwikswitch/light.py +++ b/homeassistant/components/qwikswitch/light.py @@ -4,10 +4,10 @@ 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/ """ -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 c8d33de4baf..07d0247e4f6 100644 --- a/homeassistant/components/qwikswitch/sensor.py +++ b/homeassistant/components/qwikswitch/sensor.py @@ -6,9 +6,10 @@ https://home-assistant.io/components/sensor.qwikswitch/ """ 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 3b8e6d74df2..ec544df8c75 100644 --- a/homeassistant/components/qwikswitch/switch.py +++ b/homeassistant/components/qwikswitch/switch.py @@ -4,10 +4,10 @@ Support for Qwikswitch relays. 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 36a32c79c5c..9cf57ea3230 100644 --- a/homeassistant/components/rachio/binary_sensor.py +++ b/homeassistant/components/rachio/binary_sensor.py @@ -8,17 +8,13 @@ from abc import abstractmethod 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 4797aae9a8c..fe584441afd 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -9,26 +9,15 @@ from datetime import timedelta 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 1af2b771014..3d0de04e53e 100644 --- a/homeassistant/components/rainbird/sensor.py +++ b/homeassistant/components/rainbird/sensor.py @@ -8,12 +8,13 @@ import logging 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 86f25102f70..2031769b343 100644 --- a/homeassistant/components/rainbird/switch.py +++ b/homeassistant/components/rainbird/switch.py @@ -9,13 +9,14 @@ import logging 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 810c7d201cb..cb66fc3c6af 100644 --- a/homeassistant/components/raincloud/binary_sensor.py +++ b/homeassistant/components/raincloud/binary_sensor.py @@ -8,12 +8,12 @@ import logging 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 15a346880f3..8bcccf06171 100644 --- a/homeassistant/components/raincloud/sensor.py +++ b/homeassistant/components/raincloud/sensor.py @@ -8,13 +8,13 @@ import logging 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 1b76e8974b0..3901e1e0bd8 100644 --- a/homeassistant/components/raincloud/switch.py +++ b/homeassistant/components/raincloud/switch.py @@ -3,13 +3,13 @@ import logging 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 efae9330365..929dbcf314c 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -7,13 +7,14 @@ https://home-assistant.io/components/binary_sensor.rainmachine/ 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 e0e79e8c160..4d08a871f61 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 86a97bc291c..908daa2c83d 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -6,12 +6,13 @@ https://home-assistant.io/components/sensor.rainmachine/ """ 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 e3a1ddab912..6b658c0fcbf 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -4,18 +4,19 @@ 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/ """ -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 b0ebc2e3579..29fa474f781 100644 --- a/homeassistant/components/raspihats/binary_sensor.py +++ b/homeassistant/components/raspihats/binary_sensor.py @@ -5,13 +5,14 @@ import voluptuous as vol 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 26fcda3c8d7..93538682ad8 100644 --- a/homeassistant/components/raspihats/switch.py +++ b/homeassistant/components/raspihats/switch.py @@ -3,14 +3,15 @@ import logging 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 1599d5bce3e..1a94159290f 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -6,19 +6,20 @@ https://home-assistant.io/components/binary_sensor.rest/ """ 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 73b912d62da..5318642a5b1 100644 --- a/homeassistant/components/rflink/binary_sensor.py +++ b/homeassistant/components/rflink/binary_sensor.py @@ -10,13 +10,12 @@ import voluptuous as vol 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 cdc7cac3adb..f91ef1cc682 100644 --- a/homeassistant/components/rflink/cover.py +++ b/homeassistant/components/rflink/cover.py @@ -8,15 +8,15 @@ import logging 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 726433b4f70..cdb34328b51 100644 --- a/homeassistant/components/rflink/light.py +++ b/homeassistant/components/rflink/light.py @@ -10,15 +10,15 @@ import voluptuous as vol 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 f3ec776fda8..e46cc09d0ba 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -8,17 +8,17 @@ import logging 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 25e4e367fdf..a1470bf115f 100644 --- a/homeassistant/components/rflink/switch.py +++ b/homeassistant/components/rflink/switch.py @@ -8,15 +8,15 @@ import logging 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 9a49bd02b97..d548897fb80 100644 --- a/homeassistant/components/rfxtrx/binary_sensor.py +++ b/homeassistant/components/rfxtrx/binary_sensor.py @@ -6,15 +6,14 @@ import voluptuous as vol 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 5a657923683..7ac0e2aa43f 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 d0b75c2f962..3320a67214e 100644 --- a/homeassistant/components/rfxtrx/light.py +++ b/homeassistant/components/rfxtrx/light.py @@ -5,13 +5,14 @@ import voluptuous as vol 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 74c64635563..cc54320cb67 100644 --- a/homeassistant/components/rfxtrx/sensor.py +++ b/homeassistant/components/rfxtrx/sensor.py @@ -4,15 +4,16 @@ import logging 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 141cf2c2c1a..908c07ea745 100644 --- a/homeassistant/components/rfxtrx/switch.py +++ b/homeassistant/components/rfxtrx/switch.py @@ -4,12 +4,13 @@ import logging 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 79fc61a62d4..bcc365a2e83 100644 --- a/homeassistant/components/ring/binary_sensor.py +++ b/homeassistant/components/ring/binary_sensor.py @@ -4,20 +4,18 @@ 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/ """ -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.const import ( - ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) + PLATFORM_SCHEMA, BinarySensorDevice) +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) +import homeassistant.helpers.config_validation as cv + +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 ce9ceb7b76f..efcdf8599a9 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -5,21 +5,20 @@ For more details about this platform, please refer to the documentation at 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 d58e0cf8b3f..5e323d89ad8 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -9,16 +9,15 @@ 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.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 ca8fc64eea4..4968725a4be 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 35eedabd58a..86392c0902d 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -5,8 +5,7 @@ import logging 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 96e24138b4a..86af6c34694 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -3,13 +3,14 @@ from collections import namedtuple 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 0341f65e963..da9bae3cc84 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 ffde584c0ae..0224884e18a 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 9fdeea73da8..69311778698 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -3,14 +3,14 @@ import logging 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 c9d20959696..182e5e78198 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 463ac2b6cd1..11546c3fd43 100644 --- a/homeassistant/components/sisyphus/media_player.py +++ b/homeassistant/components/sisyphus/media_player.py @@ -6,10 +6,11 @@ from homeassistant.components.media_player.const import ( 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 169e1b51a4e..8c2b8355258 100644 --- a/homeassistant/components/skybell/binary_sensor.py +++ b/homeassistant/components/skybell/binary_sensor.py @@ -5,13 +5,13 @@ import logging 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 c22489aa654..04b03f84bf7 100644 --- a/homeassistant/components/skybell/camera.py +++ b/homeassistant/components/skybell/camera.py @@ -5,13 +5,11 @@ import logging 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 02be279f609..d413f9df412 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 89841ae74ef..067e850dfcf 100644 --- a/homeassistant/components/skybell/sensor.py +++ b/homeassistant/components/skybell/sensor.py @@ -5,12 +5,12 @@ import logging 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 32f1d7f9392..674bbf22a08 100644 --- a/homeassistant/components/skybell/switch.py +++ b/homeassistant/components/skybell/switch.py @@ -3,14 +3,13 @@ import logging 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 09584851c6a..98527c769d9 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 3b9bee081f7..963caf457fe 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 9689857e546..4cd1d6f7b4b 100644 --- a/homeassistant/components/smhi/const.py +++ b/homeassistant/components/smhi/const.py @@ -13,4 +13,4 @@ ENTITY_ID_SENSOR_FORMAT = WEATHER_DOMAIN + ".smhi_{}" 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 6136d093a33..fc3399f755c 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -7,8 +7,6 @@ from typing import Dict, List 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.core import HomeAssistant 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 684e25ba599..ba7854e4f0d 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 requests 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 7adbb616774..623a4b0dbd1 100644 --- a/homeassistant/components/spc/alarm_control_panel.py +++ b/homeassistant/components/spc/alarm_control_panel.py @@ -7,12 +7,13 @@ https://home-assistant.io/components/alarm_control_panel.spc/ 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 baa25266804..6a0712d62bb 100644 --- a/homeassistant/components/spc/binary_sensor.py +++ b/homeassistant/components/spc/binary_sensor.py @@ -7,9 +7,10 @@ https://home-assistant.io/components/binary_sensor.spc/ 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 0c8908a309f..f140f881ef4 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -5,8 +5,6 @@ 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) @@ -15,6 +13,8 @@ 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'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/speedtestdotnet/sensor.py b/homeassistant/components/speedtestdotnet/sensor.py index 11bc9a37ca0..fb92bb76ac8 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 b3380ec8fb4..3b612441a88 100644 --- a/homeassistant/components/spider/climate.py +++ b/homeassistant/components/spider/climate.py @@ -4,12 +4,12 @@ import logging 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 227e4748515..e43762be460 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 d5f152bbd76..71ebeadaeed 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 a1eb918ac5d..8fa858977a1 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 69855f7cb57..948c6f90a58 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.components.tahoma import ( - DOMAIN as TAHOMA_DOMAIN, TahomaDevice) -from homeassistant.const import (STATE_OFF, STATE_ON, ATTR_BATTERY_LEVEL) +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON + +from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice DEPENDENCIES = ['tahoma'] diff --git a/homeassistant/components/tahoma/cover.py b/homeassistant/components/tahoma/cover.py index 6dbf9a39807..85e785f9ca3 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 643cc65aa19..eedb95d1a77 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 8a2ea976ba7..3c03911804a 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 779bff9ce0d..71f00ed8937 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 4a12febd871..80d77cd52a1 100644 --- a/homeassistant/components/tcp/binary_sensor.py +++ b/homeassistant/components/tcp/binary_sensor.py @@ -7,8 +7,8 @@ https://home-assistant.io/components/binary_sensor.tcp/ 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 eb52ab496d7..a129ebf6604 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 9936b690985..7d0039319e3 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 41a206944e7..424ece81549 100644 --- a/homeassistant/components/telegram_bot/webhooks.py +++ b/homeassistant/components/telegram_bot/webhooks.py @@ -7,14 +7,14 @@ import voluptuous as vol 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 85faeca96d4..fc13f75838a 100644 --- a/homeassistant/components/tellduslive/binary_sensor.py +++ b/homeassistant/components/tellduslive/binary_sensor.py @@ -3,9 +3,10 @@ import logging 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 1bd3158d100..6dac00ed7a2 100644 --- a/homeassistant/components/tellduslive/cover.py +++ b/homeassistant/components/tellduslive/cover.py @@ -3,9 +3,10 @@ import logging 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 12baf8384f6..3847c66b6cb 100644 --- a/homeassistant/components/tellduslive/light.py +++ b/homeassistant/components/tellduslive/light.py @@ -4,9 +4,10 @@ import logging 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 42c93aa52f9..156c11c95a7 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 bb0164b10bb..55275b5b754 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 d0c9c031435..b90e34229fd 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 5deee5e08a6..15c8b0c5eb9 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 56d563e494c..75c18bee8c5 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 2c037140f0a..a87239d2430 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 118e7204bca..603ce1a4d61 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 0aeab5b1c7d..5a7693d8370 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 34d660ac83c..ade394496d6 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 1d4505ed9a4..99705d3f793 100644 --- a/homeassistant/components/tesla/sensor.py +++ b/homeassistant/components/tesla/sensor.py @@ -3,12 +3,12 @@ from datetime import timedelta 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 a1787e9993e..e00164ff1a7 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 05da90bf7ac..d59b429721b 100644 --- a/homeassistant/components/thethingsnetwork/sensor.py +++ b/homeassistant/components/thethingsnetwork/sensor.py @@ -8,13 +8,13 @@ import async_timeout 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 6ae22c34209..604fadb870c 100644 --- a/homeassistant/components/tibber/notify.py +++ b/homeassistant/components/tibber/notify.py @@ -4,7 +4,8 @@ import logging 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 fb224a25468..9f3e9cdcc62 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 de1a943c33a..0ba1dfaa33a 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -8,15 +8,14 @@ import logging 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 65b884169c7..a75945e9956 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -8,12 +8,12 @@ import logging 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 e5e27ecbed1..38ce428b51b 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 97c7dc9627d..acc84a93590 100644 --- a/homeassistant/components/tradfri/sensor.py +++ b/homeassistant/components/tradfri/sensor.py @@ -1,12 +1,12 @@ """Support for IKEA Tradfri sensors.""" +from datetime import timedelta import logging -from datetime import timedelta - 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 23e6cb20c8f..ef9a9537cff 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 061ed2c0c64..dfd4c195097 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 373397eddd6..854a2e727b0 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 0102a1fec09..12383df115a 100644 --- a/homeassistant/components/tts/amazon_polly.py +++ b/homeassistant/components/tts/amazon_polly.py @@ -8,9 +8,10 @@ import logging 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 609f38454fe..e7a1f368f1d 100644 --- a/homeassistant/components/tts/baidu.py +++ b/homeassistant/components/tts/baidu.py @@ -6,12 +6,14 @@ https://home-assistant.io/components/tts.baidu/ """ 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 ba854fc2f5e..6784e7cea61 100644 --- a/homeassistant/components/tts/demo.py +++ b/homeassistant/components/tts/demo.py @@ -8,7 +8,7 @@ import os 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 61f01a9b292..971d3fb5705 100644 --- a/homeassistant/components/tts/marytts.py +++ b/homeassistant/components/tts/marytts.py @@ -13,10 +13,11 @@ import async_timeout 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 3cce7c1a78d..ab9fb576c28 100644 --- a/homeassistant/components/tts/microsoft.py +++ b/homeassistant/components/tts/microsoft.py @@ -4,15 +4,16 @@ 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/ """ -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 59c698303ad..99d3b5e9786 100644 --- a/homeassistant/components/tts/picotts.py +++ b/homeassistant/components/tts/picotts.py @@ -4,14 +4,15 @@ 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/ """ +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 22eba69e510..3676dff3bc6 100644 --- a/homeassistant/components/tts/voicerss.py +++ b/homeassistant/components/tts/voicerss.py @@ -12,10 +12,11 @@ import async_timeout 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 112e78413ed..aecba2925dd 100644 --- a/homeassistant/components/tts/yandextts.py +++ b/homeassistant/components/tts/yandextts.py @@ -12,10 +12,10 @@ import async_timeout 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 06714760a02..b7a10dad862 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 ac2309cbf9e..274f4d93869 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 b6e2cb6950c..259417869dc 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 1cf2f811872..17f9b43dcbe 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 33d207d8545..24383dca6e4 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 1e8fab2cc1b..c2e32eedc59 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 8fe90823c49..fdeb15ee4ad 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 425b9878f6d..e90da2dbcd8 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 3fd54b349a2..a0c3c9f34c6 100644 --- a/homeassistant/components/upcloud/binary_sensor.py +++ b/homeassistant/components/upcloud/binary_sensor.py @@ -3,11 +3,11 @@ import logging 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 0b44d787f6f..7e84adccf55 100644 --- a/homeassistant/components/upcloud/switch.py +++ b/homeassistant/components/upcloud/switch.py @@ -3,11 +3,11 @@ import logging 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 04932488acd..9d2957660dc 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 8eccf8834a1..5f544e1a134 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -10,9 +10,8 @@ import logging 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 d4769102d14..5b5eaca4ce2 100644 --- a/homeassistant/components/usps/camera.py +++ b/homeassistant/components/usps/camera.py @@ -3,7 +3,8 @@ from datetime import timedelta 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 1603715861d..3e5fea5c4ee 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 12507683f51..2a19337c0db 100644 --- a/homeassistant/components/vacuum/demo.py +++ b/homeassistant/components/vacuum/demo.py @@ -6,13 +6,12 @@ https://home-assistant.io/components/demo/ """ 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 43ffa232b40..cbe1350bd4f 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 1f45408a666..470524bb6f3 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -4,10 +4,9 @@ import logging 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 72a5a7af79b..b176ab76c4b 100644 --- a/homeassistant/components/velbus/cover.py +++ b/homeassistant/components/velbus/cover.py @@ -5,12 +5,12 @@ import time 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 10ad89ab847..ad78a795a30 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 7104bb0750d..b5ef89ca480 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 6abaa42bb9d..1893909b706 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 b0716dc2cb8..614d3f349a2 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 837422dbc7c..c81fa31938f 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 9c812da9208..f8ff9c21b89 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) + ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT) +from homeassistant.util import convert -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice DEPENDENCIES = ['vera'] diff --git a/homeassistant/components/vera/cover.py b/homeassistant/components/vera/cover.py index 1168cca8425..4cf2aac3bb4 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 93e54b915c7..e4e315bb52e 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 61d5f0baf28..5ace07b87d7 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 0960512f6d1..5000f9bc50f 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 8b68cc9190f..3c026046b3e 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 2f4d18e34e1..f422e49bf42 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 adcdcd668cb..dc73be056db 100644 --- a/homeassistant/components/verisure/alarm_control_panel.py +++ b/homeassistant/components/verisure/alarm_control_panel.py @@ -3,11 +3,11 @@ import logging 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 4c9e79724fe..1c1e0ee15c3 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 7112e535a95..fad3d2bef04 100644 --- a/homeassistant/components/verisure/camera.py +++ b/homeassistant/components/verisure/camera.py @@ -5,8 +5,8 @@ import os 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 cdd230ea7f7..2010504c990 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 13706d8408f..cf5205a6116 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 a418eec6bc5..eb69d4c02a1 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 7158e4df69b..4b4f4ff77cc 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 d4838c01505..6c7e0914f6e 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 f281ea64461..4a81f9017ff 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 07f16e580bd..8921bf057c1 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 d3985557cff..372909d1e0a 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 149a6c28290..dccb648c9c2 100644 --- a/homeassistant/components/vultr/binary_sensor.py +++ b/homeassistant/components/vultr/binary_sensor.py @@ -8,15 +8,16 @@ import logging 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 a727e5bd2ec..7ca731cabac 100644 --- a/homeassistant/components/vultr/sensor.py +++ b/homeassistant/components/vultr/sensor.py @@ -9,13 +9,14 @@ import logging 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 874d1979d7d..1f7d9ceaa28 100644 --- a/homeassistant/components/vultr/switch.py +++ b/homeassistant/components/vultr/switch.py @@ -8,14 +8,15 @@ import logging 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 855a5f3ed0a..c9424834953 100644 --- a/homeassistant/components/w800rf32/binary_sensor.py +++ b/homeassistant/components/w800rf32/binary_sensor.py @@ -5,13 +5,13 @@ import voluptuous as vol 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 b551993aca5..37ae535cdfc 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 65632f51494..8a43a7dac77 100644 --- a/homeassistant/components/waterfurnace/sensor.py +++ b/homeassistant/components/waterfurnace/sensor.py @@ -6,14 +6,13 @@ https://home-assistant.io/components/sensor.waterfurnace/ """ 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 594198ddd12..73ca9a3cac4 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 12e1b557a73..f3757d7bf39 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 efd8eecf5af..f5e75c1fb8d 100644 --- a/homeassistant/components/wink/climate.py +++ b/homeassistant/components/wink/climate.py @@ -4,17 +4,17 @@ import logging 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 19ff792592f..f4c4841c2a2 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 5bc6e03c49b..52a27eb3c3d 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 14a983154f8..95747bcc1b2 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 0ef4f30dc5a..8e6fb9b2805 100644 --- a/homeassistant/components/wink/lock.py +++ b/homeassistant/components/wink/lock.py @@ -4,11 +4,12 @@ import logging 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 d05fe58a427..e77402c4d45 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 ab61769c94d..3dfd704d564 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 cd55026879a..6ee777dd1fc 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 34cd86a50f4..343f4a76601 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 6f2c24a7788..aefa5ed34a9 100644 --- a/homeassistant/components/wirelesstag/binary_sensor.py +++ b/homeassistant/components/wirelesstag/binary_sensor.py @@ -3,17 +3,16 @@ import logging import voluptuous as vol -from homeassistant.core import callback -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, - WirelessTagBaseSensor) -from homeassistant.const import ( - CONF_MONITORED_CONDITIONS, STATE_ON, STATE_OFF) + 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 . import ( + DOMAIN as WIRELESSTAG_DOMAIN, SIGNAL_BINARY_EVENT_UPDATE, + WirelessTagBaseSensor) DEPENDENCIES = ['wirelesstag'] diff --git a/homeassistant/components/wirelesstag/sensor.py b/homeassistant/components/wirelesstag/sensor.py index 3703e214d83..ca26e07b985 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 913438e9d8c..4a2b64acda1 100644 --- a/homeassistant/components/wirelesstag/switch.py +++ b/homeassistant/components/wirelesstag/switch.py @@ -3,15 +3,12 @@ import logging 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 afffb71bae5..7eb72e91eef 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 ead2c0e9219..f4bf1f269b5 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 30433ccea3d..d0fbdea8fad 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 f19492664b1..56d68f38be9 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 133814e216e..c5cc00f14ba 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 c3cde8ede6d..211f9d127c4 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 e579761474b..080b87c1346 100644 --- a/homeassistant/components/xs1/climate.py +++ b/homeassistant/components/xs1/climate.py @@ -3,12 +3,11 @@ from functools import partial 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 9de91a6b012..f5fdcf1fb34 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 35568587b19..d8b344fc716 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 01a927afbf8..bc90a56cc10 100644 --- a/homeassistant/components/zamg/weather.py +++ b/homeassistant/components/zamg/weather.py @@ -3,9 +3,6 @@ import logging 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 @@ 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 ( + 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 eec1832f07d..ccf4e70df34 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 e5016900be7..b9be0d89323 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 48503e396a4..48301ac9728 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 ef36e17d74b..ddfd47a047e 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 f20f09e70a6..ce59d4573be 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 f9dd8718b8f..fe3333fa3ed 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 9eb6beb491c..e205d921422 100644 --- a/homeassistant/components/zoneminder/sensor.py +++ b/homeassistant/components/zoneminder/sensor.py @@ -3,12 +3,13 @@ import logging 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 b411a148d43..78e72c5fd4a 100644 --- a/homeassistant/components/zoneminder/switch.py +++ b/homeassistant/components/zoneminder/switch.py @@ -3,11 +3,12 @@ import logging 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 097/290] 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 c988cb811b2..15bf73f459d 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 2eab75cb92a..46dc3c045b2 100644 --- a/tests/util/test_yaml.py +++ b/tests/util/test_yaml.py @@ -95,7 +95,7 @@ class TestYaml(unittest.TestCase): 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 @@ class TestYaml(unittest.TestCase): 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 098/290] 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 1b346d409c4..7825867df64 100644 --- a/homeassistant/components/time_date/sensor.py +++ b/homeassistant/components/time_date/sensor.py @@ -25,6 +25,7 @@ OPTION_TYPES = { '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 @@ class TimeDateSensor(Entity): 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 b2a22439c1f..84331abfba1 100644 --- a/tests/components/time_date/test_sensor.py +++ b/tests/components/time_date/test_sensor.py @@ -71,6 +71,10 @@ class TestTimeDateSensor(unittest.TestCase): 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 @@ class TestTimeDateSensor(unittest.TestCase): 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 099/290] 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 1aec3647d61..e7ea9fbbb40 100644 --- a/homeassistant/components/xiaomi_miio/device_tracker.py +++ b/homeassistant/components/xiaomi_miio/device_tracker.py @@ -8,7 +8,7 @@ from homeassistant.components.device_tracker import ( 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 c4cfa6bbe6b..51d4780160d 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -13,7 +13,7 @@ from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TOKEN, 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 = { 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_AIRPURIFIER_V3 = (FEATURE_SET_BUZZER | 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_LED_BRIGHTNESS = AIRPURIFIER_SERVICE_SCHEMA.extend({ 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 3ae6bfe6cd7..ecf7b12e4ac 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -16,7 +16,7 @@ from homeassistant.exceptions import PlatformNotReady 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 @@ XIAOMI_MIIO_SERVICE_SCHEMA = vol.Schema({ 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 1c4ae85a0d8..450279c1825 100644 --- a/homeassistant/components/xiaomi_miio/remote.py +++ b/homeassistant/components/xiaomi_miio/remote.py @@ -17,7 +17,7 @@ from homeassistant.exceptions import PlatformNotReady 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 bd5f8642e54..41d3ce65b13 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -9,7 +9,7 @@ from homeassistant.exceptions import PlatformNotReady 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 eb7f09f95e6..f330030922d 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -12,7 +12,7 @@ from homeassistant.const import ( 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 36196158600..c7f69a40330 100644 --- a/homeassistant/components/xiaomi_miio/vacuum.py +++ b/homeassistant/components/xiaomi_miio/vacuum.py @@ -16,7 +16,7 @@ from homeassistant.const import ( 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 ef5aa8f8cfc..786e9b82022 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 100/290] 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 fbb0ee58c5a..aae2e9b6af9 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -29,7 +29,7 @@ from homeassistant.helpers.typing import HomeAssistantType 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 a9fb84f733e..ce72eff2ba8 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -23,7 +23,7 @@ from .const import DOMAIN 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 786e9b82022..3854fd2c00f 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 101/290] 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 3a3e19d9703..d0196761968 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 102/290] 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 fa386a17705..d144d84cbf8 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -143,7 +143,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 103/290] 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 ef3ef71477f..d8a3918889d 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 @@ class ZigbeeChannel: 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 @@ class ZigbeeChannel: """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 @@ class ZigbeeChannel: 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 @@ class ZDOChannel: 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 c0b0367be99..bf0f1044efb 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -11,8 +11,7 @@ from . import ZigbeeChannel, parse_and_log_command 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 @@ class BasicChannel(ZigbeeChannel): 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 2518889fcb1..e4b67dd0db7 100644 --- a/homeassistant/components/zha/core/channels/homeautomation.py +++ b/homeassistant/components/zha/core/channels/homeautomation.py @@ -15,18 +15,15 @@ _LOGGER = logging.getLogger(__name__) 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 @@ class ElectricalMeasurementChannel(AttributeListeningChannel): 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 c62ec66588e..3da881e75d8 100644 --- a/homeassistant/components/zha/core/channels/hvac.py +++ b/homeassistant/components/zha/core/channels/hvac.py @@ -8,7 +8,7 @@ import logging 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 9c904a7a001..696a15b483b 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -6,7 +6,6 @@ https://home-assistant.io/components/zha/ """ 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 e8c0e71a263..03b50b7c7ba 100644 --- a/homeassistant/components/zha/core/channels/security.py +++ b/homeassistant/components/zha/core/channels/security.py @@ -9,7 +9,7 @@ from homeassistant.core import callback 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 @@ _LOGGER = logging.getLogger(__name__) 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 58cecb3600f..b7f418253d8 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -87,12 +87,12 @@ ZDO_CHANNEL = 'zdo' 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 104/290] 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 56fc0cb704c..6808729685e 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -10,7 +10,7 @@ from homeassistant.helpers import config_validation as cv 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 71ebeadaeed..56c670184b5 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -3,7 +3,7 @@ import logging 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 @@ OPERATION_LIST = { 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 @@ class TadoClimate(ClimateDevice): """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 8804bef5616..7812bbd812b 100644 --- a/homeassistant/components/tado/device_tracker.py +++ b/homeassistant/components/tado/device_tracker.py @@ -52,9 +52,9 @@ class TadoDeviceScanner(DeviceScanner): # 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 3854fd2c00f..da790b33b99 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 105/290] 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 0b64bac5956..548a38711bd 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 a5edc93fce6..4daf37cac55 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 106/290] 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 ba8155fde93..a272a22abe5 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -18,7 +18,7 @@ from homeassistant.const import ( 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 da790b33b99..bb40704d0d1 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 107/290] 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 aff24f30512..f5b959285db 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -21,6 +21,7 @@ from homeassistant.const import ( 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 @@ class TemperatureSettingTrait(_Trait): 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 @@ class TemperatureSettingTrait(_Trait): 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 @@ class TemperatureSettingTrait(_Trait): }, 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 03cc327a5c5..a8ea4a3f888 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -231,10 +231,7 @@ DEMO_DEVICES = [{ '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 18f99c82685..fbaf1b47898 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 301de9c8c25..3af60e2f014 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 108/290] 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 ff1b2344ac8..75874d6759e 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ from .const import ( 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 bb40704d0d1..56339fe7bda 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 98ef1dbab43..68119e6b54e 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 109/290] 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 6e05299ec52..30b9d350df6 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 56339fe7bda..349f8d48304 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 68119e6b54e..b88e6b97db2 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 110/290] 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 00000000000..2099038f004 --- /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 00000000000..63cf17f0b22 --- /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 00000000000..f0b06a06dc9 --- /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 00000000000..beb1bc0d6e6 --- /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 dae3afdba6f..6fa2b664a30 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 2ea65762063..c2cc8f97a89 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 56399a3c6fd..d18df13701e 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 00000000000..e40765e8220 --- /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 00000000000..6f06d5c00c6 --- /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 9a8e1c1eb11..3395b31b4c7 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 00000000000..f188fe09a48 --- /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 00000000000..66a79f926a3 --- /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 00000000000..77a96095c25 --- /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 5da2d437a35..629e006564b 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 00000000000..3574965cae6 --- /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 84000783435..58dbba34fa8 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 a52f6159797..b230a73c354 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 697fbf0311e..d24fb929068 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 94dafeb3c2e..4f4c2f32c61 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 00000000000..6f06d5c00c6 --- /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 142f40754b9..b390f2dab44 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 09d93e6911e..b2b8b606723 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 00000000000..6f06d5c00c6 --- /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 3d82ac6fa5a..726ec2ad9b2 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 ae7fc861b77..7b6645edca2 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 00000000000..be7d5d323ac --- /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 1f2dfe66dd2..126bb0362a8 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 00000000000..fb8d63080ce --- /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 00000000000..73cbbdf046a --- /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 00000000000..cae3361a6ec --- /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 276f5053bf7..6a828282f52 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 00000000000..0b91783f804 --- /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 00000000000..d0fb2a5a04e --- /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 7064364ebe6..270e74945a3 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 00000000000..58028d1caae --- /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 00000000000..70d2c6ef6bc --- /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 00000000000..058908db36b --- /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 00000000000..1e80c86e862 --- /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 81950c49b4c..a90f7ff989c 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 89f6881593a..917744c32fd 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 00000000000..ecd7f17c84b --- /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 00000000000..2aeb29fcf0a --- /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 00000000000..6f06d5c00c6 --- /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 ba89efab0c2..ccdc368afff 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 00000000000..25af1d5e18d --- /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 00000000000..816d281752d --- /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 646151a5229..79a5fe1fba8 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 00000000000..417d0627616 --- /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 00000000000..faf30e5f985 --- /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 00000000000..a66ae603291 --- /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 00000000000..7189bc53c16 --- /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 00000000000..feb00c20779 --- /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 00000000000..202b7383253 --- /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 00000000000..654eb7675a8 --- /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 00000000000..3b1ab72f7d3 --- /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 56eaf8d3b4c..e294579da09 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 19f9985accb..a440fdde4f2 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 2467a705d50..4e1a9f7acab 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 00000000000..4312bdba6ec --- /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 117a4500d58..78239148a4e 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 00000000000..1989e1a0703 --- /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 00000000000..46a0f2f2921 --- /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 85a39095f87..fd6cba18237 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 00000000000..ba1b1e27668 --- /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 00000000000..0a3c4393c7c --- /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 d7983448417..bb654eed228 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 00000000000..80fb5f07f13 --- /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 00000000000..64d8f582ad3 --- /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 f5a7da57e7f..9b313e511c9 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 00000000000..94af741f5f4 --- /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 00000000000..c764ccbc3e0 --- /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 00000000000..e294579da09 --- /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 00000000000..ef1d5bf5f13 --- /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 00000000000..c2406de68bb --- /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 00000000000..310ebf9c359 --- /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 00000000000..587b9496114 --- /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 00000000000..71cfab736cb --- /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 00000000000..3ce14cd4c77 --- /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 00000000000..0d82e0d8f94 --- /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 00000000000..10f8bb9b8a6 --- /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 00000000000..fac2b654a46 --- /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 00000000000..6531d3c8265 --- /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 00000000000..0fcd02a6961 --- /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 00000000000..3ce14204fb5 --- /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 00000000000..39c7f22f7af --- /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 00000000000..7e62250b892 --- /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 00000000000..2aa7ea03db7 --- /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 00000000000..5e78d429b9f --- /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 00000000000..7c516bcce50 --- /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 00000000000..19f9985accb --- /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 00000000000..85dfe79bae4 --- /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 00000000000..93b17784a4e --- /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 00000000000..14961ab98f0 --- /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 00000000000..6f64e497c74 --- /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 00000000000..6db932a1aed --- /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 00000000000..1b69e10e6f9 --- /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 00000000000..ae69c1c9654 --- /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 00000000000..2467a705d50 --- /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 00000000000..22ab0d49f62 --- /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 00000000000..9cf4aad011e --- /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 00000000000..0dbe4a131ee --- /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 00000000000..e3865ca42e5 --- /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 00000000000..9bce187ec65 --- /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 00000000000..e2d7e7919be --- /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 00000000000..0d1553ac3ea --- /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 00000000000..9cded2f9c0f --- /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 00000000000..50d702340b9 --- /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 00000000000..b42100215ca --- /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 00000000000..65df6a58b10 --- /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 00000000000..65df6a58b10 --- /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 00000000000..1415a3b907b --- /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 00000000000..f226d920043 --- /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 00000000000..f01f6451549 --- /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 00000000000..ec9f9657428 --- /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 00000000000..282c24f3ad9 --- /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 00000000000..63596b09784 --- /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 00000000000..ed0666aee36 --- /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 00000000000..d9138f6b16e --- /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 00000000000..e441b1aa8ac --- /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 00000000000..f2bf0a7bae5 --- /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 00000000000..f33afde7a07 --- /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 00000000000..a96e1112f71 --- /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 00000000000..6054a8e2be5 --- /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 00000000000..dbcff7ef819 --- /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 00000000000..9d520dae6a5 --- /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 00000000000..f5a7da57e7f --- /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 00000000000..fde45ad6c8e --- /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 00000000000..fde45ad6c8e --- /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 00000000000..04f90318290 --- /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 00000000000..2b04886b72d --- /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 00000000000..f715a3ec13a --- /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 00000000000..02332d76906 --- /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 00000000000..09799730389 --- /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 00000000000..766e59a43da --- /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 00000000000..a3bb21dee27 --- /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 00000000000..78801f4b1df --- /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 00000000000..78801f4b1df --- /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 00000000000..8a13a76a2a9 --- /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 6503634b92c..56c30acbf1b 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 00000000000..4efff6bffb4 --- /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 00000000000..beb1bc0d6e6 --- /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 a20e6bff2b5..a7ddd4c6fa6 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 00000000000..e4aa0d8c088 --- /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 db064def53b..a0ce81495a8 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 5bf0c60199e..7c41cdc0d24 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 00000000000..25ffb753076 --- /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 00000000000..15d052c758f --- /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 00000000000..6f06d5c00c6 --- /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 19c835c4b8c..2b963ff1be5 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 00000000000..beb1bc0d6e6 --- /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 00000000000..a19d2d44159 --- /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 00000000000..de1a2274dd3 --- /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 00000000000..7140e3956df --- /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 a96010a74a8..c392f0093a0 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 bc85d47878ac6cfdf94145859de8e541f1d2c2e4 Mon Sep 17 00:00:00 2001 From: Ryan Bahm Date: Thu, 21 Mar 2019 12:59:07 -0700 Subject: [PATCH 111/290] 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 c9b222f2b26..84a2b426e9e 100644 --- a/homeassistant/components/season/sensor.py +++ b/homeassistant/components/season/sensor.py @@ -130,5 +130,5 @@ class Season(Entity): def update(self): """Update season.""" - self.datetime = datetime.now() + self.datetime = datetime.utcnow() self.season = get_season(self.datetime, self.hemisphere, self.type) 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 112/290] 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 9c5f375e19f..eeae1587099 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_ACCESS_TOKEN, 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 349f8d48304..80ee0ea3da6 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 113/290] 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 75874d6759e..9517971b16d 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ from .const import ( 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 80ee0ea3da6..ec922ff34da 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 b88e6b97db2..c910e97747a 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 114/290] 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 6c338457b34..0df1fa42ad4 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -25,7 +25,7 @@ from . import migration, purge 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 bd246c0d01c..bc40d5efb42 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -15,7 +15,7 @@ from homeassistant.helpers.entity import Entity _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 ec922ff34da..d6d82bd42ea 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 c910e97747a..adcd2db3c43 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 115/290] 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 35000fa35c3..f3dd9dfc1a7 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -170,21 +170,31 @@ def setup_plexserver( 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 116/290] 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 1282a40cac5..5ec4ef325f0 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv 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 d6d82bd42ea..b82d38d409d 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 117/290] 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 5ec4ef325f0..1d186484f40 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -163,7 +163,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): 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 @@ class ADBDevice(MediaPlayerDevice): 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 118/290] 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 | 31 +++++---- .../components/netgear_lte/sensor_types.py | 8 +++ 4 files changed, 96 insertions(+), 49 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 5f8c680b7f0..34330426e34 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -8,12 +8,17 @@ import attr 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 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) 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 20a20b21291..fba1a335ace 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 - 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) + targets = kwargs.get(ATTR_TARGET, self.config[DOMAIN][CONF_RECIPIENT]) + if not targets: + _LOGGER.warning("No recipients") + return + + if not message: + return + + 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 339fa678d61..774cdc5536a 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 attr -import voluptuous as vol +import logging -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_HOST, CONF_SENSORS +import attr + +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 00000000000..b05ecf6074a --- /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 119/290] 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 e3ad5ff206e..67b0c9f76a9 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 1fbef2c5896..00000000000 --- 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 01145275b31..4c3e0d564dc 100644 --- a/homeassistant/components/websocket_api/const.py +++ b/homeassistant/components/websocket_api/const.py @@ -20,3 +20,7 @@ TYPE_RESULT = 'result' # 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 1ab2b09d7fa..85cb256df90 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -13,7 +13,9 @@ from homeassistant.core import callback 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 @@ class WebSocketHandler: 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 @@ class WebSocketHandler: 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 00000000000..fd9108c0513 --- /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 48ab7e1c3f9..00000000000 --- 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 e2c6e303326..cd940708497 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 00000000000..b02cc53f38d --- /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 120/290] 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 f3dd9dfc1a7..a68a2faade8 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -322,6 +322,7 @@ class PlexClient(MediaPlayerDevice): 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 @@ class PlexClient(MediaPlayerDevice): 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 @@ class PlexClient(MediaPlayerDevice): 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 @@ class PlexClient(MediaPlayerDevice): 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 @@ class PlexClient(MediaPlayerDevice): """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 121/290] 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 | 41 +++-- tests/components/reddit/__init__.py | 1 + tests/components/reddit/test_sensor.py | 175 ++++++++++++++++++++++ 3 files changed, 203 insertions(+), 14 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 1b6a960669c..3ba43196551 100644 --- a/homeassistant/components/reddit/sensor.py +++ b/homeassistant/components/reddit/sensor.py @@ -15,6 +15,7 @@ _LOGGER = logging.getLogger(__name__) 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 @@ ATTR_URL = 'url' 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class RedditSensor(Entity): """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 @@ class RedditSensor(Entity): try: subreddit = self._reddit.subreddit(self._subreddit) + if hasattr(subreddit, self._sort_by): + method_to_call = getattr(subreddit, self._sort_by) - 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 - }) + 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 00000000000..67e0db82f42 --- /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 00000000000..2bb22a0024b --- /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 122/290] 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 9053a872134..de0de8ae162 100644 --- a/homeassistant/components/prometheus/__init__.py +++ b/homeassistant/components/prometheus/__init__.py @@ -103,6 +103,16 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: '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 @@ class PrometheusMetrics: 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 @@ class PrometheusMetrics: ) 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 123/290] 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 f5b959285db..26d1ccc2088 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -583,15 +583,24 @@ class TemperatureSettingTrait(_Trait): 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 @@ class TemperatureSettingTrait(_Trait): "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 @@ class TemperatureSettingTrait(_Trait): (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 fbaf1b47898..69964a11fdc 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 3af60e2f014..9d067f3314f 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 124/290] 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 33853f1c08b..e880177380f 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 125/290] 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 a8466bd8721..4d2cd863b12 100644 --- a/homeassistant/components/srp_energy/sensor.py +++ b/homeassistant/components/srp_energy/sensor.py @@ -19,7 +19,7 @@ from homeassistant.util import Throttle 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 b82d38d409d..14dfb457d29 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 adcd2db3c43..b532b7b386d 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 126/290] 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 f77d1752d6a..d369bd89098 100644 --- a/homeassistant/util/location.py +++ b/homeassistant/util/location.py @@ -9,7 +9,7 @@ from typing import Any, Optional, Tuple, Dict 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 127/290] 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 dda2214ce46..3373149a013 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 128/290] 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 bb5a09771c2..e4e28eff4f1 100644 --- a/homeassistant/components/lastfm/sensor.py +++ b/homeassistant/components/lastfm/sensor.py @@ -9,7 +9,7 @@ from homeassistant.const import CONF_API_KEY, ATTR_ATTRIBUTION 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 14dfb457d29..99f25919566 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 129/290] 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 f69d0b285ba..98d22c20d15 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.components.sensor import PLATFORM_SCHEMA 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 99f25919566..ac20e50cd8f 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 130/290] 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 3cd6b10b260..67135b947e4 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 ac2d5ccb109..a017f0ee3e8 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class HpIloSensor(Entity): 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 @@ class HpIloSensor(Entity): @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 @@ class HpIloSensor(Entity): 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 @@ class HpIloData: @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 131/290] 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 9940e6960db..0b815d0ec7e 100644 --- a/homeassistant/components/homematicip_cloud/device.py +++ b/homeassistant/components/homematicip_cloud/device.py @@ -6,21 +6,13 @@ from homeassistant.helpers.entity import Entity _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 @@ class HomematicipGenericDevice(Entity): """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 132/290] 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 41e60d884ce..7accf7820f4 100644 --- a/homeassistant/components/freebox/__init__.py +++ b/homeassistant/components/freebox/__init__.py @@ -9,7 +9,7 @@ from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP 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 00000000000..4de194fc902 --- /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 ac20e50cd8f..cd74cbc0dd4 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 133/290] 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 95d6dba50c3..046b6d3947c 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -60,6 +60,7 @@ STATE_IDLE = 'idle' # 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 c8d6721ac18..c9f8616f637 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -18,7 +18,7 @@ from homeassistant.const import ( 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 @@ class GenericCamera(Camera): 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 @@ class GenericCamera(Camera): 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 220ed6dd58c..543404dd34e 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -21,6 +21,7 @@ DEFAULT_EXPOSED_DOMAINS = [ 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 fa272c25012..88cbea345b1 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -12,6 +12,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, ) from homeassistant.components import ( + camera, climate, cover, fan, @@ -30,7 +31,7 @@ from homeassistant.components import ( 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 @@ HANDLERS = Registry() _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 @@ class _GoogleEntity: self.hass = hass self.config = config self.state = state + self._traits = None @property def entity_id(self): @@ -83,13 +86,17 @@ class _GoogleEntity: @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 @@ class _GoogleEntity: """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 26d1ccc2088..bd903575762 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 @@ from .helpers import SmartHomeError _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 @@ TRAIT_MODES = PREFIX_TRAITS + 'Modes' 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 @@ class BrightnessTrait(_Trait): }, 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 302e8d8674f..cccbe0d0a9d 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.components.light.demo import DemoLight 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 9d067f3314f..e42e4bdc915 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 @@ from homeassistant.const import ( 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 134/290] 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 84e187fa5a4..15f29339087 100644 --- a/homeassistant/components/nmbs/sensor.py +++ b/homeassistant/components/nmbs/sensor.py @@ -124,7 +124,6 @@ class NMBSLiveBoard(Entity): 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 135/290] 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 2e78b777f7d..60a0a2d3210 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 16be7d45825..1fc5f78d0e8 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 socket 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 136/290] 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 fb4dccc1c86..4d317f52daa 100644 --- a/homeassistant/components/demo/alarm_control_panel.py +++ b/homeassistant/components/demo/alarm_control_panel.py @@ -5,7 +5,7 @@ For more details about this platform, please refer to the documentation 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 bd5724ca455..720b4cc5180 100644 --- a/homeassistant/components/calendar/demo.py +++ b/homeassistant/components/demo/calendar.py @@ -9,7 +9,7 @@ import copy 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 f9be3f47c35..34a0894ac60 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/demo/camera.py @@ -7,7 +7,7 @@ https://home-assistant.io/components/demo/ 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 2dd31c1b20d..b1dd1b0ba45 100644 --- a/homeassistant/components/climate/demo.py +++ b/homeassistant/components/demo/climate.py @@ -6,8 +6,8 @@ https://home-assistant.io/components/demo/ """ 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 1f31cecc996..ddcf07fd5e5 100644 --- a/homeassistant/components/cover/demo.py +++ b/homeassistant/components/demo/cover.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/demo/ """ 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 e67fbef650e..53729795f71 100644 --- a/homeassistant/components/fan/demo.py +++ b/homeassistant/components/demo/fan.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/demo/ """ 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 d63280ce609..6b91faac92f 100644 --- a/homeassistant/components/geo_location/demo.py +++ b/homeassistant/components/demo/geo_location.py @@ -7,7 +7,7 @@ from typing import Optional 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 d9affb03db3..a5b22108e81 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/demo/light.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/demo/ """ 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 94a67fb87f6..03935c4f603 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/demo/lock.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/demo/ """ 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 070701f6322..33d2b98d225 100644 --- a/homeassistant/components/media_player/demo.py +++ b/homeassistant/components/demo/media_player.py @@ -7,8 +7,8 @@ https://home-assistant.io/components/demo/ 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 2a19337c0db..5ec7030f56c 100644 --- a/homeassistant/components/vacuum/demo.py +++ b/homeassistant/components/demo/vacuum.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/demo/ """ 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 37ae535cdfc..6ee17bf0088 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 00000000000..d0bc392e4f4 --- /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 af0a3eea6ab..1eb0f8e79db 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -20,8 +20,8 @@ from homeassistant.helpers.event import ( 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 aba57284da8..c1825211433 100644 --- a/homeassistant/components/cover/group.py +++ b/homeassistant/components/group/cover.py @@ -15,7 +15,7 @@ from homeassistant.core import callback 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 7e9d11d3a02..c37c5cc4e8e 100644 --- a/homeassistant/components/light/group.py +++ b/homeassistant/components/group/light.py @@ -20,7 +20,7 @@ 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 ( +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 4b4f3313349..64f8779e4ab 100644 --- a/homeassistant/components/light/switch.py +++ b/homeassistant/components/switch/light.py @@ -16,7 +16,7 @@ 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 +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 6912c83f770..fa6a8429ff3 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 840e30161f3..0359d14df63 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 @@ class TestGetImage: """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 f6e2513380c..6329a36543f 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 d1c11fe55bf..5a46ca99839 100644 --- a/tests/components/geo_location/test_demo.py +++ b/tests/components/demo/test_geo_location.py @@ -3,7 +3,7 @@ import unittest 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 8cbe0a594d2..83acf8be601 100644 --- a/tests/components/media_player/test_demo.py +++ b/tests/components/demo/test_media_player.py @@ -184,7 +184,7 @@ class TestDemoMediaPlayer(unittest.TestCase): 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 e0b560bbb48..523fe17f824 100644 --- a/tests/components/vacuum/test_demo.py +++ b/tests/components/demo/test_vacuum.py @@ -9,7 +9,7 @@ from homeassistant.components.vacuum import ( 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 a4e7890f3fa..971b6830ae1 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 cccbe0d0a9d..bc59cc8ff2d 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.climate.const import ( 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 2211c8c77bc..04e8f9c964d 100644 --- a/tests/components/cover/test_group.py +++ b/tests/components/group/test_cover.py @@ -6,7 +6,7 @@ import pytest 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 472bdf42385..51580e503bd 100644 --- a/tests/components/light/test_group.py +++ b/tests/components/group/test_light.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock 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 8bec3bd71f5..86f5f820be3 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -80,7 +80,7 @@ class TestImageProcessing: """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 14326b3f32d..a6e59af64d5 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 3d063f8a34a..f5558331bce 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): # 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): # 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): # 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): 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 137/290] 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 f3dd8b6d0c9..e14bd9f1098 100644 --- a/homeassistant/components/familyhub/camera.py +++ b/homeassistant/components/familyhub/camera.py @@ -8,8 +8,7 @@ import logging 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 138/290] 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 e1c5895b89a..63a6421d5f6 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 139/290] 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 eec08be093f..25b352c1454 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -12,9 +12,10 @@ import threading 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 @@ ICONS = { 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']) + dest_depart_time = '{} {}'.format( + dest_depart.strftime(dt_util.DATE_STR_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) - - 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 @@ class GTFSDepartureSensor(Entity): 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 @@ class GTFSDepartureSensor(Entity): """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 @@ class GTFSDepartureSensor(Entity): """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 @@ class GTFSDepartureSensor(Entity): 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 @@ class GTFSDepartureSensor(Entity): 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 140/290] 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 d4816213f50..1fb727642bc 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -46,6 +46,7 @@ SERVICE_ROKU = 'roku' 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_HANDLERS = { 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 @@ SERVICE_HANDLERS = { '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 a2863482477..cdf82e97429 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 d8c1f23bcbb..32e3c5f69e3 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 18a0bf750a1..8c7a94d3020 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 @@ YEELIGHT_EFFECT_LIST = [ 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 _cmd(func): 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 not discovery_info: + return - if DATA_KEY not in hass.data: - hass.data[DATA_KEY] = {} + yeelight_data = hass.data[DATA_YEELIGHT] + ipaddr = discovery_info[CONF_HOST] + device = yeelight_data[CONF_DEVICES][ipaddr] + _LOGGER.debug("Adding %s", device.name) - lights = [] - if discovery_info is not None: - _LOGGER.debug("Adding autodetected %s", discovery_info['hostname']) + custom_effects = discovery_info[CONF_CUSTOM_EFFECTS] + light = YeelightLight(device, custom_effects=custom_effects) - device_type = discovery_info['device_type'] - legacy_device_type = LEGACY_DEVICE_TYPE_MAP.get(device_type, - device_type) - - # 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): @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 @@ class YeelightLight(Light): @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 @@ class YeelightLight(Light): """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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 00000000000..14dcfb27a4d --- /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 cd74cbc0dd4..ea91ef5e9f4 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 141/290] 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 | 276 +++------------ .../components/axis/binary_sensor.py | 81 ++--- homeassistant/components/axis/camera.py | 71 ++-- 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, 1288 insertions(+), 324 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 67b0c9f76a9..42e7d84dc09 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 00000000000..3c528dfbb16 --- /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 df723272a7a..324c2cf369e 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] = {} - - 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() - - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) - - 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 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) - - # 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] + + for device_name, device_config in config[DOMAIN].items(): + 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]) + device_config[CONF_NAME] = device_name - 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 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 + )) - # Register service with Home Assistant. - hass.services.register( - DOMAIN, SERVICE_VAPIX_CALL, vapix_service, schema=SERVICE_SCHEMA) return True -def setup_device(hass, config, device_config): - """Set up an Axis device.""" - import axis +async def async_setup_entry(hass, config_entry): + """Set up the Axis component.""" + if DOMAIN not in hass.data: + hass.data[DOMAIN] = {} - 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) + if not config_entry.options: + await async_populate_options(hass, config_entry) - event_types = [ - event - for event in device_config[CONF_INCLUDE] - if event in EVENT_TYPES - ] + device = AxisNetworkDevice(hass, config_entry) - 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 - - except axis.Unauthorized: - _LOGGER.error("Credentials for %s are faulty", - device_config[CONF_HOST]) + if not await device.async_setup(): return False - except axis.RequestError: - return False + hass.data[DOMAIN][device.serial] = device - device.name = device_config[CONF_NAME] + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.shutdown) - 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) - - if event_types: - hass.add_job(device.start) return True + + +async def async_populate_options(hass, config_entry): + """Populate default options for device.""" + from axis.vapix import VAPIX_IMAGE_FORMAT + + device = await get_device(hass, config_entry.data[CONF_DEVICE]) + + supported_formats = device.vapix.get_param(VAPIX_IMAGE_FORMAT) + + camera = bool(supported_formats) + + options = { + CONF_CAMERA: camera, + CONF_EVENTS: True, + CONF_TRIGGER_TIME: DEFAULT_TRIGGER_TIME + } + + 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 11014dc4bc9..ec4c27ea343 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 -_LOGGER = logging.getLogger(__name__) +DEPENDENCIES = [AXIS_DOMAIN] -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the Axis binary devices.""" - add_entities([AxisBinarySensor(discovery_info)], True) +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) + + 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 + return - self.remove_timer = async_track_point_in_utc_time( - self.hass, _delay_update, - utcnow() + timedelta(seconds=self.delay)) + @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 adf380eee43..60dab841048 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 00000000000..24c286b140a --- /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 00000000000..c6cd6976129 --- /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 00000000000..02591e348a5 --- /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 00000000000..56105b28b1b --- /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 03db5ce7af8..00000000000 --- 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 00000000000..3c528dfbb16 --- /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 1fb727642bc..ecbbe7ea5e0 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -52,6 +52,7 @@ SERVICE_WINK = 'wink' SERVICE_XIAOMI_GW = 'xiaomi_gw' CONFIG_ENTRY_HANDLERS = { + SERVICE_AXIS: 'axis', SERVICE_DAIKIN: 'daikin', SERVICE_DECONZ: 'deconz', 'esphome': 'esphome', @@ -69,7 +70,6 @@ SERVICE_HANDLERS = { 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 e00d7204a79..df635807abe 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -143,6 +143,7 @@ HANDLERS = Registry() # 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 ea91ef5e9f4..14e845074e6 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 b532b7b386d..731f7fa9d22 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 fa6a8429ff3..3c605ef7ae3 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -46,6 +46,7 @@ TEST_REQUIREMENTS = ( '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 00000000000..c7e0f05a814 --- /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 00000000000..9ca8b81793b --- /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 00000000000..c585ada6319 --- /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 00000000000..7e18b36c6a6 --- /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 00000000000..2a0a7d6391c --- /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 00000000000..0586ffd96f6 --- /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 142/290] 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 d5cbcb4785a..5b3db4312bf 100644 --- a/homeassistant/components/darksky/weather.py +++ b/homeassistant/components/darksky/weather.py @@ -12,10 +12,10 @@ from homeassistant.components.weather import ( 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 @@ class DarkSkyWeather(WeatherEntity): @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 58016dd3e2c..8a37bc97575 100644 --- a/homeassistant/components/openweathermap/weather.py +++ b/homeassistant/components/openweathermap/weather.py @@ -10,10 +10,10 @@ from homeassistant.components.weather import ( 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 @@ class OpenWeatherMapWeather(WeatherEntity): @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 f825e066f76..2d2f00f1e16 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -343,6 +343,13 @@ LENGTH_FEET = 'ft' # type: str 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 @@ UNIT_NOT_RECOGNIZED_TEMPLATE = '{} is not a recognized {} unit.' # type: str 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 00000000000..ecfa6344d29 --- /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 5f6d202b5e9..8e506dfca2e 100644 --- a/homeassistant/util/unit_system.py +++ b/homeassistant/util/unit_system.py @@ -5,27 +5,19 @@ from typing import Optional 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_UNITS = [ 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 @@ class UnitSystem: (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 @@ class UnitSystem: 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 @@ class UnitSystem: 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 @@ class UnitSystem: 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 3febd4037ad..73fe36af26d 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -15,6 +15,7 @@ from homeassistant.const import ( LENGTH_METERS, TEMP_CELSIUS, MASS_GRAMS, + PRESSURE_PA, VOLUME_LITERS, MATCH_ALL, ) @@ -33,7 +34,7 @@ class TestHelpersTemplate(unittest.TestCase): 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 00000000000..a3e6efb3754 --- /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 31b2d49b4ec..533ce3c0a15 100644 --- a/tests/util/test_unit_system.py +++ b/tests/util/test_unit_system.py @@ -10,10 +10,12 @@ from homeassistant.const import ( LENGTH_METERS, LENGTH_KILOMETERS, MASS_GRAMS, + PRESSURE_PA, VOLUME_LITERS, TEMP_CELSIUS, LENGTH, MASS, + PRESSURE, TEMPERATURE, VOLUME ) @@ -30,19 +32,23 @@ class TestUnitSystem(unittest.TestCase): """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 @@ class TestUnitSystem(unittest.TestCase): 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 @@ class TestUnitSystem(unittest.TestCase): 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 @@ class TestUnitSystem(unittest.TestCase): 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 143/290] 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 7eb72e91eef..56818c51b81 100644 --- a/homeassistant/components/xiaomi_aqara/binary_sensor.py +++ b/homeassistant/components/xiaomi_aqara/binary_sensor.py @@ -476,18 +476,24 @@ class XiaomiCube(XiaomiBinarySensor): 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 144/290] 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 d107cba8f7b..957bb569110 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -12,7 +12,7 @@ from .config_flow import configured_hosts 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_setup_entry(hass, config_entry): 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 8f90f303fca..cabb5b46ece 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): ) 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 00000000000..be13e579ce0 --- /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 829485e1e92..6629d4eec14 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 homeassistant.util import slugify 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 @@ class DeconzGateway: 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 @@ class DeconzGateway: 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 14e845074e6..15ae90ebe0b 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 731f7fa9d22..05a14e18fc0 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 fa274f1d676..953bb776419 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 9e1d6a2fca1..20c74a82883 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 d73f225b2ac..6006ff66898 100644 --- a/tests/components/deconz/test_gateway.py +++ b/tests/components/deconz/test_gateway.py @@ -4,10 +4,13 @@ from unittest.mock import Mock, patch 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', - return_value=mock_coro(False)): + 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', + 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 cbba47eb431..e0afadccc81 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 145/290] 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 972862e7a9c..f81dd9e736f 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 146/290] 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 ecf7b12e4ac..ec07a557342 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -9,8 +9,9 @@ from math import ceil 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 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb): @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 147/290] 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 f330030922d..d1acce02e47 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -36,6 +36,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ '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 @@ class XiaomiPlugGenericSwitch(SwitchDevice): 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 @@ class XiaomiPowerStripSwitch(XiaomiPlugGenericSwitch): 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 @@ class ChuangMiPlugSwitch(XiaomiPlugGenericSwitch): 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 @@ class ChuangMiPlugSwitch(XiaomiPlugGenericSwitch): 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 @@ class ChuangMiPlugSwitch(XiaomiPlugGenericSwitch): 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 148/290] 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 482c7383d89..148b0ae6d84 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -79,7 +79,11 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): # 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 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): # 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 149/290] 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 3f715af0e04..c881ec1276a 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 150/290] 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 df30c7e0782..2fb284bb24a 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -179,7 +179,7 @@ class OnkyoDevice(MediaPlayerDevice): 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 151/290] 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 2e580627543..2036e55b3a8 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -12,7 +12,7 @@ from homeassistant.helpers import discovery 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 15ae90ebe0b..8a65d124f1c 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 152/290] 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 2d8b06dd466..d0537c5912b 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -315,6 +315,8 @@ class HomeData: 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 153/290] 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 aae2e9b6af9..9cf42bfec60 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -17,6 +17,9 @@ import voluptuous as vol 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) 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 @@ class DlnaDmrDevice(MediaPlayerDevice): 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 bf7e6b4e0ce..54e28d2d17e 100644 --- a/homeassistant/components/media_player/const.py +++ b/homeassistant/components/media_player/const.py @@ -36,6 +36,7 @@ MEDIA_TYPE_VIDEO = 'video' 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 3c91f19469b..c9da38d3657 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 154/290] 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 f9bf36e61f0..0e99727e81b 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -45,8 +45,23 @@ ATTR_VAPID_PUB_KEY = 'vapid_pub_key' 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 155/290] 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 e6546f7c1e2..f1ac0cd90d4 100644 --- a/homeassistant/components/panasonic_viera/media_player.py +++ b/homeassistant/components/panasonic_viera/media_player.py @@ -19,12 +19,15 @@ from homeassistant.const import ( 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class PanasonicVieraTVDevice(MediaPlayerDevice): self._remote = remote self._host = host self._volume = 0 + self._app_power = app_power @property def unique_id(self) -> str: @@ -134,7 +141,7 @@ class PanasonicVieraTVDevice(MediaPlayerDevice): @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 @@ class PanasonicVieraTVDevice(MediaPlayerDevice): 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 8a65d124f1c..1891d2a385a 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 156/290] 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 6bbf0a3dd53..5ebe2a78d0d 100644 --- a/homeassistant/components/upnp/device.py +++ b/homeassistant/components/upnp/device.py @@ -23,7 +23,10 @@ class 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 5f544e1a134..708ef314ab4 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -8,8 +8,10 @@ from datetime import datetime 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 @@ OUT = 'sent' 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 @@ class UpnpSensor(Entity): '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 157/290] 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 42e7d84dc09..ec6aad90628 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 e880177380f..717da8b219e 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 00000000000..bb097a7edd0 --- /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 00000000000..44fa1909823 --- /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 1891d2a385a..80274fdbfd3 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 158/290] 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 00000000000..bd1f6b55090 --- /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 00000000000..c21f2a94137 --- /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 00000000000..c8b0eed8b6b --- /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 00000000000..020d92200b9 --- /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 e605f82c3f1..8f639a653c3 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -38,6 +38,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 9363576fc1a..7fa0e25b32a 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/notify/aws_sns.py @@ -35,6 +35,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 ed22147cfed..92782429939 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/notify/aws_sqs.py @@ -33,6 +33,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 80274fdbfd3..de994fa9122 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 05a14e18fc0..fe026a3813c 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 3c605ef7ae3..5d21681aeda 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -41,6 +41,7 @@ COMMENT_REQUIREMENTS = ( 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 00000000000..270922b1e1e --- /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 00000000000..89dd9deaa0a --- /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 159/290] 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 66fc1fa13dd..e98655f9d76 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -61,8 +61,7 @@ SERVICE_SCHEMA_REMOVE_DEVICE = vol.Schema({ }) -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 @@ GATEWAY_CONFIG = vol.Schema({ 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 160/290] 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 d79c68ffd5e..24275c87061 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -442,10 +442,18 @@ class TemplateMethods: 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 73fe36af26d..e0aeb09976d 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -97,6 +97,16 @@ class TestHelpersTemplate(unittest.TestCase): '{{ 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 161/290] 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 bfc67e7cfaf..44a3800afa9 100644 --- a/homeassistant/components/ecobee/climate.py +++ b/homeassistant/components/ecobee/climate.py @@ -279,6 +279,7 @@ class Thermostat(ClimateDevice): "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 f4412b5d564..3215a9d5b4c 100644 --- a/tests/components/ecobee/test_climate.py +++ b/tests/components/ecobee/test_climate.py @@ -183,7 +183,8 @@ class TestEcobee(unittest.TestCase): '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 @@ class TestEcobee(unittest.TestCase): '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 @@ class TestEcobee(unittest.TestCase): '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 @@ class TestEcobee(unittest.TestCase): '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 @@ class TestEcobee(unittest.TestCase): '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 162/290] 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 1481fea1783..2a7730c4a27 100644 --- a/homeassistant/components/notify/clicksend_tts.py +++ b/homeassistant/components/notify/clicksend_tts.py @@ -27,6 +27,7 @@ HEADERS = {CONTENT_TYPE: CONTENT_TYPE_JSON} CONF_LANGUAGE = 'language' CONF_VOICE = 'voice' +CONF_CALLER = 'caller' DEFAULT_LANGUAGE = 'en-us' DEFAULT_VOICE = 'female' @@ -38,6 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class ClicksendNotificationService(BaseNotificationService): 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 163/290] 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 a2aac5767bd..b084d7525d3 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -41,7 +41,6 @@ class HomeKitLock(HomeKitEntity, LockDevice): """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 @@ class HomeKitLock(HomeKitEntity, LockDevice): 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 164/290] 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 32e3c5f69e3..ed4e704a6a5 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_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' @@ -56,9 +50,6 @@ 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): @@ -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 setup(hass, config): 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 setup(hass, config): 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 @@ class YeelightDevice: 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 8c7a94d3020..d208d1f72b0 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 @@ from homeassistant.components.light import ( 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_YEELIGHT_RGB = (SUPPORT_YEELIGHT | 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 _cmd(func): 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 @@ class YeelightLight(Light): 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 165/290] 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 | 110 ++++++++++++------ 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, 227 insertions(+), 71 deletions(-) diff --git a/homeassistant/components/ps4/.translations/en.json b/homeassistant/components/ps4/.translations/en.json index c0b476ff4e2..662f6fb6116 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 edefb5e4709..d5833ae1673 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -6,13 +6,15 @@ https://home-assistant.io/components/ps4/ """ 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 148b0ae6d84..1b184a3774f 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -8,10 +8,14 @@ from homeassistant import config_entries 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 @@ PORT_MSG = {UDP_PORT: 'port_987_bind_error', TCP_PORT: 'port_997_bind_error'} 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 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): 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 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): 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 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): 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_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() + + mode_schema = OrderedDict() + mode_schema[vol.Required( + CONF_MODE, default=CONF_AUTO)] = vol.In(list(mode)) + mode_schema[vol.Optional(CONF_IP_ADDRESS)] = str + + return self.async_show_form( + step_id='mode', + data_schema=vol.Schema(mode_schema), + errors=errors, + ) + 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 = {} - # Search for device. - devices = await self.hass.async_add_executor_job( - self.helper.has_devices) + 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') + # Abort if can't find device. + if not devices: + return self.async_abort(reason='no_devices_found') - device_list = [ - device['host-ip'] for device in devices] + 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 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 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 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): # 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 0618ca9675f..bbf654530b0 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 60b656a469d..e2f0004f80e 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -20,7 +20,7 @@ from homeassistant.const import ( 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 @@ class PS4Device(MediaPlayerDevice): 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 @@ class PS4Device(MediaPlayerDevice): 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 @@ class PS4Device(MediaPlayerDevice): @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 5f4e2a7c8b4..d8fdc9e18db 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 de994fa9122..8603d7a91de 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 fe026a3813c..1a8298d196b 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 271db46d856..06fe1ef65da 100644 --- a/tests/components/ps4/test_config_flow.py +++ b/tests/components/ps4/test_config_flow.py @@ -44,6 +44,9 @@ MOCK_DATA = { 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 166/290] 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 1d186484f40..2db3110f2d9 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -40,6 +40,8 @@ CONF_ADB_SERVER_IP = 'adb_server_ip' 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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_decorator(override_available=False): 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 @@ class ADBDevice(MediaPlayerDevice): 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 @@ class ADBDevice(MediaPlayerDevice): @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 @@ class ADBDevice(MediaPlayerDevice): 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 @@ class AndroidTVDevice(ADBDevice): 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 167/290] 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 b16f82de7f6..0cb9ecbfc07 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -28,7 +28,6 @@ HOMEKIT_IGNORE = [ _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 168/290] 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 9517971b16d..76a768385f8 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ from .const import ( 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 874c3420c58..19a6528e321 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 @@ from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN 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 @@ class CloudRemoteBinary(BinarySensorDevice): 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 8603d7a91de..020ed3248a5 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 1a8298d196b..4bd50b713e2 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 938829b809b..f6d8783a609 100644 --- a/tests/components/cloud/test_binary_sensor.py +++ b/tests/components/cloud/test_binary_sensor.py @@ -7,6 +7,9 @@ from homeassistant.components.cloud.const import DISPATCHER_REMOTE_UPDATE 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 169/290] 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 efabd03b586..0366dfa2b8b 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -32,7 +32,7 @@ ON_DEMAND = ('zwave',) 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 30b9d350df6..5a10b60f12f 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 @@ class Panel: '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 @@ async def async_setup(hass, config): 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 7f85c8cfc3f..7e47ac152e3 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 2fce5d9857c..7fe2191f4c4 100644 --- a/homeassistant/components/panel_custom/__init__.py +++ b/homeassistant/components/panel_custom/__init__.py @@ -23,6 +23,7 @@ CONF_MODULE_URL = 'module_url' 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 @@ CONFIG_SCHEMA = vol.Schema({ 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 b82f9fa9789..9319dfcc6ad 100644 --- a/homeassistant/components/panel_iframe/__init__.py +++ b/homeassistant/components/panel_iframe/__init__.py @@ -12,6 +12,7 @@ CONF_TITLE = 'title' 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 @@ CONFIG_SCHEMA = vol.Schema({ # 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 b1b9a70d594..e4ed2c15ecb 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 1326805fc93..ba642b698f7 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -8,6 +8,7 @@ import pytest 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 c265324179d..8c95f96085a 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 cb868f64b58..da7878399d4 100644 --- a/tests/components/panel_iframe/test_init.py +++ b/tests/components/panel_iframe/test_init.py @@ -41,11 +41,13 @@ class TestPanelIframe(unittest.TestCase): '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 @@ class TestPanelIframe(unittest.TestCase): '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 @@ class TestPanelIframe(unittest.TestCase): 'icon': 'mdi:weather', 'title': 'Weather', 'url_path': 'weather', + 'require_admin': True, } assert panels.get('api').to_response() == { @@ -84,6 +88,7 @@ class TestPanelIframe(unittest.TestCase): 'icon': 'mdi:weather', 'title': 'Api', 'url_path': 'api', + 'require_admin': False, } assert panels.get('ftp').to_response() == { @@ -92,4 +97,5 @@ class TestPanelIframe(unittest.TestCase): '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 170/290] 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 78f16a82d56..33a5d5f32f8 100644 --- a/homeassistant/components/tplink/device_tracker.py +++ b/homeassistant/components/tplink/device_tracker.py @@ -77,8 +77,8 @@ class TplinkDeviceScanner(DeviceScanner): 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 @@ class Tplink1DeviceScanner(DeviceScanner): 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 171/290] 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 1447f7c896c..e3ac1427941 100644 --- a/homeassistant/components/traccar/device_tracker.py +++ b/homeassistant/components/traccar/device_tracker.py @@ -4,7 +4,7 @@ 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/ """ -from datetime import timedelta +from datetime import datetime, timedelta import logging import voluptuous as vol @@ -13,14 +13,15 @@ from homeassistant.components.device_tracker import PLATFORM_SCHEMA 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_GEOFENCE = 'geofence' 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class TraccarScanner: """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 @@ class TraccarScanner: 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 @@ class TraccarScanner: 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 020ed3248a5..7fd0c906ed7 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 172/290] 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 ed4e704a6a5..8a5c1e81a93 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -8,13 +8,15 @@ from homeassistant.components.discovery import SERVICE_YEELIGHT 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_RECOVER = 'recover' 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 @@ YEELIGHT_SERVICE_SCHEMA = vol.Schema({ vol.Required(ATTR_ENTITY_ID): cv.entity_ids, }) -NIGHTLIGHT_SUPPORTED_MODELS = [ - "ceiling3", - 'ceiling4' -] - UPDATE_REQUEST_PROPERTIES = [ "power", "bright", @@ -103,8 +97,7 @@ UPDATE_REQUEST_PROPERTIES = [ "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 @@ class YeelightDevice: 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 @@ class YeelightDevice: 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 @@ class YeelightDevice: 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 @@ class YeelightDevice: 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 00000000000..cf7bbc5244e --- /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 7fd0c906ed7..f6814690309 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 173/290] 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 04cf8a584bf..c757185a5cf 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -17,7 +17,7 @@ from homeassistant.util import Throttle 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 f6814690309..1b954257e54 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 174/290] 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 46766d75010..eaf73ceb566 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -11,7 +11,7 @@ import voluptuous as vol 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_HOST = 'localhost' DEFAULT_NAME = 'Plex' DEFAULT_PORT = 32400 DEFAULT_SSL = False +DEFAULT_VERIFY_SSL = True MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) @@ -38,6 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 175/290] 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 25b352c1454..8eb5c623725 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -8,6 +8,7 @@ import os import logging import datetime import threading +from typing import Optional import voluptuous as vol @@ -25,6 +26,7 @@ CONF_DATA = 'data' CONF_DESTINATION = 'destination' CONF_ORIGIN = 'origin' CONF_OFFSET = 'offset' +CONF_TOMORROW = 'include_tomorrow' DEFAULT_NAME = 'GTFS Sensor' DEFAULT_PATH = 'gtfs' @@ -47,65 +49,162 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class GTFSDepartureSensor(Entity): """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 @@ class GTFSDepartureSensor(Entity): 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 176/290] 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 0366dfa2b8b..7807c527370 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -46,7 +46,6 @@ async def async_setup(hass, config): 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 57ea7e7a492..41a0fb089b5 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 177/290] 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 94f3be305fa..74da7a9d542 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -7,7 +7,7 @@ import voluptuous as vol 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 1b954257e54..ff1a18d9f6d 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 4bd50b713e2..8f88e5bb67b 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 178/290] 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 4db1246b992..7a4fa486ff9 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -82,11 +82,6 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice): 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 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice): 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 b084d7525d3..ac1bd8f88da 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -64,11 +64,6 @@ class HomeKitLock(HomeKitEntity, LockDevice): """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 179/290] 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 653ade806ec..4e8c8293c2d 100644 --- a/homeassistant/components/ness_alarm/__init__.py +++ b/homeassistant/components/ness_alarm/__init__.py @@ -13,7 +13,7 @@ from homeassistant.helpers import config_validation as cv 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 @@ DATA_NESS = 'ness_alarm' 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 @@ CONF_ZONE_ID = 'id' 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 @@ CONFIG_SCHEMA = vol.Schema({ 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 ff1a18d9f6d..15db65c1608 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 180/290] 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 957bb569110..8bdd946e2ef 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -12,7 +12,7 @@ from .config_flow import configured_hosts 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 cb68b842f4a..2b0c2037248 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -11,6 +11,10 @@ from .deconz_device import DeconzDevice 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 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorDevice): @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 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorDevice): 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 6629d4eec14..11fb247a6f4 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -44,7 +44,7 @@ class DeconzGateway: 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 15db65c1608..401d22cd551 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 8f88e5bb67b..17c993bf5f7 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 181/290] 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 f4bf1f269b5..cd9190dca35 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 182/290] 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 0cb9ecbfc07..5e470e1540b 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -11,8 +11,7 @@ from homeassistant.helpers.event import call_later 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 @@ RETRY_INTERVAL = 60 # seconds 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 @@ class HKDevice(): 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 @@ class HKDevice(): 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 setup(hass, config): 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 9bc15aad75d..f9bc25f4237 100644 --- a/homeassistant/components/homekit_controller/alarm_control_panel.py +++ b/homeassistant/components/homekit_controller/alarm_control_panel.py @@ -6,7 +6,7 @@ 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 +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 7fcc5b4e833..2bd03b18932 100644 --- a/homeassistant/components/homekit_controller/binary_sensor.py +++ b/homeassistant/components/homekit_controller/binary_sensor.py @@ -3,7 +3,7 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -13,7 +13,7 @@ _LOGGER = logging.getLogger(__name__) 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 243b795e792..67f1fb72bcf 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -7,7 +7,7 @@ from homeassistant.components.climate.const import ( 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 @@ DEFAULT_VALID_MODES = list(MODE_HOMEKIT_TO_HASS) 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 873f6b343d2..90a105b0ad9 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 7a4fa486ff9..26b7613ed2b 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -8,7 +8,7 @@ from homeassistant.components.cover import ( 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 db8fd332c0c..cb9259df4a9 100644 --- a/homeassistant/components/homekit_controller/light.py +++ b/homeassistant/components/homekit_controller/light.py @@ -5,7 +5,7 @@ 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 +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) 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 ac1bd8f88da..0d0275fda16 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -5,7 +5,7 @@ from homeassistant.components.lock import LockDevice 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 955a1a7927e..8cbc8f248ba 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 @@ UNIT_LUX = "lux" 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 ba19413d411..34e83c06526 100644 --- a/homeassistant/components/homekit_controller/switch.py +++ b/homeassistant/components/homekit_controller/switch.py @@ -3,7 +3,7 @@ import logging from homeassistant.components.switch import SwitchDevice -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) 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 183/290] 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 | 216 +++++++++++++++-------- tests/helpers/test_entity_platform.py | 104 ++++++++--- 3 files changed, 242 insertions(+), 121 deletions(-) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 87cc4d4fd90..a092c89405e 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -27,7 +27,6 @@ class EntityPlatform: domain: str platform_name: str scan_interval: timedelta - parallel_updates: int entity_namespace: str async_entities_added_callback: @callback method """ @@ -52,22 +51,21 @@ class EntityPlatform: # 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 + self.parallel_updates = getattr(platform, 'PARALLEL_UPDATES', None) + # semaphore will be created on demand + self.parallel_updates_semaphore = None - parallel_updates = getattr(platform, 'PARALLEL_UPDATES', - default_parallel_updates) - - 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 @@ class EntityPlatform: 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 d79f84d416d..383cd05a009 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 test_async_schedule_update_ha_state(hass): 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 test_async_parallel_updates_with_zero(hass): 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] - - test_lock.set() + assert len(updates) == 2 + assert updates == [1, 2] + finally: + test_lock.set() -@asyncio.coroutine -def test_async_parallel_updates_with_one(hass): +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): + + 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 test_async_parallel_updates_with_one(hass): 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) + + 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 -def test_async_parallel_updates_with_two(hass): +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 test_async_parallel_updates_with_two(hass): 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) + await test_lock.acquire() - while True: - if len(updates) == 2: - break - yield from asyncio.sleep(0, loop=hass.loop) + try: - assert len(updates) == 2 - assert updates == [1, 2] + 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) - test_lock.release() - yield from asyncio.sleep(0, loop=hass.loop) - test_lock.release() + while True: + if len(updates) >= 2: + break + await asyncio.sleep(0) - while True: - if len(updates) == 4: - break - yield from asyncio.sleep(0, loop=hass.loop) + assert len(updates) == 2 + assert updates == [1, 2] - assert len(updates) == 4 - assert updates == [1, 2, 3, 4] + updates.clear() + test_lock.release() + await asyncio.sleep(0) + test_lock.release() + await asyncio.sleep(0) - test_lock.release() - yield from asyncio.sleep(0, loop=hass.loop) - test_lock.release() + 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 e985771e486..6cf0bb0eeeb 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -251,80 +251,126 @@ def test_updated_state_used_for_entity_id(hass): 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 - - platform.async_setup_platform = mock_update - 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 + 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.""" + async def async_update(self): + pass + + entity = AsyncEntity() + await handle.async_add_entities([entity]) + assert entity.parallel_updates is None + + +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 = {} + + await component.async_setup({ + DOMAIN: { + 'platform': 'platform', + } + }) + + handle = list(component._platforms.values())[-1] + + assert handle.parallel_updates == 2 + + 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 not None + assert entity.parallel_updates._value == 2 + + +async def test_parallel_updates_sync_platform(hass): + """Test sync platform parallel_updates default set to 1.""" platform = MockPlatform() - @asyncio.coroutine - def mock_update(*args, **kwargs): - pass - - platform.async_setup_platform = mock_update - platform.PARALLEL_UPDATES = 1 - 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 - 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 == 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 184/290] 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 665803d38eb..59c0a6b650f 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -128,6 +128,7 @@ class StreamOutput: @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 185/290] 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 046b6d3947c..cdd8a844389 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -20,7 +20,7 @@ import voluptuous as vol 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.stream.const import ( 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 @@ ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?token={1}' 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 00000000000..f87ca47460e --- /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 00000000000..927929bdf6e --- /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 d306509b762..56780d16f56 100644 --- a/homeassistant/components/local_file/camera.py +++ b/homeassistant/components/local_file/camera.py @@ -12,7 +12,8 @@ import voluptuous as vol 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 814475d04de..ff6f14431d5 100644 --- a/homeassistant/components/logi_circle/camera.py +++ b/homeassistant/components/logi_circle/camera.py @@ -6,8 +6,9 @@ import logging 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 da0bae7c50b..f3b25e3a128 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -13,7 +13,8 @@ import voluptuous as vol 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 36c4a3109ba..5490cd1508c 100644 --- a/homeassistant/components/push/camera.py +++ b/homeassistant/components/push/camera.py @@ -14,7 +14,8 @@ import aiohttp 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 c881ec1276a..a68f1c47dbf 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 21f7244bd29..bebb991a7af 100644 --- a/tests/components/camera/common.py +++ b/tests/components/camera/common.py @@ -4,7 +4,9 @@ All containing methods are legacy helpers that should not be used by new 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 0359d14df63..701a3682830 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 @@ from tests.common import ( 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 3d70e3f77a7..a96f9768be4 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 186/290] 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 d532d9cdb86..a3b1d6d305e 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -9,10 +9,10 @@ from typing import Any, Optional, Dict, Set 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 533811e275d..88cd44f4bf2 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -7,33 +7,12 @@ Component design guidelines: 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 bb774ae7ef8..85e9c0e6886 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 7e47ac152e3..90e120d8b0e 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -6,7 +6,7 @@ import os 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 00000000000..ef01d133cff --- /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 86af6c34694..617b5624110 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/homeassistant/scene.py @@ -9,8 +9,8 @@ from homeassistant.const import ( 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 2aa1f499a76..812456a3594 100644 --- a/tests/components/conversation/test_init.py +++ b/tests/components/conversation/test_init.py @@ -5,7 +5,6 @@ import pytest 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 test_http_processing_intent(hass, hass_client): @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 5df492d3d47..09cb5752b55 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 8be99a02148..08001b0ebab 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -8,8 +8,7 @@ from aiohttp.hdrs import CONTENT_TYPE 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 @@ JSON_HEADERS = {CONTENT_TYPE: const.CONTENT_TYPE_JSON} 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 1d532f4757c..49d49fdd3d4 100644 --- a/tests/components/generic_thermostat/test_climate.py +++ b/tests/components/generic_thermostat/test_climate.py @@ -23,7 +23,6 @@ from homeassistant.util.unit_system import METRIC_SYSTEM 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 69964a11fdc..60df4a8e79c 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -8,7 +8,7 @@ import pytest 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 00000000000..334751e6de5 --- /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 da06927db3a..b72589e60e3 100644 --- a/tests/components/init/test_init.py +++ b/tests/components/homeassistant/test_init.py @@ -12,7 +12,8 @@ from homeassistant.const import ( 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 @@ class TestComponentsCore(unittest.TestCase): """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 @@ class TestComponentsCore(unittest.TestCase): 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 @@ class TestComponentsCore(unittest.TestCase): 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 b935cf060c8..00000000000 --- 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 5c04f085c86..10b053528ab 100644 --- a/tests/helpers/test_state.py +++ b/tests/helpers/test_state.py @@ -5,7 +5,7 @@ import unittest 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 187/290] 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 eeae1587099..135437801d9 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_ACCESS_TOKEN, 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 401d22cd551..8ad56528f4f 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 188/290] 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 4b4546821a7..8bfcbc705dc 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 @@ def async_load_api(hass): 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 @@ def async_load_api(hass): 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 bf0f1044efb..061541d4dae 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -7,7 +7,7 @@ https://home-assistant.io/components/zha/ 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 @@ class OnOffChannel(ZigbeeChannel): 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 189/290] 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 8a5c1e81a93..7318b088ab4 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -259,8 +259,6 @@ class YeelightDevice: _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 @@ class YeelightDevice: _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 d208d1f72b0..22e5d9cc9ce 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -494,6 +494,7 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 190/290] 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 bad20884536..4132d3c27c7 100644 --- a/homeassistant/components/radiotherm/climate.py +++ b/homeassistant/components/radiotherm/climate.py @@ -246,8 +246,8 @@ class RadioThermostat(ClimateDevice): 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 191/290] 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 885988adb6b..fcffc44eefb 100644 --- a/homeassistant/components/demo/mailbox.py +++ b/homeassistant/components/demo/mailbox.py @@ -63,7 +63,7 @@ class DemoMailbox(Mailbox): 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 6784e7cea61..1498472ef9f 100644 --- a/homeassistant/components/tts/demo.py +++ b/homeassistant/components/demo/tts.py @@ -8,7 +8,7 @@ import os 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 @@ class DemoProvider(Provider): 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 971d3fb5705..8f6a46b0c3e 100644 --- a/homeassistant/components/tts/marytts.py +++ b/homeassistant/components/marytts/tts.py @@ -16,7 +16,7 @@ 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 . 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 3676dff3bc6..20e0ee11db3 100644 --- a/homeassistant/components/tts/voicerss.py +++ b/homeassistant/components/voicerss/tts.py @@ -15,7 +15,7 @@ from homeassistant.const import CONF_API_KEY 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 aecba2925dd..281839a2d74 100644 --- a/homeassistant/components/tts/yandextts.py +++ b/homeassistant/components/yandextts/tts.py @@ -15,7 +15,7 @@ from homeassistant.const import CONF_API_KEY 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 8ad56528f4f..f726f57471b 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 00000000000..061776a1398 --- /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 7520ba2fbaa..24915dd85c8 100644 --- a/tests/components/tts/test_marytts.py +++ b/tests/components/marytts/test_tts.py @@ -11,7 +11,7 @@ from homeassistant.components.media_player.const import ( 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 4786370f24f..140a938201b 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -9,7 +9,7 @@ import requests 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 @@ class TestTTS: "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 @@ class TestTTS: 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 @@ class TestTTS: "{}/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 00000000000..9c037a14465 --- /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 af4bdf3976c..cd0e20cb9fa 100644 --- a/tests/components/tts/test_voicerss.py +++ b/tests/components/voicerss/test_tts.py @@ -11,7 +11,7 @@ from homeassistant.setup import setup_component 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 00000000000..54968b3605f --- /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 70c75b1f2ed..dd382271338 100644 --- a/tests/components/tts/test_yandextts.py +++ b/tests/components/yandextts/test_tts.py @@ -10,7 +10,7 @@ from homeassistant.components.media_player.const import ( 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 192/290] 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 90e120d8b0e..073974200a0 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 7eb3245c0df..7eddc639690 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -62,7 +62,7 @@ class HassIO: 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 ba642b698f7..0c651aa0c5a 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 193/290] 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 5e470e1540b..0ed208af979 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -102,6 +102,7 @@ class HKDevice(): 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 7519e8d4175659607cf113171943d80024eee49e Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Tue, 26 Mar 2019 07:48:26 -0700 Subject: [PATCH 194/290] Update translate, fix dev build error (#22419) --- .../components/axis/.translations/en.json | 32 +++++++++---------- .../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, 29 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/axis/.translations/en.json b/homeassistant/components/axis/.translations/en.json index 3c528dfbb16..6c5933dfd97 100644 --- a/homeassistant/components/axis/.translations/en.json +++ b/homeassistant/components/axis/.translations/en.json @@ -1,26 +1,26 @@ { "config": { - "title": "Axis device", - "step": { - "user": { - "title": "Set up Axis device", - "data": { - "host": "Host", - "username": "Username", - "password": "Password", - "port": "Port" - } - } + "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" }, - "abort": { - "already_configured": "Device is already configured", - "bad_config_file": "Bad data from config file", - "link_local_address": "Link local addresses are not supported" - } + "step": { + "user": { + "data": { + "host": "Host", + "password": "Password", + "port": "Port", + "username": "Username" + }, + "title": "Set up Axis device" + } + }, + "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 662f6fb6116..8949e77b4cc 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 d8fdc9e18db..ea69d8c7a8c 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 c2b00561858..4ed9ef597f4 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 632d5112f1a..91e4f6b7c52 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 f0da251f5eb..82489ac258e 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 195/290] 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 34330426e34..730c3851a2d 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -15,7 +15,8 @@ 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 homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.event import async_track_time_interval from . import sensor_types @@ -23,7 +24,8 @@ REQUIREMENTS = ['eternalegypt==0.0.5'] _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 @@ CONFIG_SCHEMA = vol.Schema({ 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 @@ class ModemData: 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 _login(hass, modem_data, password): 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 774cdc5536a..a13f5fbfaa7 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -6,8 +6,9 @@ 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 +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 196/290] 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 60f8761aeeb..c5e2b4284d3 100644 --- a/homeassistant/components/cisco_mobility_express/device_tracker.py +++ b/homeassistant/components/cisco_mobility_express/device_tracker.py @@ -10,7 +10,7 @@ from homeassistant.const import ( 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 f726f57471b..fc8d7357d92 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 80250add9e80f53a5e92bca5c7ac2dbaf06319c6 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 26 Mar 2019 22:42:43 +0100 Subject: [PATCH 197/290] 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 604f03387bd..4abaaa31210 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -37,7 +37,7 @@ from .discovery_schemas import DISCOVERY_SCHEMAS 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 fc8d7357d92..a5642a989d7 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 198/290] 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 40101120f12..0b6f995be97 100644 --- a/homeassistant/components/enigma2/media_player.py +++ b/homeassistant/components/enigma2/media_player.py @@ -14,7 +14,7 @@ from homeassistant.const import ( 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_END_TIME = 'media_end_time' 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_SSL = False 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 a5642a989d7..75e3ece960e 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 199/290] 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 0c651aa0c5a..fc4661e7544 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 200/290] 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 daac9fef748..d68cabebacf 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -102,8 +102,8 @@ def request_handler_factory(view, handler): 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 201/290] 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 7ba3ea04bf5..35d5e18fdd3 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -78,7 +78,7 @@ class AmcrestCam(Camera): 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 @@ class AmcrestCam(Camera): 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 43ccabb7390..95d11318bf7 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -83,7 +83,7 @@ class ArloCam(Camera): 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 @@ class ArloCam(Camera): 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 c54565d6fde..63c27d31d93 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -80,7 +80,7 @@ class CanaryCamera(Camera): """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 @@ class CanaryCamera(Camera): 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 7b7e3a81294..05bc1d99167 100644 --- a/homeassistant/components/ffmpeg/__init__.py +++ b/homeassistant/components/ffmpeg/__init__.py @@ -12,7 +12,7 @@ from homeassistant.helpers.dispatcher import ( 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 4272a3d6029..dbb51bf27c7 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -48,7 +48,7 @@ class FFmpegCamera(Camera): 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 @@ class FFmpegCamera(Camera): 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 d0e597e13c0..8183b0e66a6 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 070c8c61b00..56edf1ddfd6 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 f3b25e3a128..36f1b18eebf 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -190,7 +190,7 @@ class ONVIFHassCamera(Camera): 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 @@ class ONVIFHassCamera(Camera): 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 @@ class ONVIFHassCamera(Camera): 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 efcdf8599a9..8970e61b1a1 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -115,7 +115,7 @@ class RingCam(Camera): 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 @@ class RingCam(Camera): 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 @@ class RingCam(Camera): 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 93e9dd4a07c..d8cd59129ab 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -138,7 +138,7 @@ class XiaomiCamera(Camera): 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 @@ class XiaomiCamera(Camera): 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 7d731d2a433..c60d4971fb8 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -118,7 +118,7 @@ class YiCamera(Camera): 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 @@ class YiCamera(Camera): 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 @@ class YiCamera(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/requirements_all.txt b/requirements_all.txt index 75e3ece960e..4b4b2570d50 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 17c993bf5f7..08466d922b8 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 2c17207af32..d1fd6124b4c 100644 --- a/tests/components/binary_sensor/test_ffmpeg.py +++ b/tests/components/ffmpeg/test_sensor.py @@ -33,7 +33,8 @@ class TestFFmpegNoiseSetup: 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 @@ class TestFFmpegNoiseSetup: 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 @@ class TestFFmpegMotionSetup: 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 @@ class TestFFmpegMotionSetup: 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 202/290] Add myself as codeowner for yeelight component (#22438) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 717da8b219e..d95f2f8f946 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 203/290] Update ZHA component CODEOWNERS (#22452) --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index d95f2f8f946..e9d7a652a66 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 204/290] Add support for yeelight ceiling ambilight (#22346) --- homeassistant/components/yeelight/__init__.py | 28 +++- homeassistant/components/yeelight/light.py | 122 ++++++++++++++---- 2 files changed, 120 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 7318b088ab4..4171005d9fc 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -91,6 +91,7 @@ YEELIGHT_SERVICE_SCHEMA = vol.Schema({ UPDATE_REQUEST_PROPERTIES = [ "power", + "main_power", "bright", "ct", "rgb", @@ -98,6 +99,13 @@ UPDATE_REQUEST_PROPERTIES = [ "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 @@ class YeelightDevice: """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 22e5d9cc9ce..cc3810c4968 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 @@ class YeelightLight(Light): """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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): """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._bulb.bulb_type == yeelight.BulbType.WhiteTemp: - if self._device.is_nightlight_enabled: + 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: + 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 self._device.is_nightlight_enabled: - bright = self._properties.get('nl_br', None) + if bulb_type == yeelight.BulbType.WhiteTempMood: + self._is_on = self._get_property('main_power') == 'on' else: - bright = self._properties.get('bright', None) + self._is_on = self._get_property('power') == 'on' + + if self._is_nightlight_enabled: + bright = self._get_property('nl_br', None) + else: + 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 @@ class YeelightLight(Light): 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 205/290] 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 a3b1d6d305e..435ec317985 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 644d14cf869..adf5410516d 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 @@ import attr from homeassistant.core import callback from homeassistant.loader import bind_hass + from .typing import HomeAssistantType _LOGGER = logging.getLogger(__name__) @@ -133,14 +135,21 @@ class AreaRegistry: @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 task is None: - async def _load_reg() -> AreaRegistry: - registry = AreaRegistry(hass) - await registry.async_load() - return registry + if not reg_or_evt: + evt = hass.data[DATA_REGISTRY] = Event() - task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + reg = AreaRegistry(hass) + await reg.async_load() - return cast(AreaRegistry, await task) + hass.data[DATA_REGISTRY] = reg + evt.set() + return 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, reg_or_evt) diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 1ea6c400208..25c9933fd11 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 @@ class DeviceRegistry: @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 task is None: - async def _load_reg(): - registry = DeviceRegistry(hass) - await registry.async_load() - return registry + if not reg_or_evt: + evt = hass.data[DATA_REGISTRY] = Event() - task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + reg = DeviceRegistry(hass) + await reg.async_load() - return await task + hass.data[DATA_REGISTRY] = reg + evt.set() + return reg + + if isinstance(reg_or_evt, Event): + evt = reg_or_evt + await evt.wait() + return cast(DeviceRegistry, hass.data.get(DATA_REGISTRY)) + + return cast(DeviceRegistry, reg_or_evt) @callback diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index c0a0dfaa7d9..be50d11d17d 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -7,10 +7,11 @@ The Entity Registry will persist itself 10 seconds after a new entity is 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.loader import bind_hass 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 @@ class EntityRegistry: @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 task is None: - async def _load_reg(): - registry = EntityRegistry(hass) - await registry.async_load() - return registry + if not reg_or_evt: + evt = hass.data[DATA_REGISTRY] = Event() - task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + reg = EntityRegistry(hass) + await reg.async_load() - return await task + hass.data[DATA_REGISTRY] = reg + evt.set() + return reg + + if isinstance(reg_or_evt, Event): + evt = reg_or_evt + await evt.wait() + return cast(EntityRegistry, hass.data.get(DATA_REGISTRY)) + + return cast(EntityRegistry, reg_or_evt) @callback diff --git a/tests/common.py b/tests/common.py index 8681db1b4f3..9fe5375ad7c 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 206/290] 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 324c2cf369e..53087f2682c 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 ec4c27ea343..3a9583f6419 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -81,7 +81,20 @@ class AxisBinarySensor(BinarySensorDevice): """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 60dab841048..45801257d00 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -57,3 +57,15 @@ class AxisCamera(MjpegCamera): """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 02591e348a5..0d7d9348870 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -8,9 +8,10 @@ from homeassistant.const import ( 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 @@ class AxisNetworkDevice: """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 0586ffd96f6..c1c4c06f6ac 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 207/290] 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 d65ba4c54d8..c09e8c4c6e2 100644 --- a/homeassistant/components/websocket_api/connection.py +++ b/homeassistant/components/websocket_api/connection.py @@ -93,11 +93,11 @@ class ActiveConnection: 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 272ac3870ed..08ea655fdf0 100644 --- a/tests/components/websocket_api/test_init.py +++ b/tests/components/websocket_api/test_init.py @@ -4,6 +4,7 @@ from unittest.mock import patch, Mock 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 208/290] 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 c68bb2cd3a3..540568b5785 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -27,6 +27,7 @@ _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Powered by Dark Sky" CONF_FORECAST = 'forecast' +CONF_HOURLY_FORECAST = 'hourly_forecast' CONF_LANGUAGE = 'language' CONF_UNITS = 'units' @@ -193,6 +194,8 @@ PLATFORM_SCHEMA = vol.All( 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 @@ class DarkSkySensor(Entity): @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 @@ class DarkSkySensor(Entity): 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 25e54fac823..ffb90d8474b 100644 --- a/tests/components/darksky/test_sensor.py +++ b/tests/components/darksky/test_sensor.py @@ -20,6 +20,7 @@ VALID_CONFIG_MINIMAL = { '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 @@ INVALID_CONFIG_MINIMAL = { '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 @@ VALID_CONFIG_LANG_DE = { '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 @@ INVALID_CONFIG_LANG = { '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 @@ class TestDarkSkySetup(unittest.TestCase): 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 209/290] 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 c757185a5cf..a09991e3b72 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -17,16 +17,16 @@ from homeassistant.util import Throttle 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 00000000000..29159c60fe9 --- /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 4b4b2570d50..21d2e0365d7 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 210/290] 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 dfb135e19f6..7384d62900e 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -7,7 +7,7 @@ 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) + DOMAIN as KONNECTED_DOMAIN, STATE_HIGH, STATE_LOW) _LOGGER = logging.getLogger(__name__) @@ -41,7 +41,8 @@ class KonnectedSwitch(ToggleEntity): 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 211/290] 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 | 323 +++++++++++++++++-- requirements_all.txt | 1 + 2 files changed, 294 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/solaredge/sensor.py b/homeassistant/components/solaredge/sensor.py index a0d76c564c1..d56ccc53b68 100644 --- a/homeassistant/components/solaredge/sensor.py +++ b/homeassistant/components/solaredge/sensor.py @@ -10,6 +10,7 @@ import logging 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 @@ import homeassistant.helpers.config_validation as cv 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 @@ SENSOR_TYPES = { 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 @@ _LOGGER = logging.getLogger(__name__) 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 SolarEdgeSensor(Entity): - """Representation of an SolarEdge Monitoring API sensor.""" +class SolarEdgeSensorFactory: + """Factory which creates sensors based on the sensor_key.""" - def __init__(self, platform_name, sensor_key, data): + 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): + """Abstract class for a solaredge sensor.""" + + 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 @@ class SolarEdgeSensor(Entity): @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 SolarEdgeData: +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 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 @@ class SolarEdgeData: 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 21d2e0365d7..2e66b5b39e1 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 212/290] 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 bd1f6b55090..a15e56e9de8 100644 --- a/homeassistant/components/aws/__init__.py +++ b/homeassistant/components/aws/__init__.py @@ -13,14 +13,18 @@ from homeassistant.helpers import config_validation as cv, discovery 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 @@ AWS_CREDENTIAL_SCHEMA = vol.Schema( 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 c8b0eed8b6b..4fa88566934 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 020d92200b9..48b80b64ce2 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 @@ 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.""" +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 @@ class AWSNotify(BaseNotificationService): 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 @@ class AWSLambda(AWSNotify): 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 213/290] 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 730c3851a2d..18a9e5e7c9d 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 @@ class ModemData: 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 a13f5fbfaa7..1be960edfe3 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 @@ class LTESensor(Entity): """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 @@ class SMSSensor(LTESensor): @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 @@ class UsageSensor(LTESensor): @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 214/290] 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 0ed208af979..cf349854f52 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 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 .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 215/290] 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 | 468 ++++++++++++++++++------ 1 file changed, 361 insertions(+), 107 deletions(-) diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 8eb5c623725..5555459aa16 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -4,33 +4,69 @@ 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/ """ -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 @@ ICONS = { 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) -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 @@ class GTFSDepartureSensor(Entity): 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 + else: + self._state = dt_util.as_utc( + self._departure['departure_time']).isoformat() - # 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 + # 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] - 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'] + 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] - name = '{} {} to {} next departure' + # 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)) + name.format(agency=getattr(self._agency, + 'agency_name', + DEFAULT_NAME), + default=DEFAULT_NAME, + origin=self.origin, + destination=self.destination)) - self._icon = ICONS.get(route.route_type, ICON) + 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() - # 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 + self._attributes[ATTR_DAY] = self._departure['day'] - 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()) + 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] - 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 + 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] - 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') + # 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 216/290] 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 2db3110f2d9..5bce21f05a0 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.13'] +REQUIREMENTS = ['androidtv==0.0.14'] _LOGGER = logging.getLogger(__name__) @@ -61,21 +61,13 @@ SERVICE_ADB_COMMAND_SCHEMA = vol.Schema({ }) -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 2e66b5b39e1..2b993100f58 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 217/290] 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 1f74d72809b..5b1296b39de 100644 --- a/homeassistant/components/alarmdecoder/__init__.py +++ b/homeassistant/components/alarmdecoder/__init__.py @@ -5,7 +5,7 @@ from datetime import timedelta 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 @@ DATA_AD = 'alarmdecoder' 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 @@ SIGNAL_REL_MESSAGE = 'alarmdecoder.rel_message' 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 setup(hass, config): 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 a09991e3b72..5ad21f5954f 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -8,14 +8,13 @@ import async_timeout 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 b46e800c3d8..590b5a02738 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -6,8 +6,9 @@ import async_timeout 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 @@ class FlowHandler(config_entries.ConfigFlow): return self.async_create_entry( title=host, data={ - KEY_HOST: host, + CONF_HOST: host, KEY_MAC: mac }) @@ -55,14 +56,14 @@ class FlowHandler(config_entries.ConfigFlow): 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 cd2742b5b5e..90967904579 100644 --- a/homeassistant/components/daikin/const.py +++ b/homeassistant/components/daikin/const.py @@ -20,6 +20,5 @@ SENSOR_TYPES = { } } -KEY_HOST = 'host' KEY_MAC = 'mac' KEY_IP = 'ip' diff --git a/homeassistant/components/envisalink/__init__.py b/homeassistant/components/envisalink/__init__.py index b7590341f78..c46a26c6f85 100644 --- a/homeassistant/components/envisalink/__init__.py +++ b/homeassistant/components/envisalink/__init__.py @@ -6,7 +6,8 @@ import voluptuous as vol 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 @@ DOMAIN = 'envisalink' 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 @@ PARTITION_SCHEMA = vol.Schema({ 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 127be02dac4..00738abe4d1 100644 --- a/homeassistant/components/nad/media_player.py +++ b/homeassistant/components/nad/media_player.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player import ( 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 @@ SUPPORT_NAD = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ 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 4e8c8293c2d..97896f9aa3f 100644 --- a/homeassistant/components/ness_alarm/__init__.py +++ b/homeassistant/components/ness_alarm/__init__.py @@ -8,7 +8,7 @@ import voluptuous as vol 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 @@ _LOGGER = logging.getLogger(__name__) 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 @@ ZONE_SCHEMA = vol.Schema({ 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 8127b4b3e96..4d39083387c 100644 --- a/homeassistant/components/notify/nfandroidtv.py +++ b/homeassistant/components/notify/nfandroidtv.py @@ -12,7 +12,7 @@ import requests 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 @@ from . import ( _LOGGER = logging.getLogger(__name__) -CONF_IP = 'host' CONF_DURATION = 'duration' CONF_FONTSIZE = 'fontsize' CONF_POSITION = 'position' @@ -95,7 +94,7 @@ COLORS = { } 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 93f157cd5ec..5f5c43d961f 100644 --- a/homeassistant/components/satel_integra/__init__.py +++ b/homeassistant/components/satel_integra/__init__.py @@ -3,7 +3,7 @@ import logging 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 @@ DOMAIN = 'satel_integra' 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 @@ ZONE_SCHEMA = vol.Schema({ 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 397e21922d9..6a6b18557b0 100644 --- a/homeassistant/components/tellduslive/__init__.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -14,7 +14,7 @@ from homeassistant.helpers.event import async_call_later 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 62463bc0a9e..ff02419d624 100644 --- a/homeassistant/components/tellduslive/config_flow.py +++ b/homeassistant/components/tellduslive/config_flow.py @@ -7,10 +7,11 @@ import async_timeout 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 @@ class FlowHandler(config_entries.ConfigFlow): 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 @@ class FlowHandler(config_entries.ConfigFlow): 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 @@ class FlowHandler(config_entries.ConfigFlow): } 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 @@ class FlowHandler(config_entries.ConfigFlow): 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 @@ class FlowHandler(config_entries.ConfigFlow): 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 80b0513b763..7898cd8b1f6 100644 --- a/homeassistant/components/tellduslive/const.py +++ b/homeassistant/components/tellduslive/const.py @@ -13,7 +13,6 @@ KEY_CONFIG = 'tellduslive_config' 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 2e24fde8294..0ad269b8780 100644 --- a/homeassistant/components/tradfri/config_flow.py +++ b/homeassistant/components/tradfri/config_flow.py @@ -11,7 +11,6 @@ from homeassistant import config_entries 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 @@ class FlowHandler(config_entries.ConfigFlow): 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 @@ class FlowHandler(config_entries.ConfigFlow): 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 f4ad676b9aa..f6b2f0b1d41 100644 --- a/tests/components/daikin/test_config_flow.py +++ b/tests/components/daikin/test_config_flow.py @@ -6,7 +6,8 @@ import pytest 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 7d2104c9306..534f055dbad 100644 --- a/tests/components/ness_alarm/test_init.py +++ b/tests/components/ness_alarm/test_init.py @@ -6,20 +6,20 @@ from asynctest import patch, MagicMock 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 5be9a03742e..460b33e5cd7 100644 --- a/tests/components/tellduslive/test_config_flow.py +++ b/tests/components/tellduslive/test_config_flow.py @@ -7,8 +7,9 @@ import pytest 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 218/290] 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 131da75f4fe..53602c3643c 100644 --- a/homeassistant/components/smartthings/cover.py +++ b/homeassistant/components/smartthings/cover.py @@ -135,6 +135,8 @@ class SmartThingsCover(SmartThingsEntity, CoverDevice): @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 7e41237e3e7..fb90882eae8 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 219/290] 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 8bfcbc705dc..2f88ad3a78b 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -7,6 +7,7 @@ https://home-assistant.io/components/zha/ import asyncio import logging + import voluptuous as vol from homeassistant.components import websocket_api @@ -14,12 +15,14 @@ from homeassistant.core import callback 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 @@ IEEE_SERVICE = 'ieee_based_service' 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 @@ SERVICE_SCHEMAS = { 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 @@ SERVICE_SCHEMAS = { @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 @@ async def websocket_permit_devices(hass, connection, msg): 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 8ee2c7850e3..4f1e24aad5b 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -10,35 +10,31 @@ import collections 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 @@ class ZHAGateway: } ) - 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 b00626031ed..695f2be5960 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 0d7fe06fe25..048054077f8 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 d0763b8fb10..1d6b4fd3e01 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 a70e0e5ea40..6f31f1bcad3 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 0ccad52d6aa..e9d6370575b 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 c348ef0d0a7..ec6af7f4aa1 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 1fc21e34cd8..b0bbc103a9e 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 220/290] 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 4171005d9fc..14b4656c403 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 setup(hass, config): 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 221/290] 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 3a2dc3b2417..276e395817c 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -429,7 +429,7 @@ class KonnectedView(HomeAssistantView): 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 6e92e7f20c8..bd93ee80e21 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 222/290] 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 3a9583f6419..6d373dd638f 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -24,8 +24,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """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 0d7d9348870..ffe48e5f733 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -99,11 +99,16 @@ class AxisNetworkDevice: 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 223/290] 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 7850b08fd6b..3affaba3e1f 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -12,16 +12,15 @@ import aiohttp 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 @@ class SensiboClimate(ClimateDevice): self._id = data['id'] self._external_state = None self._units = units + self._available = False self._do_update(data) @property @@ -139,7 +139,7 @@ class SensiboClimate(ClimateDevice): 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 @@ class SensiboClimate(ClimateDevice): @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 @@ class SensiboClimate(ClimateDevice): @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 @@ class SensiboClimate(ClimateDevice): 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 224/290] 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 adc856bdd3a..f1371deed2b 100644 --- a/homeassistant/components/joaoapps_join/__init__.py +++ b/homeassistant/components/joaoapps_join/__init__.py @@ -6,7 +6,7 @@ import voluptuous as vol 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 c586147d632..0137520049d 100644 --- a/homeassistant/components/joaoapps_join/notify.py +++ b/homeassistant/components/joaoapps_join/notify.py @@ -7,7 +7,7 @@ from homeassistant.components.notify import ( 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 @@ class JoinNotificationService(BaseNotificationService): 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 225/290] 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 c5e2b4284d3..a722a994350 100644 --- a/homeassistant/components/cisco_mobility_express/device_tracker.py +++ b/homeassistant/components/cisco_mobility_express/device_tracker.py @@ -10,7 +10,7 @@ from homeassistant.const import ( 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 2b993100f58..51aac4420b1 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 226/290] 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 c87b2c3f624..e16a1d45ab7 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -65,6 +65,12 @@ API_THERMOSTAT_MODES = OrderedDict([ (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 845e59295ac..924a568dea2 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 227/290] 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 cf349854f52..c777d2944c1 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 @@ HOMEKIT_IGNORE = [ _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 5550846120b..d875b91eb2c 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 90a105b0ad9..de9663f1202 100644 --- a/homeassistant/components/homekit_controller/const.py +++ b/homeassistant/components/homekit_controller/const.py @@ -4,6 +4,9 @@ DOMAIN = 'homekit_controller' 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 2c60dc168d0..2d659d42dfb 100644 --- a/tests/components/homekit_controller/common.py +++ b/tests/components/homekit_controller/common.py @@ -8,8 +8,9 @@ from homekit.model.characteristics import ( 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 228/290] 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 18a9e5e7c9d..a259a361be4 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -30,6 +30,15 @@ DISPATCHER_NETGEAR_LTE = 'netgear_lte_update' 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 @@ CONFIG_SCHEMA = vol.Schema({ })]) }, 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 @@ async def _setup_lte(hass, lte_config): 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 00000000000..8f61e7a44b5 --- /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 229/290] 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 ec6aad90628..84aed9dfb14 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 7d8e0a18d0b..0bdce1909f4 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 330f5f8cc56..b2e22867690 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.components.sensor import PLATFORM_SCHEMA 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 @@ ATTRIBUTION = "Data provided by entur.org under NLOD" 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 @@ ICONS = { '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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 @@ class EnturProxy: """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 @@ class EnturPublicTransportSensor(Entity): @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 @@ class EnturPublicTransportSensor(Entity): """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 51aac4420b1..9dfac8a2130 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 08466d922b8..7721c236d99 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 5d21681aeda..d2d6588672f 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -54,7 +54,6 @@ TEST_REQUIREMENTS = ( '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 015a5e9103c..00000000000 --- 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 98e72d19e81..00000000000 --- 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 24eafe94b23..00000000000 --- 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 230/290] 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 84aed9dfb14..2cfac103f3c 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 f13076fb763..d845d585765 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 00000000000..aa2e0161813 --- /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 e2dda136244..0d6e5765fda 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' - -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 -}) +DEPENDENCIES = ['mopar'] +ICON = 'mdi:car' -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 @@ class MoparSensor(Entity): 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 @@ class MoparSensor(Entity): @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 @@ class MoparSensor(Entity): @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 00000000000..7915aefcb0f --- /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 00000000000..352cdafbd41 --- /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 9dfac8a2130..664e725db61 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 231/290] 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 73a4c61a6ab..e6b9aba643d 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -113,8 +113,9 @@ async def async_setup_entry(hass, config_entry): 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 232/290] 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 c777d2944c1..f9fd0409c9c 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -7,7 +7,7 @@ from homeassistant.helpers import discovery 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 233/290] 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 | 915 +++++++++--------- 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, 653 insertions(+), 582 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 2cfac103f3c..3cba8519314 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/arlo/* - homeassistant/components/asterisk_mbox/* - homeassistant/components/august/* - 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/blink/* - homeassistant/components/bloomsky/* - homeassistant/components/bmw_connected_drive/* - homeassistant/components/browser/* - homeassistant/components/caldav/calendar.py - homeassistant/components/todoist/calendar.py - homeassistant/components/bloomsky/camera.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/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/cloudflare/* - homeassistant/components/coinbase/* - 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/daikin/* - homeassistant/components/danfoss_air/* - homeassistant/components/actiontec/device_tracker.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/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/canary/alarm_control_panel.py + homeassistant/components/canary/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/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/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/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,232 +169,298 @@ 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/knx/* - homeassistant/components/konnected/* - homeassistant/components/lametric/* - homeassistant/components/lcn/* - 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_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/linode/* - homeassistant/components/lirc/* 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/lifx_cloud/scene.py + homeassistant/components/lifx_legacy/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/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/nederlandse_spoorwegen/sensor.py + homeassistant/components/nello/lock.py homeassistant/components/nest/* homeassistant/components/netatmo/* + homeassistant/components/netatmo_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/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/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 @@ -402,281 +468,210 @@ omit = 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/harmony/remote.py - homeassistant/components/itach/remote.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/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/netatmo_public/sensor.py - homeassistant/components/netdata_public/sensor.py - homeassistant/components/netdata/sensor.py - homeassistant/components/neurio_energy/sensor.py - homeassistant/components/nmbs/sensor.py - homeassistant/components/noaa_tides/sensor.py - homeassistant/components/nsw_fuel_station/sensor.py - homeassistant/components/nut/sensor.py - homeassistant/components/nzbget/sensor.py - homeassistant/components/ohmconnect/sensor.py - homeassistant/components/onewire/sensor.py - homeassistant/components/openevse/sensor.py - homeassistant/components/openexchangerates/sensor.py - homeassistant/components/opensky/sensor.py - homeassistant/components/openweathermap/sensor.py - homeassistant/components/otp/sensor.py - homeassistant/components/pi_hole/sensor.py - homeassistant/components/plex/sensor.py - homeassistant/components/pocketcasts/sensor.py - homeassistant/components/pollen/sensor.py - homeassistant/components/postnl/sensor.py - homeassistant/components/prezzibenzina/sensor.py - homeassistant/components/pushbullet/sensor.py - homeassistant/components/pvoutput/sensor.py - homeassistant/components/pyload/sensor.py - homeassistant/components/qbittorrent/sensor.py - homeassistant/components/qnap/sensor.py - homeassistant/components/radarr/sensor.py - homeassistant/components/rainbird/sensor.py - homeassistant/components/recollect_waste/sensor.py - homeassistant/components/rejseplanen/sensor.py - homeassistant/components/ripple/sensor.py - homeassistant/components/rova/sensor.py - homeassistant/components/rtorrent/sensor.py - homeassistant/components/ruter/sensor.py 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/spotcrime/sensor.py - homeassistant/components/srp_energy/sensor.py - homeassistant/components/starlingbank/sensor.py - homeassistant/components/steam_online/sensor.py - homeassistant/components/supervisord/sensor.py - homeassistant/components/swiss_hydrological_data/sensor.py - homeassistant/components/swiss_public_transport/sensor.py - homeassistant/components/syncthru/sensor.py - homeassistant/components/synologydsm/sensor.py - homeassistant/components/systemmonitor/sensor.py - homeassistant/components/sytadin/sensor.py - homeassistant/components/tank_utility/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/songpal/media_player.py homeassistant/components/sonos/* + homeassistant/components/sony_projector/switch.py 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/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/telnet/switch.py - homeassistant/components/tplink/switch.py - homeassistant/components/vesync/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/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 00000000000..9332b0d1ede --- /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 3f5403f0c13..b2c6b63864f 100644 --- a/homeassistant/components/notify/apns.py +++ b/homeassistant/components/apns/notify.py @@ -15,7 +15,7 @@ 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 ( +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 00000000000..f6d86d02e93 --- /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 8f639a653c3..d7ebb40d19a 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/aws_lambda/notify.py @@ -14,7 +14,8 @@ 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 +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 00000000000..b51698ce0dc --- /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 7fa0e25b32a..09018562cb8 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/aws_sns/notify.py @@ -12,7 +12,7 @@ import voluptuous as vol 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 00000000000..79b29a76009 --- /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 92782429939..eff9018bae9 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/aws_sqs/notify.py @@ -12,7 +12,8 @@ import voluptuous as vol 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 00000000000..f872a0257f7 --- /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 a33e9432e92..1eeb9b51f28 100644 --- a/homeassistant/components/notify/ciscospark.py +++ b/homeassistant/components/ciscospark/notify.py @@ -11,7 +11,8 @@ import voluptuous as vol 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 00000000000..6c39bc749ad --- /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 559232e2555..e473e54a3b7 100644 --- a/homeassistant/components/notify/clickatell.py +++ b/homeassistant/components/clickatell/notify.py @@ -12,7 +12,8 @@ import voluptuous as vol 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 00000000000..3037224b9a1 --- /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 6f10ac70734..3b2cdb7496d 100644 --- a/homeassistant/components/notify/clicksend.py +++ b/homeassistant/components/clicksend/notify.py @@ -16,7 +16,8 @@ from homeassistant.const import ( 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 00000000000..53b59309701 --- /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 2a7730c4a27..93e5126bbab 100644 --- a/homeassistant/components/notify/clicksend_tts.py +++ b/homeassistant/components/clicksend_tts/notify.py @@ -17,7 +17,8 @@ from homeassistant.const import ( 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 6a81dace288..7ea5a6d8880 100644 --- a/homeassistant/components/notify/command_line.py +++ b/homeassistant/components/command_line/notify.py @@ -12,7 +12,8 @@ import voluptuous as vol 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 9cb61609017..5b8e1f1688f 100644 --- a/homeassistant/components/notify/demo.py +++ b/homeassistant/components/demo/notify.py @@ -4,7 +4,7 @@ Demo notification service. 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 00000000000..a3cd87bc895 --- /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 f98c136b3f9..d73382d8bcf 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/discord/notify.py @@ -11,7 +11,9 @@ import voluptuous as vol 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 00000000000..1619b8a91f1 --- /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 b642a7b932b..2c691661a29 100644 --- a/homeassistant/components/notify/facebook.py +++ b/homeassistant/components/facebook/notify.py @@ -14,7 +14,9 @@ import voluptuous as vol 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 98f6da66e3d..d449476469b 100644 --- a/homeassistant/components/notify/file.py +++ b/homeassistant/components/file/notify.py @@ -13,7 +13,7 @@ from homeassistant.const import CONF_FILENAME 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 00000000000..1b58d21cff8 --- /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 16f38bbbb63..b37483d2f13 100644 --- a/homeassistant/components/notify/flock.py +++ b/homeassistant/components/flock/notify.py @@ -14,7 +14,8 @@ 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 +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 00000000000..002a1475fde --- /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 fb7829105fc..1c6804f6d82 100644 --- a/homeassistant/components/notify/free_mobile.py +++ b/homeassistant/components/free_mobile/notify.py @@ -11,7 +11,8 @@ import voluptuous as vol 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 00000000000..c2814f86f06 --- /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 4560eb21f31..915d33668b4 100644 --- a/homeassistant/components/notify/gntp.py +++ b/homeassistant/components/gntp/notify.py @@ -12,7 +12,7 @@ import voluptuous as vol 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 85d4d53e3be..200464bb8cb 100644 --- a/homeassistant/components/notify/group.py +++ b/homeassistant/components/group/notify.py @@ -14,7 +14,7 @@ import voluptuous as vol 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 00000000000..8b79982fa43 --- /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 8ce7b8b120e..9e415171f29 100644 --- a/homeassistant/components/notify/hipchat.py +++ b/homeassistant/components/hipchat/notify.py @@ -11,7 +11,9 @@ import voluptuous as vol 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 00000000000..88e437ef566 --- /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 0e99727e81b..8744d0afb28 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/html5/notify.py @@ -25,7 +25,7 @@ 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 ( +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 2dd33bf8990..7c2a60f3498 100644 --- a/homeassistant/components/notify/kodi.py +++ b/homeassistant/components/kodi/notify.py @@ -15,7 +15,7 @@ from homeassistant.const import ( 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 00000000000..479e9893f84 --- /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 5c975e8422f..3b2e72b398c 100644 --- a/homeassistant/components/notify/lannouncer.py +++ b/homeassistant/components/lannouncer/notify.py @@ -13,7 +13,8 @@ import voluptuous as vol 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 00000000000..f60abfb93c9 --- /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 d3689dbbd81..6b59d11e0a3 100644 --- a/homeassistant/components/notify/llamalab_automate.py +++ b/homeassistant/components/llamalab_automate/notify.py @@ -12,7 +12,8 @@ import voluptuous as vol 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 00000000000..123d23afb80 --- /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 59c787bc026..6192f6cdca5 100644 --- a/homeassistant/components/notify/mastodon.py +++ b/homeassistant/components/mastodon/notify.py @@ -11,7 +11,8 @@ import voluptuous as vol 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 00000000000..ed3828c5eda --- /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 c45d153d813..cfb22ff1d52 100644 --- a/homeassistant/components/notify/message_bird.py +++ b/homeassistant/components/message_bird/notify.py @@ -11,7 +11,8 @@ import voluptuous as vol 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 e9cd44d5d06..a8a401a9c1f 100644 --- a/homeassistant/components/notify/mycroft.py +++ b/homeassistant/components/mycroft/notify.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/notify.mycroft/ """ 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 00000000000..9965265e00d --- /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 4d39083387c..c4003a6312a 100644 --- a/homeassistant/components/notify/nfandroidtv.py +++ b/homeassistant/components/nfandroidtv/notify.py @@ -15,7 +15,7 @@ import voluptuous as vol 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 00000000000..1cf58a25120 --- /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 27ce8d0fb7a..6d911789121 100644 --- a/homeassistant/components/notify/prowl.py +++ b/homeassistant/components/prowl/notify.py @@ -14,7 +14,7 @@ from homeassistant.const import CONF_API_KEY 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 505d6f4e1c1..f0b4ec24da8 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/pushbullet/notify.py @@ -12,7 +12,7 @@ import voluptuous as vol 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 00000000000..f992fecddb7 --- /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 5db67177548..106c0641a69 100644 --- a/homeassistant/components/notify/pushetta.py +++ b/homeassistant/components/pushetta/notify.py @@ -11,7 +11,7 @@ import voluptuous as vol 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 00000000000..921d37ed332 --- /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 372ea361f13..78e9ed11c95 100644 --- a/homeassistant/components/notify/pushover.py +++ b/homeassistant/components/pushover/notify.py @@ -11,7 +11,7 @@ import voluptuous as vol 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 00000000000..81dfc7e15fd --- /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 9a3308ff21e..a1fa2b7409c 100644 --- a/homeassistant/components/notify/pushsafer.py +++ b/homeassistant/components/pushsafer/notify.py @@ -14,7 +14,7 @@ import voluptuous as vol 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 eec2ea4aa37..de75db83848 100644 --- a/homeassistant/components/notify/rest.py +++ b/homeassistant/components/rest/notify.py @@ -15,7 +15,7 @@ from homeassistant.const import ( 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 00000000000..1d2c281f661 --- /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 116c32993d8..8bf1e172264 100644 --- a/homeassistant/components/notify/rocketchat.py +++ b/homeassistant/components/rocketchat/notify.py @@ -12,7 +12,8 @@ from homeassistant.const import ( 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 00000000000..91fff97c150 --- /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 6bab566bfc7..211e288725e 100644 --- a/homeassistant/components/notify/sendgrid.py +++ b/homeassistant/components/sendgrid/notify.py @@ -12,7 +12,7 @@ from homeassistant.const import ( 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 00000000000..8253cfad8b4 --- /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 1d198b5adec..63222d4adc1 100644 --- a/homeassistant/components/notify/simplepush.py +++ b/homeassistant/components/simplepush/notify.py @@ -11,7 +11,7 @@ import voluptuous as vol 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 00000000000..a999c2375ff --- /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 7aee58ed012..eabddf01299 100644 --- a/homeassistant/components/notify/slack.py +++ b/homeassistant/components/slack/notify.py @@ -13,7 +13,7 @@ import voluptuous as vol 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 00000000000..5e7fb41c212 --- /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 995aae76cc6..4104013bcf7 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/smtp/notify.py @@ -21,7 +21,7 @@ from homeassistant.const import ( 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 00000000000..461a3ee744f --- /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 f5f5b52ab67..9d05bd17f34 100644 --- a/homeassistant/components/notify/stride.py +++ b/homeassistant/components/stride/notify.py @@ -11,7 +11,9 @@ import voluptuous as vol 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 00000000000..836eff6ee48 --- /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 023586ae532..32277dc1971 100644 --- a/homeassistant/components/notify/synology_chat.py +++ b/homeassistant/components/synology_chat/notify.py @@ -13,7 +13,8 @@ import voluptuous as vol 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 00000000000..c46e56e76ff --- /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 4f7900b8d45..740148e28e5 100644 --- a/homeassistant/components/notify/syslog.py +++ b/homeassistant/components/syslog/notify.py @@ -8,7 +8,7 @@ import logging 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 00000000000..1aca4e510c6 --- /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 a6975f60fae..428c7e093d2 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/telegram/notify.py @@ -10,7 +10,7 @@ import voluptuous as vol 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 00000000000..87b225b713a --- /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 4826ab77612..a1a28a03b18 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/twilio_call/notify.py @@ -12,7 +12,8 @@ import voluptuous as vol 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 00000000000..3bf3898ac3f --- /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 165e743977d..b3b35ea1789 100644 --- a/homeassistant/components/notify/twilio_sms.py +++ b/homeassistant/components/twilio_sms/notify.py @@ -11,7 +11,8 @@ import voluptuous as vol 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 00000000000..1ecba66a44e --- /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 43d977d26a6..9172da36785 100644 --- a/homeassistant/components/notify/twitter.py +++ b/homeassistant/components/twitter/notify.py @@ -17,7 +17,8 @@ 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 +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 00000000000..40736a4fd36 --- /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 3827674316b..5a14046bd41 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/xmpp/notify.py @@ -19,7 +19,7 @@ from homeassistant.const import ( 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 00000000000..bc5f422ba75 --- /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 19efa2a53d8..529aa4e7b6e 100644 --- a/homeassistant/components/notify/yessssms.py +++ b/homeassistant/components/yessssms/notify.py @@ -11,7 +11,8 @@ import voluptuous as vol 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 664e725db61..4f666d6e215 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 7721c236d99..3cca2d4bf5e 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 00000000000..42c980a62a7 --- /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 9964a58cd24..7303f4872e3 100644 --- a/tests/components/notify/test_apns.py +++ b/tests/components/apns/test_notify.py @@ -8,7 +8,7 @@ import yaml 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 @@ CONFIG = { } -@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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): }} 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 @@ class TestApns(unittest.TestCase): } 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 @@ class TestApns(unittest.TestCase): 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 @@ class TestApns(unittest.TestCase): 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 66aa451d389..522519f2cce 100644 --- a/tests/components/notify/test_command_line.py +++ b/tests/components/command_line/test_notify.py @@ -65,7 +65,7 @@ class TestCommandLine(unittest.TestCase): # 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 4c3f3bf3f73..35cf8abe6bd 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/demo/test_notify.py @@ -7,7 +7,7 @@ import voluptuous as vol 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 @@ class TestNotifyDemo(unittest.TestCase): """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 @@ class TestNotifyDemo(unittest.TestCase): ['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 00000000000..5f52b1f7614 --- /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 a74395d5a5e..ae4698c562f 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 e67ed532604..b6ed6e26aa1 100644 --- a/tests/components/notify/test_file.py +++ b/tests/components/file/test_notify.py @@ -52,9 +52,9 @@ class TestNotifyFile(unittest.TestCase): 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 bbd7c11ffeb..9412e9f95a4 100644 --- a/tests/components/notify/test_group.py +++ b/tests/components/group/test_notify.py @@ -4,7 +4,8 @@ from unittest.mock import MagicMock, patch 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 00000000000..9a021f82a7f --- /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 00000000000..48106286f2e --- /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 e33c297b166..140544bf9ea 100644 --- a/tests/components/notify/test_html5.py +++ b/tests/components/html5/test_notify.py @@ -5,7 +5,7 @@ from aiohttp.hdrs import AUTHORIZATION 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 00000000000..c7f7911950c --- /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 00000000000..a99f7991ff9 --- /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 fa6c5003288..e946efe61e6 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 00000000000..bf8e562009b --- /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 42dd7be1aca..837fee43f05 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 @@ class TestNotifyYesssSMS(unittest.TestCase): # 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 @@ class TestNotifyYesssSMS(unittest.TestCase): # 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 @@ class TestNotifyYesssSMS(unittest.TestCase): 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 234/290] 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 e36ad5451c1..7f0d50f93d4 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 235/290] 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 54e28d2d17e..1e53e607360 100644 --- a/homeassistant/components/media_player/const.py +++ b/homeassistant/components/media_player/const.py @@ -38,6 +38,7 @@ MEDIA_TYPE_CHANNEL = 'channel' 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 e2f0004f80e..80c1fda52de 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -12,7 +12,7 @@ import voluptuous as vol 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 @@ class PS4Device(MediaPlayerDevice): @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 236/290] 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 5a10b60f12f..80b5b744488 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 4f666d6e215..4af334dd01e 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 3cca2d4bf5e..60d9697ed19 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 237/290] 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 00000000000..866c06316f1 --- /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 a2d132d9073..5e7b1835093 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 00000000000..5e98dbf3418 --- /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 00000000000..4657d2fb355 --- /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 00000000000..c979068b922 --- /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 00000000000..cbf055e2fba --- /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 00000000000..f1543afbae8 --- /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 00000000000..e0f6ebc1553 --- /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 00000000000..7903dc63bf8 --- /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 00000000000..f303aa947ea --- /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 00000000000..41d29949873 --- /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 00000000000..ac9f3ceb2b6 --- /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 cbca935f551..f433a6215b8 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 0549aa3b160..ce1f1ab3caa 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 06211f61bf2..5bf8db46841 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 c92f1562157..5fd31ab9d8f 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 cea6f8ef4dd..686bb5b1e2e 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 00000000000..c43ca27b22a --- /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 c38e6890d8a..9b6f7706253 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 5ff0466c9bc..88d8a822696 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 1e72bd8030c..c665637ba05 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 2b631ea219c..9777a920a94 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 7745c455015..93ca607aabe 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 0a8211be449..46790868aba 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 65817470860..9a5821f0b8f 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 91d3ac74994..f99efa91c61 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 6d93ec0d18f..143d6bc88ba 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 00000000000..3451053eb07 --- /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 00000000000..afee189216d --- /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 e1aec6162f4..dde9345b2d2 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 4d534eace7d..e65286b5c64 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 b6e2ccce8ed..ce71fb670be 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 00000000000..6c5654b6388 --- /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 967671de535..d6c831d5a0e 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 00000000000..e95f4743ae3 --- /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 00000000000..6236421ffce --- /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 0fcd02a6961..fff9f51f50d 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 2aa7ea03db7..d2f95685634 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 60c1d62ab91..d2f3f90cb77 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 16d1bddbaf7..e171aedf1ce 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 350b65ca815..5e4b572ab45 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 8f4e8838673..e2eadb1fe30 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 ca77537e4e1..ba864f07320 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 15b90cb6b6b..5c5847f28c0 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 41232ddc2d4..424d0964729 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 00000000000..429a409fb7e --- /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 8c975e8170c..118226354af 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 0f5b6b71126..d95ec9eaa1b 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 e7171ca2867..f3d8308fabf 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 00000000000..2d48745436b --- /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 e274d8c9394..506eb98cc95 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 00000000000..2326c394cc0 --- /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 4ed9ef597f4..c2b00561858 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 6057d7b3212..cd219be04e1 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 80dff6dc88a..3b34e048b11 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 4b1afd548e8..bcf36cafda0 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 00000000000..18c1a739e5a --- /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 00000000000..e686ee4bc04 --- /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 00000000000..ca3ac913375 --- /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 c42ca6b7b2b..352579f810c 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 6f78beaffd6..b927e652ba7 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 381f4831c53..c061ab36e7b 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 c8796536e2f..80ed9eb2fa5 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 91e4f6b7c52..632d5112f1a 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 7d3827e76da..29dab5e09da 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 6a7c43f9e46..668b9a377fc 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 82489ac258e..f0da251f5eb 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 ce458fa32f1..2c81c603186 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 b6856e4590a..a64b4db185d 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 238/290] 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 cdd8a844389..e453cdfd1a1 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -20,7 +20,7 @@ import voluptuous as vol 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 @@ from homeassistant.components.media_player.const import ( 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 @@ CAMERA_SERVICE_PLAY_STREAM = CAMERA_SERVICE_SCHEMA.extend({ 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 @@ async def async_setup(hass, config): 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 575f1fe76f7..45a0f4cfec0 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 a68f1c47dbf..1e8ae5d60e3 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -10,15 +10,19 @@ import threading 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 @@ CONFIG_SCHEMA = vol.Schema({ 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 @@ async def async_setup(hass, config): 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 @@ class Stream: 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 @@ class Stream: 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 a87daaa9d40..9421faaff9a 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 59c0a6b650f..745c334fce0 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 @@ class StreamOutput: # 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 @@ class StreamOutput: # 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 @@ class StreamOutput: 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 8f5dd6c1884..aa5ce105764 100644 --- a/homeassistant/components/stream/hls.py +++ b/homeassistant/components/stream/hls.py @@ -110,6 +110,11 @@ class M3U8Renderer: 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 00000000000..15e2108c82a --- /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 d0196761968..3ca8ac079e3 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 701a3682830..e730f39656e 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 00000000000..7f68bf1e7bf --- /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 00000000000..4e227e463b4 --- /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 239/290] 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 d0bc45c326a..e8161a2bfb6 100644 --- a/homeassistant/auth/providers/trusted_networks.py +++ b/homeassistant/auth/providers/trusted_networks.py @@ -18,8 +18,26 @@ from ..models import Credentials, UserMeta 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 @@ class TrustedNetworksAuthProvider(AuthProvider): 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 @@ class TrustedNetworksLoginFlow(LoginFlow): 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 3a51cf8066f..c2f03341d20 100644 --- a/homeassistant/components/auth/login_flow.py +++ b/homeassistant/components/auth/login_flow.py @@ -81,7 +81,8 @@ from . import indieauth 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 @@ class LoginFlowIndexView(HomeAssistantView): 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 4bba80aa154..6513f9368b0 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -8,6 +8,7 @@ from datetime import (timedelta, datetime as datetime_sys, 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 57e74e750d5..9468799095c 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 d83d32c88e3..4a883fbf2fd 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -4,6 +4,7 @@ import enum 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 240/290] 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 2d2f00f1e16..5194c221bed 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 3a406f5677c2e88e899193e860733a6195dfda2a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 23:49:10 -0700 Subject: [PATCH 241/290] 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 45a0f4cfec0..a3e42300cbd 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 242/290] 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 5194c221bed..eacd1812485 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 9aa5b904c6bf73e30be791a477f573b2874afe5f Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Fri, 29 Mar 2019 21:41:13 +0100 Subject: [PATCH 243/290] 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 e98655f9d76..9b113170f8a 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -61,7 +61,7 @@ SERVICE_SCHEMA_REMOVE_DEVICE = vol.Schema({ }) -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 @@ GATEWAY_CONFIG_MAC_OPT = vol.Schema({ 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 @@ CONFIG_SCHEMA = vol.Schema({ 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 244/290] 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 f9fd0409c9c..44af8bffe26 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 homeassistant.helpers.entity import Entity 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 7f0d50f93d4..8ccbcaa33c4 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 163261a4b81..6da3293d597 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 324db971583..32532761ccf 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -192,7 +192,7 @@ async def test_remove_entry(hass, manager): 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 245/290] 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 acb277c0ef5..1476363c6bd 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -15,7 +15,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send 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 4af334dd01e..04c5f93ceb0 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 246/290] Fixes for yeelight availbility state (#22502) --- homeassistant/components/yeelight/__init__.py | 28 +++++-- homeassistant/components/yeelight/light.py | 81 ++++++++----------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 14b4656c403..fb218a67698 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -212,6 +212,7 @@ class YeelightDevice: 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 @@ class YeelightDevice: # 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 @@ class YeelightDevice: """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 @@ class YeelightDevice: 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 @@ class YeelightDevice: 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 cc3810c4968..92b668c6987 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -162,7 +162,6 @@ class YeelightLight(Light): self._device = device self._supported_features = SUPPORT_YEELIGHT - self._available = False self._brightness = None self._color_temp = None @@ -196,7 +195,7 @@ class YeelightLight(Light): @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 @@ class YeelightLight(Light): # 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 @@ class YeelightLight(Light): 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 247/290] 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 302910c5b08..6dd9f90197a 100644 --- a/homeassistant/scripts/credstash.py +++ b/homeassistant/scripts/credstash.py @@ -4,7 +4,7 @@ import getpass 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 a88aabe1936..7d349033f70 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 04c5f93ceb0..a43a04240ba 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 248/290] 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 6dd9f90197a..e2950f8d7a0 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 249/290] 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 8b2cd349b9d..6ba4efa9b0f 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -11,6 +11,7 @@ from homeassistant.components import light 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 @@ class Light(ZhaEntity, light.Light): 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 @@ class Light(ZhaEntity, light.Light): 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 @@ class Light(ZhaEntity, light.Light): 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 250/290] 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 d5833ae1673..9183bbe1989 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -14,7 +14,7 @@ from .const import DOMAIN # noqa: pylint: disable=unused-import _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 a43a04240ba..4468c87fde3 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 60d9697ed19..965faa6eb5c 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 251/290] 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 d8a3918889d..10370c42c66 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -15,7 +15,7 @@ from homeassistant.core import callback 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 @@ class ZigbeeChannel: 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 0ddb67484c6..435ab25acc6 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -49,7 +49,7 @@ class ZHADevice: 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 @@ class ZHADevice: @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 @@ class ZHADevice: 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 4f1e24aad5b..71e41c2509b 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -266,6 +266,11 @@ class ZHAGateway: 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 252/290] 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 00000000000..0fab4af43e6 --- /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 12383df115a..167cd9cfc78 100644 --- a/homeassistant/components/amazon_polly/tts.py +++ b/homeassistant/components/amazon_polly/tts.py @@ -8,10 +8,9 @@ import logging 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 00000000000..8a332cf52e1 --- /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 e7a1f368f1d..07b69d41dfd 100644 --- a/homeassistant/components/baidu/tts.py +++ b/homeassistant/components/baidu/tts.py @@ -9,11 +9,10 @@ import logging 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 00000000000..ec85cb6d4ab --- /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 8f6a46b0c3e..f5d19c977a4 100644 --- a/homeassistant/components/marytts/tts.py +++ b/homeassistant/components/marytts/tts.py @@ -12,12 +12,11 @@ import aiohttp 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 00000000000..2d281cd2bd8 --- /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 ab9fb576c28..55cf7a4ae7a 100644 --- a/homeassistant/components/microsoft/tts.py +++ b/homeassistant/components/microsoft/tts.py @@ -9,11 +9,10 @@ import logging 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 00000000000..7ffc80db2f9 --- /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 99d3b5e9786..c164e7fb85d 100644 --- a/homeassistant/components/picotts/tts.py +++ b/homeassistant/components/picotts/tts.py @@ -12,7 +12,7 @@ import tempfile 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 00000000000..4894ca30bbd --- /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 20e0ee11db3..436f070e503 100644 --- a/homeassistant/components/voicerss/tts.py +++ b/homeassistant/components/voicerss/tts.py @@ -11,12 +11,11 @@ import aiohttp 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 00000000000..86ac9b58f73 --- /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 281839a2d74..e60b890e84f 100644 --- a/homeassistant/components/yandextts/tts.py +++ b/homeassistant/components/yandextts/tts.py @@ -11,12 +11,11 @@ import aiohttp 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 4468c87fde3..b63c60cffe4 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 253/290] 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 9cf42bfec60..71195d66c69 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -32,7 +32,7 @@ from homeassistant.helpers.typing import HomeAssistantType 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 ce72eff2ba8..5f4abcb24c7 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -23,7 +23,7 @@ from .const import DOMAIN 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 b63c60cffe4..4cfd687db99 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 254/290] 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 adc092dcbe1..292b4fde61f 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -27,11 +27,11 @@ from .core.channels.registry import populate_channel_registry 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 4cfd687db99..237077e908b 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 965faa6eb5c..4af0b078c0b 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 255/290] 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 eacd1812485..2fb2dc5450a 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 65c47824a0863cc74f75ca20553871d9c775132e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 16:46:15 -0700 Subject: [PATCH 256/290] 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 80b5b744488..3baea2008b1 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 237077e908b..c0b458119e2 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 4af0b078c0b..003c9fa43cb 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 f9f100b57590aa9058610ef4d8586ca7a1c21af4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 17:03:02 -0700 Subject: [PATCH 257/290] 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 dbb51bf27c7..d897293124b 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -9,7 +9,8 @@ import logging 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 @@ class FFmpegCamera(Camera): 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 258/290] 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 8ccbcaa33c4..4ca19935206 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -37,6 +37,7 @@ DATA_KEY = 'components' 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 259/290] 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 2fb2dc5450a..48476c4fa90 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 ec9a58442b6a5324a36a9ad5e1245567e7505112 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 19:52:44 -0700 Subject: [PATCH 260/290] 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 3baea2008b1..f0358dbd6cc 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from homeassistant.loader import bind_hass 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 c0b458119e2..09a447b2a20 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 003c9fa43cb..27d96c5e606 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 261/290] 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 543404dd34e..852ea2469a2 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -29,6 +29,7 @@ TYPE_SCENE = PREFIX_TYPES + 'SCENE' 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 88cbea345b1..d84c8037c60 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -31,7 +31,7 @@ from homeassistant.components import ( 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 @@ _LOGGER = logging.getLogger(__name__) 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 bd903575762..81918ff2e88 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -48,6 +48,7 @@ TRAIT_TEMPERATURE_SETTING = PREFIX_TRAITS + 'TemperatureSetting' 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_THERMOSTAT_SET_MODE = PREFIX_COMMANDS + 'ThermostatSetMode' 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 @@ class BrightnessTrait(_Trait): """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 @@ class BrightnessTrait(_Trait): 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 @@ class BrightnessTrait(_Trait): 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 @@ class OnOffTrait(_Trait): switch.DOMAIN, fan.DOMAIN, light.DOMAIN, - cover.DOMAIN, media_player.DOMAIN, ) @@ -264,22 +252,13 @@ class OnOffTrait(_Trait): 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 @@ class ModesTrait(_Trait): 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 a8ea4a3f888..331c6d2d9f5 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 @@ DEMO_DEVICES = [{ '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 @@ DEMO_DEVICES = [{ '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 @@ DEMO_DEVICES = [{ '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 e42e4bdc915..a0a710d3d8c 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 262/290] 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 35d5e18fdd3..63c2c720781 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 @@ class AmcrestCam(Camera): """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 263/290] 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 852a5adf6a2..6d2304433ab 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 @@ async def test_get_entries(hass, client): 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 264/290] 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 36f1b18eebf..09d47c3c0c9 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -13,7 +13,8 @@ import voluptuous as vol 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 @@ class ONVIFHassCamera(Camera): 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 @@ class ONVIFHassCamera(Camera): 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 @@ class ONVIFHassCamera(Camera): 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 265/290] 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 888a4d51c95..6743893888d 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 @@ DEPENDENCIES = ( '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 fbb28c401ea459b55a94fac35cc5aa2aaae50b73 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 20:30:30 -0700 Subject: [PATCH 266/290] 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 48476c4fa90..ea4b51e4bd9 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 5cb69cf1631b4145da68c13df0234a60c0ddb9d7 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 17:43:29 -0700 Subject: [PATCH 267/290] 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 4736ef12391..7b8508894ce 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -190,6 +190,12 @@ def setup_auth(hass, app): 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 268/290] 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 | 80 +++----- tests/components/hassio/test_ingress.py | 162 +++++++++++++++ tests/components/hassio/test_init.py | 2 +- tests/test_util/aiohttp.py | 2 +- 8 files changed, 458 insertions(+), 88 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 073974200a0..e8d04b1596d 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -20,6 +20,7 @@ from .auth import async_setup_auth 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_setup(hass, config): # 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 964f94bfb41..e4132562c31 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -9,6 +9,7 @@ ATTR_UUID = 'uuid' 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 01ded9ca11d..7284004d72f 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -3,10 +3,11 @@ import asyncio 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 @@ _LOGGER = logging.getLogger(__name__) 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 @@ class HassIOView(HomeAssistantView): 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) - return client + # 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 + + 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 @@ class HassIOView(HomeAssistantView): 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 00000000000..6c1ef389712 --- /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 3f58c6e697e..3a58048735b 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') + aioclient_mock.post("http://127.0.0.1/beer", 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.post('/api/hassio/beer', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) + 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') + aioclient_mock.get( + "http://127.0.0.1/{}".format(build_type), 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/{}'.format(build_type)) + 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') + aioclient_mock.get( + "http://127.0.0.1/beer/logs", text="\033[32mresponse\033[0m") - 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 - }) + 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 00000000000..4e071ba24fd --- /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 fc4661e7544..f1f148f8495 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 8b3b057bfc0..ab759f03058 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -102,7 +102,7 @@ class AiohttpClientMocker: 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 269/290] 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 ceec57f7755..a11d2f48f62 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -8,7 +8,8 @@ import logging 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 @@ class FoscamCam(Camera): 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 @@ class FoscamCam(Camera): 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 270/290] 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 cb9ea5ff5f9..fea756395e4 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 PLATFORM_SCHEMA # noqa 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 d8c3f5f621f..6611fb0edfb 100644 --- a/homeassistant/components/dlib_face_identify/image_processing.py +++ b/homeassistant/components/dlib_face_identify/image_processing.py @@ -15,7 +15,7 @@ from homeassistant.components.image_processing import ( 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 09a447b2a20..057790cbc6a 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 271/290] 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 fece48655df..67b5341a4e6 100644 --- a/homeassistant/components/zwave/const.py +++ b/homeassistant/components/zwave/const.py @@ -29,7 +29,7 @@ CONF_USB_STICK_PATH = 'usb_path' 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 272/290] 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 135437801d9..19cf6fe6525 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_ACCESS_TOKEN, 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 057790cbc6a..a773c9c8c31 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 273/290] 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 6c1ef389712..04241f53de9 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -71,9 +71,14 @@ class HassIOIngress(HomeAssistantView): 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 4e071ba24fd..4b72eda4c25 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 274/290] 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 a11d2f48f62..b6f2162d57a 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -58,7 +58,10 @@ class FoscamCam(Camera): 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 @@ class FoscamCam(Camera): @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 275/290] 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 44af8bffe26..2a43d0ac9ce 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -191,8 +191,7 @@ def setup(hass, config): 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 d875b91eb2c..2ca568b547f 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -7,7 +7,8 @@ from homeassistant.helpers import discovery 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 @@ class HKDevice(): 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 276/290] 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 ea4b51e4bd9..4091bc2ed02 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 51c7cbc6b93a8bb2ed2acdba84897e15af01c3a2 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 3 Apr 2019 05:21:25 -0700 Subject: [PATCH 277/290] 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 ecbe8d70847..a4ae78959cf 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 3aa4626da29..61c50e97c6e 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -40,6 +40,8 @@ ATTR_MANUFACTURER = 'manufacturer' 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 00000000000..0120b1a6ffb --- /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 00000000000..395dee6c117 --- /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 278/290] 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 cabb5b46ece..38849fb37b3 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 @@ class DeconzFlowHandler(config_entries.ConfigFlow): 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 e728430f202..b26fddd9147 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -6,9 +6,12 @@ _LOGGER = logging.getLogger('.') 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 1bf7235713a..d0ae34e7c7a 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 804247d2407..09a98edc148 100644 --- a/homeassistant/components/hassio/discovery.py +++ b/homeassistant/components/hassio/discovery.py @@ -81,7 +81,7 @@ class HassIODiscovery(HomeAssistantView): 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 @@ class HassIODiscovery(HomeAssistantView): 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 20c74a82883..863e4e93fc5 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 279/290] 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 58ce49a9b02..60f1901d43e 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -39,6 +39,7 @@ class OpenThermGateway(ClimateDevice): 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 @@ class OpenThermGateway(ClimateDevice): 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 @@ class OpenThermGateway(ClimateDevice): @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 @@ class OpenThermGateway(ClimateDevice): """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 280/290] 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 | 43 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index dd1514f5e43..2c151634a95 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -118,7 +118,8 @@ class UtilityMeterSensor(RestoreEntity): 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 ee291439a2c..6b8705bf776 100644 --- a/tests/components/utility_meter/test_sensor.py +++ b/tests/components/utility_meter/test_sensor.py @@ -6,10 +6,11 @@ from unittest.mock import patch 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 281/290] 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 7b8508894ce..0d8e327e086 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -190,12 +190,16 @@ def setup_auth(hass, app): 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 d68cabebacf..8d5e0ee88b1 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -98,6 +98,8 @@ def request_handler_factory(view, handler): 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 282/290] 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 81918ff2e88..de3a9530b50 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -27,6 +27,7 @@ from homeassistant.const import ( 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 @@ class OpenCloseTrait(_Trait): 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 a0a710d3d8c..81a7fbe1bf7 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -21,7 +21,8 @@ from homeassistant.components.climate import const as climate 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, { + }), 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 283/290] 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 84dc0b5bb01..9f43941505b 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -26,6 +26,7 @@ DEFAULT_NAME = 'Amcrest Camera' DEFAULT_PORT = 80 DEFAULT_RESOLUTION = 'high' DEFAULT_STREAM_SOURCE = 'snapshot' +DEFAULT_ARGUMENTS = '-pred 1' TIMEOUT = 10 DATA_AMCREST = 'amcrest' @@ -77,7 +78,8 @@ CONFIG_SCHEMA = vol.Schema({ 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 95d11318bf7..d4b00f00625 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -13,6 +13,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect 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_UNSEEN_VIDEOS = 'unseen_videos' 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 @@ POWERSAVE_MODE_MAPPING = { } 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 63c27d31d93..c3a3af32450 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -18,16 +18,17 @@ from homeassistant.util import Throttle 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 d897293124b..07e56cfd51f 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -20,11 +20,13 @@ from . import CONF_EXTRA_ARGUMENTS, CONF_INPUT, DATA_FFMPEG _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 09d47c3c0c9..a196f87cd10 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -33,7 +33,7 @@ DEFAULT_NAME = 'ONVIF Camera' 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 d8cd59129ab..b0acf50ec8c 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -23,6 +23,7 @@ DEFAULT_BRAND = 'Xiaomi Home Camera' 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 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 c60d4971fb8..f82c8c38129 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -26,6 +26,7 @@ DEFAULT_PASSWORD = '' DEFAULT_PATH = '/tmp/sd/record' DEFAULT_PORT = 21 DEFAULT_USERNAME = 'root' +DEFAULT_ARGUMENTS = '-pred 1' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' @@ -36,7 +37,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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 284/290] 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 00000000000..b6a57a28381 --- /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 91b8d024aed..b486032c741 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 285/290] 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 b6a57a28381..112ce2284dd 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 286/290] 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 112ce2284dd..f9eb28bdf4a 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 +commands: + + docker-prereqs: + description: Set up docker prerequisite requirement steps: - - checkout + - 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 - - 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 - - # 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 + pip install -U pip + pip install --progress-bar off flake8 + flake8 + + - install + - run: + name: run gen_requirements_all command: | . venv/bin/activate python script/gen_requirements_all.py validate - flake8 - pylint homeassistant + + 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 287/290] 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 f9eb28bdf4a..b4f22601bb5 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 f231d6c5514..941a463fb37 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 00000000000..7c73d12e381 --- /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 5bf9fe1e121..52045e41d60 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 8423141df60..b8995d9e877 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 288/290] 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 b4f22601bb5..9c9d75d934b 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 289/290] 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 9c9d75d934b..c8d7c7ee6b2 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 290/290] 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 4091bc2ed02..36c61a51916 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)