From 71155f548f28c2414bb676131fe717fdd613bad0 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 11 Mar 2020 20:54:27 +0100 Subject: [PATCH 01/62] Bumped version to 0.107.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 66db936669b..e3a4ea8e45c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0.dev0" +PATCH_VERSION = "0b0" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 943c7ee11a8011b0d31e91a3e3e0f977a2baa583 Mon Sep 17 00:00:00 2001 From: Barry Williams Date: Thu, 12 Mar 2020 08:54:25 +0000 Subject: [PATCH 02/62] If device has volume disabled, the volume will be `None`. However in these (#32702) instances whenever the volume was requested a division calculation was made resulting in a TypeError. The volume adjustment from `0-100` to `0-1` is now calculated during the `update()` method. --- homeassistant/components/openhome/media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/openhome/media_player.py b/homeassistant/components/openhome/media_player.py index 5d6ee47c3eb..967bce6007e 100644 --- a/homeassistant/components/openhome/media_player.py +++ b/homeassistant/components/openhome/media_player.py @@ -85,7 +85,7 @@ class OpenhomeDevice(MediaPlayerDevice): self._supported_features |= ( SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET ) - self._volume_level = self._device.VolumeLevel() + self._volume_level = self._device.VolumeLevel() / 100.0 self._volume_muted = self._device.IsMuted() for source in self._device.Sources(): @@ -222,7 +222,7 @@ class OpenhomeDevice(MediaPlayerDevice): @property def volume_level(self): """Volume level of the media player (0..1).""" - return self._volume_level / 100.0 + return self._volume_level @property def is_volume_muted(self): From bfacd9a1c394b6ff5d518344703c8a377bb85ff7 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 12 Mar 2020 11:55:18 +0100 Subject: [PATCH 03/62] Remove deprecated hide_if_away from device trackers (#32705) --- .../components/device_tracker/__init__.py | 12 +------ .../components/device_tracker/const.py | 3 -- .../components/device_tracker/legacy.py | 14 +------- .../device_sun_light_trigger/test_init.py | 2 -- tests/components/device_tracker/test_init.py | 34 ------------------- .../unifi_direct/test_device_tracker.py | 3 +- 6 files changed, 3 insertions(+), 65 deletions(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index c66bb621ad4..6d8e2307145 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -25,12 +25,10 @@ from .const import ( ATTR_LOCATION_NAME, ATTR_MAC, ATTR_SOURCE_TYPE, - CONF_AWAY_HIDE, CONF_CONSIDER_HOME, CONF_NEW_DEVICE_DEFAULTS, CONF_SCAN_INTERVAL, CONF_TRACK_NEW, - DEFAULT_AWAY_HIDE, DEFAULT_CONSIDER_HOME, DEFAULT_TRACK_NEW, DOMAIN, @@ -53,15 +51,7 @@ SOURCE_TYPES = ( NEW_DEVICE_DEFAULTS_SCHEMA = vol.Any( None, - vol.All( - cv.deprecated(CONF_AWAY_HIDE, invalidation_version="0.107.0"), - vol.Schema( - { - vol.Optional(CONF_TRACK_NEW, default=DEFAULT_TRACK_NEW): cv.boolean, - vol.Optional(CONF_AWAY_HIDE, default=DEFAULT_AWAY_HIDE): cv.boolean, - } - ), - ), + vol.Schema({vol.Optional(CONF_TRACK_NEW, default=DEFAULT_TRACK_NEW): cv.boolean}), ) PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( { diff --git a/homeassistant/components/device_tracker/const.py b/homeassistant/components/device_tracker/const.py index 06313deccb6..c9ce9f2024a 100644 --- a/homeassistant/components/device_tracker/const.py +++ b/homeassistant/components/device_tracker/const.py @@ -20,9 +20,6 @@ SCAN_INTERVAL = timedelta(seconds=12) CONF_TRACK_NEW = "track_new_devices" DEFAULT_TRACK_NEW = True -CONF_AWAY_HIDE = "hide_if_away" -DEFAULT_AWAY_HIDE = False - CONF_CONSIDER_HOME = "consider_home" DEFAULT_CONSIDER_HOME = timedelta(seconds=180) diff --git a/homeassistant/components/device_tracker/legacy.py b/homeassistant/components/device_tracker/legacy.py index 68908f8c79f..515b7cbc614 100644 --- a/homeassistant/components/device_tracker/legacy.py +++ b/homeassistant/components/device_tracker/legacy.py @@ -37,11 +37,9 @@ from .const import ( ATTR_HOST_NAME, ATTR_MAC, ATTR_SOURCE_TYPE, - CONF_AWAY_HIDE, CONF_CONSIDER_HOME, CONF_NEW_DEVICE_DEFAULTS, CONF_TRACK_NEW, - DEFAULT_AWAY_HIDE, DEFAULT_CONSIDER_HOME, DEFAULT_TRACK_NEW, DOMAIN, @@ -198,7 +196,6 @@ class DeviceTracker: mac, picture=picture, icon=icon, - hide_if_away=self.defaults.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE), ) self.devices[dev_id] = device if mac is not None: @@ -303,7 +300,6 @@ class Device(RestoreEntity): picture: str = None, gravatar: str = None, icon: str = None, - hide_if_away: bool = False, ) -> None: """Initialize a device.""" self.hass = hass @@ -331,8 +327,6 @@ class Device(RestoreEntity): self.icon = icon - self.away_hide = hide_if_away - self.source_type = None self._attributes = {} @@ -372,11 +366,6 @@ class Device(RestoreEntity): """Return device state attributes.""" return self._attributes - @property - def hidden(self): - """If device should be hidden.""" - return self.away_hide and self.state != STATE_HOME - async def async_seen( self, host_name: str = None, @@ -524,7 +513,6 @@ async def async_load_config( vol.Optional(CONF_MAC, default=None): vol.Any( None, vol.All(cv.string, vol.Upper) ), - vol.Optional(CONF_AWAY_HIDE, default=DEFAULT_AWAY_HIDE): cv.boolean, vol.Optional("gravatar", default=None): vol.Any(None, cv.string), vol.Optional("picture", default=None): vol.Any(None, cv.string), vol.Optional(CONF_CONSIDER_HOME, default=consider_home): vol.All( @@ -544,6 +532,7 @@ async def async_load_config( for dev_id, device in devices.items(): # Deprecated option. We just ignore it to avoid breaking change device.pop("vendor", None) + device.pop("hide_if_away", None) try: device = dev_schema(device) device["dev_id"] = cv.slugify(dev_id) @@ -564,7 +553,6 @@ def update_config(path: str, dev_id: str, device: Device): ATTR_ICON: device.icon, "picture": device.config_picture, "track": device.track, - CONF_AWAY_HIDE: device.away_hide, } } out.write("\n") diff --git a/tests/components/device_sun_light_trigger/test_init.py b/tests/components/device_sun_light_trigger/test_init.py index bc4d44e1b42..9cc794380f9 100644 --- a/tests/components/device_sun_light_trigger/test_init.py +++ b/tests/components/device_sun_light_trigger/test_init.py @@ -34,7 +34,6 @@ def scanner(hass): "homeassistant.components.device_tracker.legacy.load_yaml_config_file", return_value={ "device_1": { - "hide_if_away": False, "mac": "DEV1", "name": "Unnamed Device", "picture": "http://example.com/dev1.jpg", @@ -42,7 +41,6 @@ def scanner(hass): "vendor": None, }, "device_2": { - "hide_if_away": False, "mac": "DEV2", "name": "Unnamed Device", "picture": "http://example.com/dev2.jpg", diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 3ad9e741aae..1a21ad4a7a4 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -15,7 +15,6 @@ from homeassistant.const import ( ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME, ATTR_GPS_ACCURACY, - ATTR_HIDDEN, ATTR_ICON, ATTR_LATITUDE, ATTR_LONGITUDE, @@ -107,7 +106,6 @@ async def test_reading_yaml_config(hass, yaml_devices): "AB:CD:EF:GH:IJ", "Test name", picture="http://test.picture", - hide_if_away=True, icon="mdi:kettle", ) await hass.async_add_executor_job( @@ -121,7 +119,6 @@ async def test_reading_yaml_config(hass, yaml_devices): assert device.track == config.track assert device.mac == config.mac assert device.config_picture == config.config_picture - assert device.away_hide == config.away_hide assert device.consider_home == config.consider_home assert device.icon == config.icon @@ -284,7 +281,6 @@ async def test_entity_attributes(hass, mock_device_tracker_conf): None, friendly_name, picture, - hide_if_away=True, icon=icon, ) devices.append(device) @@ -299,25 +295,6 @@ async def test_entity_attributes(hass, mock_device_tracker_conf): assert picture == attrs.get(ATTR_ENTITY_PICTURE) -async def test_device_hidden(hass, mock_device_tracker_conf): - """Test hidden devices.""" - devices = mock_device_tracker_conf - dev_id = "test_entity" - entity_id = f"{const.DOMAIN}.{dev_id}" - device = legacy.Device( - hass, timedelta(seconds=180), True, dev_id, None, hide_if_away=True - ) - devices.append(device) - - scanner = getattr(hass.components, "test.device_tracker").SCANNER - scanner.reset() - - with assert_setup_component(1, device_tracker.DOMAIN): - assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM) - - assert hass.states.get(entity_id).attributes.get(ATTR_HIDDEN) - - @patch("homeassistant.components.device_tracker.legacy." "DeviceTracker.async_see") async def test_see_service(mock_see, hass): """Test the see service with a unicode dev_id and NO MAC.""" @@ -609,17 +586,6 @@ async def test_picture_and_icon_on_see_discovery(mock_device_tracker_conf, hass) assert mock_device_tracker_conf[0].entity_picture == "pic_url" -async def test_default_hide_if_away_is_used(mock_device_tracker_conf, hass): - """Test that default track_new is used.""" - tracker = legacy.DeviceTracker( - hass, timedelta(seconds=60), False, {device_tracker.CONF_AWAY_HIDE: True}, [] - ) - await tracker.async_see(dev_id=12) - await hass.async_block_till_done() - assert len(mock_device_tracker_conf) == 1 - assert mock_device_tracker_conf[0].away_hide - - async def test_backward_compatibility_for_track_new(mock_device_tracker_conf, hass): """Test backward compatibility for track new.""" tracker = legacy.DeviceTracker( diff --git a/tests/components/unifi_direct/test_device_tracker.py b/tests/components/unifi_direct/test_device_tracker.py index 84602f438f4..297bf917dbf 100644 --- a/tests/components/unifi_direct/test_device_tracker.py +++ b/tests/components/unifi_direct/test_device_tracker.py @@ -7,7 +7,6 @@ import pytest import voluptuous as vol from homeassistant.components.device_tracker import ( - CONF_AWAY_HIDE, CONF_CONSIDER_HOME, CONF_NEW_DEVICE_DEFAULTS, CONF_TRACK_NEW, @@ -49,7 +48,7 @@ async def test_get_scanner(unifi_mock, hass): CONF_PASSWORD: "fake_pass", CONF_TRACK_NEW: True, CONF_CONSIDER_HOME: timedelta(seconds=180), - CONF_NEW_DEVICE_DEFAULTS: {CONF_TRACK_NEW: True, CONF_AWAY_HIDE: False}, + CONF_NEW_DEVICE_DEFAULTS: {CONF_TRACK_NEW: True}, } } From 8db426e5da1a03ddf98e70ba5400b7a8cba2b24d Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 11 Mar 2020 19:34:54 -0600 Subject: [PATCH 04/62] Broaden exception handling for IQVIA (#32708) --- homeassistant/components/iqvia/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/iqvia/__init__.py b/homeassistant/components/iqvia/__init__.py index 3e62eb9b1ee..a33dabeadeb 100644 --- a/homeassistant/components/iqvia/__init__.py +++ b/homeassistant/components/iqvia/__init__.py @@ -4,7 +4,7 @@ from datetime import timedelta import logging from pyiqvia import Client -from pyiqvia.errors import InvalidZipError, IQVIAError +from pyiqvia.errors import InvalidZipError import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT @@ -172,7 +172,7 @@ class IQVIAData: results = await asyncio.gather(*tasks.values(), return_exceptions=True) for key, result in zip(tasks, results): - if isinstance(result, IQVIAError): + if isinstance(result, Exception): _LOGGER.error("Unable to get %s data: %s", key, result) self.data[key] = {} continue From c46d0e4a49c1bc94712afda6d5f92f0d8744587a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Mar 2020 14:47:57 -0700 Subject: [PATCH 05/62] Sonos idle (#32712) * Sonos idle * F-string * Add to properties * Fixes --- .../components/sonos/media_player.py | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 0ab78195cb2..8828c27e9c7 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -93,6 +93,8 @@ ATTR_NIGHT_SOUND = "night_sound" ATTR_SPEECH_ENHANCE = "speech_enhance" ATTR_QUEUE_POSITION = "queue_position" +UNAVAILABLE_VALUES = {"", "NOT_IMPLEMENTED", None} + class SonosData: """Storage class for platform global data.""" @@ -330,7 +332,7 @@ def soco_coordinator(funct): def _timespan_secs(timespan): """Parse a time-span into number of seconds.""" - if timespan in ("", "NOT_IMPLEMENTED", None): + if timespan in UNAVAILABLE_VALUES: return None return sum(60 ** x[0] * int(x[1]) for x in enumerate(reversed(timespan.split(":")))) @@ -427,7 +429,11 @@ class SonosEntity(MediaPlayerDevice): @soco_coordinator def state(self): """Return the state of the entity.""" - if self._status in ("PAUSED_PLAYBACK", "STOPPED"): + if self._status in ("PAUSED_PLAYBACK", "STOPPED",): + # Sonos can consider itself "paused" but without having media loaded + # (happens if playing Spotify and via Spotify app you pick another device to play on) + if self._media_title is None: + return STATE_IDLE return STATE_PAUSED if self._status in ("PLAYING", "TRANSITIONING"): return STATE_PLAYING @@ -511,16 +517,14 @@ class SonosEntity(MediaPlayerDevice): def _radio_artwork(self, url): """Return the private URL with artwork for a radio stream.""" - if url not in ("", "NOT_IMPLEMENTED", None): - if url.find("tts_proxy") > 0: - # If the content is a tts don't try to fetch an image from it. - return None - url = "http://{host}:{port}/getaa?s=1&u={uri}".format( - host=self.soco.ip_address, - port=1400, - uri=urllib.parse.quote(url, safe=""), - ) - return url + if url in UNAVAILABLE_VALUES: + return None + + if url.find("tts_proxy") > 0: + # If the content is a tts don't try to fetch an image from it. + return None + + return f"http://{self.soco.ip_address}:1400/getaa?s=1&u={urllib.parse.quote(url, safe='')}" def _attach_player(self): """Get basic information and add event subscriptions.""" @@ -606,9 +610,9 @@ class SonosEntity(MediaPlayerDevice): self._media_image_url = None - self._media_artist = source + self._media_artist = None self._media_album_name = None - self._media_title = None + self._media_title = source self._source_name = source @@ -640,7 +644,7 @@ class SonosEntity(MediaPlayerDevice): # For radio streams we set the radio station name as the title. current_uri_metadata = media_info["CurrentURIMetaData"] - if current_uri_metadata not in ("", "NOT_IMPLEMENTED", None): + if current_uri_metadata not in UNAVAILABLE_VALUES: # currently soco does not have an API for this current_uri_metadata = pysonos.xml.XML.fromstring( pysonos.utils.really_utf8(current_uri_metadata) @@ -650,7 +654,7 @@ class SonosEntity(MediaPlayerDevice): ".//{http://purl.org/dc/elements/1.1/}title" ) - if md_title not in ("", "NOT_IMPLEMENTED", None): + if md_title not in UNAVAILABLE_VALUES: self._media_title = md_title if self._media_artist and self._media_title: @@ -867,25 +871,25 @@ class SonosEntity(MediaPlayerDevice): @soco_coordinator def media_artist(self): """Artist of current playing media, music track only.""" - return self._media_artist + return self._media_artist or None @property @soco_coordinator def media_album_name(self): """Album name of current playing media, music track only.""" - return self._media_album_name + return self._media_album_name or None @property @soco_coordinator def media_title(self): """Title of current playing media.""" - return self._media_title + return self._media_title or None @property @soco_coordinator def source(self): """Name of the current input source.""" - return self._source_name + return self._source_name or None @property @soco_coordinator From fe6ca522e8fed2b93282d1dbeb97fe32e40ec2dd Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 12 Mar 2020 06:01:05 -0400 Subject: [PATCH 06/62] =?UTF-8?q?Update=20Vizio=20`source`=20property=20to?= =?UTF-8?q?=20only=20return=20current=20app=20if=20i=E2=80=A6=20(#32713)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * only return current app for source if current app is set * check for None specifically * make sure current app isn't called for speaker --- homeassistant/components/vizio/media_player.py | 2 +- tests/components/vizio/test_media_player.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index 63918737411..69a430bb997 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -304,7 +304,7 @@ class VizioDevice(MediaPlayerDevice): @property def source(self) -> str: """Return current input of the device.""" - if self._current_input in INPUT_APPS: + if self._current_app is not None and self._current_input in INPUT_APPS: return self._current_app return self._current_input diff --git a/tests/components/vizio/test_media_player.py b/tests/components/vizio/test_media_player.py index 19696af73a2..68366e8e98b 100644 --- a/tests/components/vizio/test_media_player.py +++ b/tests/components/vizio/test_media_player.py @@ -112,7 +112,9 @@ async def _test_setup( ), patch( "homeassistant.components.vizio.media_player.VizioAsync.get_power_state", return_value=vizio_power_state, - ): + ), patch( + "homeassistant.components.vizio.media_player.VizioAsync.get_current_app", + ) as service_call: config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -125,6 +127,8 @@ async def _test_setup( if ha_power_state == STATE_ON: assert attr["source_list"] == INPUT_LIST assert attr["source"] == CURRENT_INPUT + if ha_device_class == DEVICE_CLASS_SPEAKER: + assert not service_call.called assert ( attr["volume_level"] == float(int(MAX_VOLUME[vizio_device_class] / 2)) From 9f76a8c12de9a4c01c6c7c81abd3574006d6d85a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 Mar 2020 04:31:55 -0500 Subject: [PATCH 07/62] =?UTF-8?q?Resolve=20Home=20Assistant=20fails=20to?= =?UTF-8?q?=20start=20when=20Sense=20integration=20i=E2=80=A6=20(#32716)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump sense_energy 0.7.1 which also fixes throwing ConfigEntryNotReady --- homeassistant/components/sense/manifest.json | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sense/manifest.json b/homeassistant/components/sense/manifest.json index 61f09fb444b..c07e1e4f5c3 100644 --- a/homeassistant/components/sense/manifest.json +++ b/homeassistant/components/sense/manifest.json @@ -3,11 +3,11 @@ "name": "Sense", "documentation": "https://www.home-assistant.io/integrations/sense", "requirements": [ - "sense_energy==0.7.0" + "sense_energy==0.7.1" ], "dependencies": [], "codeowners": [ "@kbickar" ], "config_flow": true -} \ No newline at end of file +} diff --git a/requirements_all.txt b/requirements_all.txt index 2d7434f6293..8c5439e9273 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1837,7 +1837,7 @@ sendgrid==6.1.1 sense-hat==2.2.0 # homeassistant.components.sense -sense_energy==0.7.0 +sense_energy==0.7.1 # homeassistant.components.sentry sentry-sdk==0.13.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 71aa2004eef..56370e493ab 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -632,7 +632,7 @@ samsungctl[websocket]==0.7.1 samsungtvws[websocket]==1.4.0 # homeassistant.components.sense -sense_energy==0.7.0 +sense_energy==0.7.1 # homeassistant.components.sentry sentry-sdk==0.13.5 From 2f2a9085730b89adadc12947a88c3a2ccba60b7e Mon Sep 17 00:00:00 2001 From: escoand Date: Thu, 12 Mar 2020 10:29:11 +0100 Subject: [PATCH 08/62] Fix legacy Samsung TV (#32719) * Update bridge.py * Update test_init.py --- homeassistant/components/samsungtv/bridge.py | 5 +++-- tests/components/samsungtv/test_init.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/samsungtv/bridge.py b/homeassistant/components/samsungtv/bridge.py index 5203c61a978..31f102a62a4 100644 --- a/homeassistant/components/samsungtv/bridge.py +++ b/homeassistant/components/samsungtv/bridge.py @@ -130,10 +130,11 @@ class SamsungTVLegacyBridge(SamsungTVBridge): super().__init__(method, host, None) self.config = { CONF_NAME: VALUE_CONF_NAME, - CONF_ID: VALUE_CONF_ID, CONF_DESCRIPTION: VALUE_CONF_NAME, - CONF_METHOD: method, + CONF_ID: VALUE_CONF_ID, CONF_HOST: host, + CONF_METHOD: method, + CONF_PORT: None, CONF_TIMEOUT: 1, } diff --git a/tests/components/samsungtv/test_init.py b/tests/components/samsungtv/test_init.py index 064a870931f..232a04416d5 100644 --- a/tests/components/samsungtv/test_init.py +++ b/tests/components/samsungtv/test_init.py @@ -34,6 +34,7 @@ REMOTE_CALL = { "id": "ha.component.samsung", "method": "legacy", "host": MOCK_CONFIG[SAMSUNGTV_DOMAIN][0][CONF_HOST], + "port": None, "timeout": 1, } From 9ad776e55d66e839a80a203b2d9e8c7404e58272 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 12 Mar 2020 10:04:34 -0400 Subject: [PATCH 09/62] Set self._current_app to None when vizio device is off (#32725) --- homeassistant/components/vizio/media_player.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index 69a430bb997..d013f41403a 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -190,6 +190,7 @@ class VizioDevice(MediaPlayerDevice): self._is_muted = None self._current_input = None self._available_inputs = None + self._current_app = None self._available_apps = None return From 3345d85dcaf2c93deff95ea8fcf94bf3cd644eb0 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 12 Mar 2020 22:41:04 +0100 Subject: [PATCH 10/62] Updated frontend to 20200312.0 (#32741) --- 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 ad34c0c112c..c3cf353dba1 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==20200311.1" + "home-assistant-frontend==20200312.0" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 39c1d76b999..56ab609c5af 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 hass-nabucasa==0.32.2 -home-assistant-frontend==20200311.1 +home-assistant-frontend==20200312.0 importlib-metadata==1.5.0 jinja2>=2.10.3 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 8c5439e9273..6ea136b003c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200311.1 +home-assistant-frontend==20200312.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 56370e493ab..adaeaa76198 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -263,7 +263,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200311.1 +home-assistant-frontend==20200312.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.8 From a66f4ca4ec5e27e1bb39098cdc90e3b6f5047611 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Mar 2020 17:02:38 -0700 Subject: [PATCH 11/62] Bumped version to 0.107.0b1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index e3a4ea8e45c..b0d64876dd5 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b0" +PATCH_VERSION = "0b1" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 7bdac8ef2ed51601e4a78547f05a1b935087be2f Mon Sep 17 00:00:00 2001 From: Austin Mroczek Date: Thu, 12 Mar 2020 23:34:09 -0700 Subject: [PATCH 12/62] Bump total-connect-client to 0.54.1 #32758) --- homeassistant/components/totalconnect/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/totalconnect/manifest.json b/homeassistant/components/totalconnect/manifest.json index 3ebb319ad07..4675ef0ffaf 100644 --- a/homeassistant/components/totalconnect/manifest.json +++ b/homeassistant/components/totalconnect/manifest.json @@ -2,7 +2,7 @@ "domain": "totalconnect", "name": "Honeywell Total Connect Alarm", "documentation": "https://www.home-assistant.io/integrations/totalconnect", - "requirements": ["total_connect_client==0.53"], + "requirements": ["total_connect_client==0.54.1"], "dependencies": [], "codeowners": ["@austinmroczek"] } diff --git a/requirements_all.txt b/requirements_all.txt index 6ea136b003c..0fb4804f8cd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2017,7 +2017,7 @@ todoist-python==8.0.0 toonapilib==3.2.4 # homeassistant.components.totalconnect -total_connect_client==0.53 +total_connect_client==0.54.1 # homeassistant.components.tplink_lte tp-connected==0.0.4 From 4f78e0431555320ffc70400a4b305146eaa0c2a2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 Mar 2020 12:07:53 -0500 Subject: [PATCH 13/62] Bump py-august to 0.25.0 (#32769) Fixes a bug in the conversion to async where code validation failed. --- homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index ca757ae5ad3..f1085b81554 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -3,7 +3,7 @@ "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", "requirements": [ - "py-august==0.24.0" + "py-august==0.25.0" ], "dependencies": [ "configurator" diff --git a/requirements_all.txt b/requirements_all.txt index 0fb4804f8cd..34cf1661b3c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1088,7 +1088,7 @@ pushover_complete==1.1.1 pwmled==1.5.0 # homeassistant.components.august -py-august==0.24.0 +py-august==0.25.0 # homeassistant.components.canary py-canary==0.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index adaeaa76198..ba75636010b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -397,7 +397,7 @@ pure-python-adb==0.2.2.dev0 pushbullet.py==0.11.0 # homeassistant.components.august -py-august==0.24.0 +py-august==0.25.0 # homeassistant.components.canary py-canary==0.5.0 From 7268bcd9be413360a077966e982a1c39c36291af Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 13 Mar 2020 19:55:53 +0100 Subject: [PATCH 14/62] Check if panel url used and delay dashboard reg till start (#32771) * Check if panel url used and delay dashboard reg till start * move storage_dashboard_changed * fix tests --- homeassistant/components/frontend/__init__.py | 3 +- homeassistant/components/lovelace/__init__.py | 53 ++++++++++--------- .../components/lovelace/dashboard.py | 5 +- tests/components/frontend/test_init.py | 11 +--- tests/components/lovelace/test_dashboard.py | 8 +++ 5 files changed, 42 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 1e3dea98619..d9a39ce5726 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -276,8 +276,7 @@ async def async_setup(hass, config): hass.http.app.router.register_resource(IndexView(repo_path, hass)) - for panel in ("kiosk", "states", "profile"): - async_register_built_in_panel(hass, panel) + async_register_built_in_panel(hass, "profile") # To smooth transition to new urls, add redirects to new urls of dev tools # Added June 27, 2019. Can be removed in 2021. diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py index 23e8a14e511..8ed5e1abfbb 100644 --- a/homeassistant/components/lovelace/__init__.py +++ b/homeassistant/components/lovelace/__init__.py @@ -4,7 +4,7 @@ import logging import voluptuous as vol from homeassistant.components import frontend -from homeassistant.const import CONF_FILENAME +from homeassistant.const import CONF_FILENAME, EVENT_HOMEASSISTANT_START from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import collection, config_validation as cv @@ -127,25 +127,12 @@ async def async_setup(hass, config): # We store a dictionary mapping url_path: config. None is the default. "dashboards": {None: default_config}, "resources": resource_collection, + "yaml_dashboards": config[DOMAIN].get(CONF_DASHBOARDS, {}), } if hass.config.safe_mode: return True - # Process YAML dashboards - for url_path, dashboard_conf in config[DOMAIN].get(CONF_DASHBOARDS, {}).items(): - # For now always mode=yaml - config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf) - hass.data[DOMAIN]["dashboards"][url_path] = config - - try: - _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False) - except ValueError: - _LOGGER.warning("Panel url path %s is not unique", url_path) - - # Process storage dashboards - dashboards_collection = dashboard.DashboardsCollection(hass) - async def storage_dashboard_changed(change_type, item_id, item): """Handle a storage dashboard change.""" url_path = item[CONF_URL_PATH] @@ -180,16 +167,34 @@ async def async_setup(hass, config): except ValueError: _LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path) - dashboards_collection.async_add_listener(storage_dashboard_changed) - await dashboards_collection.async_load() + async def async_setup_dashboards(event): + """Register dashboards on startup.""" + # Process YAML dashboards + for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items(): + # For now always mode=yaml + config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf) + hass.data[DOMAIN]["dashboards"][url_path] = config - collection.StorageCollectionWebsocket( - dashboards_collection, - "lovelace/dashboards", - "dashboard", - STORAGE_DASHBOARD_CREATE_FIELDS, - STORAGE_DASHBOARD_UPDATE_FIELDS, - ).async_setup(hass, create_list=False) + try: + _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False) + except ValueError: + _LOGGER.warning("Panel url path %s is not unique", url_path) + + # Process storage dashboards + dashboards_collection = dashboard.DashboardsCollection(hass) + + dashboards_collection.async_add_listener(storage_dashboard_changed) + await dashboards_collection.async_load() + + collection.StorageCollectionWebsocket( + dashboards_collection, + "lovelace/dashboards", + "dashboard", + STORAGE_DASHBOARD_CREATE_FIELDS, + STORAGE_DASHBOARD_UPDATE_FIELDS, + ).async_setup(hass, create_list=False) + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, async_setup_dashboards) return True diff --git a/homeassistant/components/lovelace/dashboard.py b/homeassistant/components/lovelace/dashboard.py index 514f1eb87b6..f32ac2ed1ff 100644 --- a/homeassistant/components/lovelace/dashboard.py +++ b/homeassistant/components/lovelace/dashboard.py @@ -6,6 +6,7 @@ import time import voluptuous as vol +from homeassistant.components.frontend import DATA_PANELS from homeassistant.const import CONF_FILENAME from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError @@ -231,8 +232,8 @@ class DashboardsCollection(collection.StorageCollection): async def _process_create_data(self, data: dict) -> dict: """Validate the config is valid.""" - if data[CONF_URL_PATH] in self.hass.data[DOMAIN]["dashboards"]: - raise vol.Invalid("Dashboard url path needs to be unique") + if data[CONF_URL_PATH] in self.hass.data[DATA_PANELS]: + raise vol.Invalid("Panel url path needs to be unique") return self.CREATE_SCHEMA(data) diff --git a/tests/components/frontend/test_init.py b/tests/components/frontend/test_init.py index 627bf23341d..36243972fb6 100644 --- a/tests/components/frontend/test_init.py +++ b/tests/components/frontend/test_init.py @@ -106,15 +106,6 @@ async def test_we_cannot_POST_to_root(mock_http_client): assert resp.status == 405 -async def test_states_routes(mock_http_client): - """All served by index.""" - resp = await mock_http_client.get("/states") - assert resp.status == 200 - - resp = await mock_http_client.get("/states/group.existing") - assert resp.status == 200 - - async def test_themes_api(hass, hass_ws_client): """Test that /api/themes returns correct data.""" assert await async_setup_component(hass, "frontend", CONFIG_THEMES) @@ -217,7 +208,7 @@ async def test_missing_themes(hass, hass_ws_client): async def test_extra_urls(mock_http_client_with_urls, mock_onboarded): """Test that extra urls are loaded.""" - resp = await mock_http_client_with_urls.get("/states?latest") + resp = await mock_http_client_with_urls.get("/lovelace?latest") assert resp.status == 200 text = await resp.text() assert text.find('href="https://domain.com/my_extra_url.html"') >= 0 diff --git a/tests/components/lovelace/test_dashboard.py b/tests/components/lovelace/test_dashboard.py index 21a44bc771d..9bfe3da38c9 100644 --- a/tests/components/lovelace/test_dashboard.py +++ b/tests/components/lovelace/test_dashboard.py @@ -5,6 +5,7 @@ import pytest from homeassistant.components import frontend from homeassistant.components.lovelace import const, dashboard +from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.setup import async_setup_component from tests.common import async_capture_events, get_system_health_info @@ -223,6 +224,8 @@ async def test_dashboard_from_yaml(hass, hass_ws_client, url_path): } }, ) + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() assert hass.data[frontend.DATA_PANELS]["test-panel"].config == {"mode": "yaml"} assert hass.data[frontend.DATA_PANELS]["test-panel-no-sidebar"].config == { "mode": "yaml" @@ -306,6 +309,8 @@ async def test_dashboard_from_yaml(hass, hass_ws_client, url_path): async def test_storage_dashboards(hass, hass_ws_client, hass_storage): """Test we load lovelace config from storage.""" assert await async_setup_component(hass, "lovelace", {}) + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() assert hass.data[frontend.DATA_PANELS]["lovelace"].config == {"mode": "storage"} client = await hass_ws_client(hass) @@ -450,6 +455,9 @@ async def test_websocket_list_dashboards(hass, hass_ws_client): }, ) + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + client = await hass_ws_client(hass) # Create a storage dashboard From 56686dd14c85e10213939f5e1bdc222076881900 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 13 Mar 2020 12:16:55 -0700 Subject: [PATCH 15/62] Bumped version to 0.107.0b2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index b0d64876dd5..17cbe0fd239 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b1" +PATCH_VERSION = "0b2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 6780bded7eb1a3645881eac8cc945836ba03213a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 13 Mar 2020 21:43:39 +0100 Subject: [PATCH 16/62] Updated frontend to 20200313.0 (#32777) --- 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 c3cf353dba1..2817b744d72 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==20200312.0" + "home-assistant-frontend==20200313.0" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 56ab609c5af..49c9d017e3a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 hass-nabucasa==0.32.2 -home-assistant-frontend==20200312.0 +home-assistant-frontend==20200313.0 importlib-metadata==1.5.0 jinja2>=2.10.3 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 34cf1661b3c..f1cdb9851c6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200312.0 +home-assistant-frontend==20200313.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ba75636010b..f9226696907 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -263,7 +263,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200312.0 +home-assistant-frontend==20200313.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.8 From dfd29e6d739d5b9274d3129e4c80036a52d4c675 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 13 Mar 2020 22:23:34 +0100 Subject: [PATCH 17/62] Bumped version to 0.107.0b3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 17cbe0fd239..d4a6dd1484f 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b2" +PATCH_VERSION = "0b3" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From e365dc398b5cced35307cafde9dd4fb5eb1b07c7 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Sat, 14 Mar 2020 19:43:09 +0000 Subject: [PATCH 18/62] Fix homekit_controller beta connectivity issues (#32810) --- homeassistant/components/homekit_controller/manifest.json | 2 +- homeassistant/components/homekit_controller/media_player.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 5ff719dde8c..a73d68227c7 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit[IP]==0.2.29"], + "requirements": ["aiohomekit[IP]==0.2.29.1"], "dependencies": [], "zeroconf": ["_hap._tcp.local."], "codeowners": ["@Jc2k"] diff --git a/homeassistant/components/homekit_controller/media_player.py b/homeassistant/components/homekit_controller/media_player.py index 2e4b05817bb..3a1a7359e13 100644 --- a/homeassistant/components/homekit_controller/media_player.py +++ b/homeassistant/components/homekit_controller/media_player.py @@ -154,7 +154,7 @@ class HomeKitTelevision(HomeKitEntity, MediaPlayerDevice): homekit_state = self.service.value(CharacteristicsTypes.CURRENT_MEDIA_STATE) if homekit_state is not None: - return HK_TO_HA_STATE[homekit_state] + return HK_TO_HA_STATE.get(homekit_state, STATE_OK) return STATE_OK diff --git a/requirements_all.txt b/requirements_all.txt index f1cdb9851c6..44367ce8f11 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -163,7 +163,7 @@ aioftp==0.12.0 aioharmony==0.1.13 # homeassistant.components.homekit_controller -aiohomekit[IP]==0.2.29 +aiohomekit[IP]==0.2.29.1 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f9226696907..45c1de0dadf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -62,7 +62,7 @@ aiobotocore==0.11.1 aioesphomeapi==2.6.1 # homeassistant.components.homekit_controller -aiohomekit[IP]==0.2.29 +aiohomekit[IP]==0.2.29.1 # homeassistant.components.emulated_hue # homeassistant.components.http From 86c4fa0fc5e18d3615db1925aadac9ff1ed7f7d0 Mon Sep 17 00:00:00 2001 From: Slava Date: Fri, 13 Mar 2020 23:42:47 +0100 Subject: [PATCH 19/62] Add brightness state to emulated hue when devices support only color temp and brightness (#31834) --- homeassistant/components/emulated_hue/hue_api.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 9a2d624a55f..06a57960898 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -677,7 +677,11 @@ def entity_to_json(config, entity): retval["type"] = "Color temperature light" retval["modelid"] = "HASS312" retval["state"].update( - {HUE_API_STATE_COLORMODE: "ct", HUE_API_STATE_CT: state[STATE_COLOR_TEMP]} + { + HUE_API_STATE_COLORMODE: "ct", + HUE_API_STATE_CT: state[STATE_COLOR_TEMP], + HUE_API_STATE_BRI: state[STATE_BRIGHTNESS], + } ) elif entity_features & ( SUPPORT_BRIGHTNESS From 1b622925a131d1bfaa9eb6ce4bec65742b9a127c Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Fri, 13 Mar 2020 23:55:21 -0500 Subject: [PATCH 20/62] Optimize directv client initialization (#32706) * Optimize directv client initialization. * Update config_flow.py * Update media_player.py * Update media_player.py * Update __init__.py * Update __init__.py * Update __init__.py * Update __init__.py * Update test_media_player.py * Update __init__.py * Update media_player.py * Update test_media_player.py * Update media_player.py * Update test_media_player.py * Update config_flow.py * Update test_config_flow.py * Update test_config_flow.py * Update test_config_flow.py * Update test_config_flow.py * Update test_config_flow.py * Update test_config_flow.py * Update __init__.py * Update test_config_flow.py * Update test_config_flow.py * Update test_media_player.py * Update test_media_player.py * Update __init__.py * Update __init__.py * Update __init__.py --- homeassistant/components/directv/__init__.py | 2 +- .../components/directv/config_flow.py | 6 +- .../components/directv/media_player.py | 27 ++----- tests/components/directv/__init__.py | 23 ++++-- tests/components/directv/test_config_flow.py | 20 ++--- tests/components/directv/test_media_player.py | 80 +++++-------------- 6 files changed, 50 insertions(+), 108 deletions(-) diff --git a/homeassistant/components/directv/__init__.py b/homeassistant/components/directv/__init__.py index d9f3f171992..fc7bb78989a 100644 --- a/homeassistant/components/directv/__init__.py +++ b/homeassistant/components/directv/__init__.py @@ -32,7 +32,7 @@ def get_dtv_data( hass: HomeAssistant, host: str, port: int = DEFAULT_PORT, client_addr: str = "0" ) -> dict: """Retrieve a DIRECTV instance, locations list, and version info for the receiver device.""" - dtv = DIRECTV(host, port, client_addr) + dtv = DIRECTV(host, port, client_addr, determine_state=False) locations = dtv.get_locations() version_info = dtv.get_version() diff --git a/homeassistant/components/directv/config_flow.py b/homeassistant/components/directv/config_flow.py index 27ddf2cda7b..d1b3a6cbe62 100644 --- a/homeassistant/components/directv/config_flow.py +++ b/homeassistant/components/directv/config_flow.py @@ -29,8 +29,7 @@ def validate_input(data: Dict) -> Dict: Data has the keys from DATA_SCHEMA with values provided by the user. """ - # directpy does IO in constructor. - dtv = DIRECTV(data["host"], DEFAULT_PORT) + dtv = DIRECTV(data["host"], DEFAULT_PORT, determine_state=False) version_info = dtv.get_version() return { @@ -76,8 +75,7 @@ class DirecTVConfigFlow(ConfigFlow, domain=DOMAIN): return self._show_form(errors) except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") - errors["base"] = ERROR_UNKNOWN - return self._show_form(errors) + return self.async_abort(reason=ERROR_UNKNOWN) await self.async_set_unique_id(info["receiver_id"]) self._abort_if_unique_id_configured() diff --git a/homeassistant/components/directv/media_player.py b/homeassistant/components/directv/media_player.py index c1c227d319d..b04ef9fed68 100644 --- a/homeassistant/components/directv/media_player.py +++ b/homeassistant/components/directv/media_player.py @@ -83,22 +83,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -def get_dtv_instance( - host: str, port: int = DEFAULT_PORT, client_addr: str = "0" -) -> DIRECTV: - """Retrieve a DIRECTV instance for the receiver or client device.""" - try: - return DIRECTV(host, port, client_addr) - except RequestException as exception: - _LOGGER.debug( - "Request exception %s trying to retrieve DIRECTV instance for client address %s on device %s", - exception, - client_addr, - host, - ) - return None - - async def async_setup_entry( hass: HomeAssistantType, entry: ConfigEntry, @@ -114,16 +98,15 @@ async def async_setup_entry( continue if loc["clientAddr"] != "0": - # directpy does IO in constructor. - dtv = await hass.async_add_executor_job( - get_dtv_instance, entry.data[CONF_HOST], DEFAULT_PORT, loc["clientAddr"] + dtv = DIRECTV( + entry.data[CONF_HOST], + DEFAULT_PORT, + loc["clientAddr"], + determine_state=False, ) else: dtv = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] - if not dtv: - continue - entities.append( DirecTvDevice( str.title(loc["locationName"]), loc["clientAddr"], dtv, version_info, diff --git a/tests/components/directv/__init__.py b/tests/components/directv/__init__.py index d7f79c76be5..876b1e311ab 100644 --- a/tests/components/directv/__init__.py +++ b/tests/components/directv/__init__.py @@ -1,4 +1,6 @@ """Tests for the DirecTV component.""" +from DirectPy import DIRECTV + from homeassistant.components.directv.const import DOMAIN from homeassistant.const import CONF_HOST from homeassistant.helpers.typing import HomeAssistantType @@ -94,18 +96,23 @@ MOCK_GET_VERSION = { } -class MockDirectvClass: +class MockDirectvClass(DIRECTV): """A fake DirecTV DVR device.""" - def __init__(self, ip, port=8080, clientAddr="0"): + def __init__(self, ip, port=8080, clientAddr="0", determine_state=False): """Initialize the fake DirecTV device.""" - self._host = ip - self._port = port - self._device = clientAddr - self._standby = True - self._play = False + super().__init__( + ip=ip, port=port, clientAddr=clientAddr, determine_state=determine_state, + ) - self.attributes = LIVE + self._play = False + self._standby = True + + if self.clientAddr == CLIENT_ADDRESS: + self.attributes = RECORDING + self._standby = False + else: + self.attributes = LIVE def get_locations(self): """Mock for get_locations method.""" diff --git a/tests/components/directv/test_config_flow.py b/tests/components/directv/test_config_flow.py index 5516b61cd46..bd5d8b83419 100644 --- a/tests/components/directv/test_config_flow.py +++ b/tests/components/directv/test_config_flow.py @@ -114,9 +114,7 @@ async def test_form_cannot_connect(hass: HomeAssistantType) -> None: ) with patch( - "homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, - ), patch( - "homeassistant.components.directv.config_flow.DIRECTV.get_version", + "tests.components.directv.test_config_flow.MockDirectvClass.get_version", side_effect=RequestException, ) as mock_validate_input: result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},) @@ -135,15 +133,13 @@ async def test_form_unknown_error(hass: HomeAssistantType) -> None: ) with patch( - "homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, - ), patch( - "homeassistant.components.directv.config_flow.DIRECTV.get_version", + "tests.components.directv.test_config_flow.MockDirectvClass.get_version", side_effect=Exception, ) as mock_validate_input: result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},) - assert result["type"] == RESULT_TYPE_FORM - assert result["errors"] == {"base": "unknown"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "unknown" await hass.async_block_till_done() assert len(mock_validate_input.mock_calls) == 1 @@ -205,9 +201,7 @@ async def test_ssdp_discovery_confirm_abort(hass: HomeAssistantType) -> None: ) with patch( - "homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, - ), patch( - "homeassistant.components.directv.config_flow.DIRECTV.get_version", + "tests.components.directv.test_config_flow.MockDirectvClass.get_version", side_effect=RequestException, ) as mock_validate_input: result = await async_configure_flow(hass, result["flow_id"], {}) @@ -227,9 +221,7 @@ async def test_ssdp_discovery_confirm_unknown_error(hass: HomeAssistantType) -> ) with patch( - "homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, - ), patch( - "homeassistant.components.directv.config_flow.DIRECTV.get_version", + "tests.components.directv.test_config_flow.MockDirectvClass.get_version", side_effect=Exception, ) as mock_validate_input: result = await async_configure_flow(hass, result["flow_id"], {}) diff --git a/tests/components/directv/test_media_player.py b/tests/components/directv/test_media_player.py index 9c06164c309..f7cf63355a8 100644 --- a/tests/components/directv/test_media_player.py +++ b/tests/components/directv/test_media_player.py @@ -54,9 +54,7 @@ from homeassistant.util import dt as dt_util from tests.common import MockConfigEntry, async_fire_time_changed from tests.components.directv import ( - CLIENT_ADDRESS, DOMAIN, - HOST, MOCK_GET_LOCATIONS_MULTIPLE, RECORDING, MockDirectvClass, @@ -70,15 +68,6 @@ MAIN_ENTITY_ID = f"{MP_DOMAIN}.main_dvr" # pylint: disable=redefined-outer-name -@fixture -def client_dtv() -> MockDirectvClass: - """Fixture for a client device.""" - mocked_dtv = MockDirectvClass(HOST, clientAddr=CLIENT_ADDRESS) - mocked_dtv.attributes = RECORDING - mocked_dtv._standby = False # pylint: disable=protected-access - return mocked_dtv - - @fixture def mock_now() -> datetime: """Fixture for dtutil.now.""" @@ -93,34 +82,19 @@ async def setup_directv(hass: HomeAssistantType) -> MockConfigEntry: return await setup_integration(hass) -async def setup_directv_with_instance_error(hass: HomeAssistantType) -> MockConfigEntry: +async def setup_directv_with_locations(hass: HomeAssistantType) -> MockConfigEntry: """Set up mock DirecTV integration.""" with patch( - "homeassistant.components.directv.DIRECTV", new=MockDirectvClass, - ), patch( - "homeassistant.components.directv.DIRECTV.get_locations", + "tests.components.directv.test_media_player.MockDirectvClass.get_locations", return_value=MOCK_GET_LOCATIONS_MULTIPLE, - ), patch( - "homeassistant.components.directv.media_player.get_dtv_instance", - return_value=None, ): - return await setup_integration(hass) - - -async def setup_directv_with_locations( - hass: HomeAssistantType, client_dtv: MockDirectvClass, -) -> MockConfigEntry: - """Set up mock DirecTV integration.""" - with patch( - "homeassistant.components.directv.DIRECTV", new=MockDirectvClass, - ), patch( - "homeassistant.components.directv.DIRECTV.get_locations", - return_value=MOCK_GET_LOCATIONS_MULTIPLE, - ), patch( - "homeassistant.components.directv.media_player.get_dtv_instance", - return_value=client_dtv, - ): - return await setup_integration(hass) + with patch( + "homeassistant.components.directv.DIRECTV", new=MockDirectvClass, + ), patch( + "homeassistant.components.directv.media_player.DIRECTV", + new=MockDirectvClass, + ): + return await setup_integration(hass) async def async_turn_on( @@ -204,27 +178,17 @@ async def test_setup(hass: HomeAssistantType) -> None: assert hass.states.get(MAIN_ENTITY_ID) -async def test_setup_with_multiple_locations( - hass: HomeAssistantType, client_dtv: MockDirectvClass -) -> None: +async def test_setup_with_multiple_locations(hass: HomeAssistantType) -> None: """Test setup with basic config with client location.""" - await setup_directv_with_locations(hass, client_dtv) + await setup_directv_with_locations(hass) assert hass.states.get(MAIN_ENTITY_ID) assert hass.states.get(CLIENT_ENTITY_ID) -async def test_setup_with_instance_error(hass: HomeAssistantType) -> None: - """Test setup with basic config with client location that results in instance error.""" - await setup_directv_with_instance_error(hass) - - assert hass.states.get(MAIN_ENTITY_ID) - assert hass.states.async_entity_ids(MP_DOMAIN) == [MAIN_ENTITY_ID] - - -async def test_unique_id(hass: HomeAssistantType, client_dtv: MockDirectvClass) -> None: +async def test_unique_id(hass: HomeAssistantType) -> None: """Test unique id.""" - await setup_directv_with_locations(hass, client_dtv) + await setup_directv_with_locations(hass) entity_registry = await hass.helpers.entity_registry.async_get_registry() @@ -235,11 +199,9 @@ async def test_unique_id(hass: HomeAssistantType, client_dtv: MockDirectvClass) assert client.unique_id == "2CA17D1CD30X" -async def test_supported_features( - hass: HomeAssistantType, client_dtv: MockDirectvClass -) -> None: +async def test_supported_features(hass: HomeAssistantType) -> None: """Test supported features.""" - await setup_directv_with_locations(hass, client_dtv) + await setup_directv_with_locations(hass) # Features supported for main DVR state = hass.states.get(MAIN_ENTITY_ID) @@ -269,10 +231,10 @@ async def test_supported_features( async def test_check_attributes( - hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass + hass: HomeAssistantType, mock_now: dt_util.dt.datetime ) -> None: """Test attributes.""" - await setup_directv_with_locations(hass, client_dtv) + await setup_directv_with_locations(hass) next_update = mock_now + timedelta(minutes=5) with patch("homeassistant.util.dt.utcnow", return_value=next_update): @@ -321,10 +283,10 @@ async def test_check_attributes( async def test_main_services( - hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass + hass: HomeAssistantType, mock_now: dt_util.dt.datetime ) -> None: """Test the different services.""" - await setup_directv_with_locations(hass, client_dtv) + await setup_directv(hass) next_update = mock_now + timedelta(minutes=5) with patch("homeassistant.util.dt.utcnow", return_value=next_update): @@ -373,10 +335,10 @@ async def test_main_services( async def test_available( - hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass + hass: HomeAssistantType, mock_now: dt_util.dt.datetime ) -> None: """Test available status.""" - entry = await setup_directv_with_locations(hass, client_dtv) + entry = await setup_directv(hass) next_update = mock_now + timedelta(minutes=5) with patch("homeassistant.util.dt.utcnow", return_value=next_update): From 0788bbd62991b989328d4b52c7c0d262fa37fb3a Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Sun, 15 Mar 2020 18:18:15 +0100 Subject: [PATCH 21/62] Add log message on timeout and update less often for upnp devices (#32740) * Catch asyncio.TimeoutError, show a proper message instead * Throttle updates to max once per 30s * Change code owner * Fix CODEOWNERS + linting * Warn on connection timeout --- CODEOWNERS | 2 +- homeassistant/components/upnp/device.py | 20 ++++++++++++++++---- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/upnp/sensor.py | 5 +++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 97b347b8415..19bb89ff51e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -386,7 +386,7 @@ homeassistant/components/unifiled/* @florisvdk homeassistant/components/upc_connect/* @pvizeli homeassistant/components/upcloud/* @scop homeassistant/components/updater/* @home-assistant/core -homeassistant/components/upnp/* @robbiet480 +homeassistant/components/upnp/* @StevenLooman homeassistant/components/uptimerobot/* @ludeeus homeassistant/components/usgs_earthquakes_feed/* @exxamalte homeassistant/components/utility_meter/* @dgomes diff --git a/homeassistant/components/upnp/device.py b/homeassistant/components/upnp/device.py index b144d2b96ed..474170050c3 100644 --- a/homeassistant/components/upnp/device.py +++ b/homeassistant/components/upnp/device.py @@ -142,16 +142,28 @@ class Device: async def async_get_total_bytes_received(self): """Get total bytes received.""" - return await self._igd_device.async_get_total_bytes_received() + try: + return await self._igd_device.async_get_total_bytes_received() + except asyncio.TimeoutError: + _LOGGER.warning("Timeout during get_total_bytes_received") async def async_get_total_bytes_sent(self): """Get total bytes sent.""" - return await self._igd_device.async_get_total_bytes_sent() + try: + return await self._igd_device.async_get_total_bytes_sent() + except asyncio.TimeoutError: + _LOGGER.warning("Timeout during get_total_bytes_sent") async def async_get_total_packets_received(self): """Get total packets received.""" - return await self._igd_device.async_get_total_packets_received() + try: + return await self._igd_device.async_get_total_packets_received() + except asyncio.TimeoutError: + _LOGGER.warning("Timeout during get_total_packets_received") async def async_get_total_packets_sent(self): """Get total packets sent.""" - return await self._igd_device.async_get_total_packets_sent() + try: + return await self._igd_device.async_get_total_packets_sent() + except asyncio.TimeoutError: + _LOGGER.warning("Timeout during get_total_packets_sent") diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 1e55d60f95e..47ad465eb36 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -5,5 +5,5 @@ "documentation": "https://www.home-assistant.io/integrations/upnp", "requirements": ["async-upnp-client==0.14.12"], "dependencies": [], - "codeowners": ["@robbiet480"] + "codeowners": ["@StevenLooman"] } diff --git a/homeassistant/components/upnp/sensor.py b/homeassistant/components/upnp/sensor.py index c77a1b6279f..9632997ac1b 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -1,4 +1,5 @@ """Support for UPnP/IGD Sensors.""" +from datetime import timedelta import logging from homeassistant.const import DATA_BYTES, DATA_KIBIBYTES, TIME_SECONDS @@ -7,6 +8,7 @@ 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 homeassistant.util import Throttle import homeassistant.util.dt as dt_util from .const import DOMAIN as DOMAIN_UPNP, SIGNAL_REMOVE_SENSOR @@ -29,6 +31,8 @@ IN = "received" OUT = "sent" KIBIBYTE = 1024 +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) + async def async_setup_platform( hass: HomeAssistantType, config, async_add_entities, discovery_info=None @@ -142,6 +146,7 @@ class RawUPnPIGDSensor(UpnpSensor): """Return the unit of measurement of this entity, if any.""" return self._type["unit"] + @Throttle(MIN_TIME_BETWEEN_UPDATES) async def async_update(self): """Get the latest information from the IGD.""" if self._type_name == BYTES_RECEIVED: From 706607f1d21a82f9a0ad40e0fa19a3fff5504965 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 13 Mar 2020 19:17:50 -0400 Subject: [PATCH 22/62] Fix handling of attribute reports in ZHA sensors and binary sensors (#32776) * Update sensor tests. * Update light tests. * Update binary_sensor tests. * Update cover tests. * Update device tracker tests. * Update fan tests. * Update lock tests. * Update switch tests. * add sensor attr to sensors * add sensor attr to binary sensors * cleanup extra var Co-authored-by: Alexei Chetroi --- homeassistant/components/zha/binary_sensor.py | 8 ++++ homeassistant/components/zha/sensor.py | 11 ++++++ tests/components/zha/common.py | 17 +++++++++ tests/components/zha/test_binary_sensor.py | 14 ++----- tests/components/zha/test_cover.py | 14 ++----- tests/components/zha/test_device_tracker.py | 13 ++----- tests/components/zha/test_fan.py | 13 ++----- tests/components/zha/test_light.py | 25 ++++-------- tests/components/zha/test_lock.py | 16 ++------ tests/components/zha/test_sensor.py | 38 +++++++------------ tests/components/zha/test_switch.py | 12 ++---- 11 files changed, 77 insertions(+), 104 deletions(-) diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index a40bd62e83c..6c88f3e1013 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -64,6 +64,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class BinarySensor(ZhaEntity, BinarySensorDevice): """ZHA BinarySensor.""" + SENSOR_ATTR = None DEVICE_CLASS = None def __init__(self, unique_id, zha_device, channels, **kwargs): @@ -105,6 +106,8 @@ class BinarySensor(ZhaEntity, BinarySensorDevice): @callback def async_set_state(self, attr_id, attr_name, value): """Set the state.""" + if self.SENSOR_ATTR is None or self.SENSOR_ATTR != attr_name: + return self._state = bool(value) self.async_write_ha_state() @@ -121,6 +124,7 @@ class BinarySensor(ZhaEntity, BinarySensorDevice): class Accelerometer(BinarySensor): """ZHA BinarySensor.""" + SENSOR_ATTR = "acceleration" DEVICE_CLASS = DEVICE_CLASS_MOVING @@ -128,6 +132,7 @@ class Accelerometer(BinarySensor): class Occupancy(BinarySensor): """ZHA BinarySensor.""" + SENSOR_ATTR = "occupancy" DEVICE_CLASS = DEVICE_CLASS_OCCUPANCY @@ -135,6 +140,7 @@ class Occupancy(BinarySensor): class Opening(BinarySensor): """ZHA BinarySensor.""" + SENSOR_ATTR = "on_off" DEVICE_CLASS = DEVICE_CLASS_OPENING @@ -142,6 +148,8 @@ class Opening(BinarySensor): class IASZone(BinarySensor): """ZHA IAS BinarySensor.""" + SENSOR_ATTR = "zone_status" + async def get_device_class(self) -> None: """Get the HA device class from the channel.""" zone_type = await self._channel.get_attribute_value("zone_type") diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 3953db27f20..8182fdcabcf 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -83,6 +83,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class Sensor(ZhaEntity): """Base ZHA sensor.""" + SENSOR_ATTR = None _decimals = 1 _device_class = None _divisor = 1 @@ -126,6 +127,8 @@ class Sensor(ZhaEntity): @callback def async_set_state(self, attr_id, attr_name, value): """Handle state update from channel.""" + if self.SENSOR_ATTR is None or self.SENSOR_ATTR != attr_name: + return if value is not None: value = self.formatter(value) self._state = value @@ -154,6 +157,7 @@ class Sensor(ZhaEntity): class AnalogInput(Sensor): """Sensor that displays analog input values.""" + SENSOR_ATTR = "present_value" pass @@ -161,6 +165,7 @@ class AnalogInput(Sensor): class Battery(Sensor): """Battery sensor of power configuration cluster.""" + SENSOR_ATTR = "battery_percentage_remaining" _device_class = DEVICE_CLASS_BATTERY _unit = UNIT_PERCENTAGE @@ -198,6 +203,7 @@ class Battery(Sensor): class ElectricalMeasurement(Sensor): """Active power measurement.""" + SENSOR_ATTR = "active_power" _device_class = DEVICE_CLASS_POWER _divisor = 10 _unit = POWER_WATT @@ -232,6 +238,7 @@ class Text(Sensor): class Humidity(Sensor): """Humidity sensor.""" + SENSOR_ATTR = "measured_value" _device_class = DEVICE_CLASS_HUMIDITY _divisor = 100 _unit = UNIT_PERCENTAGE @@ -241,6 +248,7 @@ class Humidity(Sensor): class Illuminance(Sensor): """Illuminance Sensor.""" + SENSOR_ATTR = "measured_value" _device_class = DEVICE_CLASS_ILLUMINANCE _unit = "lx" @@ -254,6 +262,7 @@ class Illuminance(Sensor): class SmartEnergyMetering(Sensor): """Metering sensor.""" + SENSOR_ATTR = "instantaneous_demand" _device_class = DEVICE_CLASS_POWER def formatter(self, value): @@ -270,6 +279,7 @@ class SmartEnergyMetering(Sensor): class Pressure(Sensor): """Pressure sensor.""" + SENSOR_ATTR = "measured_value" _device_class = DEVICE_CLASS_PRESSURE _decimals = 0 _unit = "hPa" @@ -279,6 +289,7 @@ class Pressure(Sensor): class Temperature(Sensor): """Temperature Sensor.""" + SENSOR_ATTR = "measured_value" _device_class = DEVICE_CLASS_TEMPERATURE _divisor = 100 _unit = TEMP_CELSIUS diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index 3eb6f407f32..3753136d59d 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -102,6 +102,23 @@ def make_attribute(attrid, value, status=0): return attr +def send_attribute_report(hass, cluster, attrid, value): + """Send a single attribute report.""" + return send_attributes_report(hass, cluster, {attrid: value}) + + +async def send_attributes_report(hass, cluster: int, attributes: dict): + """Cause the sensor to receive an attribute report from the network. + + This is to simulate the normal device communication that happens when a + device is paired to the zigbee network. + """ + attrs = [make_attribute(attrid, value) for attrid, value in attributes.items()] + hdr = make_zcl_header(zcl_f.Command.Report_Attributes) + cluster.handle_message(hdr, [attrs]) + await hass.async_block_till_done() + + async def find_entity_id(domain, zha_device, hass): """Find the entity id under the testing. diff --git a/tests/components/zha/test_binary_sensor.py b/tests/components/zha/test_binary_sensor.py index a22bfa54dae..730c7c844f2 100644 --- a/tests/components/zha/test_binary_sensor.py +++ b/tests/components/zha/test_binary_sensor.py @@ -2,7 +2,6 @@ import pytest import zigpy.zcl.clusters.measurement as measurement import zigpy.zcl.clusters.security as security -import zigpy.zcl.foundation as zcl_f from homeassistant.components.binary_sensor import DOMAIN from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE @@ -11,8 +10,7 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attributes_report, ) DEVICE_IAS = { @@ -36,17 +34,11 @@ DEVICE_OCCUPANCY = { async def async_test_binary_sensor_on_off(hass, cluster, entity_id): """Test getting on and off messages for binary sensors.""" # binary sensor on - attr = make_attribute(0, 1) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 0, 0: 1, 2: 2}) assert hass.states.get(entity_id).state == STATE_ON # binary sensor off - attr.value.value = 0 - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 1, 0: 0, 2: 2}) assert hass.states.get(entity_id).state == STATE_OFF diff --git a/tests/components/zha/test_cover.py b/tests/components/zha/test_cover.py index 3ece16d8116..188ddf69a23 100644 --- a/tests/components/zha/test_cover.py +++ b/tests/components/zha/test_cover.py @@ -14,8 +14,7 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attributes_report, ) from tests.common import mock_coro @@ -64,19 +63,12 @@ async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): await async_enable_traffic(hass, [zha_device]) await hass.async_block_till_done() - attr = make_attribute(8, 100) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() - # test that the state has changed from unavailable to off + await send_attributes_report(hass, cluster, {0: 0, 8: 100, 1: 1}) assert hass.states.get(entity_id).state == STATE_CLOSED # test to see if it opens - attr = make_attribute(8, 0) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {0: 1, 8: 0, 1: 100}) assert hass.states.get(entity_id).state == STATE_OPEN # close from UI diff --git a/tests/components/zha/test_device_tracker.py b/tests/components/zha/test_device_tracker.py index 3782cdc09a7..330153e5f8c 100644 --- a/tests/components/zha/test_device_tracker.py +++ b/tests/components/zha/test_device_tracker.py @@ -4,7 +4,6 @@ import time import pytest import zigpy.zcl.clusters.general as general -import zigpy.zcl.foundation as zcl_f from homeassistant.components.device_tracker import DOMAIN, SOURCE_TYPE_ROUTER from homeassistant.components.zha.core.registries import ( @@ -17,8 +16,7 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attributes_report, ) from tests.common import async_fire_time_changed @@ -66,12 +64,9 @@ async def test_device_tracker(hass, zha_device_joined_restored, zigpy_device_dt) assert hass.states.get(entity_id).state == STATE_NOT_HOME # turn state flip - attr = make_attribute(0x0020, 23) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - - attr = make_attribute(0x0021, 200) - cluster.handle_message(hdr, [[attr]]) + await send_attributes_report( + hass, cluster, {0x0000: 0, 0x0020: 23, 0x0021: 200, 0x0001: 2} + ) zigpy_device_dt.last_seen = time.time() + 10 next_update = dt_util.utcnow() + timedelta(seconds=30) diff --git a/tests/components/zha/test_fan.py b/tests/components/zha/test_fan.py index 0cf3e3e954d..5011a847a4e 100644 --- a/tests/components/zha/test_fan.py +++ b/tests/components/zha/test_fan.py @@ -3,7 +3,6 @@ from unittest.mock import call import pytest import zigpy.zcl.clusters.hvac as hvac -import zigpy.zcl.foundation as zcl_f from homeassistant.components import fan from homeassistant.components.fan import ATTR_SPEED, DOMAIN, SERVICE_SET_SPEED @@ -20,8 +19,7 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attributes_report, ) @@ -52,16 +50,11 @@ async def test_fan(hass, zha_device_joined_restored, zigpy_device): assert hass.states.get(entity_id).state == STATE_OFF # turn on at fan - attr = make_attribute(0, 1) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 2, 0: 1, 2: 3}) assert hass.states.get(entity_id).state == STATE_ON # turn off at fan - attr.value.value = 0 - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 1, 0: 0, 2: 2}) assert hass.states.get(entity_id).state == STATE_OFF # turn on from HA diff --git a/tests/components/zha/test_light.py b/tests/components/zha/test_light.py index 6f5bd23e297..f27bd329bdb 100644 --- a/tests/components/zha/test_light.py +++ b/tests/components/zha/test_light.py @@ -19,8 +19,7 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attributes_report, ) from tests.common import async_fire_time_changed @@ -190,26 +189,18 @@ async def test_light( async def async_test_on_off_from_light(hass, cluster, entity_id): """Test on off functionality from the light.""" # turn on at light - attr = make_attribute(0, 1) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 0, 0: 1, 2: 3}) assert hass.states.get(entity_id).state == STATE_ON # turn off at light - attr.value.value = 0 - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 1, 0: 0, 2: 3}) assert hass.states.get(entity_id).state == STATE_OFF async def async_test_on_from_light(hass, cluster, entity_id): """Test on off functionality from the light.""" # turn on at light - attr = make_attribute(0, 1) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: -1, 0: 1, 2: 2}) assert hass.states.get(entity_id).state == STATE_ON @@ -316,10 +307,10 @@ async def async_test_level_on_off_from_hass( async def async_test_dimmer_from_light(hass, cluster, entity_id, level, expected_state): """Test dimmer functionality from the light.""" - attr = make_attribute(0, level) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + + await send_attributes_report( + hass, cluster, {1: level + 10, 0: level, 2: level - 10 or 22} + ) assert hass.states.get(entity_id).state == expected_state # hass uses None for brightness of 0 in state attributes if level == 0: diff --git a/tests/components/zha/test_lock.py b/tests/components/zha/test_lock.py index 0442ea497d7..86ec266ffa2 100644 --- a/tests/components/zha/test_lock.py +++ b/tests/components/zha/test_lock.py @@ -10,12 +10,7 @@ import zigpy.zcl.foundation as zcl_f from homeassistant.components.lock import DOMAIN from homeassistant.const import STATE_LOCKED, STATE_UNAVAILABLE, STATE_UNLOCKED -from .common import ( - async_enable_traffic, - find_entity_id, - make_attribute, - make_zcl_header, -) +from .common import async_enable_traffic, find_entity_id, send_attributes_report from tests.common import mock_coro @@ -58,16 +53,11 @@ async def test_lock(hass, lock): assert hass.states.get(entity_id).state == STATE_UNLOCKED # set state to locked - attr = make_attribute(0, 1) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 0, 0: 1, 2: 2}) assert hass.states.get(entity_id).state == STATE_LOCKED # set state to unlocked - attr.value.value = 2 - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 0, 0: 2, 2: 3}) assert hass.states.get(entity_id).state == STATE_UNLOCKED # lock from HA diff --git a/tests/components/zha/test_sensor.py b/tests/components/zha/test_sensor.py index fce882c6949..50b85f5720f 100644 --- a/tests/components/zha/test_sensor.py +++ b/tests/components/zha/test_sensor.py @@ -6,7 +6,6 @@ import zigpy.zcl.clusters.general as general import zigpy.zcl.clusters.homeautomation as homeautomation import zigpy.zcl.clusters.measurement as measurement import zigpy.zcl.clusters.smartenergy as smartenergy -import zigpy.zcl.foundation as zcl_f from homeassistant.components.sensor import DOMAIN import homeassistant.config as config_util @@ -28,38 +27,41 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attribute_report, + send_attributes_report, ) async def async_test_humidity(hass, cluster, entity_id): """Test humidity sensor.""" - await send_attribute_report(hass, cluster, 0, 1000) + await send_attributes_report(hass, cluster, {1: 1, 0: 1000, 2: 100}) assert_state(hass, entity_id, "10.0", UNIT_PERCENTAGE) async def async_test_temperature(hass, cluster, entity_id): """Test temperature sensor.""" - await send_attribute_report(hass, cluster, 0, 2900) + await send_attributes_report(hass, cluster, {1: 1, 0: 2900, 2: 100}) assert_state(hass, entity_id, "29.0", "°C") async def async_test_pressure(hass, cluster, entity_id): """Test pressure sensor.""" - await send_attribute_report(hass, cluster, 0, 1000) + await send_attributes_report(hass, cluster, {1: 1, 0: 1000, 2: 10000}) + assert_state(hass, entity_id, "1000", "hPa") + + await send_attributes_report(hass, cluster, {0: 1000, 20: -1, 16: 10000}) assert_state(hass, entity_id, "1000", "hPa") async def async_test_illuminance(hass, cluster, entity_id): """Test illuminance sensor.""" - await send_attribute_report(hass, cluster, 0, 10) + await send_attributes_report(hass, cluster, {1: 1, 0: 10, 2: 20}) assert_state(hass, entity_id, "1.0", "lx") async def async_test_metering(hass, cluster, entity_id): """Test metering sensor.""" - await send_attribute_report(hass, cluster, 1024, 12345) + await send_attributes_report(hass, cluster, {1025: 1, 1024: 12345, 1026: 100}) assert_state(hass, entity_id, "12345.0", "unknown") @@ -73,17 +75,17 @@ async def async_test_electrical_measurement(hass, cluster, entity_id): new_callable=mock.PropertyMock, ) as divisor_mock: divisor_mock.return_value = 1 - await send_attribute_report(hass, cluster, 1291, 100) + await send_attributes_report(hass, cluster, {0: 1, 1291: 100, 10: 1000}) assert_state(hass, entity_id, "100", "W") - await send_attribute_report(hass, cluster, 1291, 99) + await send_attributes_report(hass, cluster, {0: 1, 1291: 99, 10: 1000}) assert_state(hass, entity_id, "99", "W") divisor_mock.return_value = 10 - await send_attribute_report(hass, cluster, 1291, 1000) + await send_attributes_report(hass, cluster, {0: 1, 1291: 1000, 10: 5000}) assert_state(hass, entity_id, "100", "W") - await send_attribute_report(hass, cluster, 1291, 99) + await send_attributes_report(hass, cluster, {0: 1, 1291: 99, 10: 5000}) assert_state(hass, entity_id, "9.9", "W") @@ -141,18 +143,6 @@ async def test_sensor( await async_test_rejoin(hass, zigpy_device, [cluster], (report_count,)) -async def send_attribute_report(hass, cluster, attrid, value): - """Cause the sensor to receive an attribute report from the network. - - This is to simulate the normal device communication that happens when a - device is paired to the zigbee network. - """ - attr = make_attribute(attrid, value) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() - - def assert_state(hass, entity_id, state, unit_of_measurement): """Check that the state is what is expected. diff --git a/tests/components/zha/test_switch.py b/tests/components/zha/test_switch.py index 22ceb629009..98f661cc1ab 100644 --- a/tests/components/zha/test_switch.py +++ b/tests/components/zha/test_switch.py @@ -12,8 +12,7 @@ from .common import ( async_enable_traffic, async_test_rejoin, find_entity_id, - make_attribute, - make_zcl_header, + send_attributes_report, ) from tests.common import mock_coro @@ -53,16 +52,11 @@ async def test_switch(hass, zha_device_joined_restored, zigpy_device): assert hass.states.get(entity_id).state == STATE_OFF # turn on at switch - attr = make_attribute(0, 1) - hdr = make_zcl_header(zcl_f.Command.Report_Attributes) - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 0, 0: 1, 2: 2}) assert hass.states.get(entity_id).state == STATE_ON # turn off at switch - attr.value.value = 0 - cluster.handle_message(hdr, [[attr]]) - await hass.async_block_till_done() + await send_attributes_report(hass, cluster, {1: 1, 0: 0, 2: 2}) assert hass.states.get(entity_id).state == STATE_OFF # turn on from HA From b5c8b5b91f763545f79feb41ed383667f0401084 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Fri, 13 Mar 2020 22:58:14 +0000 Subject: [PATCH 23/62] Fix onvif error with non ptz cameras (#32783) --- homeassistant/components/onvif/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 614eb4e6556..ce241f779b1 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -375,7 +375,7 @@ class ONVIFHassCamera(Camera): def setup_ptz(self): """Set up PTZ if available.""" _LOGGER.debug("Setting up the ONVIF PTZ service") - if self._camera.get_service("ptz") is None: + if self._camera.get_service("ptz", create=False) is None: _LOGGER.debug("PTZ is not available") else: self._ptz_service = self._camera.create_ptz_service() From e666485ea9ffd391541ac9d6975a6610b21c8ae9 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 14 Mar 2020 05:58:32 +0100 Subject: [PATCH 24/62] Fix brightness_pct in light device turn_on action (#32787) --- homeassistant/components/light/device_action.py | 10 +++++----- tests/components/light/test_device_action.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/light/device_action.py b/homeassistant/components/light/device_action.py index 5ee2785a700..5c534cc4150 100644 --- a/homeassistant/components/light/device_action.py +++ b/homeassistant/components/light/device_action.py @@ -15,7 +15,7 @@ from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import config_validation as cv, entity_registry from homeassistant.helpers.typing import ConfigType, TemplateVarsType -from . import ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRIGHTNESS +from . import ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRIGHTNESS TYPE_BRIGHTNESS_INCREASE = "brightness_increase" TYPE_BRIGHTNESS_DECREASE = "brightness_decrease" @@ -28,7 +28,7 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( toggle_entity.DEVICE_ACTION_TYPES + [TYPE_BRIGHTNESS_INCREASE, TYPE_BRIGHTNESS_DECREASE] ), - vol.Optional(ATTR_BRIGHTNESS): vol.All( + vol.Optional(ATTR_BRIGHTNESS_PCT): vol.All( vol.Coerce(int), vol.Range(min=0, max=100) ), } @@ -57,8 +57,8 @@ async def async_call_action_from_config( data[ATTR_BRIGHTNESS_STEP_PCT] = 10 elif config[CONF_TYPE] == TYPE_BRIGHTNESS_DECREASE: data[ATTR_BRIGHTNESS_STEP_PCT] = -10 - elif ATTR_BRIGHTNESS in config: - data[ATTR_BRIGHTNESS] = config[ATTR_BRIGHTNESS] + elif ATTR_BRIGHTNESS_PCT in config: + data[ATTR_BRIGHTNESS_PCT] = config[ATTR_BRIGHTNESS_PCT] await hass.services.async_call( DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=context @@ -125,7 +125,7 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di return { "extra_fields": vol.Schema( { - vol.Optional(ATTR_BRIGHTNESS): vol.All( + vol.Optional(ATTR_BRIGHTNESS_PCT): vol.All( vol.Coerce(int), vol.Range(min=0, max=100) ) } diff --git a/tests/components/light/test_device_action.py b/tests/components/light/test_device_action.py index 610f61dea52..6cddfc15744 100644 --- a/tests/components/light/test_device_action.py +++ b/tests/components/light/test_device_action.py @@ -126,7 +126,7 @@ async def test_get_action_capabilities_brightness(hass, device_reg, entity_reg): expected_capabilities = { "extra_fields": [ { - "name": "brightness", + "name": "brightness_pct", "optional": True, "type": "integer", "valueMax": 100, @@ -218,7 +218,7 @@ async def test_action(hass, calls): "device_id": "", "entity_id": ent1.entity_id, "type": "turn_on", - "brightness": 75, + "brightness_pct": 75, }, }, ] @@ -273,11 +273,11 @@ async def test_action(hass, calls): assert len(turn_on_calls) == 3 assert turn_on_calls[2].data["entity_id"] == ent1.entity_id - assert turn_on_calls[2].data["brightness"] == 75 + assert turn_on_calls[2].data["brightness_pct"] == 75 hass.bus.async_fire("test_on") await hass.async_block_till_done() assert len(turn_on_calls) == 4 assert turn_on_calls[3].data["entity_id"] == ent1.entity_id - assert "brightness" not in turn_on_calls[3].data + assert "brightness_pct" not in turn_on_calls[3].data From 57dd45318ddc7a2bb2afb78861992cb9af0d220d Mon Sep 17 00:00:00 2001 From: Greg <34967045+gtdiehl@users.noreply.github.com> Date: Sat, 14 Mar 2020 14:27:28 -0700 Subject: [PATCH 25/62] Bump eagle_reader API version to v0.2.4 (#32789) --- homeassistant/components/rainforest_eagle/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rainforest_eagle/manifest.json b/homeassistant/components/rainforest_eagle/manifest.json index cb8e95df42f..0649dfded99 100644 --- a/homeassistant/components/rainforest_eagle/manifest.json +++ b/homeassistant/components/rainforest_eagle/manifest.json @@ -3,7 +3,7 @@ "name": "Rainforest Eagle-200", "documentation": "https://www.home-assistant.io/integrations/rainforest_eagle", "requirements": [ - "eagle200_reader==0.2.1", + "eagle200_reader==0.2.4", "uEagle==0.0.1" ], "dependencies": [], diff --git a/requirements_all.txt b/requirements_all.txt index 44367ce8f11..fd3cedcd1a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -469,7 +469,7 @@ dweepy==0.3.0 dynalite_devices==0.1.32 # homeassistant.components.rainforest_eagle -eagle200_reader==0.2.1 +eagle200_reader==0.2.4 # homeassistant.components.ebusd ebusdpy==0.0.16 From 226a0bcaad96b3e73e540f38ada0d8409dc2289e Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Sat, 14 Mar 2020 13:12:01 -0500 Subject: [PATCH 26/62] Fix directv location of unknown error string (#32807) * Update strings.json * Update en.json --- homeassistant/components/directv/.translations/en.json | 8 ++++---- homeassistant/components/directv/strings.json | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/directv/.translations/en.json b/homeassistant/components/directv/.translations/en.json index e2a8eff5783..667d5168f8d 100644 --- a/homeassistant/components/directv/.translations/en.json +++ b/homeassistant/components/directv/.translations/en.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "already_configured": "DirecTV receiver is already configured" + "already_configured": "DirecTV receiver is already configured", + "unknown": "Unexpected error" }, "error": { - "cannot_connect": "Failed to connect, please try again", - "unknown": "Unexpected error" + "cannot_connect": "Failed to connect, please try again" }, "flow_title": "DirecTV: {name}", "step": { @@ -23,4 +23,4 @@ }, "title": "DirecTV" } -} \ No newline at end of file +} diff --git a/homeassistant/components/directv/strings.json b/homeassistant/components/directv/strings.json index 78316d663bd..e0a5a477ad2 100644 --- a/homeassistant/components/directv/strings.json +++ b/homeassistant/components/directv/strings.json @@ -16,11 +16,11 @@ } }, "error": { - "cannot_connect": "Failed to connect, please try again", - "unknown": "Unexpected error" + "cannot_connect": "Failed to connect, please try again" }, "abort": { - "already_configured": "DirecTV receiver is already configured" + "already_configured": "DirecTV receiver is already configured", + "unknown": "Unexpected error" } } } From 3b1fb2f416b74e54f13093845f9baba252cf0b20 Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Sat, 14 Mar 2020 13:32:38 -0500 Subject: [PATCH 27/62] Remove extra logging from directv init. (#32809) --- homeassistant/components/directv/media_player.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/homeassistant/components/directv/media_player.py b/homeassistant/components/directv/media_player.py index b04ef9fed68..f487e72f694 100644 --- a/homeassistant/components/directv/media_player.py +++ b/homeassistant/components/directv/media_player.py @@ -158,15 +158,6 @@ class DirecTvDevice(MediaPlayerDevice): self._model = MODEL_HOST self._software_version = version_info["stbSoftwareVersion"] - if self._is_client: - _LOGGER.debug( - "Created DirecTV media player for client %s on device %s", - self._name, - device, - ) - else: - _LOGGER.debug("Created DirecTV media player for device %s", self._name) - def update(self): """Retrieve latest state.""" _LOGGER.debug("%s: Updating status", self.entity_id) From 3b84b6e6d578dbf582392a3f164d23435d48e608 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sun, 15 Mar 2020 19:50:23 +0100 Subject: [PATCH 28/62] Require a hyphen in lovelace dashboard url (#32816) * Require a hyphen in lovelace dashboard url * Keep storage dashboards working * register during startup again * Update homeassistant/components/lovelace/dashboard.py Co-Authored-By: Paulus Schoutsen * Comments Co-authored-by: Paulus Schoutsen --- homeassistant/components/lovelace/__init__.py | 47 +++--- homeassistant/components/lovelace/const.py | 2 + .../components/lovelace/dashboard.py | 23 +++ tests/components/lovelace/test_dashboard.py | 147 ++++++++++++++---- 4 files changed, 163 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py index 8ed5e1abfbb..220161fb649 100644 --- a/homeassistant/components/lovelace/__init__.py +++ b/homeassistant/components/lovelace/__init__.py @@ -4,7 +4,7 @@ import logging import voluptuous as vol from homeassistant.components import frontend -from homeassistant.const import CONF_FILENAME, EVENT_HOMEASSISTANT_START +from homeassistant.const import CONF_FILENAME from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import collection, config_validation as cv @@ -143,6 +143,7 @@ async def async_setup(hass, config): return if change_type == collection.CHANGE_ADDED: + existing = hass.data[DOMAIN]["dashboards"].get(url_path) if existing: @@ -167,34 +168,30 @@ async def async_setup(hass, config): except ValueError: _LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path) - async def async_setup_dashboards(event): - """Register dashboards on startup.""" - # Process YAML dashboards - for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items(): - # For now always mode=yaml - config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf) - hass.data[DOMAIN]["dashboards"][url_path] = config + # Process YAML dashboards + for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items(): + # For now always mode=yaml + config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf) + hass.data[DOMAIN]["dashboards"][url_path] = config - try: - _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False) - except ValueError: - _LOGGER.warning("Panel url path %s is not unique", url_path) + try: + _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False) + except ValueError: + _LOGGER.warning("Panel url path %s is not unique", url_path) - # Process storage dashboards - dashboards_collection = dashboard.DashboardsCollection(hass) + # Process storage dashboards + dashboards_collection = dashboard.DashboardsCollection(hass) - dashboards_collection.async_add_listener(storage_dashboard_changed) - await dashboards_collection.async_load() + dashboards_collection.async_add_listener(storage_dashboard_changed) + await dashboards_collection.async_load() - collection.StorageCollectionWebsocket( - dashboards_collection, - "lovelace/dashboards", - "dashboard", - STORAGE_DASHBOARD_CREATE_FIELDS, - STORAGE_DASHBOARD_UPDATE_FIELDS, - ).async_setup(hass, create_list=False) - - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, async_setup_dashboards) + collection.StorageCollectionWebsocket( + dashboards_collection, + "lovelace/dashboards", + "dashboard", + STORAGE_DASHBOARD_CREATE_FIELDS, + STORAGE_DASHBOARD_UPDATE_FIELDS, + ).async_setup(hass, create_list=False) return True diff --git a/homeassistant/components/lovelace/const.py b/homeassistant/components/lovelace/const.py index 7205ae21cbe..8d7ee092cbe 100644 --- a/homeassistant/components/lovelace/const.py +++ b/homeassistant/components/lovelace/const.py @@ -76,6 +76,8 @@ def url_slug(value: Any) -> str: """Validate value is a valid url slug.""" if value is None: raise vol.Invalid("Slug should not be None") + if "-" not in value: + raise vol.Invalid("Url path needs to contain a hyphen (-)") str_value = str(value) slg = slugify(str_value, separator="-") if str_value == slg: diff --git a/homeassistant/components/lovelace/dashboard.py b/homeassistant/components/lovelace/dashboard.py index f32ac2ed1ff..38740672914 100644 --- a/homeassistant/components/lovelace/dashboard.py +++ b/homeassistant/components/lovelace/dashboard.py @@ -3,6 +3,7 @@ from abc import ABC, abstractmethod import logging import os import time +from typing import Optional, cast import voluptuous as vol @@ -230,8 +231,30 @@ class DashboardsCollection(collection.StorageCollection): _LOGGER, ) + async def _async_load_data(self) -> Optional[dict]: + """Load the data.""" + data = await self.store.async_load() + + if data is None: + return cast(Optional[dict], data) + + updated = False + + for item in data["items"] or []: + if "-" not in item[CONF_URL_PATH]: + updated = True + item[CONF_URL_PATH] = f"lovelace-{item[CONF_URL_PATH]}" + + if updated: + await self.store.async_save(data) + + return cast(Optional[dict], data) + async def _process_create_data(self, data: dict) -> dict: """Validate the config is valid.""" + if "-" not in data[CONF_URL_PATH]: + raise vol.Invalid("Url path needs to contain a hyphen (-)") + if data[CONF_URL_PATH] in self.hass.data[DATA_PANELS]: raise vol.Invalid("Panel url path needs to be unique") diff --git a/tests/components/lovelace/test_dashboard.py b/tests/components/lovelace/test_dashboard.py index 9bfe3da38c9..1effb10be27 100644 --- a/tests/components/lovelace/test_dashboard.py +++ b/tests/components/lovelace/test_dashboard.py @@ -5,10 +5,13 @@ import pytest from homeassistant.components import frontend from homeassistant.components.lovelace import const, dashboard -from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.setup import async_setup_component -from tests.common import async_capture_events, get_system_health_info +from tests.common import ( + assert_setup_component, + async_capture_events, + get_system_health_info, +) async def test_lovelace_from_storage(hass, hass_ws_client, hass_storage): @@ -224,8 +227,6 @@ async def test_dashboard_from_yaml(hass, hass_ws_client, url_path): } }, ) - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() assert hass.data[frontend.DATA_PANELS]["test-panel"].config == {"mode": "yaml"} assert hass.data[frontend.DATA_PANELS]["test-panel-no-sidebar"].config == { "mode": "yaml" @@ -306,11 +307,32 @@ async def test_dashboard_from_yaml(hass, hass_ws_client, url_path): assert len(events) == 1 +async def test_wrong_key_dashboard_from_yaml(hass): + """Test we don't load lovelace dashboard without hyphen config from yaml.""" + with assert_setup_component(0): + assert not await async_setup_component( + hass, + "lovelace", + { + "lovelace": { + "dashboards": { + "testpanel": { + "mode": "yaml", + "filename": "bla.yaml", + "title": "Test Panel", + "icon": "mdi:test-icon", + "show_in_sidebar": False, + "require_admin": True, + } + } + } + }, + ) + + async def test_storage_dashboards(hass, hass_ws_client, hass_storage): """Test we load lovelace config from storage.""" assert await async_setup_component(hass, "lovelace", {}) - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() assert hass.data[frontend.DATA_PANELS]["lovelace"].config == {"mode": "storage"} client = await hass_ws_client(hass) @@ -321,12 +343,24 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): assert response["success"] assert response["result"] == [] - # Add a dashboard + # Add a wrong dashboard await client.send_json( { "id": 6, "type": "lovelace/dashboards/create", - "url_path": "created_url_path", + "url_path": "path", + "title": "Test path without hyphen", + } + ) + response = await client.receive_json() + assert not response["success"] + + # Add a dashboard + await client.send_json( + { + "id": 7, + "type": "lovelace/dashboards/create", + "url_path": "created-url-path", "require_admin": True, "title": "New Title", "icon": "mdi:map", @@ -339,10 +373,11 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): assert response["result"]["icon"] == "mdi:map" dashboard_id = response["result"]["id"] + dashboard_path = response["result"]["url_path"] - assert "created_url_path" in hass.data[frontend.DATA_PANELS] + assert "created-url-path" in hass.data[frontend.DATA_PANELS] - await client.send_json({"id": 7, "type": "lovelace/dashboards/list"}) + await client.send_json({"id": 8, "type": "lovelace/dashboards/list"}) response = await client.receive_json() assert response["success"] assert len(response["result"]) == 1 @@ -354,7 +389,7 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): # Fetch config await client.send_json( - {"id": 8, "type": "lovelace/config", "url_path": "created_url_path"} + {"id": 9, "type": "lovelace/config", "url_path": "created-url-path"} ) response = await client.receive_json() assert not response["success"] @@ -365,22 +400,22 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): await client.send_json( { - "id": 9, + "id": 10, "type": "lovelace/config/save", - "url_path": "created_url_path", + "url_path": "created-url-path", "config": {"yo": "hello"}, } ) response = await client.receive_json() assert response["success"] - assert hass_storage[dashboard.CONFIG_STORAGE_KEY.format(dashboard_id)]["data"] == { - "config": {"yo": "hello"} - } + assert hass_storage[dashboard.CONFIG_STORAGE_KEY.format(dashboard_path)][ + "data" + ] == {"config": {"yo": "hello"}} assert len(events) == 1 - assert events[0].data["url_path"] == "created_url_path" + assert events[0].data["url_path"] == "created-url-path" await client.send_json( - {"id": 10, "type": "lovelace/config", "url_path": "created_url_path"} + {"id": 11, "type": "lovelace/config", "url_path": "created-url-path"} ) response = await client.receive_json() assert response["success"] @@ -389,7 +424,7 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): # Update a dashboard await client.send_json( { - "id": 11, + "id": 12, "type": "lovelace/dashboards/update", "dashboard_id": dashboard_id, "require_admin": False, @@ -401,19 +436,19 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): response = await client.receive_json() assert response["success"] assert response["result"]["mode"] == "storage" - assert response["result"]["url_path"] == "created_url_path" + assert response["result"]["url_path"] == "created-url-path" assert response["result"]["title"] == "Updated Title" assert response["result"]["icon"] == "mdi:updated" assert response["result"]["show_in_sidebar"] is False assert response["result"]["require_admin"] is False # List dashboards again and make sure we see latest config - await client.send_json({"id": 12, "type": "lovelace/dashboards/list"}) + await client.send_json({"id": 13, "type": "lovelace/dashboards/list"}) response = await client.receive_json() assert response["success"] assert len(response["result"]) == 1 assert response["result"][0]["mode"] == "storage" - assert response["result"][0]["url_path"] == "created_url_path" + assert response["result"][0]["url_path"] == "created-url-path" assert response["result"][0]["title"] == "Updated Title" assert response["result"][0]["icon"] == "mdi:updated" assert response["result"][0]["show_in_sidebar"] is False @@ -421,22 +456,75 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): # Add dashboard with existing url path await client.send_json( - {"id": 13, "type": "lovelace/dashboards/create", "url_path": "created_url_path"} + {"id": 14, "type": "lovelace/dashboards/create", "url_path": "created-url-path"} ) response = await client.receive_json() assert not response["success"] # Delete dashboards await client.send_json( - {"id": 14, "type": "lovelace/dashboards/delete", "dashboard_id": dashboard_id} + {"id": 15, "type": "lovelace/dashboards/delete", "dashboard_id": dashboard_id} ) response = await client.receive_json() assert response["success"] - assert "created_url_path" not in hass.data[frontend.DATA_PANELS] + assert "created-url-path" not in hass.data[frontend.DATA_PANELS] assert dashboard.CONFIG_STORAGE_KEY.format(dashboard_id) not in hass_storage +async def test_storage_dashboard_migrate(hass, hass_ws_client, hass_storage): + """Test changing url path from storage config.""" + hass_storage[dashboard.DASHBOARDS_STORAGE_KEY] = { + "key": "lovelace_dashboards", + "version": 1, + "data": { + "items": [ + { + "icon": "mdi:tools", + "id": "tools", + "mode": "storage", + "require_admin": True, + "show_in_sidebar": True, + "title": "Tools", + "url_path": "tools", + }, + { + "icon": "mdi:tools", + "id": "tools2", + "mode": "storage", + "require_admin": True, + "show_in_sidebar": True, + "title": "Tools", + "url_path": "dashboard-tools", + }, + ] + }, + } + + assert await async_setup_component(hass, "lovelace", {}) + + client = await hass_ws_client(hass) + + # Fetch data + await client.send_json({"id": 5, "type": "lovelace/dashboards/list"}) + response = await client.receive_json() + assert response["success"] + without_hyphen, with_hyphen = response["result"] + + assert without_hyphen["icon"] == "mdi:tools" + assert without_hyphen["id"] == "tools" + assert without_hyphen["mode"] == "storage" + assert without_hyphen["require_admin"] + assert without_hyphen["show_in_sidebar"] + assert without_hyphen["title"] == "Tools" + assert without_hyphen["url_path"] == "lovelace-tools" + + assert ( + with_hyphen + == hass_storage[dashboard.DASHBOARDS_STORAGE_KEY]["data"]["items"][1] + ) + + async def test_websocket_list_dashboards(hass, hass_ws_client): """Test listing dashboards both storage + YAML.""" assert await async_setup_component( @@ -455,9 +543,6 @@ async def test_websocket_list_dashboards(hass, hass_ws_client): }, ) - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() - client = await hass_ws_client(hass) # Create a storage dashboard @@ -465,7 +550,7 @@ async def test_websocket_list_dashboards(hass, hass_ws_client): { "id": 6, "type": "lovelace/dashboards/create", - "url_path": "created_url_path", + "url_path": "created-url-path", "title": "Test Storage", } ) @@ -473,7 +558,7 @@ async def test_websocket_list_dashboards(hass, hass_ws_client): assert response["success"] # List dashboards - await client.send_json({"id": 7, "type": "lovelace/dashboards/list"}) + await client.send_json({"id": 8, "type": "lovelace/dashboards/list"}) response = await client.receive_json() assert response["success"] assert len(response["result"]) == 2 @@ -486,4 +571,4 @@ async def test_websocket_list_dashboards(hass, hass_ws_client): assert without_sb["mode"] == "storage" assert without_sb["title"] == "Test Storage" - assert without_sb["url_path"] == "created_url_path" + assert without_sb["url_path"] == "created-url-path" From 875671cc2bff3888019c69ad885efe09d3ae4c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 15 Mar 2020 12:41:19 +0100 Subject: [PATCH 29/62] Add Netatmo Home Coach as model (#32829) --- homeassistant/components/netatmo/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/netatmo/const.py b/homeassistant/components/netatmo/const.py index 9216a678e68..0a0c9575600 100644 --- a/homeassistant/components/netatmo/const.py +++ b/homeassistant/components/netatmo/const.py @@ -19,6 +19,7 @@ MODELS = { "NAModule4": "Smart Additional Indoor module", "NAModule3": "Smart Rain Gauge", "NAModule2": "Smart Anemometer", + "NHC": "Home Coach", } AUTH = "netatmo_auth" From 42998f898b1c167bf47ecac94c4cfc986f83abfc Mon Sep 17 00:00:00 2001 From: SukramJ Date: Sun, 15 Mar 2020 19:01:50 +0100 Subject: [PATCH 30/62] Add SF transition to HmIP-BSL and remove obsolete code in HMIPC (#32833) --- homeassistant/components/homematicip_cloud/hap.py | 5 ----- homeassistant/components/homematicip_cloud/light.py | 3 ++- .../components/homematicip_cloud/test_alarm_control_panel.py | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index 0d6fc726050..dd85827f1ae 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -137,11 +137,6 @@ class HomematicipHAP: job = self.hass.async_create_task(self.get_state()) job.add_done_callback(self.get_state_finished) self._accesspoint_connected = True - else: - # Update home with the given json from arg[0], - # without devices and groups. - - self.home.update_home_only(args[0]) @callback def async_create_entity(self, *args, **kwargs) -> None: diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index 4e081f4d8fa..cead186db95 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -20,6 +20,7 @@ from homeassistant.components.light import ( ATTR_TRANSITION, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, + SUPPORT_TRANSITION, Light, ) from homeassistant.config_entries import ConfigEntry @@ -197,7 +198,7 @@ class HomematicipNotificationLight(HomematicipGenericDevice, Light): @property def supported_features(self) -> int: """Flag supported features.""" - return SUPPORT_BRIGHTNESS | SUPPORT_COLOR + return SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_TRANSITION @property def unique_id(self) -> str: diff --git a/tests/components/homematicip_cloud/test_alarm_control_panel.py b/tests/components/homematicip_cloud/test_alarm_control_panel.py index 23e5beb40eb..92782f2cbb2 100644 --- a/tests/components/homematicip_cloud/test_alarm_control_panel.py +++ b/tests/components/homematicip_cloud/test_alarm_control_panel.py @@ -31,9 +31,7 @@ async def _async_manipulate_security_zones( internal_zone = home.search_group_by_id(internal_zone_id) internal_zone.active = internal_active - home.from_json(json) - home._get_functionalHomes(json) - home._load_functionalChannels() + home.update_home_only(json) home.fire_update_event(json) await hass.async_block_till_done() From d88275d6d2d8dfefdb63db49b0029d51085f53cb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 15 Mar 2020 11:51:02 -0700 Subject: [PATCH 31/62] Make sure panel_custom won't crash on invalid data (#32835) * Make sure panel_custom won't crash on invalid data * Add a test --- homeassistant/components/hassio/manifest.json | 3 ++- homeassistant/components/panel_custom/__init__.py | 15 +++++++++------ tests/components/panel_custom/test_init.py | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/hassio/manifest.json b/homeassistant/components/hassio/manifest.json index d3dd7dc9c94..cd004db4c93 100644 --- a/homeassistant/components/hassio/manifest.json +++ b/homeassistant/components/hassio/manifest.json @@ -3,6 +3,7 @@ "name": "Hass.io", "documentation": "https://www.home-assistant.io/hassio", "requirements": [], - "dependencies": ["http", "panel_custom"], + "dependencies": ["http"], + "after_dependencies": ["panel_custom"], "codeowners": ["@home-assistant/hass-io"] } diff --git a/homeassistant/components/panel_custom/__init__.py b/homeassistant/components/panel_custom/__init__.py index cf861992bd6..82572d7396c 100644 --- a/homeassistant/components/panel_custom/__init__.py +++ b/homeassistant/components/panel_custom/__init__.py @@ -146,8 +146,6 @@ async def async_setup(hass, config): if DOMAIN not in config: return True - success = False - for panel in config[DOMAIN]: name = panel[CONF_COMPONENT_NAME] @@ -182,8 +180,13 @@ async def async_setup(hass, config): hass.http.register_static_path(url, panel_path) kwargs["html_url"] = url - await async_register_panel(hass, **kwargs) + try: + await async_register_panel(hass, **kwargs) + except ValueError as err: + _LOGGER.error( + "Unable to register panel %s: %s", + panel.get(CONF_SIDEBAR_TITLE, name), + err, + ) - success = True - - return success + return True diff --git a/tests/components/panel_custom/test_init.py b/tests/components/panel_custom/test_init.py index e6bc56d080e..5f7161089f6 100644 --- a/tests/components/panel_custom/test_init.py +++ b/tests/components/panel_custom/test_init.py @@ -181,3 +181,17 @@ async def test_url_option_conflict(hass): for config in to_try: result = await setup.async_setup_component(hass, "panel_custom", config) assert not result + + +async def test_url_path_conflict(hass): + """Test config with overlapping url path.""" + assert await setup.async_setup_component( + hass, + "panel_custom", + { + "panel_custom": [ + {"name": "todo-mvc", "js_url": "/local/bla.js"}, + {"name": "todo-mvc", "js_url": "/local/bla.js"}, + ] + }, + ) From a3d74651a89d817e8c140e97695b40d5d9928511 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 15 Mar 2020 11:56:56 -0700 Subject: [PATCH 32/62] Bumped version to 0.107.0b4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d4a6dd1484f..c8817e4c344 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b3" +PATCH_VERSION = "0b4" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From cee72724b6a3248d7ce44f912ce601350f27a110 Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Mon, 16 Mar 2020 10:04:12 +0000 Subject: [PATCH 33/62] Ensure unique_ids for all evohome thermostats (#32604) * initial commit * small tweak --- homeassistant/components/evohome/climate.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index 8b65d837171..b7899afdd7b 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -149,7 +149,12 @@ class EvoZone(EvoChild, EvoClimateDevice): """Initialize a Honeywell TCC Zone.""" super().__init__(evo_broker, evo_device) - self._unique_id = evo_device.zoneId + if evo_device.modelType.startswith("VisionProWifi"): + # this system does not have a distinct ID for the zone + self._unique_id = f"{evo_device.zoneId}z" + else: + self._unique_id = evo_device.zoneId + self._name = evo_device.name self._icon = "mdi:radiator" From fb1ba86b0866ee32c8bfa9edd4c395c8fbc43cb8 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 15 Mar 2020 20:42:07 -0700 Subject: [PATCH 34/62] Bump teslajsonpy to 0.5.1 (#32827) --- homeassistant/components/tesla/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tesla/manifest.json b/homeassistant/components/tesla/manifest.json index 21605d16579..950a860b308 100644 --- a/homeassistant/components/tesla/manifest.json +++ b/homeassistant/components/tesla/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tesla", "requirements": [ - "teslajsonpy==0.4.0" + "teslajsonpy==0.5.1" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index fd3cedcd1a1..ade07e108d1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1996,7 +1996,7 @@ temperusb==1.5.3 # tensorflow==1.13.2 # homeassistant.components.tesla -teslajsonpy==0.4.0 +teslajsonpy==0.5.1 # homeassistant.components.thermoworks_smoke thermoworks_smoke==0.1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 45c1de0dadf..6ab064e6c3b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -684,7 +684,7 @@ sunwatcher==0.2.1 tellduslive==0.10.10 # homeassistant.components.tesla -teslajsonpy==0.4.0 +teslajsonpy==0.5.1 # homeassistant.components.toon toonapilib==3.2.4 From 104665d8499bd64b263575a895c257e0f08af3a2 Mon Sep 17 00:00:00 2001 From: Kit Klein <33464407+kit-klein@users.noreply.github.com> Date: Sun, 15 Mar 2020 20:11:26 -0400 Subject: [PATCH 35/62] Ignore the ignored konnected config entries (#32845) * ignore the ignored konnected config entries * key off data instead of source --- homeassistant/components/konnected/__init__.py | 1 + tests/components/konnected/test_init.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/homeassistant/components/konnected/__init__.py b/homeassistant/components/konnected/__init__.py index 94508b01483..72d82fd31be 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -306,6 +306,7 @@ class KonnectedView(HomeAssistantView): [ entry.data[CONF_ACCESS_TOKEN] for entry in hass.config_entries.async_entries(DOMAIN) + if entry.data.get(CONF_ACCESS_TOKEN) ] ) if auth is None or not next( diff --git a/tests/components/konnected/test_init.py b/tests/components/konnected/test_init.py index 907f83cd981..e410aa9d60a 100644 --- a/tests/components/konnected/test_init.py +++ b/tests/components/konnected/test_init.py @@ -582,6 +582,10 @@ async def test_state_updates(hass, aiohttp_client, mock_panel): ) entry.add_to_hass(hass) + # Add empty data field to ensure we process it correctly (possible if entry is ignored) + entry = MockConfigEntry(domain="konnected", title="Konnected Alarm Panel", data={},) + entry.add_to_hass(hass) + assert ( await async_setup_component( hass, From 65423bb62b7aa32987837d63d547148e9b362637 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Sun, 15 Mar 2020 16:50:23 -0400 Subject: [PATCH 36/62] Bump insteonplm to 0.16.8 (#32847) --- homeassistant/components/insteon/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json index 69c35477b8d..64c4b6a67be 100644 --- a/homeassistant/components/insteon/manifest.json +++ b/homeassistant/components/insteon/manifest.json @@ -2,7 +2,7 @@ "domain": "insteon", "name": "Insteon", "documentation": "https://www.home-assistant.io/integrations/insteon", - "requirements": ["insteonplm==0.16.7"], + "requirements": ["insteonplm==0.16.8"], "dependencies": [], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index ade07e108d1..b4f00a10ccf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -747,7 +747,7 @@ incomfort-client==0.4.0 influxdb==5.2.3 # homeassistant.components.insteon -insteonplm==0.16.7 +insteonplm==0.16.8 # homeassistant.components.iperf3 iperf3==0.1.11 From b6a3bcf87f0b775bd503e863d87c3bf1808a650b Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 16 Mar 2020 11:40:21 +0100 Subject: [PATCH 37/62] Update pyozw 0.1.9 (#32864) --- homeassistant/components/zwave/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave/manifest.json b/homeassistant/components/zwave/manifest.json index 1fc6401f25b..81978aa96cd 100644 --- a/homeassistant/components/zwave/manifest.json +++ b/homeassistant/components/zwave/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave", - "requirements": ["homeassistant-pyozw==0.1.8", "pydispatcher==2.0.5"], + "requirements": ["homeassistant-pyozw==0.1.9", "pydispatcher==2.0.5"], "dependencies": [], "codeowners": ["@home-assistant/z-wave"] } diff --git a/requirements_all.txt b/requirements_all.txt index b4f00a10ccf..85bd69463af 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -699,7 +699,7 @@ holidays==0.10.1 home-assistant-frontend==20200313.0 # homeassistant.components.zwave -homeassistant-pyozw==0.1.8 +homeassistant-pyozw==0.1.9 # homeassistant.components.homematicip_cloud homematicip==0.10.17 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6ab064e6c3b..4ded6f8f917 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -266,7 +266,7 @@ holidays==0.10.1 home-assistant-frontend==20200313.0 # homeassistant.components.zwave -homeassistant-pyozw==0.1.8 +homeassistant-pyozw==0.1.9 # homeassistant.components.homematicip_cloud homematicip==0.10.17 From f2c3f76b8ee904cf47115e217503e683c4874c9c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 16 Mar 2020 13:30:59 +0100 Subject: [PATCH 38/62] Updated frontend to 20200316.0 (#32866) --- 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 2817b744d72..211e5bc7e84 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==20200313.0" + "home-assistant-frontend==20200316.0" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 49c9d017e3a..96cf257e61e 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 hass-nabucasa==0.32.2 -home-assistant-frontend==20200313.0 +home-assistant-frontend==20200316.0 importlib-metadata==1.5.0 jinja2>=2.10.3 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 85bd69463af..f51cef5125f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200313.0 +home-assistant-frontend==20200316.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4ded6f8f917..2ba38990639 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -263,7 +263,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200313.0 +home-assistant-frontend==20200316.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.9 From 7f6b3c11308a214d522f9e24635e89643c2e97cc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 16 Mar 2020 13:59:27 +0100 Subject: [PATCH 39/62] Bumped version to 0.107.0b5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index c8817e4c344..7ff286fd460 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b4" +PATCH_VERSION = "0b5" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From ccb34083fe518a8635f10066a122497cdc17c866 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 16 Mar 2020 20:25:23 +0100 Subject: [PATCH 40/62] Add lovelace reload service for yaml resources (#32865) * Lovelace add reload service for yaml resources * Clean up imports * Comments --- homeassistant/components/lovelace/__init__.py | 64 +++++++++++++++---- homeassistant/components/lovelace/const.py | 3 + .../components/lovelace/services.yaml | 4 ++ 3 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 homeassistant/components/lovelace/services.yaml diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py index 220161fb649..95508c2f8f3 100644 --- a/homeassistant/components/lovelace/__init__.py +++ b/homeassistant/components/lovelace/__init__.py @@ -4,10 +4,14 @@ import logging import voluptuous as vol from homeassistant.components import frontend +from homeassistant.config import async_hass_config_yaml, async_process_component_config from homeassistant.const import CONF_FILENAME from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import collection, config_validation as cv +from homeassistant.helpers.service import async_register_admin_service +from homeassistant.helpers.typing import ConfigType, HomeAssistantType, ServiceCallType +from homeassistant.loader import async_get_integration from homeassistant.util import sanitize_filename from . import dashboard, resources, websocket @@ -25,8 +29,10 @@ from .const import ( MODE_STORAGE, MODE_YAML, RESOURCE_CREATE_FIELDS, + RESOURCE_RELOAD_SERVICE_SCHEMA, RESOURCE_SCHEMA, RESOURCE_UPDATE_FIELDS, + SERVICE_RELOAD_RESOURCES, STORAGE_DASHBOARD_CREATE_FIELDS, STORAGE_DASHBOARD_UPDATE_FIELDS, url_slug, @@ -62,29 +68,41 @@ CONFIG_SCHEMA = vol.Schema( ) -async def async_setup(hass, config): +async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the Lovelace commands.""" mode = config[DOMAIN][CONF_MODE] yaml_resources = config[DOMAIN].get(CONF_RESOURCES) frontend.async_register_built_in_panel(hass, DOMAIN, config={"mode": mode}) + async def reload_resources_service_handler(service_call: ServiceCallType) -> None: + """Reload yaml resources.""" + try: + conf = await async_hass_config_yaml(hass) + except HomeAssistantError as err: + _LOGGER.error(err) + return + + integration = await async_get_integration(hass, DOMAIN) + + config = await async_process_component_config(hass, conf, integration) + + resource_collection = await create_yaml_resource_col( + hass, config[DOMAIN].get(CONF_RESOURCES) + ) + hass.data[DOMAIN]["resources"] = resource_collection + if mode == MODE_YAML: default_config = dashboard.LovelaceYAML(hass, None, None) + resource_collection = await create_yaml_resource_col(hass, yaml_resources) - if yaml_resources is None: - try: - ll_conf = await default_config.async_load(False) - except HomeAssistantError: - pass - else: - if CONF_RESOURCES in ll_conf: - _LOGGER.warning( - "Resources need to be specified in your configuration.yaml. Please see the docs." - ) - yaml_resources = ll_conf[CONF_RESOURCES] - - resource_collection = resources.ResourceYAMLCollection(yaml_resources or []) + async_register_admin_service( + hass, + DOMAIN, + SERVICE_RELOAD_RESOURCES, + reload_resources_service_handler, + schema=RESOURCE_RELOAD_SERVICE_SCHEMA, + ) else: default_config = dashboard.LovelaceStorage(hass, None) @@ -196,6 +214,24 @@ async def async_setup(hass, config): return True +async def create_yaml_resource_col(hass, yaml_resources): + """Create yaml resources collection.""" + if yaml_resources is None: + default_config = dashboard.LovelaceYAML(hass, None, None) + try: + ll_conf = await default_config.async_load(False) + except HomeAssistantError: + pass + else: + if CONF_RESOURCES in ll_conf: + _LOGGER.warning( + "Resources need to be specified in your configuration.yaml. Please see the docs." + ) + yaml_resources = ll_conf[CONF_RESOURCES] + + return resources.ResourceYAMLCollection(yaml_resources or []) + + async def system_health_info(hass): """Get info for the info page.""" return await hass.data[DOMAIN]["dashboards"][None].async_get_info() diff --git a/homeassistant/components/lovelace/const.py b/homeassistant/components/lovelace/const.py index 8d7ee092cbe..a093c672dd6 100644 --- a/homeassistant/components/lovelace/const.py +++ b/homeassistant/components/lovelace/const.py @@ -41,6 +41,9 @@ RESOURCE_UPDATE_FIELDS = { vol.Optional(CONF_URL): cv.string, } +SERVICE_RELOAD_RESOURCES = "reload_resources" +RESOURCE_RELOAD_SERVICE_SCHEMA = vol.Schema({}) + CONF_TITLE = "title" CONF_REQUIRE_ADMIN = "require_admin" CONF_SHOW_IN_SIDEBAR = "show_in_sidebar" diff --git a/homeassistant/components/lovelace/services.yaml b/homeassistant/components/lovelace/services.yaml new file mode 100644 index 00000000000..1147f287e59 --- /dev/null +++ b/homeassistant/components/lovelace/services.yaml @@ -0,0 +1,4 @@ +# Describes the format for available lovelace services + +reload_resources: + description: Reload Lovelace resources from yaml configuration. From 661f1b69f2e903fdb8ba1ea205b5ba100b60d811 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 16 Mar 2020 15:29:14 -0400 Subject: [PATCH 41/62] Bump ZHA quirks to 0.0.37 (#32867) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index fec85625ee4..19940eaea00 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ "bellows-homeassistant==0.14.0", - "zha-quirks==0.0.36", + "zha-quirks==0.0.37", "zigpy-cc==0.1.0", "zigpy-deconz==0.7.0", "zigpy-homeassistant==0.16.0", diff --git a/requirements_all.txt b/requirements_all.txt index f51cef5125f..c11c4e76749 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2158,7 +2158,7 @@ zengge==0.2 zeroconf==0.24.5 # homeassistant.components.zha -zha-quirks==0.0.36 +zha-quirks==0.0.37 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2ba38990639..97dc27163e9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -747,7 +747,7 @@ yahooweather==0.10 zeroconf==0.24.5 # homeassistant.components.zha -zha-quirks==0.0.36 +zha-quirks==0.0.37 # homeassistant.components.zha zigpy-cc==0.1.0 From 03b1c6ddee35f682a03e79219f0766972a5076be Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 16 Mar 2020 14:47:44 -0700 Subject: [PATCH 42/62] Remove group as a dependency from entity integrations (#32870) * remove group dependency * Update device sun light trigger * Add zone dep back to device tracker --- homeassistant/components/automation/manifest.json | 3 ++- homeassistant/components/cover/manifest.json | 2 +- .../components/device_sun_light_trigger/manifest.json | 3 ++- homeassistant/components/device_tracker/manifest.json | 3 ++- homeassistant/components/fan/manifest.json | 2 +- homeassistant/components/light/manifest.json | 2 +- homeassistant/components/lock/manifest.json | 2 +- homeassistant/components/plant/manifest.json | 2 +- homeassistant/components/remote/manifest.json | 2 +- homeassistant/components/script/manifest.json | 2 +- homeassistant/components/switch/manifest.json | 2 +- homeassistant/components/vacuum/manifest.json | 2 +- script/hassfest/dependencies.py | 4 ++-- script/hassfest/manifest.py | 4 ++-- 14 files changed, 19 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/automation/manifest.json b/homeassistant/components/automation/manifest.json index 34261cba5a9..48d8c58dfe1 100644 --- a/homeassistant/components/automation/manifest.json +++ b/homeassistant/components/automation/manifest.json @@ -3,7 +3,8 @@ "name": "Automation", "documentation": "https://www.home-assistant.io/integrations/automation", "requirements": [], - "dependencies": ["device_automation", "group", "webhook"], + "dependencies": [], + "after_dependencies": ["device_automation", "webhook"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal" } diff --git a/homeassistant/components/cover/manifest.json b/homeassistant/components/cover/manifest.json index aa43e934dc9..788d72b707f 100644 --- a/homeassistant/components/cover/manifest.json +++ b/homeassistant/components/cover/manifest.json @@ -3,7 +3,7 @@ "name": "Cover", "documentation": "https://www.home-assistant.io/integrations/cover", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": ["@home-assistant/core"], "quality_scale": "internal" } diff --git a/homeassistant/components/device_sun_light_trigger/manifest.json b/homeassistant/components/device_sun_light_trigger/manifest.json index 702f8704564..edeb10dcec2 100644 --- a/homeassistant/components/device_sun_light_trigger/manifest.json +++ b/homeassistant/components/device_sun_light_trigger/manifest.json @@ -3,7 +3,8 @@ "name": "Presence-based Lights", "documentation": "https://www.home-assistant.io/integrations/device_sun_light_trigger", "requirements": [], - "dependencies": ["device_tracker", "group", "light", "person"], + "dependencies": [], + "after_dependencies": ["device_tracker", "group", "light", "person"], "codeowners": [], "quality_scale": "internal" } diff --git a/homeassistant/components/device_tracker/manifest.json b/homeassistant/components/device_tracker/manifest.json index 35b9a4a3fdb..4bd9846f76d 100644 --- a/homeassistant/components/device_tracker/manifest.json +++ b/homeassistant/components/device_tracker/manifest.json @@ -3,7 +3,8 @@ "name": "Device Tracker", "documentation": "https://www.home-assistant.io/integrations/device_tracker", "requirements": [], - "dependencies": ["group", "zone"], + "dependencies": ["zone"], + "after_dependencies": [], "codeowners": [], "quality_scale": "internal" } diff --git a/homeassistant/components/fan/manifest.json b/homeassistant/components/fan/manifest.json index 02ed368feac..53b7873612c 100644 --- a/homeassistant/components/fan/manifest.json +++ b/homeassistant/components/fan/manifest.json @@ -3,7 +3,7 @@ "name": "Fan", "documentation": "https://www.home-assistant.io/integrations/fan", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": [], "quality_scale": "internal" } diff --git a/homeassistant/components/light/manifest.json b/homeassistant/components/light/manifest.json index e0a9652a10c..64e21654afd 100644 --- a/homeassistant/components/light/manifest.json +++ b/homeassistant/components/light/manifest.json @@ -3,7 +3,7 @@ "name": "Light", "documentation": "https://www.home-assistant.io/integrations/light", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": [], "quality_scale": "internal" } diff --git a/homeassistant/components/lock/manifest.json b/homeassistant/components/lock/manifest.json index ab05166d15f..cd2fdf27f2d 100644 --- a/homeassistant/components/lock/manifest.json +++ b/homeassistant/components/lock/manifest.json @@ -3,7 +3,7 @@ "name": "Lock", "documentation": "https://www.home-assistant.io/integrations/lock", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": [], "quality_scale": "internal" } diff --git a/homeassistant/components/plant/manifest.json b/homeassistant/components/plant/manifest.json index de5f0c1f880..f0ff20f3759 100644 --- a/homeassistant/components/plant/manifest.json +++ b/homeassistant/components/plant/manifest.json @@ -3,7 +3,7 @@ "name": "Plant Monitor", "documentation": "https://www.home-assistant.io/integrations/plant", "requirements": [], - "dependencies": ["group", "zone"], + "dependencies": [], "after_dependencies": ["recorder"], "codeowners": ["@ChristianKuehnel"], "quality_scale": "internal" diff --git a/homeassistant/components/remote/manifest.json b/homeassistant/components/remote/manifest.json index 24616bc5947..8f559b758d6 100644 --- a/homeassistant/components/remote/manifest.json +++ b/homeassistant/components/remote/manifest.json @@ -3,6 +3,6 @@ "name": "Remote", "documentation": "https://www.home-assistant.io/integrations/remote", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": [] } diff --git a/homeassistant/components/script/manifest.json b/homeassistant/components/script/manifest.json index dac37110172..ce9899f021c 100644 --- a/homeassistant/components/script/manifest.json +++ b/homeassistant/components/script/manifest.json @@ -3,7 +3,7 @@ "name": "Scripts", "documentation": "https://www.home-assistant.io/integrations/script", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": ["@home-assistant/core"], "quality_scale": "internal" } diff --git a/homeassistant/components/switch/manifest.json b/homeassistant/components/switch/manifest.json index b14c8ca48d5..37cdf77172c 100644 --- a/homeassistant/components/switch/manifest.json +++ b/homeassistant/components/switch/manifest.json @@ -3,7 +3,7 @@ "name": "Switch", "documentation": "https://www.home-assistant.io/integrations/switch", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": [], "quality_scale": "internal" } diff --git a/homeassistant/components/vacuum/manifest.json b/homeassistant/components/vacuum/manifest.json index 895311ae5b6..a6f7ddb2bda 100644 --- a/homeassistant/components/vacuum/manifest.json +++ b/homeassistant/components/vacuum/manifest.json @@ -3,6 +3,6 @@ "name": "Vacuum", "documentation": "https://www.home-assistant.io/integrations/vacuum", "requirements": [], - "dependencies": ["group"], + "dependencies": [], "codeowners": [] } diff --git a/script/hassfest/dependencies.py b/script/hassfest/dependencies.py index 934400533e1..660e8065966 100644 --- a/script/hassfest/dependencies.py +++ b/script/hassfest/dependencies.py @@ -156,7 +156,7 @@ def calc_allowed_references(integration: Integration) -> Set[str]: """Return a set of allowed references.""" allowed_references = ( ALLOWED_USED_COMPONENTS - | set(integration.manifest["dependencies"]) + | set(integration.manifest.get("dependencies", [])) | set(integration.manifest.get("after_dependencies", [])) ) @@ -250,7 +250,7 @@ def validate(integrations: Dict[str, Integration], config): validate_dependencies(integrations, integration) # check that all referenced dependencies exist - for dep in integration.manifest["dependencies"]: + for dep in integration.manifest.get("dependencies", []): if dep not in integrations: integration.add_error( "dependencies", f"Dependency {dep} does not exist" diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 7852953dc92..758279cabf8 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -52,8 +52,8 @@ MANIFEST_SCHEMA = vol.Schema( vol.Url(), documentation_url # pylint: disable=no-value-for-parameter ), vol.Optional("quality_scale"): vol.In(SUPPORTED_QUALITY_SCALES), - vol.Required("requirements"): [str], - vol.Required("dependencies"): [str], + vol.Optional("requirements"): [str], + vol.Optional("dependencies"): [str], vol.Optional("after_dependencies"): [str], vol.Required("codeowners"): [str], vol.Optional("logo"): vol.Url(), # pylint: disable=no-value-for-parameter From a7aca106681cd340fd2c9a25b3756dd7f52c21eb Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 16 Mar 2020 20:08:00 +0100 Subject: [PATCH 43/62] Lovelace: storage key based on id instead of url_path (#32873) * Fix storage key based on url_path * Fix test --- homeassistant/components/lovelace/dashboard.py | 2 +- tests/components/lovelace/test_dashboard.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/lovelace/dashboard.py b/homeassistant/components/lovelace/dashboard.py index 38740672914..cdb104a150b 100644 --- a/homeassistant/components/lovelace/dashboard.py +++ b/homeassistant/components/lovelace/dashboard.py @@ -88,7 +88,7 @@ class LovelaceStorage(LovelaceConfig): storage_key = CONFIG_STORAGE_KEY_DEFAULT else: url_path = config[CONF_URL_PATH] - storage_key = CONFIG_STORAGE_KEY.format(url_path) + storage_key = CONFIG_STORAGE_KEY.format(config["id"]) super().__init__(hass, url_path, config) diff --git a/tests/components/lovelace/test_dashboard.py b/tests/components/lovelace/test_dashboard.py index 1effb10be27..775b2760c96 100644 --- a/tests/components/lovelace/test_dashboard.py +++ b/tests/components/lovelace/test_dashboard.py @@ -373,7 +373,6 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): assert response["result"]["icon"] == "mdi:map" dashboard_id = response["result"]["id"] - dashboard_path = response["result"]["url_path"] assert "created-url-path" in hass.data[frontend.DATA_PANELS] @@ -408,9 +407,9 @@ async def test_storage_dashboards(hass, hass_ws_client, hass_storage): ) response = await client.receive_json() assert response["success"] - assert hass_storage[dashboard.CONFIG_STORAGE_KEY.format(dashboard_path)][ - "data" - ] == {"config": {"yo": "hello"}} + assert hass_storage[dashboard.CONFIG_STORAGE_KEY.format(dashboard_id)]["data"] == { + "config": {"yo": "hello"} + } assert len(events) == 1 assert events[0].data["url_path"] == "created-url-path" From 4f78674a4c46d0829365a2947eefbf227c2347a9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 16 Mar 2020 22:27:20 +0100 Subject: [PATCH 44/62] Updated frontend to 20200316.1 (#32878) --- 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 211e5bc7e84..174bab5a189 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==20200316.0" + "home-assistant-frontend==20200316.1" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 96cf257e61e..37a0a8e357e 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 hass-nabucasa==0.32.2 -home-assistant-frontend==20200316.0 +home-assistant-frontend==20200316.1 importlib-metadata==1.5.0 jinja2>=2.10.3 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index c11c4e76749..3db1a513ed9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200316.0 +home-assistant-frontend==20200316.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 97dc27163e9..b361464e823 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -263,7 +263,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200316.0 +home-assistant-frontend==20200316.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.9 From d196fd136d246ed640040960f14dc04489f0923b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 16 Mar 2020 14:53:13 -0700 Subject: [PATCH 45/62] Bumped version to 0.107.0b6 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 7ff286fd460..6ca593de34e 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b5" +PATCH_VERSION = "0b6" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 67a721d39b17c9960de3a22326400234f5514ad1 Mon Sep 17 00:00:00 2001 From: Quentame Date: Tue, 17 Mar 2020 18:16:58 +0100 Subject: [PATCH 46/62] Fix iCloud init while pending (#32750) * Fix iCloud init while pending Continue if device is pending while setup Create devices and fetch 15s if pending, otherwise determine interval to fetch. * Add retried_fetch guard --- homeassistant/components/icloud/account.py | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/icloud/account.py b/homeassistant/components/icloud/account.py index 6c4d9c5c25f..50a3e74f78f 100644 --- a/homeassistant/components/icloud/account.py +++ b/homeassistant/components/icloud/account.py @@ -97,6 +97,7 @@ class IcloudAccount: self._owner_fullname = None self._family_members_fullname = {} self._devices = {} + self._retried_fetch = False self.listeners = [] @@ -122,10 +123,6 @@ class IcloudAccount: _LOGGER.error("No iCloud device found") raise ConfigEntryNotReady - if DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending": - _LOGGER.warning("Pending devices, trying again ...") - raise ConfigEntryNotReady - self._owner_fullname = f"{user_info['firstName']} {user_info['lastName']}" self._family_members_fullname = {} @@ -157,28 +154,15 @@ class IcloudAccount: ) return - if DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending": - _LOGGER.warning("Pending devices, trying again in 15s") - self._fetch_interval = 0.25 - dispatcher_send(self.hass, self.signal_device_update) - track_point_in_utc_time( - self.hass, - self.keep_alive, - utcnow() + timedelta(minutes=self._fetch_interval), - ) - return - # Gets devices infos new_device = False for device in api_devices: status = device.status(DEVICE_STATUS_SET) device_id = status[DEVICE_ID] device_name = status[DEVICE_NAME] - device_status = DEVICE_STATUS_CODES.get(status[DEVICE_STATUS], "error") if ( - device_status == "pending" - or status[DEVICE_BATTERY_STATUS] == "Unknown" + status[DEVICE_BATTERY_STATUS] == "Unknown" or status.get(DEVICE_BATTERY_LEVEL) is None ): continue @@ -198,7 +182,16 @@ class IcloudAccount: self._devices[device_id].update(status) new_device = True - self._fetch_interval = self._determine_interval() + if ( + DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending" + and not self._retried_fetch + ): + _LOGGER.warning("Pending devices, trying again in 15s") + self._fetch_interval = 0.25 + self._retried_fetch = True + else: + self._fetch_interval = self._determine_interval() + self._retried_fetch = False dispatcher_send(self.hass, self.signal_device_update) if new_device: From ac8c889b0fe99d1cf91b91beb76eb35d7dc06841 Mon Sep 17 00:00:00 2001 From: Paolo Tuninetto Date: Tue, 17 Mar 2020 00:37:10 +0100 Subject: [PATCH 47/62] Add default port to samsung tv (#32820) * Default port for websocket tv * Update config entry * move bridge creation * fix indent * remove loop --- homeassistant/components/samsungtv/bridge.py | 2 ++ .../components/samsungtv/media_player.py | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/samsungtv/bridge.py b/homeassistant/components/samsungtv/bridge.py index 31f102a62a4..b582f6269e4 100644 --- a/homeassistant/components/samsungtv/bridge.py +++ b/homeassistant/components/samsungtv/bridge.py @@ -46,6 +46,7 @@ class SamsungTVBridge(ABC): self.method = method self.host = host self.token = None + self.default_port = None self._remote = None self._callback = None @@ -191,6 +192,7 @@ class SamsungTVWSBridge(SamsungTVBridge): """Initialize Bridge.""" super().__init__(method, host, port) self.token = token + self.default_port = 8001 def try_connect(self): """Try to connect to the Websocket TV.""" diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 8fa6a93088a..8f12341ee4a 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -71,13 +71,27 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ): turn_on_action = hass.data[DOMAIN][ip_address][CONF_ON_ACTION] on_script = Script(hass, turn_on_action) - async_add_entities([SamsungTVDevice(config_entry, on_script)]) + + # Initialize bridge + data = config_entry.data.copy() + bridge = SamsungTVBridge.get_bridge( + data[CONF_METHOD], data[CONF_HOST], data[CONF_PORT], data.get(CONF_TOKEN), + ) + if bridge.port is None and bridge.default_port is not None: + # For backward compat, set default port for websocket tv + data[CONF_PORT] = bridge.default_port + hass.config_entries.async_update_entry(config_entry, data=data) + bridge = SamsungTVBridge.get_bridge( + data[CONF_METHOD], data[CONF_HOST], data[CONF_PORT], data.get(CONF_TOKEN), + ) + + async_add_entities([SamsungTVDevice(bridge, config_entry, on_script)]) class SamsungTVDevice(MediaPlayerDevice): """Representation of a Samsung TV.""" - def __init__(self, config_entry, on_script): + def __init__(self, bridge, config_entry, on_script): """Initialize the Samsung device.""" self._config_entry = config_entry self._manufacturer = config_entry.data.get(CONF_MANUFACTURER) @@ -93,13 +107,7 @@ class SamsungTVDevice(MediaPlayerDevice): # Mark the end of a shutdown command (need to wait 15 seconds before # sending the next command to avoid turning the TV back ON). self._end_of_power_off = None - # Initialize bridge - self._bridge = SamsungTVBridge.get_bridge( - config_entry.data[CONF_METHOD], - config_entry.data[CONF_HOST], - config_entry.data[CONF_PORT], - config_entry.data.get(CONF_TOKEN), - ) + self._bridge = bridge self._bridge.register_reauth_callback(self.access_denied) def access_denied(self): From 912409ed0c8384b654db4b26c4e6472c32d066bd Mon Sep 17 00:00:00 2001 From: Jason Lachowsky Date: Mon, 16 Mar 2020 05:58:12 -0500 Subject: [PATCH 48/62] Corrected minor misspellings (#32857) --- .../components/homekit_controller/.translations/en.json | 2 +- homeassistant/components/homekit_controller/strings.json | 2 +- homeassistant/components/system_log/__init__.py | 4 ++-- homeassistant/components/toon/.translations/en.json | 2 +- homeassistant/components/toon/strings.json | 2 +- tests/components/system_log/test_init.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/homekit_controller/.translations/en.json b/homeassistant/components/homekit_controller/.translations/en.json index 72aa720b449..eb994289a62 100644 --- a/homeassistant/components/homekit_controller/.translations/en.json +++ b/homeassistant/components/homekit_controller/.translations/en.json @@ -14,7 +14,7 @@ "busy_error": "Device refused to add pairing as it is already pairing with another controller.", "max_peers_error": "Device refused to add pairing as it has no free pairing storage.", "max_tries_error": "Device refused to add pairing as it has received more than 100 unsuccessful authentication attempts.", - "pairing_failed": "An unhandled error occured while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently.", + "pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently.", "unable_to_pair": "Unable to pair, please try again.", "unknown_error": "Device reported an unknown error. Pairing failed." }, diff --git a/homeassistant/components/homekit_controller/strings.json b/homeassistant/components/homekit_controller/strings.json index 55718e35b59..80370717183 100644 --- a/homeassistant/components/homekit_controller/strings.json +++ b/homeassistant/components/homekit_controller/strings.json @@ -25,7 +25,7 @@ "max_peers_error": "Device refused to add pairing as it has no free pairing storage.", "busy_error": "Device refused to add pairing as it is already pairing with another controller.", "max_tries_error": "Device refused to add pairing as it has received more than 100 unsuccessful authentication attempts.", - "pairing_failed": "An unhandled error occured while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently." + "pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently." }, "abort": { "no_devices": "No unpaired devices could be found", diff --git a/homeassistant/components/system_log/__init__.py b/homeassistant/components/system_log/__init__.py index 2ddf02f76ed..bf49de5a731 100644 --- a/homeassistant/components/system_log/__init__.py +++ b/homeassistant/components/system_log/__init__.py @@ -91,7 +91,7 @@ class LogEntry: def __init__(self, record, stack, source): """Initialize a log entry.""" - self.first_occured = self.timestamp = record.created + self.first_occurred = self.timestamp = record.created self.name = record.name self.level = record.levelname self.message = deque([record.getMessage()], maxlen=5) @@ -117,7 +117,7 @@ class LogEntry: "timestamp": self.timestamp, "exception": self.exception, "count": self.count, - "first_occured": self.first_occured, + "first_occurred": self.first_occurred, } diff --git a/homeassistant/components/toon/.translations/en.json b/homeassistant/components/toon/.translations/en.json index cea3146a3a5..7d7d6c73e16 100644 --- a/homeassistant/components/toon/.translations/en.json +++ b/homeassistant/components/toon/.translations/en.json @@ -5,7 +5,7 @@ "client_secret": "The client secret from the configuration is invalid.", "no_agreements": "This account has no Toon displays.", "no_app": "You need to configure Toon before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/toon/).", - "unknown_auth_fail": "Unexpected error occured, while authenticating." + "unknown_auth_fail": "Unexpected error occurred, while authenticating." }, "error": { "credentials": "The provided credentials are invalid.", diff --git a/homeassistant/components/toon/strings.json b/homeassistant/components/toon/strings.json index 80d71d4e421..20d6ba3d72c 100644 --- a/homeassistant/components/toon/strings.json +++ b/homeassistant/components/toon/strings.json @@ -26,7 +26,7 @@ "abort": { "client_id": "The client ID from the configuration is invalid.", "client_secret": "The client secret from the configuration is invalid.", - "unknown_auth_fail": "Unexpected error occured, while authenticating.", + "unknown_auth_fail": "Unexpected error occurred, while authenticating.", "no_agreements": "This account has no Toon displays.", "no_app": "You need to configure Toon before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/toon/)." } diff --git a/tests/components/system_log/test_init.py b/tests/components/system_log/test_init.py index 9862260c5f8..92f0ed9fd16 100644 --- a/tests/components/system_log/test_init.py +++ b/tests/components/system_log/test_init.py @@ -157,7 +157,7 @@ async def test_dedup_logs(hass, hass_client): log_msg() log = await get_error_log(hass, hass_client, 3) assert_log(log[0], "", ["error message 2", "error message 2-2"], "ERROR") - assert log[0]["timestamp"] > log[0]["first_occured"] + assert log[0]["timestamp"] > log[0]["first_occurred"] log_msg("2-3") log_msg("2-4") From 67d728fc500cda8d93dd47252b8b5d1348d22971 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Mar 2020 03:59:39 -0700 Subject: [PATCH 49/62] Make zone dependency of device tracker an after dep (#32880) * Make zone dependency of device tracker an after dep * Fix test --- .../components/device_tracker/manifest.json | 4 +-- tests/components/mobile_app/test_webhook.py | 6 ++-- tests/components/unifi/test_device_tracker.py | 24 +++++++-------- tests/components/unifi/test_sensor.py | 4 +-- tests/components/unifi/test_switch.py | 30 +++++++++---------- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/device_tracker/manifest.json b/homeassistant/components/device_tracker/manifest.json index 4bd9846f76d..2d0e9a82a53 100644 --- a/homeassistant/components/device_tracker/manifest.json +++ b/homeassistant/components/device_tracker/manifest.json @@ -3,8 +3,8 @@ "name": "Device Tracker", "documentation": "https://www.home-assistant.io/integrations/device_tracker", "requirements": [], - "dependencies": ["zone"], - "after_dependencies": [], + "dependencies": [], + "after_dependencies": ["zone"], "codeowners": [], "quality_scale": "internal" } diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index 39837543a47..974fb577606 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -160,8 +160,10 @@ async def test_webhook_handle_get_zones(hass, create_registrations, webhook_clie assert resp.status == 200 json = await resp.json() - assert len(json) == 1 - assert json[0]["entity_id"] == "zone.home" + assert len(json) == 2 + zones = sorted(json, key=lambda entry: entry["entity_id"]) + assert zones[0]["entity_id"] == "zone.home" + assert zones[1]["entity_id"] == "zone.test" async def test_webhook_handle_get_config(hass, create_registrations, webhook_client): diff --git a/tests/components/unifi/test_device_tracker.py b/tests/components/unifi/test_device_tracker.py index cbef7c31922..f225ddb44a9 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -108,7 +108,7 @@ async def test_no_clients(hass): """Test the update_clients function when no clients are found.""" await setup_unifi_integration(hass) - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 async def test_tracked_devices(hass): @@ -123,7 +123,7 @@ async def test_tracked_devices(hass): devices_response=[DEVICE_1, DEVICE_2], known_wireless_clients=(CLIENT_4["mac"],), ) - assert len(hass.states.async_all()) == 7 + assert len(hass.states.async_all()) == 6 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None @@ -184,7 +184,7 @@ async def test_controller_state_change(hass): controller = await setup_unifi_integration( hass, clients_response=[CLIENT_1], devices_response=[DEVICE_1], ) - assert len(hass.states.async_all()) == 3 + assert len(hass.states.async_all()) == 2 # Controller unavailable controller.async_unifi_signalling_callback( @@ -214,7 +214,7 @@ async def test_option_track_clients(hass): controller = await setup_unifi_integration( hass, clients_response=[CLIENT_1, CLIENT_2], devices_response=[DEVICE_1], ) - assert len(hass.states.async_all()) == 4 + assert len(hass.states.async_all()) == 3 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None @@ -259,7 +259,7 @@ async def test_option_track_wired_clients(hass): controller = await setup_unifi_integration( hass, clients_response=[CLIENT_1, CLIENT_2], devices_response=[DEVICE_1], ) - assert len(hass.states.async_all()) == 4 + assert len(hass.states.async_all()) == 3 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None @@ -304,7 +304,7 @@ async def test_option_track_devices(hass): controller = await setup_unifi_integration( hass, clients_response=[CLIENT_1, CLIENT_2], devices_response=[DEVICE_1], ) - assert len(hass.states.async_all()) == 4 + assert len(hass.states.async_all()) == 3 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None @@ -349,7 +349,7 @@ async def test_option_ssid_filter(hass): controller = await setup_unifi_integration( hass, options={CONF_SSID_FILTER: ["ssid"]}, clients_response=[CLIENT_3], ) - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 # SSID filter active client_3 = hass.states.get("device_tracker.client_3") @@ -387,7 +387,7 @@ async def test_wireless_client_go_wired_issue(hass): client_1_client["last_seen"] = dt_util.as_timestamp(dt_util.utcnow()) controller = await setup_unifi_integration(hass, clients_response=[client_1_client]) - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None @@ -460,7 +460,7 @@ async def test_restoring_client(hass): clients_response=[CLIENT_2], clients_all_response=[CLIENT_1], ) - assert len(hass.states.async_all()) == 3 + assert len(hass.states.async_all()) == 2 device_1 = hass.states.get("device_tracker.client_1") assert device_1 is not None @@ -474,7 +474,7 @@ async def test_dont_track_clients(hass): clients_response=[CLIENT_1], devices_response=[DEVICE_1], ) - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is None @@ -492,7 +492,7 @@ async def test_dont_track_devices(hass): clients_response=[CLIENT_1], devices_response=[DEVICE_1], ) - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None @@ -509,7 +509,7 @@ async def test_dont_track_wired_clients(hass): options={unifi.controller.CONF_TRACK_WIRED_CLIENTS: False}, clients_response=[CLIENT_1, CLIENT_2], ) - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 client_1 = hass.states.get("device_tracker.client_1") assert client_1 is not None diff --git a/tests/components/unifi/test_sensor.py b/tests/components/unifi/test_sensor.py index 7d0600f5885..a858bc9a649 100644 --- a/tests/components/unifi/test_sensor.py +++ b/tests/components/unifi/test_sensor.py @@ -55,7 +55,7 @@ async def test_no_clients(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 async def test_sensors(hass): @@ -71,7 +71,7 @@ async def test_sensors(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 4 wired_client_rx = hass.states.get("sensor.wired_client_name_rx") assert wired_client_rx.state == "1234.0" diff --git a/tests/components/unifi/test_switch.py b/tests/components/unifi/test_switch.py index bc30161b77f..a06be14024b 100644 --- a/tests/components/unifi/test_switch.py +++ b/tests/components/unifi/test_switch.py @@ -209,7 +209,7 @@ async def test_no_clients(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 async def test_controller_not_client(hass): @@ -222,7 +222,7 @@ async def test_controller_not_client(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 cloudkey = hass.states.get("switch.cloud_key") assert cloudkey is None @@ -240,7 +240,7 @@ async def test_not_admin(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 async def test_switches(hass): @@ -258,7 +258,7 @@ async def test_switches(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 4 + assert len(hass.states.async_all()) == 3 switch_1 = hass.states.get("switch.poe_client_1") assert switch_1 is not None @@ -312,7 +312,7 @@ async def test_new_client_discovered_on_block_control(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 blocked = hass.states.get("switch.block_client_1") assert blocked is None @@ -324,7 +324,7 @@ async def test_new_client_discovered_on_block_control(hass): controller.api.session_handler("data") await hass.async_block_till_done() - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 blocked = hass.states.get("switch.block_client_1") assert blocked is not None @@ -336,7 +336,7 @@ async def test_option_block_clients(hass): options={CONF_BLOCK_CLIENT: [BLOCKED["mac"]]}, clients_all_response=[BLOCKED, UNBLOCKED], ) - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 # Add a second switch hass.config_entries.async_update_entry( @@ -344,28 +344,28 @@ async def test_option_block_clients(hass): options={CONF_BLOCK_CLIENT: [BLOCKED["mac"], UNBLOCKED["mac"]]}, ) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 3 + assert len(hass.states.async_all()) == 2 # Remove the second switch again hass.config_entries.async_update_entry( controller.config_entry, options={CONF_BLOCK_CLIENT: [BLOCKED["mac"]]}, ) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 # Enable one and remove another one hass.config_entries.async_update_entry( controller.config_entry, options={CONF_BLOCK_CLIENT: [UNBLOCKED["mac"]]}, ) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 # Remove one hass.config_entries.async_update_entry( controller.config_entry, options={CONF_BLOCK_CLIENT: []}, ) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 1 + assert len(hass.states.async_all()) == 0 async def test_new_client_discovered_on_poe_control(hass): @@ -378,7 +378,7 @@ async def test_new_client_discovered_on_poe_control(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_all()) == 1 controller.api.websocket._data = { "meta": {"message": "sta:sync"}, @@ -391,7 +391,7 @@ async def test_new_client_discovered_on_poe_control(hass): "switch", "turn_off", {"entity_id": "switch.poe_client_1"}, blocking=True ) assert len(controller.mock_requests) == 5 - assert len(hass.states.async_all()) == 3 + assert len(hass.states.async_all()) == 2 assert controller.mock_requests[4] == { "json": { "port_overrides": [{"port_idx": 1, "portconf_id": "1a1", "poe_mode": "off"}] @@ -430,7 +430,7 @@ async def test_ignore_multiple_poe_clients_on_same_port(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 4 + assert len(hass.states.async_all()) == 3 switch_1 = hass.states.get("switch.poe_client_1") switch_2 = hass.states.get("switch.poe_client_2") @@ -481,7 +481,7 @@ async def test_restoring_client(hass): ) assert len(controller.mock_requests) == 4 - assert len(hass.states.async_all()) == 3 + assert len(hass.states.async_all()) == 2 device_1 = hass.states.get("switch.client_1") assert device_1 is not None From 4908d4358cf3db658abcd276309b16d8ba174fa6 Mon Sep 17 00:00:00 2001 From: Quentame Date: Tue, 17 Mar 2020 17:46:30 +0100 Subject: [PATCH 50/62] Bump iCloud to 0.9.5 (#32901) --- homeassistant/components/icloud/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/icloud/manifest.json b/homeassistant/components/icloud/manifest.json index 76b6b9b39ae..b4ef46cfbaf 100644 --- a/homeassistant/components/icloud/manifest.json +++ b/homeassistant/components/icloud/manifest.json @@ -3,7 +3,7 @@ "name": "Apple iCloud", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/icloud", - "requirements": ["pyicloud==0.9.4"], + "requirements": ["pyicloud==0.9.5"], "dependencies": [], "codeowners": ["@Quentame"] } diff --git a/requirements_all.txt b/requirements_all.txt index 3db1a513ed9..c608c6f0596 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1315,7 +1315,7 @@ pyhomeworks==0.0.6 pyialarm==0.3 # homeassistant.components.icloud -pyicloud==0.9.4 +pyicloud==0.9.5 # homeassistant.components.intesishome pyintesishome==1.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b361464e823..f531ccf6726 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -483,7 +483,7 @@ pyheos==0.6.0 pyhomematic==0.1.65 # homeassistant.components.icloud -pyicloud==0.9.4 +pyicloud==0.9.5 # homeassistant.components.ipma pyipma==2.0.5 From 9797b09d4476163efdee20dddf8a11059cbdc688 Mon Sep 17 00:00:00 2001 From: brubaked <37672083+brubaked@users.noreply.github.com> Date: Tue, 17 Mar 2020 10:17:18 -0700 Subject: [PATCH 51/62] Changed Sensor icons to be more emotionally sensitive (#32904) The existing sensor icons, while descriptive - dead = dead - are perhaps too matter of fact and don't accurately convey the tragedy. I changed emoticon-dead-outline to emoticon-cry-outline, as I think it better conveys the reality of the situation along with the emotions tied to the statistic. --- homeassistant/components/coronavirus/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/coronavirus/sensor.py b/homeassistant/components/coronavirus/sensor.py index 3885dbebf24..2887427ec6b 100644 --- a/homeassistant/components/coronavirus/sensor.py +++ b/homeassistant/components/coronavirus/sensor.py @@ -7,9 +7,9 @@ from .const import ATTRIBUTION, OPTION_WORLDWIDE SENSORS = { "confirmed": "mdi:emoticon-neutral-outline", - "current": "mdi:emoticon-frown-outline", + "current": "mdi:emoticon-sad-outline", "recovered": "mdi:emoticon-happy-outline", - "deaths": "mdi:emoticon-dead-outline", + "deaths": "mdi:emoticon-cry-outline", } From ab38e7d98aa3fcd1c803e21612f982212ea966f4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Mar 2020 10:30:56 -0700 Subject: [PATCH 52/62] Bump cast to 4.2.0 (#32906) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index 51558e78266..be0b64dc0b1 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==4.1.1"], + "requirements": ["pychromecast==4.2.0"], "dependencies": [], "after_dependencies": ["cloud"], "zeroconf": ["_googlecast._tcp.local."], diff --git a/requirements_all.txt b/requirements_all.txt index c608c6f0596..1664f72f5b9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1188,7 +1188,7 @@ pycfdns==0.0.1 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==4.1.1 +pychromecast==4.2.0 # homeassistant.components.cmus pycmus==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f531ccf6726..8aa311a1de6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -443,7 +443,7 @@ pyblackbird==0.5 pybotvac==0.0.17 # homeassistant.components.cast -pychromecast==4.1.1 +pychromecast==4.2.0 # homeassistant.components.coolmaster pycoolmasternet==0.0.4 From ae98f13181fe1452ba04882ee8a1754d2f3faff3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Mar 2020 10:36:59 -0700 Subject: [PATCH 53/62] Bumped version to 0.107.0b7 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 6ca593de34e..9b6f3aaca6a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b6" +PATCH_VERSION = "0b7" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From f3e682004231abd693d3e0233089cf9839a58394 Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Tue, 17 Mar 2020 22:19:42 +0200 Subject: [PATCH 54/62] Fix setting up options due to config data freeze (#32872) --- homeassistant/components/mikrotik/hub.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mikrotik/hub.py b/homeassistant/components/mikrotik/hub.py index 300d73b6b11..023bdc74a7e 100644 --- a/homeassistant/components/mikrotik/hub.py +++ b/homeassistant/components/mikrotik/hub.py @@ -332,16 +332,17 @@ class MikrotikHub: async def async_add_options(self): """Populate default options for Mikrotik.""" if not self.config_entry.options: + data = dict(self.config_entry.data) options = { - CONF_ARP_PING: self.config_entry.data.pop(CONF_ARP_PING, False), - CONF_FORCE_DHCP: self.config_entry.data.pop(CONF_FORCE_DHCP, False), - CONF_DETECTION_TIME: self.config_entry.data.pop( + CONF_ARP_PING: data.pop(CONF_ARP_PING, False), + CONF_FORCE_DHCP: data.pop(CONF_FORCE_DHCP, False), + CONF_DETECTION_TIME: data.pop( CONF_DETECTION_TIME, DEFAULT_DETECTION_TIME ), } self.hass.config_entries.async_update_entry( - self.config_entry, options=options + self.config_entry, data=data, options=options ) async def request_update(self): From fddb565e4cc2dea9b7e3278ab608a7d50f7ea7e2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 17 Mar 2020 20:42:55 +0100 Subject: [PATCH 55/62] Fix input text reload (#32911) * Fix input text reload * FIx schema instead --- .../components/input_text/__init__.py | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/input_text/__init__.py b/homeassistant/components/input_text/__init__.py index 692a0101249..c512bc221db 100644 --- a/homeassistant/components/input_text/__init__.py +++ b/homeassistant/components/input_text/__init__.py @@ -88,24 +88,22 @@ def _cv_input_text(cfg): CONFIG_SCHEMA = vol.Schema( { DOMAIN: cv.schema_with_slug_keys( - vol.Any( - vol.All( - { - vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_MIN, default=CONF_MIN_VALUE): vol.Coerce(int), - vol.Optional(CONF_MAX, default=CONF_MAX_VALUE): vol.Coerce(int), - vol.Optional(CONF_INITIAL, ""): cv.string, - vol.Optional(CONF_ICON): cv.icon, - vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, - vol.Optional(CONF_PATTERN): cv.string, - vol.Optional(CONF_MODE, default=MODE_TEXT): vol.In( - [MODE_TEXT, MODE_PASSWORD] - ), - }, - _cv_input_text, - ), - None, - ) + vol.All( + lambda value: value or {}, + { + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_MIN, default=CONF_MIN_VALUE): vol.Coerce(int), + vol.Optional(CONF_MAX, default=CONF_MAX_VALUE): vol.Coerce(int), + vol.Optional(CONF_INITIAL, ""): cv.string, + vol.Optional(CONF_ICON): cv.icon, + vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, + vol.Optional(CONF_PATTERN): cv.string, + vol.Optional(CONF_MODE, default=MODE_TEXT): vol.In( + [MODE_TEXT, MODE_PASSWORD] + ), + }, + _cv_input_text, + ), ) }, extra=vol.ALLOW_EXTRA, @@ -203,13 +201,6 @@ class InputText(RestoreEntity): @classmethod def from_yaml(cls, config: typing.Dict) -> "InputText": """Return entity instance initialized from yaml storage.""" - # set defaults for empty config - config = { - CONF_MAX: CONF_MAX_VALUE, - CONF_MIN: CONF_MIN_VALUE, - CONF_MODE: MODE_TEXT, - **config, - } input_text = cls(config) input_text.entity_id = f"{DOMAIN}.{config[CONF_ID]}" input_text.editable = False From b70be5f2f232b18bb4db2e4041f5aa96385c121e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Mar 2020 12:14:17 -0700 Subject: [PATCH 56/62] Blacklist auto_backup (#32912) * Blacklist auto_backup * Mock with a set --- homeassistant/setup.py | 8 ++++++++ tests/test_setup.py | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/homeassistant/setup.py b/homeassistant/setup.py index f62228b28f5..e00c5fac03f 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -19,6 +19,8 @@ DATA_DEPS_REQS = "deps_reqs_processed" SLOW_SETUP_WARNING = 10 +BLACKLIST = set(["auto_backup"]) + def setup_component(hass: core.HomeAssistant, domain: str, config: Dict) -> bool: """Set up a component and all its dependencies.""" @@ -37,6 +39,12 @@ async def async_setup_component( if domain in hass.config.components: return True + if domain in BLACKLIST: + _LOGGER.error( + "Integration %s is blacklisted because it is causing issues.", domain + ) + return False + setup_tasks = hass.data.setdefault(DATA_SETUP, {}) if domain in setup_tasks: diff --git a/tests/test_setup.py b/tests/test_setup.py index f90a7269752..95fd1e0a15d 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -6,6 +6,7 @@ import os import threading from unittest import mock +from asynctest import patch import voluptuous as vol from homeassistant import setup @@ -535,3 +536,15 @@ async def test_setup_import_blows_up(hass): "homeassistant.loader.Integration.get_component", side_effect=ValueError ): assert not await setup.async_setup_component(hass, "sun", {}) + + +async def test_blacklist(caplog): + """Test setup blacklist.""" + with patch("homeassistant.setup.BLACKLIST", {"bad_integration"}): + assert not await setup.async_setup_component( + mock.Mock(config=mock.Mock(components=[])), "bad_integration", {} + ) + assert ( + "Integration bad_integration is blacklisted because it is causing issues." + in caplog.text + ) From 8348878e7e2534e8d3c8105a648509fc3026283e Mon Sep 17 00:00:00 2001 From: Hans Oischinger Date: Tue, 17 Mar 2020 21:19:57 +0100 Subject: [PATCH 57/62] Introduce safe scan_interval for vicare (#32915) --- homeassistant/components/vicare/climate.py | 4 ++++ homeassistant/components/vicare/water_heater.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/homeassistant/components/vicare/climate.py b/homeassistant/components/vicare/climate.py index 1b101cc7612..ef5533523f8 100644 --- a/homeassistant/components/vicare/climate.py +++ b/homeassistant/components/vicare/climate.py @@ -1,4 +1,5 @@ """Viessmann ViCare climate device.""" +from datetime import timedelta import logging import requests @@ -79,6 +80,9 @@ HA_TO_VICARE_PRESET_HEATING = { PYVICARE_ERROR = "error" +# Scan interval of 15 minutes seems to be safe to not hit the ViCare server rate limit +SCAN_INTERVAL = timedelta(seconds=900) + def setup_platform(hass, config, add_entities, discovery_info=None): """Create the ViCare climate devices.""" diff --git a/homeassistant/components/vicare/water_heater.py b/homeassistant/components/vicare/water_heater.py index eea3d81faf6..fdac2962739 100644 --- a/homeassistant/components/vicare/water_heater.py +++ b/homeassistant/components/vicare/water_heater.py @@ -1,4 +1,5 @@ """Viessmann ViCare water_heater device.""" +from datetime import timedelta import logging import requests @@ -42,6 +43,9 @@ HA_TO_VICARE_HVAC_DHW = { PYVICARE_ERROR = "error" +# Scan interval of 15 minutes seems to be safe to not hit the ViCare server rate limit +SCAN_INTERVAL = timedelta(seconds=900) + def setup_platform(hass, config, add_entities, discovery_info=None): """Create the ViCare water_heater devices.""" From d0d9d853f21e17d699af58dfef32b404e6193f14 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Mar 2020 17:54:57 -0700 Subject: [PATCH 58/62] Fix hassio panel load (#32922) * Fix loading hassio panel * Remove blacklist --- homeassistant/components/hassio/__init__.py | 19 +++++++++---------- homeassistant/setup.py | 8 -------- tests/test_setup.py | 13 ------------- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index cc03f26085c..bcb751faa64 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -190,16 +190,15 @@ async def async_setup(hass, config): hass.http.register_view(HassIOView(host, websession)) - if "frontend" in hass.config.components: - await hass.components.panel_custom.async_register_panel( - frontend_url_path="hassio", - webcomponent_name="hassio-main", - sidebar_title="Supervisor", - sidebar_icon="hass:home-assistant", - js_url="/api/hassio/app/entrypoint.js", - embed_iframe=True, - require_admin=True, - ) + await hass.components.panel_custom.async_register_panel( + frontend_url_path="hassio", + webcomponent_name="hassio-main", + sidebar_title="Supervisor", + 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) diff --git a/homeassistant/setup.py b/homeassistant/setup.py index e00c5fac03f..f62228b28f5 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -19,8 +19,6 @@ DATA_DEPS_REQS = "deps_reqs_processed" SLOW_SETUP_WARNING = 10 -BLACKLIST = set(["auto_backup"]) - def setup_component(hass: core.HomeAssistant, domain: str, config: Dict) -> bool: """Set up a component and all its dependencies.""" @@ -39,12 +37,6 @@ async def async_setup_component( if domain in hass.config.components: return True - if domain in BLACKLIST: - _LOGGER.error( - "Integration %s is blacklisted because it is causing issues.", domain - ) - return False - setup_tasks = hass.data.setdefault(DATA_SETUP, {}) if domain in setup_tasks: diff --git a/tests/test_setup.py b/tests/test_setup.py index 95fd1e0a15d..f90a7269752 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -6,7 +6,6 @@ import os import threading from unittest import mock -from asynctest import patch import voluptuous as vol from homeassistant import setup @@ -536,15 +535,3 @@ async def test_setup_import_blows_up(hass): "homeassistant.loader.Integration.get_component", side_effect=ValueError ): assert not await setup.async_setup_component(hass, "sun", {}) - - -async def test_blacklist(caplog): - """Test setup blacklist.""" - with patch("homeassistant.setup.BLACKLIST", {"bad_integration"}): - assert not await setup.async_setup_component( - mock.Mock(config=mock.Mock(components=[])), "bad_integration", {} - ) - assert ( - "Integration bad_integration is blacklisted because it is causing issues." - in caplog.text - ) From 0ca87007fdfcd510ca1e5bd5c2cdfae870569995 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Mar 2020 17:56:18 -0700 Subject: [PATCH 59/62] Bumped version to 0.107.0b8 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 9b6f3aaca6a..fac3badfa53 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b7" +PATCH_VERSION = "0b8" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 657bf33e326a744db3b2481490d55ad4a04e666a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 18 Mar 2020 13:02:27 +0100 Subject: [PATCH 60/62] Updated frontend to 20200318.0 (#32931) --- 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 174bab5a189..fc9cd188565 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==20200316.1" + "home-assistant-frontend==20200318.0" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 37a0a8e357e..36499440c19 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 hass-nabucasa==0.32.2 -home-assistant-frontend==20200316.1 +home-assistant-frontend==20200318.0 importlib-metadata==1.5.0 jinja2>=2.10.3 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 1664f72f5b9..afe893dbc56 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200316.1 +home-assistant-frontend==20200318.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8aa311a1de6..4234621441f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -263,7 +263,7 @@ hole==0.5.0 holidays==0.10.1 # homeassistant.components.frontend -home-assistant-frontend==20200316.1 +home-assistant-frontend==20200318.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.9 From c2f615839d22ab58e260508ea46defe094826efb Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 18 Mar 2020 13:31:02 +0100 Subject: [PATCH 61/62] Bumped version to 0.107.0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fac3badfa53..a8a8b898ebb 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 107 -PATCH_VERSION = "0b8" +PATCH_VERSION = "0" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 1e469b39ad159b9a34210c800322861c48eeee4c Mon Sep 17 00:00:00 2001 From: SukramJ Date: Sat, 14 Mar 2020 19:35:15 +0100 Subject: [PATCH 62/62] Fix flaky tests for HMIPC (#32806) --- .../components/homematicip_cloud/conftest.py | 30 ++++++++++++++++--- .../homematicip_cloud/test_config_flow.py | 7 +++-- .../components/homematicip_cloud/test_hap.py | 7 ++--- .../components/homematicip_cloud/test_init.py | 16 +++++++--- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/tests/components/homematicip_cloud/conftest.py b/tests/components/homematicip_cloud/conftest.py index 502e9d1b73e..927690d881f 100644 --- a/tests/components/homematicip_cloud/conftest.py +++ b/tests/components/homematicip_cloud/conftest.py @@ -1,5 +1,5 @@ """Initializer helpers for HomematicIP fake server.""" -from asynctest import CoroutineMock, MagicMock, Mock +from asynctest import CoroutineMock, MagicMock, Mock, patch from homematicip.aio.auth import AsyncAuth from homematicip.aio.connection import AsyncConnection from homematicip.aio.home import AsyncHome @@ -106,9 +106,10 @@ async def mock_hap_with_service_fixture( @pytest.fixture(name="simple_mock_home") -def simple_mock_home_fixture() -> AsyncHome: - """Return a simple AsyncHome Mock.""" - return Mock( +def simple_mock_home_fixture(): + """Return a simple mocked connection.""" + + mock_home = Mock( spec=AsyncHome, name="Demo", devices=[], @@ -120,6 +121,27 @@ def simple_mock_home_fixture() -> AsyncHome: connected=True, ) + with patch( + "homeassistant.components.homematicip_cloud.hap.AsyncHome", + autospec=True, + return_value=mock_home, + ): + yield + + +@pytest.fixture(name="mock_connection_init") +def mock_connection_init_fixture(): + """Return a simple mocked connection.""" + + with patch( + "homeassistant.components.homematicip_cloud.hap.AsyncHome.init", + return_value=None, + ), patch( + "homeassistant.components.homematicip_cloud.hap.AsyncAuth.init", + return_value=None, + ): + yield + @pytest.fixture(name="simple_mock_auth") def simple_mock_auth_fixture() -> AsyncAuth: diff --git a/tests/components/homematicip_cloud/test_config_flow.py b/tests/components/homematicip_cloud/test_config_flow.py index 01e820e7565..6436433a147 100644 --- a/tests/components/homematicip_cloud/test_config_flow.py +++ b/tests/components/homematicip_cloud/test_config_flow.py @@ -16,12 +16,15 @@ DEFAULT_CONFIG = {HMIPC_HAPID: "ABC123", HMIPC_PIN: "123", HMIPC_NAME: "hmip"} IMPORT_CONFIG = {HMIPC_HAPID: "ABC123", HMIPC_AUTHTOKEN: "123", HMIPC_NAME: "hmip"} -async def test_flow_works(hass): +async def test_flow_works(hass, simple_mock_home): """Test config flow.""" with patch( "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", return_value=False, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.get_auth", + return_value=True, ): result = await hass.config_entries.flow.async_init( HMIPC_DOMAIN, context={"source": "user"}, data=DEFAULT_CONFIG @@ -137,7 +140,7 @@ async def test_init_already_configured(hass): assert result["reason"] == "already_configured" -async def test_import_config(hass): +async def test_import_config(hass, simple_mock_home): """Test importing a host with an existing config file.""" with patch( "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", diff --git a/tests/components/homematicip_cloud/test_hap.py b/tests/components/homematicip_cloud/test_hap.py index 1dd5b2fc789..e6e143973f3 100644 --- a/tests/components/homematicip_cloud/test_hap.py +++ b/tests/components/homematicip_cloud/test_hap.py @@ -125,14 +125,11 @@ async def test_hap_create(hass, hmip_config_entry, simple_mock_home): hass.config.components.add(HMIPC_DOMAIN) hap = HomematicipHAP(hass, hmip_config_entry) assert hap - with patch( - "homeassistant.components.homematicip_cloud.hap.AsyncHome", - return_value=simple_mock_home, - ), patch.object(hap, "async_connect"): + with patch.object(hap, "async_connect"): assert await hap.async_setup() -async def test_hap_create_exception(hass, hmip_config_entry): +async def test_hap_create_exception(hass, hmip_config_entry, mock_connection_init): """Mock AsyncHome to execute get_hap.""" hass.config.components.add(HMIPC_DOMAIN) diff --git a/tests/components/homematicip_cloud/test_init.py b/tests/components/homematicip_cloud/test_init.py index ef7f5fa24ae..f97e7114b94 100644 --- a/tests/components/homematicip_cloud/test_init.py +++ b/tests/components/homematicip_cloud/test_init.py @@ -24,7 +24,9 @@ from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry -async def test_config_with_accesspoint_passed_to_config_entry(hass): +async def test_config_with_accesspoint_passed_to_config_entry( + hass, mock_connection, simple_mock_home +): """Test that config for a accesspoint are loaded via config entry.""" entry_config = { @@ -51,7 +53,9 @@ async def test_config_with_accesspoint_passed_to_config_entry(hass): assert isinstance(hass.data[HMIPC_DOMAIN]["ABC123"], HomematicipHAP) -async def test_config_already_registered_not_passed_to_config_entry(hass): +async def test_config_already_registered_not_passed_to_config_entry( + hass, simple_mock_home +): """Test that an already registered accesspoint does not get imported.""" mock_config = {HMIPC_AUTHTOKEN: "123", HMIPC_HAPID: "ABC123", HMIPC_NAME: "name"} @@ -87,7 +91,9 @@ async def test_config_already_registered_not_passed_to_config_entry(hass): assert config_entries[0].unique_id == "ABC123" -async def test_load_entry_fails_due_to_connection_error(hass, hmip_config_entry): +async def test_load_entry_fails_due_to_connection_error( + hass, hmip_config_entry, mock_connection_init +): """Test load entry fails due to connection error.""" hmip_config_entry.add_to_hass(hass) @@ -101,7 +107,9 @@ async def test_load_entry_fails_due_to_connection_error(hass, hmip_config_entry) assert hmip_config_entry.state == ENTRY_STATE_SETUP_RETRY -async def test_load_entry_fails_due_to_generic_exception(hass, hmip_config_entry): +async def test_load_entry_fails_due_to_generic_exception( + hass, hmip_config_entry, simple_mock_home +): """Test load entry fails due to generic exception.""" hmip_config_entry.add_to_hass(hass)