From caedef5f1a5fcebd454325b1d49caedd865832e3 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 11 Feb 2022 22:16:41 +0100 Subject: [PATCH 01/25] Reduce Spotify API usage (#66315) --- homeassistant/components/spotify/__init__.py | 33 +++++++++++++++++ homeassistant/components/spotify/const.py | 5 +++ .../components/spotify/media_player.py | 37 +++++++++++++++---- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/spotify/__init__.py b/homeassistant/components/spotify/__init__.py index 5c36a0c71c3..c3e9504c05c 100644 --- a/homeassistant/components/spotify/__init__.py +++ b/homeassistant/components/spotify/__init__.py @@ -1,6 +1,11 @@ """The spotify integration.""" +from __future__ import annotations + +from datetime import timedelta +from typing import Any import aiohttp +import requests from spotipy import Spotify, SpotifyException import voluptuous as vol @@ -20,13 +25,16 @@ from homeassistant.helpers.config_entry_oauth2_flow import ( async_get_config_entry_implementation, ) from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from . import config_flow from .const import ( DATA_SPOTIFY_CLIENT, + DATA_SPOTIFY_DEVICES, DATA_SPOTIFY_ME, DATA_SPOTIFY_SESSION, DOMAIN, + LOGGER, MEDIA_PLAYER_PREFIX, SPOTIFY_SCOPES, ) @@ -112,9 +120,34 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except SpotifyException as err: raise ConfigEntryNotReady from err + async def _update_devices() -> list[dict[str, Any]]: + try: + devices: dict[str, Any] | None = await hass.async_add_executor_job( + spotify.devices + ) + except (requests.RequestException, SpotifyException) as err: + raise UpdateFailed from err + + if devices is None: + return [] + + return devices.get("devices", []) + + device_coordinator: DataUpdateCoordinator[ + list[dict[str, Any]] + ] = DataUpdateCoordinator( + hass, + LOGGER, + name=f"{entry.title} Devices", + update_interval=timedelta(minutes=5), + update_method=_update_devices, + ) + await device_coordinator.async_config_entry_first_refresh() + hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { DATA_SPOTIFY_CLIENT: spotify, + DATA_SPOTIFY_DEVICES: device_coordinator, DATA_SPOTIFY_ME: current_user, DATA_SPOTIFY_SESSION: session, } diff --git a/homeassistant/components/spotify/const.py b/homeassistant/components/spotify/const.py index 7978ac8712f..0ed7cd2412e 100644 --- a/homeassistant/components/spotify/const.py +++ b/homeassistant/components/spotify/const.py @@ -1,8 +1,13 @@ """Define constants for the Spotify integration.""" +import logging + DOMAIN = "spotify" +LOGGER = logging.getLogger(__package__) + DATA_SPOTIFY_CLIENT = "spotify_client" +DATA_SPOTIFY_DEVICES = "spotify_devices" DATA_SPOTIFY_ME = "spotify_me" DATA_SPOTIFY_SESSION = "spotify_session" diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index e279b150883..b3bb2efd1c0 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -52,7 +52,7 @@ from homeassistant.const import ( STATE_PAUSED, STATE_PLAYING, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session from homeassistant.helpers.device_registry import DeviceEntryType @@ -62,6 +62,7 @@ from homeassistant.util.dt import utc_from_timestamp from .const import ( DATA_SPOTIFY_CLIENT, + DATA_SPOTIFY_DEVICES, DATA_SPOTIFY_ME, DATA_SPOTIFY_SESSION, DOMAIN, @@ -269,7 +270,6 @@ class SpotifyMediaPlayer(MediaPlayerEntity): ) self._currently_playing: dict | None = {} - self._devices: list[dict] | None = [] self._playlist: dict | None = None self._attr_name = self._name @@ -290,6 +290,11 @@ class SpotifyMediaPlayer(MediaPlayerEntity): """Return spotify API.""" return self._spotify_data[DATA_SPOTIFY_CLIENT] + @property + def _devices(self) -> list: + """Return spotify devices.""" + return self._spotify_data[DATA_SPOTIFY_DEVICES].data + @property def device_info(self) -> DeviceInfo: """Return device information about this entity.""" @@ -517,13 +522,13 @@ class SpotifyMediaPlayer(MediaPlayerEntity): current = self._spotify.current_playback() self._currently_playing = current or {} - self._playlist = None context = self._currently_playing.get("context") - if context is not None and context["type"] == MEDIA_TYPE_PLAYLIST: - self._playlist = self._spotify.playlist(current["context"]["uri"]) - - devices = self._spotify.devices() or {} - self._devices = devices.get("devices", []) + if context is not None and ( + self._playlist is None or self._playlist["uri"] != context["uri"] + ): + self._playlist = None + if context["type"] == MEDIA_TYPE_PLAYLIST: + self._playlist = self._spotify.playlist(current["context"]["uri"]) async def async_browse_media(self, media_content_type=None, media_content_id=None): """Implement the websocket media browsing helper.""" @@ -543,6 +548,22 @@ class SpotifyMediaPlayer(MediaPlayerEntity): media_content_id, ) + @callback + def _handle_devices_update(self) -> None: + """Handle updated data from the coordinator.""" + if not self.enabled: + return + self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """When entity is added to hass.""" + await super().async_added_to_hass() + self.async_on_remove( + self._spotify_data[DATA_SPOTIFY_DEVICES].async_add_listener( + self._handle_devices_update + ) + ) + async def async_browse_media_internal( hass, From 5976238126cee8b56e52d3b8fef345da30924507 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 9 Feb 2022 16:52:32 +0100 Subject: [PATCH 02/25] Fix hdmi-cec initialization (#66172) --- homeassistant/components/hdmi_cec/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/hdmi_cec/__init__.py b/homeassistant/components/hdmi_cec/__init__.py index 3d4851d8852..056eacb6a5b 100644 --- a/homeassistant/components/hdmi_cec/__init__.py +++ b/homeassistant/components/hdmi_cec/__init__.py @@ -191,6 +191,8 @@ def parse_mapping(mapping, parents=None): def setup(hass: HomeAssistant, base_config: ConfigType) -> bool: # noqa: C901 """Set up the CEC capability.""" + hass.data[DOMAIN] = {} + # Parse configuration into a dict of device name to physical address # represented as a list of four elements. device_aliases = {} From 7cc9a4310d0403af70b177f4343e5897179de865 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 9 Feb 2022 14:27:46 +0100 Subject: [PATCH 03/25] Fix controlling nested groups (#66176) --- homeassistant/components/group/cover.py | 2 + homeassistant/components/group/fan.py | 2 + homeassistant/components/group/light.py | 3 ++ tests/components/group/test_cover.py | 50 ++++++++++++++++++ tests/components/group/test_fan.py | 56 +++++++++++++++++++++ tests/components/group/test_light.py | 23 +++++++-- tests/components/group/test_media_player.py | 28 ++++++++--- 7 files changed, 155 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/group/cover.py b/homeassistant/components/group/cover.py index a98f75fceb8..a4c550b8119 100644 --- a/homeassistant/components/group/cover.py +++ b/homeassistant/components/group/cover.py @@ -57,6 +57,8 @@ KEY_POSITION = "position" DEFAULT_NAME = "Cover Group" +# No limit on parallel updates to enable a group calling another group +PARALLEL_UPDATES = 0 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { diff --git a/homeassistant/components/group/fan.py b/homeassistant/components/group/fan.py index cef30dc3c69..7920e0f5d20 100644 --- a/homeassistant/components/group/fan.py +++ b/homeassistant/components/group/fan.py @@ -52,6 +52,8 @@ SUPPORTED_FLAGS = {SUPPORT_SET_SPEED, SUPPORT_DIRECTION, SUPPORT_OSCILLATE} DEFAULT_NAME = "Fan Group" +# No limit on parallel updates to enable a group calling another group +PARALLEL_UPDATES = 0 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { diff --git a/homeassistant/components/group/light.py b/homeassistant/components/group/light.py index 201156db600..ea74136b204 100644 --- a/homeassistant/components/group/light.py +++ b/homeassistant/components/group/light.py @@ -58,6 +58,9 @@ from .util import find_state_attributes, mean_tuple, reduce_attribute DEFAULT_NAME = "Light Group" +# No limit on parallel updates to enable a group calling another group +PARALLEL_UPDATES = 0 + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/tests/components/group/test_cover.py b/tests/components/group/test_cover.py index cf1fba992e7..d090141a9d2 100644 --- a/tests/components/group/test_cover.py +++ b/tests/components/group/test_cover.py @@ -1,6 +1,7 @@ """The tests for the group cover platform.""" from datetime import timedelta +import async_timeout import pytest from homeassistant.components.cover import ( @@ -735,3 +736,52 @@ async def test_is_opening_closing(hass, setup_comp): assert hass.states.get(DEMO_COVER_TILT).state == STATE_OPENING assert hass.states.get(DEMO_COVER_POS).state == STATE_OPEN assert hass.states.get(COVER_GROUP).state == STATE_OPENING + + +async def test_nested_group(hass): + """Test nested cover group.""" + await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: [ + {"platform": "demo"}, + { + "platform": "group", + "entities": ["cover.bedroom_group"], + "name": "Nested Group", + }, + { + "platform": "group", + CONF_ENTITIES: [DEMO_COVER_POS, DEMO_COVER_TILT], + "name": "Bedroom Group", + }, + ] + }, + ) + await hass.async_block_till_done() + await hass.async_start() + await hass.async_block_till_done() + + state = hass.states.get("cover.bedroom_group") + assert state is not None + assert state.state == STATE_OPEN + assert state.attributes.get(ATTR_ENTITY_ID) == [DEMO_COVER_POS, DEMO_COVER_TILT] + + state = hass.states.get("cover.nested_group") + assert state is not None + assert state.state == STATE_OPEN + assert state.attributes.get(ATTR_ENTITY_ID) == ["cover.bedroom_group"] + + # Test controlling the nested group + async with async_timeout.timeout(0.5): + await hass.services.async_call( + DOMAIN, + SERVICE_CLOSE_COVER, + {ATTR_ENTITY_ID: "cover.nested_group"}, + blocking=True, + ) + assert hass.states.get(DEMO_COVER_POS).state == STATE_CLOSING + assert hass.states.get(DEMO_COVER_TILT).state == STATE_CLOSING + assert hass.states.get("cover.bedroom_group").state == STATE_CLOSING + assert hass.states.get("cover.nested_group").state == STATE_CLOSING diff --git a/tests/components/group/test_fan.py b/tests/components/group/test_fan.py index abb1dcf245a..19b4fe4670a 100644 --- a/tests/components/group/test_fan.py +++ b/tests/components/group/test_fan.py @@ -1,6 +1,7 @@ """The tests for the group fan platform.""" from unittest.mock import patch +import async_timeout import pytest from homeassistant import config as hass_config @@ -497,3 +498,58 @@ async def test_service_calls(hass, setup_comp): assert percentage_full_fan_state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE fan_group_state = hass.states.get(FAN_GROUP) assert fan_group_state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE + + +async def test_nested_group(hass): + """Test nested fan group.""" + await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: [ + {"platform": "demo"}, + { + "platform": "group", + "entities": ["fan.bedroom_group"], + "name": "Nested Group", + }, + { + "platform": "group", + CONF_ENTITIES: [ + LIVING_ROOM_FAN_ENTITY_ID, + PERCENTAGE_FULL_FAN_ENTITY_ID, + ], + "name": "Bedroom Group", + }, + ] + }, + ) + await hass.async_block_till_done() + await hass.async_start() + await hass.async_block_till_done() + + state = hass.states.get("fan.bedroom_group") + assert state is not None + assert state.state == STATE_OFF + assert state.attributes.get(ATTR_ENTITY_ID) == [ + LIVING_ROOM_FAN_ENTITY_ID, + PERCENTAGE_FULL_FAN_ENTITY_ID, + ] + + state = hass.states.get("fan.nested_group") + assert state is not None + assert state.state == STATE_OFF + assert state.attributes.get(ATTR_ENTITY_ID) == ["fan.bedroom_group"] + + # Test controlling the nested group + async with async_timeout.timeout(0.5): + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.nested_group"}, + blocking=True, + ) + assert hass.states.get(LIVING_ROOM_FAN_ENTITY_ID).state == STATE_ON + assert hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID).state == STATE_ON + assert hass.states.get("fan.bedroom_group").state == STATE_ON + assert hass.states.get("fan.nested_group").state == STATE_ON diff --git a/tests/components/group/test_light.py b/tests/components/group/test_light.py index 843f15c7113..d356b20b40f 100644 --- a/tests/components/group/test_light.py +++ b/tests/components/group/test_light.py @@ -2,6 +2,7 @@ import unittest.mock from unittest.mock import MagicMock, patch +import async_timeout import pytest from homeassistant import config as hass_config @@ -1470,12 +1471,12 @@ async def test_reload_with_base_integration_platform_not_setup(hass): async def test_nested_group(hass): """Test nested light group.""" - hass.states.async_set("light.kitchen", "on") await async_setup_component( hass, LIGHT_DOMAIN, { LIGHT_DOMAIN: [ + {"platform": "demo"}, { "platform": DOMAIN, "entities": ["light.bedroom_group"], @@ -1483,7 +1484,7 @@ async def test_nested_group(hass): }, { "platform": DOMAIN, - "entities": ["light.kitchen", "light.bedroom"], + "entities": ["light.bed_light", "light.kitchen_lights"], "name": "Bedroom Group", }, ] @@ -1496,9 +1497,25 @@ async def test_nested_group(hass): state = hass.states.get("light.bedroom_group") assert state is not None assert state.state == STATE_ON - assert state.attributes.get(ATTR_ENTITY_ID) == ["light.kitchen", "light.bedroom"] + assert state.attributes.get(ATTR_ENTITY_ID) == [ + "light.bed_light", + "light.kitchen_lights", + ] state = hass.states.get("light.nested_group") assert state is not None assert state.state == STATE_ON assert state.attributes.get(ATTR_ENTITY_ID) == ["light.bedroom_group"] + + # Test controlling the nested group + async with async_timeout.timeout(0.5): + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TOGGLE, + {ATTR_ENTITY_ID: "light.nested_group"}, + blocking=True, + ) + assert hass.states.get("light.bed_light").state == STATE_OFF + assert hass.states.get("light.kitchen_lights").state == STATE_OFF + assert hass.states.get("light.bedroom_group").state == STATE_OFF + assert hass.states.get("light.nested_group").state == STATE_OFF diff --git a/tests/components/group/test_media_player.py b/tests/components/group/test_media_player.py index 27962297952..f741e2d1a84 100644 --- a/tests/components/group/test_media_player.py +++ b/tests/components/group/test_media_player.py @@ -1,6 +1,7 @@ """The tests for the Media group platform.""" from unittest.mock import patch +import async_timeout import pytest from homeassistant.components.group import DOMAIN @@ -486,12 +487,12 @@ async def test_service_calls(hass, mock_media_seek): async def test_nested_group(hass): """Test nested media group.""" - hass.states.async_set("media_player.player_1", "on") await async_setup_component( hass, MEDIA_DOMAIN, { MEDIA_DOMAIN: [ + {"platform": "demo"}, { "platform": DOMAIN, "entities": ["media_player.group_1"], @@ -499,7 +500,7 @@ async def test_nested_group(hass): }, { "platform": DOMAIN, - "entities": ["media_player.player_1", "media_player.player_2"], + "entities": ["media_player.bedroom", "media_player.kitchen"], "name": "Group 1", }, ] @@ -511,13 +512,28 @@ async def test_nested_group(hass): state = hass.states.get("media_player.group_1") assert state is not None - assert state.state == STATE_ON + assert state.state == STATE_PLAYING assert state.attributes.get(ATTR_ENTITY_ID) == [ - "media_player.player_1", - "media_player.player_2", + "media_player.bedroom", + "media_player.kitchen", ] state = hass.states.get("media_player.nested_group") assert state is not None - assert state.state == STATE_ON + assert state.state == STATE_PLAYING assert state.attributes.get(ATTR_ENTITY_ID) == ["media_player.group_1"] + + # Test controlling the nested group + async with async_timeout.timeout(0.5): + await hass.services.async_call( + MEDIA_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "media_player.group_1"}, + blocking=True, + ) + + await hass.async_block_till_done() + assert hass.states.get("media_player.bedroom").state == STATE_OFF + assert hass.states.get("media_player.kitchen").state == STATE_OFF + assert hass.states.get("media_player.group_1").state == STATE_OFF + assert hass.states.get("media_player.nested_group").state == STATE_OFF From 0199e8cc43afcb197d5555638c492d0b0b48e0b8 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 9 Feb 2022 17:54:27 +0100 Subject: [PATCH 04/25] Bump aioesphomeapi from 10.8.1 to 10.8.2 (#66189) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 042bf930d0e..25e3abe9700 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==10.8.1"], + "requirements": ["aioesphomeapi==10.8.2"], "zeroconf": ["_esphomelib._tcp.local."], "codeowners": ["@OttoWinter", "@jesserockz"], "after_dependencies": ["zeroconf", "tag"], diff --git a/requirements_all.txt b/requirements_all.txt index a50bc68b199..196dbe9c49c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -166,7 +166,7 @@ aioeagle==1.1.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==10.8.1 +aioesphomeapi==10.8.2 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a5bd1a6443c..21c1ffbbd9b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -119,7 +119,7 @@ aioeagle==1.1.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==10.8.1 +aioesphomeapi==10.8.2 # homeassistant.components.flo aioflo==2021.11.0 From 854308fec21b16fdbdc058b64f9c759729754e89 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Thu, 10 Feb 2022 14:25:07 -0600 Subject: [PATCH 05/25] Handle more Sonos favorites in media browser (#66205) --- homeassistant/components/sonos/const.py | 3 +++ .../components/sonos/media_browser.py | 20 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sonos/const.py b/homeassistant/components/sonos/const.py index abb0696360b..6aec401b412 100644 --- a/homeassistant/components/sonos/const.py +++ b/homeassistant/components/sonos/const.py @@ -44,6 +44,7 @@ SONOS_ALBUM_ARTIST = "album_artists" SONOS_TRACKS = "tracks" SONOS_COMPOSER = "composers" SONOS_RADIO = "radio" +SONOS_OTHER_ITEM = "other items" SONOS_STATE_PLAYING = "PLAYING" SONOS_STATE_TRANSITIONING = "TRANSITIONING" @@ -76,6 +77,7 @@ SONOS_TO_MEDIA_CLASSES = { "object.container.person.musicArtist": MEDIA_CLASS_ARTIST, "object.container.playlistContainer.sameArtist": MEDIA_CLASS_ARTIST, "object.container.playlistContainer": MEDIA_CLASS_PLAYLIST, + "object.item": MEDIA_CLASS_TRACK, "object.item.audioItem.musicTrack": MEDIA_CLASS_TRACK, "object.item.audioItem.audioBroadcast": MEDIA_CLASS_GENRE, } @@ -121,6 +123,7 @@ SONOS_TYPES_MAPPING = { "object.container.person.musicArtist": SONOS_ALBUM_ARTIST, "object.container.playlistContainer.sameArtist": SONOS_ARTIST, "object.container.playlistContainer": SONOS_PLAYLISTS, + "object.item": SONOS_OTHER_ITEM, "object.item.audioItem.musicTrack": SONOS_TRACKS, "object.item.audioItem.audioBroadcast": SONOS_RADIO, } diff --git a/homeassistant/components/sonos/media_browser.py b/homeassistant/components/sonos/media_browser.py index 2e3bf9d1fcb..2d971469928 100644 --- a/homeassistant/components/sonos/media_browser.py +++ b/homeassistant/components/sonos/media_browser.py @@ -162,8 +162,17 @@ def build_item_response(media_library, payload, get_thumbnail_url=None): payload["idstring"].split("/")[2:] ) + try: + search_type = MEDIA_TYPES_TO_SONOS[payload["search_type"]] + except KeyError: + _LOGGER.debug( + "Unknown media type received when building item response: %s", + payload["search_type"], + ) + return + media = media_library.browse_by_idstring( - MEDIA_TYPES_TO_SONOS[payload["search_type"]], + search_type, payload["idstring"], full_album_art_uri=True, max_items=0, @@ -371,11 +380,16 @@ def favorites_payload(favorites): group_types = {fav.reference.item_class for fav in favorites} for group_type in sorted(group_types): - media_content_type = SONOS_TYPES_MAPPING[group_type] + try: + media_content_type = SONOS_TYPES_MAPPING[group_type] + media_class = SONOS_TO_MEDIA_CLASSES[group_type] + except KeyError: + _LOGGER.debug("Unknown media type or class received %s", group_type) + continue children.append( BrowseMedia( title=media_content_type.title(), - media_class=SONOS_TO_MEDIA_CLASSES[group_type], + media_class=media_class, media_content_id=group_type, media_content_type="favorites_folder", can_play=False, From a2e7897b1ebcae560db3aad066a98c3ecde26d36 Mon Sep 17 00:00:00 2001 From: Maximilian <43999966+DeerMaximum@users.noreply.github.com> Date: Wed, 9 Feb 2022 21:08:46 +0100 Subject: [PATCH 06/25] Add missing nina warnings (#66211) --- homeassistant/components/nina/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/nina/const.py b/homeassistant/components/nina/const.py index 12df703a480..18af5021544 100644 --- a/homeassistant/components/nina/const.py +++ b/homeassistant/components/nina/const.py @@ -26,7 +26,7 @@ CONST_LIST_E_TO_H: list[str] = ["E", "F", "G", "H"] CONST_LIST_I_TO_L: list[str] = ["I", "J", "K", "L"] CONST_LIST_M_TO_Q: list[str] = ["M", "N", "O", "Ö", "P", "Q"] CONST_LIST_R_TO_U: list[str] = ["R", "S", "T", "U", "Ü"] -CONST_LIST_V_TO_Z: list[str] = ["V", "W", "X", "Y"] +CONST_LIST_V_TO_Z: list[str] = ["V", "W", "X", "Y", "Z"] CONST_REGION_A_TO_D: Final = "_a_to_d" CONST_REGION_E_TO_H: Final = "_e_to_h" From eb781060e8973e208d7a9d549fa8339dd0a6bddf Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Thu, 10 Feb 2022 08:43:48 +0100 Subject: [PATCH 07/25] bump py-synologydsm-api to 1.0.6 (#66226) --- homeassistant/components/synology_dsm/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/synology_dsm/manifest.json b/homeassistant/components/synology_dsm/manifest.json index c436557cea9..124679c3516 100644 --- a/homeassistant/components/synology_dsm/manifest.json +++ b/homeassistant/components/synology_dsm/manifest.json @@ -2,7 +2,7 @@ "domain": "synology_dsm", "name": "Synology DSM", "documentation": "https://www.home-assistant.io/integrations/synology_dsm", - "requirements": ["py-synologydsm-api==1.0.5"], + "requirements": ["py-synologydsm-api==1.0.6"], "codeowners": ["@hacf-fr", "@Quentame", "@mib1185"], "config_flow": true, "ssdp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 196dbe9c49c..351e4271aa7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1331,7 +1331,7 @@ py-nightscout==1.2.2 py-schluter==0.1.7 # homeassistant.components.synology_dsm -py-synologydsm-api==1.0.5 +py-synologydsm-api==1.0.6 # homeassistant.components.zabbix py-zabbix==1.1.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 21c1ffbbd9b..ab07d28ff57 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -829,7 +829,7 @@ py-melissa-climate==2.1.4 py-nightscout==1.2.2 # homeassistant.components.synology_dsm -py-synologydsm-api==1.0.5 +py-synologydsm-api==1.0.6 # homeassistant.components.seventeentrack py17track==2021.12.2 From 92bc780dd759e996ffa0eb6bf133e13da2bd05d2 Mon Sep 17 00:00:00 2001 From: Milan Meulemans Date: Thu, 10 Feb 2022 08:37:35 +0100 Subject: [PATCH 08/25] Bump aioaseko to 0.0.2 to fix issue (#66240) --- homeassistant/components/aseko_pool_live/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/aseko_pool_live/manifest.json b/homeassistant/components/aseko_pool_live/manifest.json index f6323b49354..d1fc553a9ff 100644 --- a/homeassistant/components/aseko_pool_live/manifest.json +++ b/homeassistant/components/aseko_pool_live/manifest.json @@ -3,7 +3,7 @@ "name": "Aseko Pool Live", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/aseko_pool_live", - "requirements": ["aioaseko==0.0.1"], + "requirements": ["aioaseko==0.0.2"], "codeowners": [ "@milanmeu" ], diff --git a/requirements_all.txt b/requirements_all.txt index 351e4271aa7..1b838e8b372 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -138,7 +138,7 @@ aio_georss_gdacs==0.5 aioambient==2021.11.0 # homeassistant.components.aseko_pool_live -aioaseko==0.0.1 +aioaseko==0.0.2 # homeassistant.components.asuswrt aioasuswrt==1.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ab07d28ff57..06475e17373 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -91,7 +91,7 @@ aio_georss_gdacs==0.5 aioambient==2021.11.0 # homeassistant.components.aseko_pool_live -aioaseko==0.0.1 +aioaseko==0.0.2 # homeassistant.components.asuswrt aioasuswrt==1.4.0 From dfcad3a13d37a8b3f0d6508dcf127b9338fc6d94 Mon Sep 17 00:00:00 2001 From: ufodone <35497351+ufodone@users.noreply.github.com> Date: Fri, 11 Feb 2022 02:38:50 -0800 Subject: [PATCH 09/25] Disable zone bypass switch feature (#66243) * Add configuration option to disable the creation of zone bypass switches * Removed temporary workaround and bumped pyenvisalink version to pick up the correct fix. * Remove zone bypass configuration option and disable zone bypass switches per code review instructions. --- CODEOWNERS | 1 + .../components/envisalink/__init__.py | 18 ++++-------------- .../components/envisalink/manifest.json | 4 ++-- requirements_all.txt | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index e5b6ed9798a..88669e8b5c7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -264,6 +264,7 @@ tests/components/enphase_envoy/* @gtdiehl homeassistant/components/entur_public_transport/* @hfurubotten homeassistant/components/environment_canada/* @gwww @michaeldavie tests/components/environment_canada/* @gwww @michaeldavie +homeassistant/components/envisalink/* @ufodone homeassistant/components/ephember/* @ttroy50 homeassistant/components/epson/* @pszafer tests/components/epson/* @pszafer diff --git a/homeassistant/components/envisalink/__init__.py b/homeassistant/components/envisalink/__init__.py index 183627fdfa6..aa276af492c 100644 --- a/homeassistant/components/envisalink/__init__.py +++ b/homeassistant/components/envisalink/__init__.py @@ -137,6 +137,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: keep_alive, hass.loop, connection_timeout, + False, ) hass.data[DATA_EVL] = controller @@ -181,12 +182,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.debug("The envisalink sent a partition update event") async_dispatcher_send(hass, SIGNAL_PARTITION_UPDATE, data) - @callback - def async_zone_bypass_update(data): - """Handle zone bypass status updates.""" - _LOGGER.debug("Envisalink sent a zone bypass update event. Updating zones") - async_dispatcher_send(hass, SIGNAL_ZONE_BYPASS_UPDATE, data) - @callback def stop_envisalink(event): """Shutdown envisalink connection and thread on exit.""" @@ -206,7 +201,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: controller.callback_login_failure = async_login_fail_callback controller.callback_login_timeout = async_connection_fail_callback controller.callback_login_success = async_connection_success_callback - controller.callback_zone_bypass_update = async_zone_bypass_update _LOGGER.info("Start envisalink") controller.start() @@ -240,13 +234,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass, Platform.BINARY_SENSOR, "envisalink", {CONF_ZONES: zones}, config ) ) - # Only DSC panels support getting zone bypass status - if panel_type == PANEL_TYPE_DSC: - hass.async_create_task( - async_load_platform( - hass, "switch", "envisalink", {CONF_ZONES: zones}, config - ) - ) + + # Zone bypass switches are not currently created due to an issue with some panels. + # These switches will be re-added in the future after some further refactoring of the integration. hass.services.async_register( DOMAIN, SERVICE_CUSTOM_FUNCTION, handle_custom_function, schema=SERVICE_SCHEMA diff --git a/homeassistant/components/envisalink/manifest.json b/homeassistant/components/envisalink/manifest.json index 25290a5d431..52ac06ff8c3 100644 --- a/homeassistant/components/envisalink/manifest.json +++ b/homeassistant/components/envisalink/manifest.json @@ -2,7 +2,7 @@ "domain": "envisalink", "name": "Envisalink", "documentation": "https://www.home-assistant.io/integrations/envisalink", - "requirements": ["pyenvisalink==4.3"], - "codeowners": [], + "requirements": ["pyenvisalink==4.4"], + "codeowners": ["@ufodone"], "iot_class": "local_push" } diff --git a/requirements_all.txt b/requirements_all.txt index 1b838e8b372..5b9e5f1dcd3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1500,7 +1500,7 @@ pyeight==0.2.0 pyemby==1.8 # homeassistant.components.envisalink -pyenvisalink==4.3 +pyenvisalink==4.4 # homeassistant.components.ephember pyephember==0.3.1 From 65c83633235be83d9227aae11fe184c49fd77562 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Fri, 11 Feb 2022 04:25:31 +0800 Subject: [PATCH 10/25] Catch ConnectionResetError when writing MJPEG in camera (#66245) --- homeassistant/components/camera/__init__.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 110cf11cde9..466dce1c84d 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -222,7 +222,12 @@ async def async_get_mjpeg_stream( """Fetch an mjpeg stream from a camera entity.""" camera = _get_camera_from_entity_id(hass, entity_id) - return await camera.handle_async_mjpeg_stream(request) + try: + stream = await camera.handle_async_mjpeg_stream(request) + except ConnectionResetError: + stream = None + _LOGGER.debug("Error while writing MJPEG stream to transport") + return stream async def async_get_still_stream( @@ -784,7 +789,11 @@ class CameraMjpegStream(CameraView): async def handle(self, request: web.Request, camera: Camera) -> web.StreamResponse: """Serve camera stream, possibly with interval.""" if (interval_str := request.query.get("interval")) is None: - stream = await camera.handle_async_mjpeg_stream(request) + try: + stream = await camera.handle_async_mjpeg_stream(request) + except ConnectionResetError: + stream = None + _LOGGER.debug("Error while writing MJPEG stream to transport") if stream is None: raise web.HTTPBadGateway() return stream From 2594500452ba59e8056bcdee8f2f884d0cd68172 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 10 Feb 2022 21:59:42 +0100 Subject: [PATCH 11/25] Correct philips_js usage of the overloaded coordinator (#66287) --- homeassistant/components/philips_js/media_player.py | 7 +++---- homeassistant/components/philips_js/remote.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/philips_js/media_player.py b/homeassistant/components/philips_js/media_player.py index 1a3c6b52a0d..be24be632fc 100644 --- a/homeassistant/components/philips_js/media_player.py +++ b/homeassistant/components/philips_js/media_player.py @@ -82,7 +82,7 @@ async def async_setup_entry( class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity): """Representation of a Philips TV exposing the JointSpace API.""" - _coordinator: PhilipsTVDataUpdateCoordinator + coordinator: PhilipsTVDataUpdateCoordinator _attr_device_class = MediaPlayerDeviceClass.TV def __init__( @@ -91,7 +91,6 @@ class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity): ) -> None: """Initialize the Philips TV.""" self._tv = coordinator.api - self._coordinator = coordinator self._sources = {} self._channels = {} self._supports = SUPPORT_PHILIPS_JS @@ -125,7 +124,7 @@ class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity): def supported_features(self): """Flag media player features that are supported.""" supports = self._supports - if self._coordinator.turn_on or ( + if self.coordinator.turn_on or ( self._tv.on and self._tv.powerstate is not None ): supports |= SUPPORT_TURN_ON @@ -170,7 +169,7 @@ class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity): await self._tv.setPowerState("On") self._state = STATE_ON else: - await self._coordinator.turn_on.async_run(self.hass, self._context) + await self.coordinator.turn_on.async_run(self.hass, self._context) await self._async_update_soon() async def async_turn_off(self): diff --git a/homeassistant/components/philips_js/remote.py b/homeassistant/components/philips_js/remote.py index 6bf60f7f5b0..09fe16215b6 100644 --- a/homeassistant/components/philips_js/remote.py +++ b/homeassistant/components/philips_js/remote.py @@ -30,7 +30,7 @@ async def async_setup_entry( class PhilipsTVRemote(CoordinatorEntity, RemoteEntity): """Device that sends commands.""" - _coordinator: PhilipsTVDataUpdateCoordinator + coordinator: PhilipsTVDataUpdateCoordinator def __init__( self, @@ -63,7 +63,7 @@ class PhilipsTVRemote(CoordinatorEntity, RemoteEntity): if self._tv.on and self._tv.powerstate: await self._tv.setPowerState("On") else: - await self._coordinator.turn_on.async_run(self.hass, self._context) + await self.coordinator.turn_on.async_run(self.hass, self._context) self.async_write_ha_state() async def async_turn_off(self, **kwargs): From 76872e37899982c2fd0913c8469235edf0e2f953 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Feb 2022 03:15:50 -0600 Subject: [PATCH 12/25] Fix august token refresh when data contains characters outside of latin1 (#66303) * WIP * bump version * bump --- homeassistant/components/august/__init__.py | 1 + homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 9b340096fde..8a7c0f93592 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -75,6 +75,7 @@ async def async_setup_august( hass.config_entries.async_update_entry(config_entry, data=config_data) await august_gateway.async_authenticate() + await august_gateway.async_refresh_access_token_if_needed() hass.data.setdefault(DOMAIN, {}) data = hass.data[DOMAIN][config_entry.entry_id] = { diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index ea6769fabcf..0dfcb6094b1 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.20"], + "requirements": ["yalexs==1.1.22"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index 5b9e5f1dcd3..236abecdfe0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2513,7 +2513,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.7 # homeassistant.components.august -yalexs==1.1.20 +yalexs==1.1.22 # homeassistant.components.yeelight yeelight==0.7.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 06475e17373..4271d855f5d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1541,7 +1541,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.7 # homeassistant.components.august -yalexs==1.1.20 +yalexs==1.1.22 # homeassistant.components.yeelight yeelight==0.7.8 From 669c99474b13f6669697f7f3da8a805dd6154092 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 8 Feb 2022 08:40:06 -0800 Subject: [PATCH 13/25] Bump python-nest to 4.2.0 for python 3.10 fixes (#66090) --- homeassistant/components/nest/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json index 478e608700c..e1ae022a4ad 100644 --- a/homeassistant/components/nest/manifest.json +++ b/homeassistant/components/nest/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["ffmpeg", "http", "media_source"], "documentation": "https://www.home-assistant.io/integrations/nest", - "requirements": ["python-nest==4.1.0", "google-nest-sdm==1.6.0"], + "requirements": ["python-nest==4.2.0", "google-nest-sdm==1.6.0"], "codeowners": ["@allenporter"], "quality_scale": "platinum", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 236abecdfe0..44220a07bf4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1954,7 +1954,7 @@ python-mpd2==3.0.4 python-mystrom==1.1.2 # homeassistant.components.nest -python-nest==4.1.0 +python-nest==4.2.0 # homeassistant.components.ozw python-openzwave-mqtt[mqtt-client]==1.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4271d855f5d..6717a97609b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1206,7 +1206,7 @@ python-kasa==0.4.1 python-miio==0.5.9.2 # homeassistant.components.nest -python-nest==4.1.0 +python-nest==4.2.0 # homeassistant.components.ozw python-openzwave-mqtt[mqtt-client]==1.4.0 From 27752f7ad36da2630a5e5294832a2437314fb986 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 9 Feb 2022 00:12:49 -0800 Subject: [PATCH 14/25] Bump google-nest-sdm to 1.7.0 (#66145) --- homeassistant/components/nest/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json index e1ae022a4ad..45d976c03c6 100644 --- a/homeassistant/components/nest/manifest.json +++ b/homeassistant/components/nest/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["ffmpeg", "http", "media_source"], "documentation": "https://www.home-assistant.io/integrations/nest", - "requirements": ["python-nest==4.2.0", "google-nest-sdm==1.6.0"], + "requirements": ["python-nest==4.2.0", "google-nest-sdm==1.7.0"], "codeowners": ["@allenporter"], "quality_scale": "platinum", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 44220a07bf4..09c6e4e979d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -764,7 +764,7 @@ google-cloud-pubsub==2.9.0 google-cloud-texttospeech==0.4.0 # homeassistant.components.nest -google-nest-sdm==1.6.0 +google-nest-sdm==1.7.0 # homeassistant.components.google_travel_time googlemaps==2.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6717a97609b..673196688f7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -492,7 +492,7 @@ google-api-python-client==1.6.4 google-cloud-pubsub==2.9.0 # homeassistant.components.nest -google-nest-sdm==1.6.0 +google-nest-sdm==1.7.0 # homeassistant.components.google_travel_time googlemaps==2.5.1 From 60b460001959424c407de569f3e3730028bdb8ea Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 11 Feb 2022 01:13:00 -0800 Subject: [PATCH 15/25] Bump google-nest-sdm to 1.7.1 (minor patch) (#66304) --- homeassistant/components/nest/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json index 45d976c03c6..832c186db35 100644 --- a/homeassistant/components/nest/manifest.json +++ b/homeassistant/components/nest/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["ffmpeg", "http", "media_source"], "documentation": "https://www.home-assistant.io/integrations/nest", - "requirements": ["python-nest==4.2.0", "google-nest-sdm==1.7.0"], + "requirements": ["python-nest==4.2.0", "google-nest-sdm==1.7.1"], "codeowners": ["@allenporter"], "quality_scale": "platinum", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 09c6e4e979d..41286e377ec 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -764,7 +764,7 @@ google-cloud-pubsub==2.9.0 google-cloud-texttospeech==0.4.0 # homeassistant.components.nest -google-nest-sdm==1.7.0 +google-nest-sdm==1.7.1 # homeassistant.components.google_travel_time googlemaps==2.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 673196688f7..3bd1a2bbf86 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -492,7 +492,7 @@ google-api-python-client==1.6.4 google-cloud-pubsub==2.9.0 # homeassistant.components.nest -google-nest-sdm==1.7.0 +google-nest-sdm==1.7.1 # homeassistant.components.google_travel_time googlemaps==2.5.1 From 27c5460febf03c3415531f1015136b58e8feae86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 11 Feb 2022 14:57:45 +0100 Subject: [PATCH 16/25] Add guard for invalid EntityCategory value (#66316) --- homeassistant/helpers/entity.py | 5 ++++- tests/helpers/test_entity_registry.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index b670c734b47..e9038d1f658 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -223,7 +223,10 @@ def convert_to_entity_category( "EntityCategory instead" % (type(value).__name__, value), error_if_core=False, ) - return EntityCategory(value) + try: + return EntityCategory(value) + except ValueError: + return None return value diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index f299177a08e..1eaac0e72bf 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -1124,3 +1124,15 @@ async def test_deprecated_disabled_by_str(hass, registry, caplog): assert entry.disabled_by is er.RegistryEntryDisabler.USER assert " str for entity registry disabled_by. This is deprecated " in caplog.text + + +async def test_invalid_entity_category_str(hass, registry, caplog): + """Test use of invalid entity category.""" + entry = er.RegistryEntry( + "light", + "hue", + "5678", + entity_category="invalid", + ) + + assert entry.entity_category is None From aef2588f9c85e50d86caa3c2a01196f7ed60db94 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 7 Feb 2022 10:57:42 +0100 Subject: [PATCH 17/25] bump motionblinds to 0.5.11 (#65988) --- homeassistant/components/motion_blinds/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 8636bd6ed94..90ae8cacbe9 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -3,7 +3,7 @@ "name": "Motion Blinds", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/motion_blinds", - "requirements": ["motionblinds==0.5.10"], + "requirements": ["motionblinds==0.5.11"], "dependencies": ["network"], "codeowners": ["@starkillerOG"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 41286e377ec..b3634333d1e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1049,7 +1049,7 @@ minio==5.0.10 mitemp_bt==0.0.5 # homeassistant.components.motion_blinds -motionblinds==0.5.10 +motionblinds==0.5.11 # homeassistant.components.motioneye motioneye-client==0.3.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3bd1a2bbf86..b41544b424d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -655,7 +655,7 @@ millheater==0.9.0 minio==5.0.10 # homeassistant.components.motion_blinds -motionblinds==0.5.10 +motionblinds==0.5.11 # homeassistant.components.motioneye motioneye-client==0.3.12 From 6857562e9e6999b1d44ed253549330af5218894a Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 11 Feb 2022 15:49:54 +0100 Subject: [PATCH 18/25] bump motionblinds to 0.5.12 (#66323) --- homeassistant/components/motion_blinds/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 90ae8cacbe9..fc664910c84 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -3,7 +3,7 @@ "name": "Motion Blinds", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/motion_blinds", - "requirements": ["motionblinds==0.5.11"], + "requirements": ["motionblinds==0.5.12"], "dependencies": ["network"], "codeowners": ["@starkillerOG"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index b3634333d1e..dc022f016f6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1049,7 +1049,7 @@ minio==5.0.10 mitemp_bt==0.0.5 # homeassistant.components.motion_blinds -motionblinds==0.5.11 +motionblinds==0.5.12 # homeassistant.components.motioneye motioneye-client==0.3.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b41544b424d..82235fde402 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -655,7 +655,7 @@ millheater==0.9.0 minio==5.0.10 # homeassistant.components.motion_blinds -motionblinds==0.5.11 +motionblinds==0.5.12 # homeassistant.components.motioneye motioneye-client==0.3.12 From 6084b323dfaf2251fff5dccfa89365757c902e16 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Feb 2022 11:07:32 -0600 Subject: [PATCH 19/25] Reduce number of parallel api calls to august (#66328) --- homeassistant/components/august/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 8a7c0f93592..031f513843f 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -107,11 +107,10 @@ class AugustData(AugustSubscriberMixin): async def async_setup(self): """Async setup of august device data and activities.""" token = self._august_gateway.access_token - user_data, locks, doorbells = await asyncio.gather( - self._api.async_get_user(token), - self._api.async_get_operable_locks(token), - self._api.async_get_doorbells(token), - ) + # This used to be a gather but it was less reliable with august's recent api changes. + user_data = await self._api.async_get_user(token) + locks = await self._api.async_get_operable_locks(token) + doorbells = await self._api.async_get_doorbells(token) if not doorbells: doorbells = [] if not locks: From fcee1ff865b48e3c19f645692c9cf72f8129604b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 11 Feb 2022 18:08:19 +0100 Subject: [PATCH 20/25] Fix raspihats initialization (#66330) Co-authored-by: epenet --- homeassistant/components/raspihats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/raspihats/__init__.py b/homeassistant/components/raspihats/__init__.py index 3d28086db43..8f4a8b0aca4 100644 --- a/homeassistant/components/raspihats/__init__.py +++ b/homeassistant/components/raspihats/__init__.py @@ -40,7 +40,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: "https://github.com/home-assistant/architecture/blob/master/adr/0019-GPIO.md" ) - hass.data[DOMAIN][I2C_HATS_MANAGER] = I2CHatsManager() + hass.data[DOMAIN] = {I2C_HATS_MANAGER: I2CHatsManager()} def start_i2c_hats_keep_alive(event): """Start I2C-HATs keep alive.""" From 087f443368116888c0b104c0f989ec698ff01152 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 11 Feb 2022 13:17:19 -0800 Subject: [PATCH 21/25] Fix nest streams that get stuck broken (#66334) --- homeassistant/components/stream/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index b6bfd2122ba..79506c0bda2 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -338,7 +338,6 @@ class Stream: ) except StreamWorkerError as err: self._logger.error("Error from stream worker: %s", str(err)) - self._available = False stream_state.discontinuity() if not self.keepalive or self._thread_quit.is_set(): From f3a3ff28f2f219df56214a1f6a76ac4a45c9fe98 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 11 Feb 2022 19:11:06 +0100 Subject: [PATCH 22/25] Fix PVOutput when no data is available (#66338) --- .../components/pvoutput/config_flow.py | 2 +- .../components/pvoutput/coordinator.py | 6 ++-- .../components/pvoutput/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/pvoutput/test_config_flow.py | 28 +++++++++---------- tests/components/pvoutput/test_init.py | 10 +++++-- 7 files changed, 30 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/pvoutput/config_flow.py b/homeassistant/components/pvoutput/config_flow.py index a1933ff9315..53eabe225f6 100644 --- a/homeassistant/components/pvoutput/config_flow.py +++ b/homeassistant/components/pvoutput/config_flow.py @@ -23,7 +23,7 @@ async def validate_input(hass: HomeAssistant, *, api_key: str, system_id: int) - api_key=api_key, system_id=system_id, ) - await pvoutput.status() + await pvoutput.system() class PVOutputFlowHandler(ConfigFlow, domain=DOMAIN): diff --git a/homeassistant/components/pvoutput/coordinator.py b/homeassistant/components/pvoutput/coordinator.py index cadef8c8a0d..7b307f20274 100644 --- a/homeassistant/components/pvoutput/coordinator.py +++ b/homeassistant/components/pvoutput/coordinator.py @@ -1,14 +1,14 @@ """DataUpdateCoordinator for the PVOutput integration.""" from __future__ import annotations -from pvo import PVOutput, PVOutputAuthenticationError, Status +from pvo import PVOutput, PVOutputAuthenticationError, PVOutputNoDataError, Status from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import CONF_SYSTEM_ID, DOMAIN, LOGGER, SCAN_INTERVAL @@ -33,5 +33,7 @@ class PVOutputDataUpdateCoordinator(DataUpdateCoordinator[Status]): """Fetch system status from PVOutput.""" try: return await self.pvoutput.status() + except PVOutputNoDataError as err: + raise UpdateFailed("PVOutput has no data available") from err except PVOutputAuthenticationError as err: raise ConfigEntryAuthFailed from err diff --git a/homeassistant/components/pvoutput/manifest.json b/homeassistant/components/pvoutput/manifest.json index 042c6b9aa99..021fffe0e01 100644 --- a/homeassistant/components/pvoutput/manifest.json +++ b/homeassistant/components/pvoutput/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/pvoutput", "config_flow": true, "codeowners": ["@fabaff", "@frenck"], - "requirements": ["pvo==0.2.1"], + "requirements": ["pvo==0.2.2"], "iot_class": "cloud_polling", "quality_scale": "platinum" } diff --git a/requirements_all.txt b/requirements_all.txt index dc022f016f6..b524400e811 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1310,7 +1310,7 @@ pushbullet.py==0.11.0 pushover_complete==1.1.1 # homeassistant.components.pvoutput -pvo==0.2.1 +pvo==0.2.2 # homeassistant.components.rpi_gpio_pwm pwmled==1.6.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 82235fde402..358a1d21748 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -814,7 +814,7 @@ pure-python-adb[async]==0.3.0.dev0 pushbullet.py==0.11.0 # homeassistant.components.pvoutput -pvo==0.2.1 +pvo==0.2.2 # homeassistant.components.canary py-canary==0.5.1 diff --git a/tests/components/pvoutput/test_config_flow.py b/tests/components/pvoutput/test_config_flow.py index 0c060a75a9d..8cd776beea3 100644 --- a/tests/components/pvoutput/test_config_flow.py +++ b/tests/components/pvoutput/test_config_flow.py @@ -47,7 +47,7 @@ async def test_full_user_flow( } assert len(mock_setup_entry.mock_calls) == 1 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 1 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 1 async def test_full_flow_with_authentication_error( @@ -68,7 +68,7 @@ async def test_full_flow_with_authentication_error( assert result.get("step_id") == SOURCE_USER assert "flow_id" in result - mock_pvoutput_config_flow.status.side_effect = PVOutputAuthenticationError + mock_pvoutput_config_flow.system.side_effect = PVOutputAuthenticationError result2 = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ @@ -83,9 +83,9 @@ async def test_full_flow_with_authentication_error( assert "flow_id" in result2 assert len(mock_setup_entry.mock_calls) == 0 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 1 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 1 - mock_pvoutput_config_flow.status.side_effect = None + mock_pvoutput_config_flow.system.side_effect = None result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], user_input={ @@ -102,14 +102,14 @@ async def test_full_flow_with_authentication_error( } assert len(mock_setup_entry.mock_calls) == 1 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 2 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 2 async def test_connection_error( hass: HomeAssistant, mock_pvoutput_config_flow: MagicMock ) -> None: """Test API connection error.""" - mock_pvoutput_config_flow.status.side_effect = PVOutputConnectionError + mock_pvoutput_config_flow.system.side_effect = PVOutputConnectionError result = await hass.config_entries.flow.async_init( DOMAIN, @@ -123,7 +123,7 @@ async def test_connection_error( assert result.get("type") == RESULT_TYPE_FORM assert result.get("errors") == {"base": "cannot_connect"} - assert len(mock_pvoutput_config_flow.status.mock_calls) == 1 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 1 async def test_already_configured( @@ -175,7 +175,7 @@ async def test_import_flow( } assert len(mock_setup_entry.mock_calls) == 1 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 1 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 1 async def test_reauth_flow( @@ -214,7 +214,7 @@ async def test_reauth_flow( } assert len(mock_setup_entry.mock_calls) == 1 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 1 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 1 async def test_reauth_with_authentication_error( @@ -243,7 +243,7 @@ async def test_reauth_with_authentication_error( assert result.get("step_id") == "reauth_confirm" assert "flow_id" in result - mock_pvoutput_config_flow.status.side_effect = PVOutputAuthenticationError + mock_pvoutput_config_flow.system.side_effect = PVOutputAuthenticationError result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_API_KEY: "invalid_key"}, @@ -256,9 +256,9 @@ async def test_reauth_with_authentication_error( assert "flow_id" in result2 assert len(mock_setup_entry.mock_calls) == 0 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 1 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 1 - mock_pvoutput_config_flow.status.side_effect = None + mock_pvoutput_config_flow.system.side_effect = None result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], user_input={CONF_API_KEY: "valid_key"}, @@ -273,7 +273,7 @@ async def test_reauth_with_authentication_error( } assert len(mock_setup_entry.mock_calls) == 1 - assert len(mock_pvoutput_config_flow.status.mock_calls) == 2 + assert len(mock_pvoutput_config_flow.system.mock_calls) == 2 async def test_reauth_api_error( @@ -297,7 +297,7 @@ async def test_reauth_api_error( assert result.get("step_id") == "reauth_confirm" assert "flow_id" in result - mock_pvoutput_config_flow.status.side_effect = PVOutputConnectionError + mock_pvoutput_config_flow.system.side_effect = PVOutputConnectionError result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_API_KEY: "some_new_key"}, diff --git a/tests/components/pvoutput/test_init.py b/tests/components/pvoutput/test_init.py index faaff3d4214..b583e0807e0 100644 --- a/tests/components/pvoutput/test_init.py +++ b/tests/components/pvoutput/test_init.py @@ -1,7 +1,11 @@ """Tests for the PVOutput integration.""" from unittest.mock import MagicMock -from pvo import PVOutputAuthenticationError, PVOutputConnectionError +from pvo import ( + PVOutputAuthenticationError, + PVOutputConnectionError, + PVOutputNoDataError, +) import pytest from homeassistant.components.pvoutput.const import CONF_SYSTEM_ID, DOMAIN @@ -35,13 +39,15 @@ async def test_load_unload_config_entry( assert mock_config_entry.state is ConfigEntryState.NOT_LOADED +@pytest.mark.parametrize("side_effect", [PVOutputConnectionError, PVOutputNoDataError]) async def test_config_entry_not_ready( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_pvoutput: MagicMock, + side_effect: Exception, ) -> None: """Test the PVOutput configuration entry not ready.""" - mock_pvoutput.status.side_effect = PVOutputConnectionError + mock_pvoutput.status.side_effect = side_effect mock_config_entry.add_to_hass(hass) await hass.config_entries.async_setup(mock_config_entry.entry_id) From 646c56e0e9d3ac45129baeeff1fd5defbecebb3f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 11 Feb 2022 19:38:55 +0100 Subject: [PATCH 23/25] Fix CPUSpeed with missing info (#66339) --- homeassistant/components/cpuspeed/sensor.py | 4 ++-- tests/components/cpuspeed/test_sensor.py | 22 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cpuspeed/sensor.py b/homeassistant/components/cpuspeed/sensor.py index 86c02ae9ee9..99b23834549 100644 --- a/homeassistant/components/cpuspeed/sensor.py +++ b/homeassistant/components/cpuspeed/sensor.py @@ -81,8 +81,8 @@ class CPUSpeedSensor(SensorEntity): if info: self._attr_extra_state_attributes = { - ATTR_ARCH: info["arch_string_raw"], - ATTR_BRAND: info["brand_raw"], + ATTR_ARCH: info.get("arch_string_raw"), + ATTR_BRAND: info.get("brand_raw"), } if HZ_ADVERTISED in info: self._attr_extra_state_attributes[ATTR_HZ] = round( diff --git a/tests/components/cpuspeed/test_sensor.py b/tests/components/cpuspeed/test_sensor.py index 134d19b31ea..ebf9f0111bd 100644 --- a/tests/components/cpuspeed/test_sensor.py +++ b/tests/components/cpuspeed/test_sensor.py @@ -61,3 +61,25 @@ async def test_sensor( assert state.attributes.get(ATTR_ARCH) == "aargh" assert state.attributes.get(ATTR_BRAND) == "Intel Ryzen 7" assert state.attributes.get(ATTR_HZ) == 3.6 + + +async def test_sensor_partial_info( + hass: HomeAssistant, + mock_cpuinfo: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test the CPU Speed sensor missing info.""" + mock_config_entry.add_to_hass(hass) + + # Pop some info from the mocked CPUSpeed + mock_cpuinfo.return_value.pop("brand_raw") + mock_cpuinfo.return_value.pop("arch_string_raw") + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get("sensor.cpu_speed") + assert state + assert state.state == "3.2" + assert state.attributes.get(ATTR_ARCH) is None + assert state.attributes.get(ATTR_BRAND) is None From c2545983318a23089eb9ae55d41cec646df53c9a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Feb 2022 15:19:57 -0600 Subject: [PATCH 24/25] Add unique id to lutron caseta config entry when missing (#66346) --- homeassistant/components/lutron_caseta/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index 546bb055ca8..0408f547f25 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -138,6 +138,11 @@ async def async_setup_entry( devices = bridge.get_devices() bridge_device = devices[BRIDGE_DEVICE_ID] + if not config_entry.unique_id: + hass.config_entries.async_update_entry( + config_entry, unique_id=hex(bridge_device["serial"])[2:].zfill(8) + ) + buttons = bridge.buttons _async_register_bridge_device(hass, entry_id, bridge_device) button_devices = _async_register_button_devices( From cb7f7dff725ff9ef8bb0775658f541af4dd7b776 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 11 Feb 2022 13:31:16 -0800 Subject: [PATCH 25/25] Bumped version to 2022.2.6 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 75cd20528bb..13250b3f9e5 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 2 -PATCH_VERSION: Final = "5" +PATCH_VERSION: Final = "6" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index 7d73060f322..c912d68ed55 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = homeassistant -version = 2022.2.5 +version = 2022.2.6 author = The Home Assistant Authors author_email = hello@home-assistant.io license = Apache-2.0