From 3b934166a5300c1ac0cc67fe3b87e6542937d02a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 23 Oct 2019 13:37:01 -0700 Subject: [PATCH 01/29] Bumped version to 0.101.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index cac0386b812..770669b2d82 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0.dev0" +PATCH_VERSION = "0b0" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 059d2572a28446acc8fd87237d61a9ee0dc314b7 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Thu, 24 Oct 2019 12:23:02 -0400 Subject: [PATCH 02/29] Fixes/zha ieee tail (#28160) * Fix ZHA entity_id assignment. * Update tests. --- homeassistant/components/zha/entity.py | 2 +- tests/components/zha/common.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index 00c3942358e..c11cd405a99 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -40,7 +40,7 @@ class ZhaEntity(RestoreEntity, LogMixin, entity.Entity): self._unique_id = unique_id if not skip_entity_id: ieee = zha_device.ieee - ieeetail = "".join(["%02x" % (o,) for o in ieee[-4:]]) + ieeetail = "".join([f"{o:02x}" for o in ieee[:4]]) self.entity_id = "{}.{}_{}_{}_{}{}".format( self._domain, slugify(zha_device.manufacturer), diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index 5f9172749b0..788faaaec73 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -168,7 +168,7 @@ def make_entity_id(domain, device, cluster, use_suffix=True): machine so that we can test state changes. """ ieee = device.ieee - ieeetail = "".join(["%02x" % (o,) for o in ieee[-4:]]) + ieeetail = "".join([f"{o:02x}" for o in ieee[:4]]) entity_id = "{}.{}_{}_{}_{}{}".format( domain, slugify(device.manufacturer), From 8f232f3c692d1042c45ff24faca566bfe519245a Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 24 Oct 2019 20:24:46 +0200 Subject: [PATCH 03/29] Bump aioesphomeapi to 2.4.1 (#28170) * Bump aioesphomeapi to 2.4.1 * Update requirements * Bump to 2.4.2 --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index b2286b8ab67..40691c653f5 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", "requirements": [ - "aioesphomeapi==2.4.0" + "aioesphomeapi==2.4.2" ], "dependencies": [], "zeroconf": ["_esphomelib._tcp.local."], diff --git a/requirements_all.txt b/requirements_all.txt index eacaee7d927..6a6c569b4a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -139,7 +139,7 @@ aiobotocore==0.10.2 aiodns==2.0.0 # homeassistant.components.esphome -aioesphomeapi==2.4.0 +aioesphomeapi==2.4.2 # homeassistant.components.freebox aiofreepybox==0.0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 39903b3606a..51b7ae9d71f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -70,7 +70,7 @@ aioautomatic==0.6.5 aiobotocore==0.10.2 # homeassistant.components.esphome -aioesphomeapi==2.4.0 +aioesphomeapi==2.4.2 # homeassistant.components.emulated_hue # homeassistant.components.http From c64fe19260bedc1eba4e0bae5f8144223d57c34e Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 24 Oct 2019 22:36:47 +0200 Subject: [PATCH 04/29] Fix ESPHome stacktraces when removing entity and shutting down (#28185) --- homeassistant/components/esphome/__init__.py | 20 +++++++++++++++++-- .../components/esphome/entry_data.py | 7 +++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index dd4ac699089..a669726ca38 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -95,8 +95,11 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool """Cleanup the socket client on HA stop.""" await _cleanup_instance(hass, entry) + # Use async_listen instead of async_listen_once so that we don't deregister + # the callback twice when shutting down Home Assistant. + # "Unable to remove unknown listener .onetime_listener>" entry_data.cleanup_callbacks.append( - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_stop) + hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, on_stop) ) @callback @@ -365,6 +368,7 @@ async def platform_async_setup_entry( """ entry_data: RuntimeEntryData = hass.data[DOMAIN][entry.entry_id] entry_data.info[component_key] = {} + entry_data.old_info[component_key] = {} entry_data.state[component_key] = {} @callback @@ -390,7 +394,13 @@ async def platform_async_setup_entry( # Remove old entities for info in old_infos.values(): entry_data.async_remove_entity(hass, component_key, info.key) + + # First copy the now-old info into the backup object + entry_data.old_info[component_key] = entry_data.info[component_key] + # Then update the actual info entry_data.info[component_key] = new_infos + + # Add entities to Home Assistant async_add_entities(add_entities) signal = DISPATCHER_ON_LIST.format(entry_id=entry.entry_id) @@ -524,7 +534,13 @@ class EsphomeEntity(Entity): @property def _static_info(self) -> EntityInfo: - return self._entry_data.info[self._component_key][self._key] + # Check if value is in info database. Use a single lookup. + info = self._entry_data.info[self._component_key].get(self._key) + if info is not None: + return info + # This entity is in the removal project and has been removed from .info + # already, look in old_info + return self._entry_data.old_info[self._component_key].get(self._key) @property def _device_info(self) -> DeviceInfo: diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index b7f9ad9b347..d916e1a90c8 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -56,6 +56,13 @@ class RuntimeEntryData: reconnect_task = attr.ib(type=Optional[asyncio.Task], default=None) state = attr.ib(type=Dict[str, Dict[str, Any]], factory=dict) info = attr.ib(type=Dict[str, Dict[str, Any]], factory=dict) + + # A second list of EntityInfo objects + # This is necessary for when an entity is being removed. HA requires + # some static info to be accessible during removal (unique_id, maybe others) + # If an entity can't find anything in the info array, it will look for info here. + old_info = attr.ib(type=Dict[str, Dict[str, Any]], factory=dict) + services = attr.ib(type=Dict[int, "UserService"], factory=dict) available = attr.ib(type=bool, default=False) device_info = attr.ib(type=DeviceInfo, default=None) From 0a5cde7ac3b33a9e20dfdbd8a1e6a80f7ed54ec2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 24 Oct 2019 13:53:30 -0700 Subject: [PATCH 05/29] Bumped version to 0.101.0b1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 770669b2d82..ce00e38bc42 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0b0" +PATCH_VERSION = "0b1" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 05ee15c28c90c22e919f3f621fa198e4ff14a436 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Fri, 25 Oct 2019 11:37:50 -0500 Subject: [PATCH 06/29] Update Plex via websockets (#28158) * Save client identifier from auth for future use * Use websocket events to update Plex * Handle websocket disconnections * Use aiohttp, shut down socket cleanly * Bad rebase fix * Don't connect websocket during config_flow validation, fix tests * Move websocket handling to external library * Close websocket session on HA stop * Use external library, revert unnecessary test change * Async & lint fixes * Clean up websocket stopper on entry unload * Setup websocket in component, pass actual needed object to library --- .coveragerc | 1 + homeassistant/components/plex/__init__.py | 45 ++++++++++++++++----- homeassistant/components/plex/const.py | 3 +- homeassistant/components/plex/manifest.json | 3 +- homeassistant/components/plex/server.py | 7 ++++ requirements_all.txt | 3 ++ requirements_test_all.txt | 3 ++ 7 files changed, 52 insertions(+), 13 deletions(-) diff --git a/.coveragerc b/.coveragerc index f97a7524a21..748ca511dd5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -514,6 +514,7 @@ omit = homeassistant/components/plex/media_player.py homeassistant/components/plex/sensor.py homeassistant/components/plex/server.py + homeassistant/components/plex/websockets.py homeassistant/components/plugwise/* homeassistant/components/plum_lightpad/* homeassistant/components/pocketcasts/sensor.py diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index b6ed3245115..1aaa8a8e3aa 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -1,9 +1,9 @@ """Support to embed Plex.""" import asyncio -from datetime import timedelta import logging import plexapi.exceptions +from plexwebsocket import PlexWebsocket import requests.exceptions import voluptuous as vol @@ -16,9 +16,14 @@ from homeassistant.const import ( CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL, + EVENT_HOMEASSISTANT_STOP, ) from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from .const import ( CONF_USE_EPISODE_ART, @@ -33,8 +38,9 @@ from .const import ( PLATFORMS, PLEX_MEDIA_PLAYER_OPTIONS, PLEX_SERVER_CONFIG, - REFRESH_LISTENERS, + PLEX_UPDATE_PLATFORMS_SIGNAL, SERVERS, + WEBSOCKETS, ) from .server import PlexServer @@ -67,9 +73,7 @@ _LOGGER = logging.getLogger(__package__) def setup(hass, config): """Set up the Plex component.""" - hass.data.setdefault( - PLEX_DOMAIN, {SERVERS: {}, REFRESH_LISTENERS: {}, DISPATCHERS: {}} - ) + hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}, DISPATCHERS: {}, WEBSOCKETS: {}}) plex_config = config.get(PLEX_DOMAIN, {}) if plex_config: @@ -136,7 +140,6 @@ async def async_setup_entry(hass, entry): ) server_id = plex_server.machine_identifier hass.data[PLEX_DOMAIN][SERVERS][server_id] = plex_server - hass.data[PLEX_DOMAIN][DISPATCHERS][server_id] = [] for platform in PLATFORMS: hass.async_create_task( @@ -145,9 +148,29 @@ async def async_setup_entry(hass, entry): entry.add_update_listener(async_options_updated) - hass.data[PLEX_DOMAIN][REFRESH_LISTENERS][server_id] = async_track_time_interval( - hass, lambda now: plex_server.update_platforms(), timedelta(seconds=10) + unsub = async_dispatcher_connect( + hass, + PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id), + plex_server.update_platforms, ) + hass.data[PLEX_DOMAIN][DISPATCHERS].setdefault(server_id, []) + hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub) + + def update_plex(): + async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + + session = async_get_clientsession(hass) + websocket = PlexWebsocket(plex_server.plex_server, update_plex, session) + hass.loop.create_task(websocket.listen()) + hass.data[PLEX_DOMAIN][WEBSOCKETS][server_id] = websocket + + def close_websocket_session(_): + websocket.close() + + unsub = hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, close_websocket_session + ) + hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub) return True @@ -156,8 +179,8 @@ async def async_unload_entry(hass, entry): """Unload a config entry.""" server_id = entry.data[CONF_SERVER_IDENTIFIER] - cancel = hass.data[PLEX_DOMAIN][REFRESH_LISTENERS].pop(server_id) - cancel() + websocket = hass.data[PLEX_DOMAIN][WEBSOCKETS].pop(server_id) + websocket.close() dispatchers = hass.data[PLEX_DOMAIN][DISPATCHERS].pop(server_id) for unsub in dispatchers: diff --git a/homeassistant/components/plex/const.py b/homeassistant/components/plex/const.py index 0d512101e11..d3c79e60bc4 100644 --- a/homeassistant/components/plex/const.py +++ b/homeassistant/components/plex/const.py @@ -10,8 +10,8 @@ DEFAULT_VERIFY_SSL = True DISPATCHERS = "dispatchers" PLATFORMS = ["media_player", "sensor"] -REFRESH_LISTENERS = "refresh_listeners" SERVERS = "servers" +WEBSOCKETS = "websockets" PLEX_CONFIG_FILE = "plex.conf" PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options" @@ -19,6 +19,7 @@ PLEX_SERVER_CONFIG = "server_config" PLEX_NEW_MP_SIGNAL = "plex_new_mp_signal.{}" PLEX_UPDATE_MEDIA_PLAYER_SIGNAL = "plex_update_mp_signal.{}" +PLEX_UPDATE_PLATFORMS_SIGNAL = "plex_update_platforms_signal.{}" PLEX_UPDATE_SENSOR_SIGNAL = "plex_update_sensor_signal.{}" CONF_CLIENT_IDENTIFIER = "client_id" diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index 3c570a0e64c..90ae305148e 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -5,7 +5,8 @@ "documentation": "https://www.home-assistant.io/integrations/plex", "requirements": [ "plexapi==3.0.6", - "plexauth==0.0.5" + "plexauth==0.0.5", + "plexwebsocket==0.0.1" ], "dependencies": [ "http" diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index c0461ee0f54..e6f77a310f1 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -103,6 +103,8 @@ class PlexServer: def update_platforms(self): """Update the platform entities.""" + _LOGGER.debug("Updating devices") + available_clients = {} new_clients = set() @@ -164,6 +166,11 @@ class PlexServer: sessions, ) + @property + def plex_server(self): + """Return the plexapi PlexServer instance.""" + return self._plex_server + @property def friendly_name(self): """Return name of connected Plex server.""" diff --git a/requirements_all.txt b/requirements_all.txt index 6a6c569b4a1..8f5e83a8e67 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -973,6 +973,9 @@ plexapi==3.0.6 # homeassistant.components.plex plexauth==0.0.5 +# homeassistant.components.plex +plexwebsocket==0.0.1 + # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 51b7ae9d71f..0af9b338987 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -345,6 +345,9 @@ plexapi==3.0.6 # homeassistant.components.plex plexauth==0.0.5 +# homeassistant.components.plex +plexwebsocket==0.0.1 + # homeassistant.components.mhz19 # homeassistant.components.serial_pm pmsensor==0.4 From 4df6b3c76a793495f1425395e215dfdec17c6fbd Mon Sep 17 00:00:00 2001 From: SukramJ Date: Fri, 25 Oct 2019 01:41:13 +0200 Subject: [PATCH 07/29] Partially revert tensorflow import move (#28184) * Revert "Refactor imports for tensorflow (#27617)" This reverts commit 5a83a92390e8a3255885198c80622556f886b9b3. * move only some imports to top * fix lint * add comments --- .../components/tensorflow/image_processing.py | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/tensorflow/image_processing.py b/homeassistant/components/tensorflow/image_processing.py index 1f49888cb95..ea73d52fe4a 100644 --- a/homeassistant/components/tensorflow/image_processing.py +++ b/homeassistant/components/tensorflow/image_processing.py @@ -1,23 +1,12 @@ """Support for performing TensorFlow classification on images.""" +import io import logging import os import sys -import io -import voluptuous as vol + from PIL import Image, ImageDraw import numpy as np - -try: - import cv2 -except ImportError: - cv2 = None - -try: - # Verify that the TensorFlow Object Detection API is pre-installed - import tensorflow as tf # noqa - from object_detection.utils import label_map_util # noqa -except ImportError: - label_map_util = None +import voluptuous as vol from homeassistant.components.image_processing import ( CONF_CONFIDENCE, @@ -98,8 +87,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None): # append custom model path to sys.path sys.path.append(model_dir) - os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" - if label_map_util is None: + try: + # Verify that the TensorFlow Object Detection API is pre-installed + # pylint: disable=unused-import,unused-variable + os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" + # These imports shouldn't be moved to the top, because they depend on code from the model_dir. + # (The model_dir is created during the manual setup process. See integration docs.) + import tensorflow as tf # noqa + from object_detection.utils import label_map_util # noqa + except ImportError: + # pylint: disable=line-too-long _LOGGER.error( "No TensorFlow Object Detection library found! Install or compile " "for your system following instructions here: " @@ -107,7 +104,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None): ) # noqa return - if cv2 is None: + try: + # Display warning that PIL will be used if no OpenCV is found. + # pylint: disable=unused-import,unused-variable + import cv2 # noqa + except ImportError: _LOGGER.warning( "No OpenCV library found. TensorFlow will process image with " "PIL at reduced resolution" @@ -282,7 +283,13 @@ class TensorFlowImageProcessor(ImageProcessingEntity): def process_image(self, image): """Process the image.""" - if cv2 is None: + try: + import cv2 # pylint: disable=import-error + + img = cv2.imdecode(np.asarray(bytearray(image)), cv2.IMREAD_UNCHANGED) + inp = img[:, :, [2, 1, 0]] # BGR->RGB + inp_expanded = inp.reshape(1, inp.shape[0], inp.shape[1], 3) + except ImportError: img = Image.open(io.BytesIO(bytearray(image))).convert("RGB") img.thumbnail((460, 460), Image.ANTIALIAS) img_width, img_height = img.size @@ -292,10 +299,6 @@ class TensorFlowImageProcessor(ImageProcessingEntity): .astype(np.uint8) ) inp_expanded = np.expand_dims(inp, axis=0) - else: - img = cv2.imdecode(np.asarray(bytearray(image)), cv2.IMREAD_UNCHANGED) - inp = img[:, :, [2, 1, 0]] # BGR->RGB - inp_expanded = inp.reshape(1, inp.shape[0], inp.shape[1], 3) image_tensor = self._graph.get_tensor_by_name("image_tensor:0") boxes = self._graph.get_tensor_by_name("detection_boxes:0") From 637a16799f61f71231dde1e85c8df3b1a8aa2d96 Mon Sep 17 00:00:00 2001 From: gngj Date: Fri, 25 Oct 2019 20:42:23 +0300 Subject: [PATCH 08/29] Fix microsoft tts (#28199) * Update pycsspeechtts From 1.0.2 to 1.0.3 as the old one is using an api that doesn't work * Give a option to choose region Api is now region dependent, so gave it a config --- homeassistant/components/microsoft/manifest.json | 2 +- homeassistant/components/microsoft/tts.py | 11 +++++++++-- requirements_all.txt | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/microsoft/manifest.json b/homeassistant/components/microsoft/manifest.json index 16ae94c212e..5834897ee90 100644 --- a/homeassistant/components/microsoft/manifest.json +++ b/homeassistant/components/microsoft/manifest.json @@ -3,7 +3,7 @@ "name": "Microsoft", "documentation": "https://www.home-assistant.io/integrations/microsoft", "requirements": [ - "pycsspeechtts==1.0.2" + "pycsspeechtts==1.0.3" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/microsoft/tts.py b/homeassistant/components/microsoft/tts.py index 3536c788bb9..d214f6648dd 100644 --- a/homeassistant/components/microsoft/tts.py +++ b/homeassistant/components/microsoft/tts.py @@ -14,6 +14,7 @@ CONF_RATE = "rate" CONF_VOLUME = "volume" CONF_PITCH = "pitch" CONF_CONTOUR = "contour" +CONF_REGION = "region" _LOGGER = logging.getLogger(__name__) @@ -72,6 +73,7 @@ DEFAULT_RATE = 0 DEFAULT_VOLUME = 0 DEFAULT_PITCH = "default" DEFAULT_CONTOUR = "" +DEFAULT_REGION = "eastus" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { @@ -87,6 +89,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ), vol.Optional(CONF_PITCH, default=DEFAULT_PITCH): cv.string, vol.Optional(CONF_CONTOUR, default=DEFAULT_CONTOUR): cv.string, + vol.Optional(CONF_REGION, default=DEFAULT_REGION): cv.string, } ) @@ -102,13 +105,16 @@ def get_engine(hass, config): config[CONF_VOLUME], config[CONF_PITCH], config[CONF_CONTOUR], + config[CONF_REGION], ) class MicrosoftProvider(Provider): """The Microsoft speech API provider.""" - def __init__(self, apikey, lang, gender, ttype, rate, volume, pitch, contour): + def __init__( + self, apikey, lang, gender, ttype, rate, volume, pitch, contour, region + ): """Init Microsoft TTS service.""" self._apikey = apikey self._lang = lang @@ -119,6 +125,7 @@ class MicrosoftProvider(Provider): self._volume = f"{volume}%" self._pitch = pitch self._contour = contour + self._region = region self.name = "Microsoft" @property @@ -138,7 +145,7 @@ class MicrosoftProvider(Provider): from pycsspeechtts import pycsspeechtts try: - trans = pycsspeechtts.TTSTranslator(self._apikey) + trans = pycsspeechtts.TTSTranslator(self._apikey, self._region) data = trans.speak( language=language, gender=self._gender, diff --git a/requirements_all.txt b/requirements_all.txt index 8f5e83a8e67..9b73d46dfbf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1132,7 +1132,7 @@ pycomfoconnect==0.3 pycoolmasternet==0.0.4 # homeassistant.components.microsoft -pycsspeechtts==1.0.2 +pycsspeechtts==1.0.3 # homeassistant.components.cups # pycups==1.9.73 From 524f5a7264d11a75ec19e439d1671d0e65a05500 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 25 Oct 2019 19:20:42 +0200 Subject: [PATCH 09/29] Updated frontend to 20191025.0 (#28208) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index ae53b972dca..b23d40605dd 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", "requirements": [ - "home-assistant-frontend==20191023.0" + "home-assistant-frontend==20191025.0" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d2f10c891a9..682d9b1e1db 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ contextvars==2.4;python_version<"3.7" cryptography==2.8 distro==1.4.0 hass-nabucasa==0.22 -home-assistant-frontend==20191023.0 +home-assistant-frontend==20191025.0 importlib-metadata==0.23 jinja2>=2.10.1 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 9b73d46dfbf..9508423927e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -646,7 +646,7 @@ hole==0.5.0 holidays==0.9.11 # homeassistant.components.frontend -home-assistant-frontend==20191023.0 +home-assistant-frontend==20191025.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0af9b338987..1783f192b24 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -242,7 +242,7 @@ hole==0.5.0 holidays==0.9.11 # homeassistant.components.frontend -home-assistant-frontend==20191023.0 +home-assistant-frontend==20191025.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.4 From c456b725fdebd5d32bc29f4367d3e7747c1c8b17 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 25 Oct 2019 10:49:42 -0700 Subject: [PATCH 10/29] Bumped version to 0.101.0b2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ce00e38bc42..4d858deec87 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0b1" +PATCH_VERSION = "0b2" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 82ed84ba43a6ce9ac950495d1877d7cbb4d1cb3f Mon Sep 17 00:00:00 2001 From: michaeldavie Date: Sat, 26 Oct 2019 16:27:21 -0400 Subject: [PATCH 11/29] Bump env_canada to 0.0.27 (#28239) --- homeassistant/components/environment_canada/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/environment_canada/manifest.json b/homeassistant/components/environment_canada/manifest.json index c62e1e356b6..4d1a8094663 100644 --- a/homeassistant/components/environment_canada/manifest.json +++ b/homeassistant/components/environment_canada/manifest.json @@ -3,7 +3,7 @@ "name": "Environment Canada", "documentation": "https://www.home-assistant.io/integrations/environment_canada", "requirements": [ - "env_canada==0.0.25" + "env_canada==0.0.27" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 9508423927e..cd4a6fceb85 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -456,7 +456,7 @@ enocean==0.50 enturclient==0.2.0 # homeassistant.components.environment_canada -env_canada==0.0.25 +env_canada==0.0.27 # homeassistant.components.envirophat # envirophat==0.0.6 From 3cedee3feaced5752093b8f48aaf90bf1f027d24 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 26 Oct 2019 06:55:42 +0800 Subject: [PATCH 12/29] Add above and below to sensor condition extra_fields (#27364) * Add above and below to sensor condition extra_fields * Change unit_of_measurement to suffix in extra_fields * Check if sensor has unit when getting capabilities * Improve tests --- .../components/device_automation/__init__.py | 6 +- .../components/sensor/device_condition.py | 27 +++++++ .../components/sensor/device_trigger.py | 8 +- .../sensor/test_device_condition.py | 81 +++++++++++++++++++ .../components/sensor/test_device_trigger.py | 35 ++++++++ 5 files changed, 155 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/device_automation/__init__.py b/homeassistant/components/device_automation/__init__.py index 0be1c3eb1dd..80e64033295 100644 --- a/homeassistant/components/device_automation/__init__.py +++ b/homeassistant/components/device_automation/__init__.py @@ -156,7 +156,11 @@ async def _async_get_device_automation_capabilities(hass, automation_type, autom # The device automation has no capabilities return {} - capabilities = await getattr(platform, function_name)(hass, automation) + try: + capabilities = await getattr(platform, function_name)(hass, automation) + except InvalidDeviceAutomationConfig: + return {} + capabilities = capabilities.copy() extra_fields = capabilities.get("extra_fields") diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index 26479807991..259fb5dbab9 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -2,6 +2,9 @@ from typing import Dict, List import voluptuous as vol +from homeassistant.components.device_automation.exceptions import ( + InvalidDeviceAutomationConfig, +) from homeassistant.core import HomeAssistant from homeassistant.const import ( ATTR_DEVICE_CLASS, @@ -141,3 +144,27 @@ def async_condition_from_config( numeric_state_config[condition.CONF_BELOW] = config[CONF_BELOW] return condition.async_numeric_state_from_config(numeric_state_config) + + +async def async_get_condition_capabilities(hass, config): + """List condition capabilities.""" + state = hass.states.get(config[CONF_ENTITY_ID]) + unit_of_measurement = ( + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) if state else None + ) + + if not state or not unit_of_measurement: + raise InvalidDeviceAutomationConfig + + return { + "extra_fields": vol.Schema( + { + vol.Optional( + CONF_ABOVE, description={"suffix": unit_of_measurement} + ): vol.Coerce(float), + vol.Optional( + CONF_BELOW, description={"suffix": unit_of_measurement} + ): vol.Coerce(float), + } + ) + } diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index b462124165a..73e55340da9 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -3,6 +3,9 @@ import voluptuous as vol import homeassistant.components.automation.numeric_state as numeric_state_automation from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation.exceptions import ( + InvalidDeviceAutomationConfig, +) from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT, @@ -146,9 +149,12 @@ async def async_get_trigger_capabilities(hass, config): """List trigger capabilities.""" state = hass.states.get(config[CONF_ENTITY_ID]) unit_of_measurement = ( - state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) if state else "" + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) if state else None ) + if not state or not unit_of_measurement: + raise InvalidDeviceAutomationConfig + return { "extra_fields": vol.Schema( { diff --git a/tests/components/sensor/test_device_condition.py b/tests/components/sensor/test_device_condition.py index e28e487f4ef..f3ff15c3ad9 100644 --- a/tests/components/sensor/test_device_condition.py +++ b/tests/components/sensor/test_device_condition.py @@ -14,6 +14,7 @@ from tests.common import ( mock_device_registry, mock_registry, async_get_device_automations, + async_get_device_automation_capabilities, ) from tests.testing_config.custom_components.test.sensor import DEVICE_CLASSES @@ -73,6 +74,86 @@ async def test_get_conditions(hass, device_reg, entity_reg): assert conditions == expected_conditions +async def test_get_condition_capabilities(hass, device_reg, entity_reg): + """Test we get the expected capabilities from a sensor condition.""" + platform = getattr(hass.components, f"test.{DOMAIN}") + platform.init() + + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create( + DOMAIN, + "test", + platform.ENTITIES["battery"].unique_id, + device_id=device_entry.id, + ) + + assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) + + expected_capabilities = { + "extra_fields": [ + { + "description": {"suffix": "%"}, + "name": "above", + "optional": True, + "type": "float", + }, + { + "description": {"suffix": "%"}, + "name": "below", + "optional": True, + "type": "float", + }, + ] + } + conditions = await async_get_device_automations(hass, "condition", device_entry.id) + assert len(conditions) == 1 + for condition in conditions: + capabilities = await async_get_device_automation_capabilities( + hass, "condition", condition + ) + assert capabilities == expected_capabilities + + +async def test_get_condition_capabilities_none(hass, device_reg, entity_reg): + """Test we get the expected capabilities from a sensor condition.""" + platform = getattr(hass.components, f"test.{DOMAIN}") + platform.init() + + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + + assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) + + conditions = [ + { + "condition": "device", + "device_id": "8770c43885354d5fa27604db6817f63f", + "domain": "sensor", + "entity_id": "sensor.beer", + "type": "is_battery_level", + }, + { + "condition": "device", + "device_id": "8770c43885354d5fa27604db6817f63f", + "domain": "sensor", + "entity_id": platform.ENTITIES["none"].entity_id, + "type": "is_battery_level", + }, + ] + + expected_capabilities = {} + for condition in conditions: + capabilities = await async_get_device_automation_capabilities( + hass, "condition", condition + ) + assert capabilities == expected_capabilities + + async def test_if_state_not_above_below(hass, calls, caplog): """Test for bad value conditions.""" platform = getattr(hass.components, f"test.{DOMAIN}") diff --git a/tests/components/sensor/test_device_trigger.py b/tests/components/sensor/test_device_trigger.py index a21839fcebc..b7a921fff18 100644 --- a/tests/components/sensor/test_device_trigger.py +++ b/tests/components/sensor/test_device_trigger.py @@ -124,6 +124,41 @@ async def test_get_trigger_capabilities(hass, device_reg, entity_reg): assert capabilities == expected_capabilities +async def test_get_trigger_capabilities_none(hass, device_reg, entity_reg): + """Test we get the expected capabilities from a sensor trigger.""" + platform = getattr(hass.components, f"test.{DOMAIN}") + platform.init() + + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + + assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) + + triggers = [ + { + "platform": "device", + "device_id": "8770c43885354d5fa27604db6817f63f", + "domain": "sensor", + "entity_id": "sensor.beer", + "type": "is_battery_level", + }, + { + "platform": "device", + "device_id": "8770c43885354d5fa27604db6817f63f", + "domain": "sensor", + "entity_id": platform.ENTITIES["none"].entity_id, + "type": "is_battery_level", + }, + ] + + expected_capabilities = {} + for trigger in triggers: + capabilities = await async_get_device_automation_capabilities( + hass, "trigger", trigger + ) + assert capabilities == expected_capabilities + + async def test_if_fires_not_on_above_below(hass, calls, caplog): """Test for value triggers firing.""" platform = getattr(hass.components, f"test.{DOMAIN}") From 4a25bab1b30fcdecc125762ea63548ea608266a9 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 26 Oct 2019 04:40:05 +0800 Subject: [PATCH 13/29] Fix broken deconz trigger (#28211) --- homeassistant/components/deconz/device_trigger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index 27ff6fcd590..2d097d30c0b 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -235,6 +235,7 @@ async def async_attach_trigger(hass, config, action, automation_info): event_id = deconz_event.serial event_config = { + event.CONF_PLATFORM: "event", event.CONF_EVENT_TYPE: CONF_DECONZ_EVENT, event.CONF_EVENT_DATA: {CONF_UNIQUE_ID: event_id, CONF_EVENT: trigger}, } From 0e2b55e60e7fb65bf4a1854b6f1e4acd3a20e883 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 28 Oct 2019 12:39:37 -0500 Subject: [PATCH 14/29] Bump library to 0.0.3 (#28294) --- homeassistant/components/plex/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index 90ae305148e..8edccda75e0 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -6,7 +6,7 @@ "requirements": [ "plexapi==3.0.6", "plexauth==0.0.5", - "plexwebsocket==0.0.1" + "plexwebsocket==0.0.3" ], "dependencies": [ "http" diff --git a/requirements_all.txt b/requirements_all.txt index cd4a6fceb85..869c6ea90c3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -974,7 +974,7 @@ plexapi==3.0.6 plexauth==0.0.5 # homeassistant.components.plex -plexwebsocket==0.0.1 +plexwebsocket==0.0.3 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1783f192b24..72aa75a4676 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ plexapi==3.0.6 plexauth==0.0.5 # homeassistant.components.plex -plexwebsocket==0.0.1 +plexwebsocket==0.0.3 # homeassistant.components.mhz19 # homeassistant.components.serial_pm From 070790ccc9f613eb1596897031d09cfe4a444b3a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 28 Oct 2019 11:28:45 -0700 Subject: [PATCH 15/29] Bumped version to 0.101.0b3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4d858deec87..2aa5e97fa94 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0b2" +PATCH_VERSION = "0b3" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From f021e5832a0b7f01892e0281e1072a3bf877c847 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 29 Oct 2019 12:05:05 +0100 Subject: [PATCH 16/29] Cleanup not needed websocket flags for ingress (#28295) --- homeassistant/components/hassio/ingress.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 4ecb9a8419f..53235f80dca 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -167,7 +167,14 @@ def _init_header( # filter flags for name, value in request.headers.items(): - if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_ENCODING): + if name in ( + hdrs.CONTENT_LENGTH, + hdrs.CONTENT_ENCODING, + hdrs.SEC_WEBSOCKET_EXTENSIONS, + hdrs.SEC_WEBSOCKET_PROTOCOL, + hdrs.SEC_WEBSOCKET_VERSION, + hdrs.SEC_WEBSOCKET_KEY, + ): continue headers[name] = value From c104efc18d28acc765fbac6c6b50354dbfed57c4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 29 Oct 2019 16:30:33 +0100 Subject: [PATCH 17/29] Updated frontend to 20191025.1 (#28327) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index b23d40605dd..aa7ad8b18f9 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", "requirements": [ - "home-assistant-frontend==20191025.0" + "home-assistant-frontend==20191025.1" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 682d9b1e1db..85bb00ce6eb 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ contextvars==2.4;python_version<"3.7" cryptography==2.8 distro==1.4.0 hass-nabucasa==0.22 -home-assistant-frontend==20191025.0 +home-assistant-frontend==20191025.1 importlib-metadata==0.23 jinja2>=2.10.1 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 869c6ea90c3..df9cafe7aa0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -646,7 +646,7 @@ hole==0.5.0 holidays==0.9.11 # homeassistant.components.frontend -home-assistant-frontend==20191025.0 +home-assistant-frontend==20191025.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 72aa75a4676..36f423860d0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -242,7 +242,7 @@ hole==0.5.0 holidays==0.9.11 # homeassistant.components.frontend -home-assistant-frontend==20191025.0 +home-assistant-frontend==20191025.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.4 From cf6d11db8d6ab2be890ac06e03765bc28dd085fa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 29 Oct 2019 16:17:34 -0700 Subject: [PATCH 18/29] Bumped version to 0.101.0b4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2aa5e97fa94..7367d9723f6 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0b3" +PATCH_VERSION = "0b4" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 8aee92347f12fcb677fbce734ef41b49e3bb13ad Mon Sep 17 00:00:00 2001 From: springstan <46536646+springstan@users.noreply.github.com> Date: Wed, 30 Oct 2019 07:57:40 +0100 Subject: [PATCH 19/29] Fix KeyError in decora setup (#28279) * Imported homeassistant.util and slugified address if no name is specified * Added a custom validator function in case name is not set in config * Removed logger.debug line only used for testing --- homeassistant/components/decora/light.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/decora/light.py b/homeassistant/components/decora/light.py index 4d2d10ccbd5..6ca427f2476 100644 --- a/homeassistant/components/decora/light.py +++ b/homeassistant/components/decora/light.py @@ -1,4 +1,5 @@ """Support for Decora dimmers.""" +import copy from functools import wraps import logging import time @@ -15,17 +16,34 @@ from homeassistant.components.light import ( ) from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME import homeassistant.helpers.config_validation as cv +import homeassistant.util as util _LOGGER = logging.getLogger(__name__) SUPPORT_DECORA_LED = SUPPORT_BRIGHTNESS + +def _name_validator(config): + """Validate the name.""" + config = copy.deepcopy(config) + for address, device_config in config[CONF_DEVICES].items(): + if CONF_NAME not in device_config: + device_config[CONF_NAME] = util.slugify(address) + + return config + + DEVICE_SCHEMA = vol.Schema( {vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_API_KEY): cv.string} ) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - {vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}} +PLATFORM_SCHEMA = vol.Schema( + vol.All( + PLATFORM_SCHEMA.extend( + {vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}} + ), + _name_validator, + ) ) From 7eceedea108399a738b81eb57ddf9304630a6296 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 30 Oct 2019 19:50:48 +0000 Subject: [PATCH 20/29] Bump version 0.101.0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 7367d9723f6..f6f1a4f2de2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0b4" +PATCH_VERSION = "0" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 54481598b7f7572a8db3819dd0b6696b659db23a Mon Sep 17 00:00:00 2001 From: Teemu R Date: Wed, 23 Oct 2019 23:13:56 +0200 Subject: [PATCH 21/29] Bump songpal to fix a regression (#28115) The new release fixes a single regression from requests to aiohttp conversion. Some devices do not respond with the correct mimetype which was not enforced by requests but is enforced by aiohttp. Related PR https://github.com/rytilahti/python-songpal/pull/59 --- homeassistant/components/songpal/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/songpal/manifest.json b/homeassistant/components/songpal/manifest.json index 55b02b66a59..b090a90d719 100644 --- a/homeassistant/components/songpal/manifest.json +++ b/homeassistant/components/songpal/manifest.json @@ -3,7 +3,7 @@ "name": "Songpal", "documentation": "https://www.home-assistant.io/integrations/songpal", "requirements": [ - "python-songpal==0.11.1" + "python-songpal==0.11.2" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index df9cafe7aa0..52f124d46d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1564,7 +1564,7 @@ python-ripple-api==0.0.3 python-sochain-api==0.0.2 # homeassistant.components.songpal -python-songpal==0.11.1 +python-songpal==0.11.2 # homeassistant.components.synologydsm python-synology==0.2.0 From 5571c0c60a19e7afffa87daf2d5285874b16cde3 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 30 Oct 2019 09:05:13 -0600 Subject: [PATCH 22/29] Bump pymyq to 2.0.1 (#28348) --- homeassistant/components/myq/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/myq/manifest.json b/homeassistant/components/myq/manifest.json index 73265b61c83..115c9cf515b 100644 --- a/homeassistant/components/myq/manifest.json +++ b/homeassistant/components/myq/manifest.json @@ -3,7 +3,7 @@ "name": "Myq", "documentation": "https://www.home-assistant.io/integrations/myq", "requirements": [ - "pymyq==2.0.0" + "pymyq==2.0.1" ], "dependencies": [], "codeowners": [] diff --git a/requirements_all.txt b/requirements_all.txt index 52f124d46d5..307ee717571 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1328,7 +1328,7 @@ pymsteams==0.1.12 pymusiccast==0.1.6 # homeassistant.components.myq -pymyq==2.0.0 +pymyq==2.0.1 # homeassistant.components.mysensors pymysensors==0.18.0 From 3c9482b2d3679d4947ddf2b6577859eaefc3ae48 Mon Sep 17 00:00:00 2001 From: fredericvl <34839323+fredericvl@users.noreply.github.com> Date: Thu, 31 Oct 2019 09:39:27 +0100 Subject: [PATCH 23/29] Bump pysaj to v0.0.13 (fix for sensor date) (#28351) --- homeassistant/components/saj/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/saj/manifest.json b/homeassistant/components/saj/manifest.json index 2dd701e9c7c..4d02ab74840 100644 --- a/homeassistant/components/saj/manifest.json +++ b/homeassistant/components/saj/manifest.json @@ -3,7 +3,7 @@ "name": "SAJ", "documentation": "https://www.home-assistant.io/integrations/saj", "requirements": [ - "pysaj==0.0.12" + "pysaj==0.0.13" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 307ee717571..276710b520b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1423,7 +1423,7 @@ pyrepetier==3.0.5 pysabnzbd==1.1.0 # homeassistant.components.saj -pysaj==0.0.12 +pysaj==0.0.13 # homeassistant.components.sony_projector pysdcp==1 From d92060a461ea35c672ad42f5c7053f3841e99cd4 Mon Sep 17 00:00:00 2001 From: Steve M Date: Thu, 31 Oct 2019 04:38:53 -0400 Subject: [PATCH 24/29] Bump env_canada to fixed 0.0.29 version (#28360) * Bump env_canada to fixed 0.0.29 version * bump env_canada to 0.29 --- homeassistant/components/environment_canada/manifest.json | 8 ++------ requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/environment_canada/manifest.json b/homeassistant/components/environment_canada/manifest.json index 4d1a8094663..7a8313059fa 100644 --- a/homeassistant/components/environment_canada/manifest.json +++ b/homeassistant/components/environment_canada/manifest.json @@ -2,11 +2,7 @@ "domain": "environment_canada", "name": "Environment Canada", "documentation": "https://www.home-assistant.io/integrations/environment_canada", - "requirements": [ - "env_canada==0.0.27" - ], + "requirements": ["env_canada==0.0.29"], "dependencies": [], - "codeowners": [ - "@michaeldavie" - ] + "codeowners": ["@michaeldavie"] } diff --git a/requirements_all.txt b/requirements_all.txt index 276710b520b..60cd7bf74d0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -456,7 +456,7 @@ enocean==0.50 enturclient==0.2.0 # homeassistant.components.environment_canada -env_canada==0.0.27 +env_canada==0.0.29 # homeassistant.components.envirophat # envirophat==0.0.6 From b71e1affdb6ca40042c45d028bc61ff83052a15d Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Thu, 31 Oct 2019 17:29:27 +0100 Subject: [PATCH 25/29] Fix Airly asyncio timeout error (#28387) * Raise ConfigEntryNotReady * Better asyncio.TimeoutError handling * Sort imports * Increase asyncio timeout --- homeassistant/components/airly/__init__.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/airly/__init__.py b/homeassistant/components/airly/__init__.py index dc2323ddd4e..80f3518c652 100644 --- a/homeassistant/components/airly/__init__.py +++ b/homeassistant/components/airly/__init__.py @@ -1,15 +1,16 @@ """The Airly component.""" import asyncio -import logging from datetime import timedelta +import logging -import async_timeout from aiohttp.client_exceptions import ClientConnectorError from airly import Airly from airly.exceptions import AirlyError +import async_timeout from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE from homeassistant.core import Config, HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import Throttle @@ -45,6 +46,9 @@ async def async_setup_entry(hass, config_entry): await airly.async_update() + if not airly.data: + raise ConfigEntryNotReady() + hass.data[DOMAIN] = {} hass.data[DOMAIN][DATA_CLIENT] = {} hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = airly @@ -81,7 +85,7 @@ class AirlyData: """Update Airly data.""" try: - with async_timeout.timeout(10): + with async_timeout.timeout(20): measurements = self.airly.create_measurements_session_point( self.latitude, self.longitude ) @@ -104,11 +108,8 @@ class AirlyData: self.data[ATTR_API_CAQI_DESCRIPTION] = index["description"] self.data[ATTR_API_ADVICE] = index["advice"] _LOGGER.debug("Data retrieved from Airly") - except ( - ValueError, - AirlyError, - asyncio.TimeoutError, - ClientConnectorError, - ) as error: + except asyncio.TimeoutError: + _LOGGER.error("Asyncio Timeout Error") + except (ValueError, AirlyError, ClientConnectorError) as error: _LOGGER.error(error) self.data = {} From 75b8070c99c366bfbf11a2b0647740c9e6dd2617 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Thu, 31 Oct 2019 20:16:27 +0200 Subject: [PATCH 26/29] Fix hdate spamming homeassistant log (#28392) * Fix hdate spamming homeassistant log * Lower verbosity of another spammy message --- homeassistant/components/jewish_calendar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/jewish_calendar/manifest.json b/homeassistant/components/jewish_calendar/manifest.json index 08182daedd0..b343963153d 100644 --- a/homeassistant/components/jewish_calendar/manifest.json +++ b/homeassistant/components/jewish_calendar/manifest.json @@ -3,7 +3,7 @@ "name": "Jewish calendar", "documentation": "https://www.home-assistant.io/integrations/jewish_calendar", "requirements": [ - "hdate==0.9.1" + "hdate==0.9.3" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 60cd7bf74d0..6621104f26d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -622,7 +622,7 @@ hass-nabucasa==0.22 hbmqtt==0.9.5 # homeassistant.components.jewish_calendar -hdate==0.9.1 +hdate==0.9.3 # homeassistant.components.heatmiser heatmiserV3==0.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 36f423860d0..4861c5793d6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -230,7 +230,7 @@ hass-nabucasa==0.22 hbmqtt==0.9.5 # homeassistant.components.jewish_calendar -hdate==0.9.1 +hdate==0.9.3 # homeassistant.components.here_travel_time herepy==0.6.3.1 From d57fe0334f127d9521497d0fa095da88253ec158 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 31 Oct 2019 11:39:26 -0700 Subject: [PATCH 27/29] Fix check config (#28393) --- homeassistant/requirements.py | 12 ++++++++++-- tests/test_requirements.py | 21 ++++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index 95738084f1f..74469ef2fcd 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -35,13 +35,21 @@ async def async_get_integration_with_requirements( This can raise IntegrationNotFound if manifest or integration is invalid, RequirementNotFound if there was some type of failure to install requirements. + + Does not handle circular dependencies. """ integration = await async_get_integration(hass, domain) - if hass.config.skip_pip or not integration.requirements: + if hass.config.skip_pip: return integration - await async_process_requirements(hass, integration.domain, integration.requirements) + if integration.requirements: + await async_process_requirements( + hass, integration.domain, integration.requirements + ) + + for dependency in integration.dependencies: + await async_get_integration_with_requirements(hass, dependency) return integration diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 780b175778e..548ea645360 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -112,7 +112,17 @@ async def test_install_missing_package(hass): async def test_get_integration_with_requirements(hass): """Check getting an integration with loaded requirements.""" hass.config.skip_pip = False - mock_integration(hass, MockModule("test_component", requirements=["hello==1.0.0"])) + mock_integration( + hass, MockModule("test_component_dep", requirements=["test-comp-dep==1.0.0"]) + ) + mock_integration( + hass, + MockModule( + "test_component", + requirements=["test-comp==1.0.0"], + dependencies=["test_component_dep"], + ), + ) with patch( "homeassistant.util.package.is_installed", return_value=False @@ -126,8 +136,13 @@ async def test_get_integration_with_requirements(hass): assert integration assert integration.domain == "test_component" - assert len(mock_is_installed.mock_calls) == 1 - assert len(mock_inst.mock_calls) == 1 + assert len(mock_is_installed.mock_calls) == 2 + assert mock_is_installed.mock_calls[0][1][0] == "test-comp==1.0.0" + assert mock_is_installed.mock_calls[1][1][0] == "test-comp-dep==1.0.0" + + assert len(mock_inst.mock_calls) == 2 + assert mock_inst.mock_calls[0][1][0] == "test-comp==1.0.0" + assert mock_inst.mock_calls[1][1][0] == "test-comp-dep==1.0.0" async def test_install_with_wheels_index(hass): From 4e2a8fde863857cdad35215d8aaff095b795c36a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 31 Oct 2019 11:38:06 -0700 Subject: [PATCH 28/29] Check for import errors before validating config (#28395) --- homeassistant/setup.py | 17 +++++++++++------ tests/test_setup.py | 8 ++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 07de3b2942d..314938feeed 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -132,6 +132,17 @@ async def _async_setup_component( log_error(str(err)) return False + # Some integrations fail on import because they call functions incorrectly. + # So we do it before validating config to catch these errors. + try: + component = integration.get_component() + except ImportError: + log_error("Unable to import component", False) + return False + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Setup failed for %s: unknown error", domain) + return False + processed_config = await conf_util.async_process_component_config( hass, config, integration ) @@ -143,12 +154,6 @@ async def _async_setup_component( start = timer() _LOGGER.info("Setting up %s", domain) - try: - component = integration.get_component() - except ImportError: - log_error("Unable to import component", False) - return False - if hasattr(component, "PLATFORM_SCHEMA"): # Entity components have their own warning warn_task = None diff --git a/tests/test_setup.py b/tests/test_setup.py index 9612c1784ef..8fd25091eb6 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -527,3 +527,11 @@ async def test_when_setup_already_loaded(hass): setup.async_when_setup(hass, "test", mock_callback) await hass.async_block_till_done() assert calls == ["test", "test"] + + +async def test_setup_import_blows_up(hass): + """Test that we handle it correctly when importing integration blows up.""" + with mock.patch( + "homeassistant.loader.Integration.get_component", side_effect=ValueError + ): + assert not await setup.async_setup_component(hass, "sun", {}) From 633d006554dc8df87d810435f2e4f92b3a5a1915 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 31 Oct 2019 12:11:35 -0700 Subject: [PATCH 29/29] Bumped version to 0.101.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index f6f1a4f2de2..4e25e8c7dc3 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0" +PATCH_VERSION = "1" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1)