From 94800cb11e8c29d1284cf32c6781201996316ebf Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 21 Jun 2024 10:55:21 +0200 Subject: [PATCH 01/26] UniFi temp fix to handle runtime data (#120031) Co-authored-by: Franck Nijhof --- homeassistant/components/unifi/config_flow.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index e703f393d68..af4e0fde137 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -164,10 +164,14 @@ class UnifiFlowHandler(ConfigFlow, domain=UNIFI_DOMAIN): abort_reason = "reauth_successful" if config_entry: - hub = config_entry.runtime_data + try: + hub = config_entry.runtime_data - if hub and hub.available: - return self.async_abort(reason="already_configured") + if hub and hub.available: + return self.async_abort(reason="already_configured") + + except AttributeError: + pass return self.async_update_reload_and_abort( config_entry, data=self.config, reason=abort_reason From ddec6d04e1b7b03b3d5535d4f92ddc5b82fc8f9f Mon Sep 17 00:00:00 2001 From: Lode Smets <31108717+lodesmets@users.noreply.github.com> Date: Sun, 16 Jun 2024 00:48:08 +0200 Subject: [PATCH 02/26] Fix for Synology DSM shared images (#117695) * Fix for shared images * - FIX: Synology shared photos * - changes after review * Added test * added test * fix test --- .../components/synology_dsm/const.py | 2 ++ .../components/synology_dsm/media_source.py | 21 +++++++++++---- .../synology_dsm/test_media_source.py | 26 ++++++++++++++++--- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/synology_dsm/const.py b/homeassistant/components/synology_dsm/const.py index 11839caf8be..e6367458578 100644 --- a/homeassistant/components/synology_dsm/const.py +++ b/homeassistant/components/synology_dsm/const.py @@ -46,6 +46,8 @@ DEFAULT_SNAPSHOT_QUALITY = SNAPSHOT_PROFILE_BALANCED ENTITY_UNIT_LOAD = "load" +SHARED_SUFFIX = "_shared" + # Signals SIGNAL_CAMERA_SOURCE_CHANGED = "synology_dsm.camera_stream_source_changed" diff --git a/homeassistant/components/synology_dsm/media_source.py b/homeassistant/components/synology_dsm/media_source.py index 4b0c19b2b55..ace5733c222 100644 --- a/homeassistant/components/synology_dsm/media_source.py +++ b/homeassistant/components/synology_dsm/media_source.py @@ -21,7 +21,7 @@ from homeassistant.components.media_source import ( from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from .const import DOMAIN +from .const import DOMAIN, SHARED_SUFFIX from .models import SynologyDSMData @@ -45,6 +45,7 @@ class SynologyPhotosMediaSourceIdentifier: self.album_id = None self.cache_key = None self.file_name = None + self.is_shared = False if parts: self.unique_id = parts[0] @@ -54,6 +55,9 @@ class SynologyPhotosMediaSourceIdentifier: self.cache_key = parts[2] if len(parts) > 3: self.file_name = parts[3] + if self.file_name.endswith(SHARED_SUFFIX): + self.is_shared = True + self.file_name = self.file_name.removesuffix(SHARED_SUFFIX) class SynologyPhotosMediaSource(MediaSource): @@ -160,10 +164,13 @@ class SynologyPhotosMediaSource(MediaSource): if isinstance(mime_type, str) and mime_type.startswith("image/"): # Force small small thumbnails album_item.thumbnail_size = "sm" + suffix = "" + if album_item.is_shared: + suffix = SHARED_SUFFIX ret.append( BrowseMediaSource( domain=DOMAIN, - identifier=f"{identifier.unique_id}/{identifier.album_id}/{album_item.thumbnail_cache_key}/{album_item.file_name}", + identifier=f"{identifier.unique_id}/{identifier.album_id}/{album_item.thumbnail_cache_key}/{album_item.file_name}{suffix}", media_class=MediaClass.IMAGE, media_content_type=mime_type, title=album_item.file_name, @@ -186,8 +193,11 @@ class SynologyPhotosMediaSource(MediaSource): mime_type, _ = mimetypes.guess_type(identifier.file_name) if not isinstance(mime_type, str): raise Unresolvable("No file extension") + suffix = "" + if identifier.is_shared: + suffix = SHARED_SUFFIX return PlayMedia( - f"/synology_dsm/{identifier.unique_id}/{identifier.cache_key}/{identifier.file_name}", + f"/synology_dsm/{identifier.unique_id}/{identifier.cache_key}/{identifier.file_name}{suffix}", mime_type, ) @@ -223,13 +233,14 @@ class SynologyDsmMediaView(http.HomeAssistantView): # location: {cache_key}/{filename} cache_key, file_name = location.split("/") image_id = int(cache_key.split("_")[0]) + if shared := file_name.endswith(SHARED_SUFFIX): + file_name = file_name.removesuffix(SHARED_SUFFIX) mime_type, _ = mimetypes.guess_type(file_name) if not isinstance(mime_type, str): raise web.HTTPNotFound diskstation: SynologyDSMData = self.hass.data[DOMAIN][source_dir_id] - assert diskstation.api.photos is not None - item = SynoPhotosItem(image_id, "", "", "", cache_key, "", False) + item = SynoPhotosItem(image_id, "", "", "", cache_key, "", shared) try: image = await diskstation.api.photos.download_item(item) except SynologyDSMException as exc: diff --git a/tests/components/synology_dsm/test_media_source.py b/tests/components/synology_dsm/test_media_source.py index 2a792d174f8..433a4b15c23 100644 --- a/tests/components/synology_dsm/test_media_source.py +++ b/tests/components/synology_dsm/test_media_source.py @@ -50,7 +50,8 @@ def dsm_with_photos() -> MagicMock: dsm.photos.get_albums = AsyncMock(return_value=[SynoPhotosAlbum(1, "Album 1", 10)]) dsm.photos.get_items_from_album = AsyncMock( return_value=[ - SynoPhotosItem(10, "", "filename.jpg", 12345, "10_1298753", "sm", False) + SynoPhotosItem(10, "", "filename.jpg", 12345, "10_1298753", "sm", False), + SynoPhotosItem(10, "", "filename.jpg", 12345, "10_1298753", "sm", True), ] ) dsm.photos.get_item_thumbnail_url = AsyncMock( @@ -102,6 +103,11 @@ async def test_resolve_media_bad_identifier( "/synology_dsm/ABC012345/12631_47189/filename.png", "image/png", ), + ( + "ABC012345/12/12631_47189/filename.png_shared", + "/synology_dsm/ABC012345/12631_47189/filename.png_shared", + "image/png", + ), ], ) async def test_resolve_media_success( @@ -333,7 +339,7 @@ async def test_browse_media_get_items_thumbnail_error( result = await source.async_browse_media(item) assert result - assert len(result.children) == 1 + assert len(result.children) == 2 item = result.children[0] assert isinstance(item, BrowseMedia) assert item.thumbnail is None @@ -372,7 +378,7 @@ async def test_browse_media_get_items( result = await source.async_browse_media(item) assert result - assert len(result.children) == 1 + assert len(result.children) == 2 item = result.children[0] assert isinstance(item, BrowseMedia) assert item.identifier == "mocked_syno_dsm_entry/1/10_1298753/filename.jpg" @@ -382,6 +388,15 @@ async def test_browse_media_get_items( assert item.can_play assert not item.can_expand assert item.thumbnail == "http://my.thumbnail.url" + item = result.children[1] + assert isinstance(item, BrowseMedia) + assert item.identifier == "mocked_syno_dsm_entry/1/10_1298753/filename.jpg_shared" + assert item.title == "filename.jpg" + assert item.media_class == MediaClass.IMAGE + assert item.media_content_type == "image/jpeg" + assert item.can_play + assert not item.can_expand + assert item.thumbnail == "http://my.thumbnail.url" @pytest.mark.usefixtures("setup_media_source") @@ -435,3 +450,8 @@ async def test_media_view( request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg" ) assert isinstance(result, web.Response) + with patch.object(tempfile, "tempdir", tmp_path): + result = await view.get( + request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg_shared" + ) + assert isinstance(result, web.Response) From 8e63bd3ac09ee8426639c1c82b9b415b83e40a5e Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Mon, 17 Jun 2024 13:37:30 +0300 Subject: [PATCH 03/26] Fix Jewish Calendar unique id migration (#119683) * Implement correct passing fix * Keep the test as is, as it simulates the current behavior * Last minor change --- homeassistant/components/jewish_calendar/__init__.py | 5 ++++- tests/components/jewish_calendar/test_init.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/jewish_calendar/__init__.py b/homeassistant/components/jewish_calendar/__init__.py index 8383f9181fc..81fe6cb5377 100644 --- a/homeassistant/components/jewish_calendar/__init__.py +++ b/homeassistant/components/jewish_calendar/__init__.py @@ -72,11 +72,14 @@ def get_unique_prefix( havdalah_offset: int | None, ) -> str: """Create a prefix for unique ids.""" + # location.altitude was unset before 2024.6 when this method + # was used to create the unique id. As such it would always + # use the default altitude of 754. config_properties = [ location.latitude, location.longitude, location.timezone, - location.altitude, + 754, location.diaspora, language, candle_lighting_offset, diff --git a/tests/components/jewish_calendar/test_init.py b/tests/components/jewish_calendar/test_init.py index f052d4e7f46..b8454b41a60 100644 --- a/tests/components/jewish_calendar/test_init.py +++ b/tests/components/jewish_calendar/test_init.py @@ -38,7 +38,6 @@ async def test_import_unique_id_migration(hass: HomeAssistant) -> None: latitude=yaml_conf[DOMAIN][CONF_LATITUDE], longitude=yaml_conf[DOMAIN][CONF_LONGITUDE], timezone=hass.config.time_zone, - altitude=hass.config.elevation, diaspora=DEFAULT_DIASPORA, ) old_prefix = get_unique_prefix(location, DEFAULT_LANGUAGE, 20, 50) From 7a9537dcc93b62e47e1ab54aac1184769cf215ee Mon Sep 17 00:00:00 2001 From: jjlawren Date: Sat, 15 Jun 2024 17:47:47 -0500 Subject: [PATCH 04/26] Fix model import in Spotify (#119747) * Always import HomeAssistantSpotifyData in spotify.media_browser Relocate HomeAssistantSpotifyData to avoid circular import * Fix moved import * Rename module to 'models' * Adjust docstring --- homeassistant/components/spotify/__init__.py | 12 +----------- .../components/spotify/browse_media.py | 6 ++---- .../components/spotify/media_player.py | 3 ++- homeassistant/components/spotify/models.py | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 homeassistant/components/spotify/models.py diff --git a/homeassistant/components/spotify/__init__.py b/homeassistant/components/spotify/__init__.py index 632871ba36e..becf90b04cd 100644 --- a/homeassistant/components/spotify/__init__.py +++ b/homeassistant/components/spotify/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations -from dataclasses import dataclass from datetime import timedelta from typing import Any @@ -22,6 +21,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .browse_media import async_browse_media from .const import DOMAIN, LOGGER, SPOTIFY_SCOPES +from .models import HomeAssistantSpotifyData from .util import ( is_spotify_media_type, resolve_spotify_media_type, @@ -39,16 +39,6 @@ __all__ = [ ] -@dataclass -class HomeAssistantSpotifyData: - """Spotify data stored in the Home Assistant data object.""" - - client: Spotify - current_user: dict[str, Any] - devices: DataUpdateCoordinator[list[dict[str, Any]]] - session: OAuth2Session - - type SpotifyConfigEntry = ConfigEntry[HomeAssistantSpotifyData] diff --git a/homeassistant/components/spotify/browse_media.py b/homeassistant/components/spotify/browse_media.py index a1d3d9c804a..cff7cae5ebd 100644 --- a/homeassistant/components/spotify/browse_media.py +++ b/homeassistant/components/spotify/browse_media.py @@ -5,7 +5,7 @@ from __future__ import annotations from enum import StrEnum from functools import partial import logging -from typing import TYPE_CHECKING, Any +from typing import Any from spotipy import Spotify import yarl @@ -20,11 +20,9 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session from .const import DOMAIN, MEDIA_PLAYER_PREFIX, MEDIA_TYPE_SHOW, PLAYABLE_MEDIA_TYPES +from .models import HomeAssistantSpotifyData from .util import fetch_image_url -if TYPE_CHECKING: - from . import HomeAssistantSpotifyData - BROWSE_LIMIT = 48 diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index fe9614374f7..bd1bcdfd43e 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -29,9 +29,10 @@ from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import utcnow -from . import HomeAssistantSpotifyData, SpotifyConfigEntry +from . import SpotifyConfigEntry from .browse_media import async_browse_media_internal from .const import DOMAIN, MEDIA_PLAYER_PREFIX, PLAYABLE_MEDIA_TYPES, SPOTIFY_SCOPES +from .models import HomeAssistantSpotifyData from .util import fetch_image_url _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/spotify/models.py b/homeassistant/components/spotify/models.py new file mode 100644 index 00000000000..bbec134d89d --- /dev/null +++ b/homeassistant/components/spotify/models.py @@ -0,0 +1,19 @@ +"""Models for use in Spotify integration.""" + +from dataclasses import dataclass +from typing import Any + +from spotipy import Spotify + +from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + + +@dataclass +class HomeAssistantSpotifyData: + """Spotify data stored in the Home Assistant data object.""" + + client: Spotify + current_user: dict[str, Any] + devices: DataUpdateCoordinator[list[dict[str, Any]]] + session: OAuth2Session From 98aeb0b034b295b380c1039911ece99a16601d3f Mon Sep 17 00:00:00 2001 From: dubstomp <156379311+dubstomp@users.noreply.github.com> Date: Mon, 17 Jun 2024 02:31:18 -0700 Subject: [PATCH 05/26] Add Kasa Dimmer to Matter TRANSITION_BLOCKLIST (#119751) --- homeassistant/components/matter/light.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/matter/light.py b/homeassistant/components/matter/light.py index 89400c98989..007bcd1a33a 100644 --- a/homeassistant/components/matter/light.py +++ b/homeassistant/components/matter/light.py @@ -56,6 +56,7 @@ TRANSITION_BLOCKLIST = ( (5010, 769, "3.0", "1.0.0"), (4999, 25057, "1.0", "27.0"), (4448, 36866, "V1", "V1.0.0.5"), + (5009, 514, "1.0", "1.0.0"), ) From 0b4bbbffc8ab3be7be6a4c7d21664f53ecf0d4b9 Mon Sep 17 00:00:00 2001 From: 0bmay <57501269+0bmay@users.noreply.github.com> Date: Mon, 17 Jun 2024 01:02:42 -0700 Subject: [PATCH 06/26] Bump py-canary to v0.5.4 (#119793) fix gathering data from Canary sensors --- homeassistant/components/canary/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/canary/manifest.json b/homeassistant/components/canary/manifest.json index e6bc52540d5..4d5adf4a32b 100644 --- a/homeassistant/components/canary/manifest.json +++ b/homeassistant/components/canary/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/canary", "iot_class": "cloud_polling", "loggers": ["canary"], - "requirements": ["py-canary==0.5.3"] + "requirements": ["py-canary==0.5.4"] } diff --git a/requirements_all.txt b/requirements_all.txt index 289a4eead5d..f6b135167ba 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1619,7 +1619,7 @@ pvo==2.1.1 py-aosmith==1.0.8 # homeassistant.components.canary -py-canary==0.5.3 +py-canary==0.5.4 # homeassistant.components.ccm15 py-ccm15==0.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6bf487f7ef9..9cb5481b9a9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1284,7 +1284,7 @@ pvo==2.1.1 py-aosmith==1.0.8 # homeassistant.components.canary -py-canary==0.5.3 +py-canary==0.5.4 # homeassistant.components.ccm15 py-ccm15==0.0.9 From 08578147f505b2c85c8cf37be9ecd1850957060b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:17:35 +0200 Subject: [PATCH 07/26] Pin tenacity to 8.3.0 (#119815) --- homeassistant/package_constraints.txt | 3 +++ script/gen_requirements_all.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 94f030c6104..ac1db19684c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -197,3 +197,6 @@ scapy>=2.5.0 # Only tuf>=4 includes a constraint to <1.0. # https://github.com/theupdateframework/python-tuf/releases/tag/v4.0.0 tuf>=4.0.0 + +# https://github.com/jd/tenacity/issues/471 +tenacity<8.4.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 1f2f4bcab66..a12decd5b2c 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -219,6 +219,9 @@ scapy>=2.5.0 # Only tuf>=4 includes a constraint to <1.0. # https://github.com/theupdateframework/python-tuf/releases/tag/v4.0.0 tuf>=4.0.0 + +# https://github.com/jd/tenacity/issues/471 +tenacity<8.4.0 """ GENERATED_MESSAGE = ( From 99d1de901ee85146638e17ec113ef0f537c1bf5d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 17 Jun 2024 12:05:44 -0500 Subject: [PATCH 08/26] Bump aiozoneinfo to 0.2.0 (#119845) --- homeassistant/package_constraints.txt | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index ac1db19684c..8a64a42cc9d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -7,7 +7,7 @@ aiohttp-fast-url-dispatcher==0.3.0 aiohttp-fast-zlib==0.1.0 aiohttp==3.9.5 aiohttp_cors==0.7.0 -aiozoneinfo==0.1.0 +aiozoneinfo==0.2.0 astral==2.2 async-interrupt==1.1.1 async-upnp-client==0.38.3 diff --git a/pyproject.toml b/pyproject.toml index 1ca2b5cb40e..a03f3533a80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "aiohttp_cors==0.7.0", "aiohttp-fast-url-dispatcher==0.3.0", "aiohttp-fast-zlib==0.1.0", - "aiozoneinfo==0.1.0", + "aiozoneinfo==0.2.0", "astral==2.2", "async-interrupt==1.1.1", "attrs==23.2.0", diff --git a/requirements.txt b/requirements.txt index 05b0eb35c1e..4790de4d064 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.9.5 aiohttp_cors==0.7.0 aiohttp-fast-url-dispatcher==0.3.0 aiohttp-fast-zlib==0.1.0 -aiozoneinfo==0.1.0 +aiozoneinfo==0.2.0 astral==2.2 async-interrupt==1.1.1 attrs==23.2.0 From e1225d3f565ca4548e884a525edbfb359e51f464 Mon Sep 17 00:00:00 2001 From: Brent Petit Date: Tue, 18 Jun 2024 00:58:00 -0500 Subject: [PATCH 09/26] Fix up ecobee windspeed unit (#119870) --- homeassistant/components/ecobee/weather.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/ecobee/weather.py b/homeassistant/components/ecobee/weather.py index b7961f956eb..b6378504c65 100644 --- a/homeassistant/components/ecobee/weather.py +++ b/homeassistant/components/ecobee/weather.py @@ -59,7 +59,7 @@ class EcobeeWeather(WeatherEntity): _attr_native_pressure_unit = UnitOfPressure.HPA _attr_native_temperature_unit = UnitOfTemperature.FAHRENHEIT _attr_native_visibility_unit = UnitOfLength.METERS - _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND + _attr_native_wind_speed_unit = UnitOfSpeed.MILES_PER_HOUR _attr_has_entity_name = True _attr_name = None _attr_supported_features = WeatherEntityFeature.FORECAST_DAILY From 5b1b137fd2f954b5093ac5390c2eee4665439dcd Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Wed, 19 Jun 2024 17:21:43 +0300 Subject: [PATCH 10/26] Bump hdate to 0.10.9 (#119887) --- homeassistant/components/jewish_calendar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/jewish_calendar/manifest.json b/homeassistant/components/jewish_calendar/manifest.json index 20eb28929bd..6d2fe8ecfa1 100644 --- a/homeassistant/components/jewish_calendar/manifest.json +++ b/homeassistant/components/jewish_calendar/manifest.json @@ -6,6 +6,6 @@ "documentation": "https://www.home-assistant.io/integrations/jewish_calendar", "iot_class": "calculated", "loggers": ["hdate"], - "requirements": ["hdate==0.10.8"], + "requirements": ["hdate==0.10.9"], "single_config_entry": true } diff --git a/requirements_all.txt b/requirements_all.txt index f6b135167ba..91e5557f06d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1056,7 +1056,7 @@ hass-splunk==0.1.1 hassil==1.7.1 # homeassistant.components.jewish_calendar -hdate==0.10.8 +hdate==0.10.9 # homeassistant.components.heatmiser heatmiserV3==1.1.18 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9cb5481b9a9..6acad0b2132 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -867,7 +867,7 @@ hass-nabucasa==0.81.1 hassil==1.7.1 # homeassistant.components.jewish_calendar -hdate==0.10.8 +hdate==0.10.9 # homeassistant.components.here_travel_time here-routing==0.2.0 From 91064697b53fec6433eb03e3e82e1f0c4cf2a451 Mon Sep 17 00:00:00 2001 From: Jeef Date: Tue, 18 Jun 2024 14:31:59 -0600 Subject: [PATCH 11/26] Bump weatherflow4py to 0.2.21 (#119889) --- homeassistant/components/weatherflow_cloud/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/manifest.json b/homeassistant/components/weatherflow_cloud/manifest.json index 361349dcbe8..93df04d833c 100644 --- a/homeassistant/components/weatherflow_cloud/manifest.json +++ b/homeassistant/components/weatherflow_cloud/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/weatherflow_cloud", "iot_class": "cloud_polling", - "requirements": ["weatherflow4py==0.2.20"] + "requirements": ["weatherflow4py==0.2.21"] } diff --git a/requirements_all.txt b/requirements_all.txt index 91e5557f06d..58a7e9a0aa0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2867,7 +2867,7 @@ watchdog==2.3.1 waterfurnace==1.1.0 # homeassistant.components.weatherflow_cloud -weatherflow4py==0.2.20 +weatherflow4py==0.2.21 # homeassistant.components.webmin webmin-xmlrpc==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6acad0b2132..2a41ba11b62 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2226,7 +2226,7 @@ wallbox==0.6.0 watchdog==2.3.1 # homeassistant.components.weatherflow_cloud -weatherflow4py==0.2.20 +weatherflow4py==0.2.21 # homeassistant.components.webmin webmin-xmlrpc==0.0.2 From 16314c5c7c8cede70999c416aee760735d0b623d Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 7 Jun 2024 10:53:54 +0200 Subject: [PATCH 12/26] Bump babel to 2.15.0 (#119006) --- homeassistant/components/holiday/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/holiday/manifest.json b/homeassistant/components/holiday/manifest.json index bc7ce0e8dd1..c026c3e6363 100644 --- a/homeassistant/components/holiday/manifest.json +++ b/homeassistant/components/holiday/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/holiday", "iot_class": "local_polling", - "requirements": ["holidays==0.50", "babel==2.13.1"] + "requirements": ["holidays==0.50", "babel==2.15.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 58a7e9a0aa0..72889580c33 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -526,7 +526,7 @@ azure-kusto-ingest==3.1.0 azure-servicebus==7.10.0 # homeassistant.components.holiday -babel==2.13.1 +babel==2.15.0 # homeassistant.components.baidu baidu-aip==1.6.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2a41ba11b62..d6abb87bc0f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -463,7 +463,7 @@ azure-kusto-data[aio]==3.1.0 azure-kusto-ingest==3.1.0 # homeassistant.components.holiday -babel==2.13.1 +babel==2.15.0 # homeassistant.components.homekit base36==0.1.1 From c3607bd6d50389f1ed5aca86c543f49ff09c84a4 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Tue, 18 Jun 2024 22:05:11 +0200 Subject: [PATCH 13/26] Bump python-holidays to 0.51 (#119918) --- homeassistant/components/holiday/manifest.json | 2 +- homeassistant/components/workday/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/holiday/manifest.json b/homeassistant/components/holiday/manifest.json index c026c3e6363..cb67039f374 100644 --- a/homeassistant/components/holiday/manifest.json +++ b/homeassistant/components/holiday/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/holiday", "iot_class": "local_polling", - "requirements": ["holidays==0.50", "babel==2.15.0"] + "requirements": ["holidays==0.51", "babel==2.15.0"] } diff --git a/homeassistant/components/workday/manifest.json b/homeassistant/components/workday/manifest.json index 71c26a30e94..1148f46e2d1 100644 --- a/homeassistant/components/workday/manifest.json +++ b/homeassistant/components/workday/manifest.json @@ -7,5 +7,5 @@ "iot_class": "local_polling", "loggers": ["holidays"], "quality_scale": "internal", - "requirements": ["holidays==0.50"] + "requirements": ["holidays==0.51"] } diff --git a/requirements_all.txt b/requirements_all.txt index 72889580c33..be954357eb7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1084,7 +1084,7 @@ hole==0.8.0 # homeassistant.components.holiday # homeassistant.components.workday -holidays==0.50 +holidays==0.51 # homeassistant.components.frontend home-assistant-frontend==20240610.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d6abb87bc0f..4b3c08a2e24 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -886,7 +886,7 @@ hole==0.8.0 # homeassistant.components.holiday # homeassistant.components.workday -holidays==0.50 +holidays==0.51 # homeassistant.components.frontend home-assistant-frontend==20240610.1 From a55e82366ad269f291cab909efdfda8fd661c751 Mon Sep 17 00:00:00 2001 From: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com> Date: Wed, 19 Jun 2024 22:48:34 +0200 Subject: [PATCH 14/26] Fix Onkyo zone volume (#119949) --- homeassistant/components/onkyo/media_player.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index 7575443c793..97e0b3e3631 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -341,7 +341,7 @@ class OnkyoDevice(MediaPlayerEntity): del self._attr_extra_state_attributes[ATTR_PRESET] self._attr_is_volume_muted = bool(mute_raw[1] == "on") - # AMP_VOL/MAX_RECEIVER_VOL*(MAX_VOL/100) + # AMP_VOL / (MAX_RECEIVER_VOL * (MAX_VOL / 100)) self._attr_volume_level = volume_raw[1] / ( self._receiver_max_volume * self._max_volume / 100 ) @@ -511,9 +511,9 @@ class OnkyoDeviceZone(OnkyoDevice): elif ATTR_PRESET in self._attr_extra_state_attributes: del self._attr_extra_state_attributes[ATTR_PRESET] if self._supports_volume: - # AMP_VOL/MAX_RECEIVER_VOL*(MAX_VOL/100) - self._attr_volume_level = ( - volume_raw[1] / self._receiver_max_volume * (self._max_volume / 100) + # AMP_VOL / (MAX_RECEIVER_VOL * (MAX_VOL / 100)) + self._attr_volume_level = volume_raw[1] / ( + self._receiver_max_volume * self._max_volume / 100 ) @property From 500ef94ad4b304576ee80f998a19d5fdaa3dca80 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Wed, 19 Jun 2024 19:23:14 +0200 Subject: [PATCH 15/26] Bump plugwise to v0.37.4.1 (#119963) * Bump plugwise to v0.37.4 * bump plugwise to v0.37.4.1 --- homeassistant/components/plugwise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index ada7d2d2533..b1937ee219d 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -7,6 +7,6 @@ "integration_type": "hub", "iot_class": "local_polling", "loggers": ["plugwise"], - "requirements": ["plugwise==0.37.3"], + "requirements": ["plugwise==0.37.4.1"], "zeroconf": ["_plugwise._tcp.local."] } diff --git a/requirements_all.txt b/requirements_all.txt index be954357eb7..0a97310fdd5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1566,7 +1566,7 @@ plexauth==0.0.6 plexwebsocket==0.0.14 # homeassistant.components.plugwise -plugwise==0.37.3 +plugwise==0.37.4.1 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4b3c08a2e24..ace048b35ba 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1243,7 +1243,7 @@ plexauth==0.0.6 plexwebsocket==0.0.14 # homeassistant.components.plugwise -plugwise==0.37.3 +plugwise==0.37.4.1 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 From 96adf986255efa53b24743a0be9a13af66740e2d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 19 Jun 2024 22:45:59 +0200 Subject: [PATCH 16/26] Always create a new HomeAssistant object when falling back to recovery mode (#119969) --- homeassistant/bootstrap.py | 59 ++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 74196cdc625..8435fe73d40 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -256,22 +256,39 @@ async def async_setup_hass( runtime_config: RuntimeConfig, ) -> core.HomeAssistant | None: """Set up Home Assistant.""" - hass = core.HomeAssistant(runtime_config.config_dir) - async_enable_logging( - hass, - runtime_config.verbose, - runtime_config.log_rotate_days, - runtime_config.log_file, - runtime_config.log_no_color, - ) + def create_hass() -> core.HomeAssistant: + """Create the hass object and do basic setup.""" + hass = core.HomeAssistant(runtime_config.config_dir) + loader.async_setup(hass) - if runtime_config.debug or hass.loop.get_debug(): - hass.config.debug = True + async_enable_logging( + hass, + runtime_config.verbose, + runtime_config.log_rotate_days, + runtime_config.log_file, + runtime_config.log_no_color, + ) + + if runtime_config.debug or hass.loop.get_debug(): + hass.config.debug = True + + hass.config.safe_mode = runtime_config.safe_mode + hass.config.skip_pip = runtime_config.skip_pip + hass.config.skip_pip_packages = runtime_config.skip_pip_packages + + return hass + + async def stop_hass(hass: core.HomeAssistant) -> None: + """Stop hass.""" + # Ask integrations to shut down. It's messy but we can't + # do a clean stop without knowing what is broken + with contextlib.suppress(TimeoutError): + async with hass.timeout.async_timeout(10): + await hass.async_stop() + + hass = create_hass() - hass.config.safe_mode = runtime_config.safe_mode - hass.config.skip_pip = runtime_config.skip_pip - hass.config.skip_pip_packages = runtime_config.skip_pip_packages if runtime_config.skip_pip or runtime_config.skip_pip_packages: _LOGGER.warning( "Skipping pip installation of required modules. This may cause issues" @@ -283,7 +300,6 @@ async def async_setup_hass( _LOGGER.info("Config directory: %s", runtime_config.config_dir) - loader.async_setup(hass) block_async_io.enable() config_dict = None @@ -309,27 +325,28 @@ async def async_setup_hass( if config_dict is None: recovery_mode = True + await stop_hass(hass) + hass = create_hass() elif not basic_setup_success: _LOGGER.warning("Unable to set up core integrations. Activating recovery mode") recovery_mode = True + await stop_hass(hass) + hass = create_hass() elif any(domain not in hass.config.components for domain in CRITICAL_INTEGRATIONS): _LOGGER.warning( "Detected that %s did not load. Activating recovery mode", ",".join(CRITICAL_INTEGRATIONS), ) - # Ask integrations to shut down. It's messy but we can't - # do a clean stop without knowing what is broken - with contextlib.suppress(TimeoutError): - async with hass.timeout.async_timeout(10): - await hass.async_stop() - recovery_mode = True old_config = hass.config old_logging = hass.data.get(DATA_LOGGING) - hass = core.HomeAssistant(old_config.config_dir) + recovery_mode = True + await stop_hass(hass) + hass = create_hass() + if old_logging: hass.data[DATA_LOGGING] = old_logging hass.config.debug = old_config.debug From 5edf480a151da520aaff38195db08700fdb0519a Mon Sep 17 00:00:00 2001 From: Thomas Kistler Date: Fri, 21 Jun 2024 00:07:14 -0700 Subject: [PATCH 17/26] Fix Hydrawise volume unit bug (#119988) --- homeassistant/components/hydrawise/sensor.py | 15 +++++--- tests/components/hydrawise/conftest.py | 7 +++- tests/components/hydrawise/test_sensor.py | 36 ++++++++++++++++++-- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/hydrawise/sensor.py b/homeassistant/components/hydrawise/sensor.py index 87dc5e73afe..4b377108b16 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -71,7 +71,6 @@ FLOW_CONTROLLER_SENSORS: tuple[HydrawiseSensorEntityDescription, ...] = ( key="daily_total_water_use", translation_key="daily_total_water_use", device_class=SensorDeviceClass.VOLUME, - native_unit_of_measurement=UnitOfVolume.GALLONS, suggested_display_precision=1, value_fn=_get_controller_daily_total_water_use, ), @@ -79,7 +78,6 @@ FLOW_CONTROLLER_SENSORS: tuple[HydrawiseSensorEntityDescription, ...] = ( key="daily_active_water_use", translation_key="daily_active_water_use", device_class=SensorDeviceClass.VOLUME, - native_unit_of_measurement=UnitOfVolume.GALLONS, suggested_display_precision=1, value_fn=_get_controller_daily_active_water_use, ), @@ -87,7 +85,6 @@ FLOW_CONTROLLER_SENSORS: tuple[HydrawiseSensorEntityDescription, ...] = ( key="daily_inactive_water_use", translation_key="daily_inactive_water_use", device_class=SensorDeviceClass.VOLUME, - native_unit_of_measurement=UnitOfVolume.GALLONS, suggested_display_precision=1, value_fn=_get_controller_daily_inactive_water_use, ), @@ -98,7 +95,6 @@ FLOW_ZONE_SENSORS: tuple[SensorEntityDescription, ...] = ( key="daily_active_water_use", translation_key="daily_active_water_use", device_class=SensorDeviceClass.VOLUME, - native_unit_of_measurement=UnitOfVolume.GALLONS, suggested_display_precision=1, value_fn=_get_zone_daily_active_water_use, ), @@ -165,6 +161,17 @@ class HydrawiseSensor(HydrawiseEntity, SensorEntity): entity_description: HydrawiseSensorEntityDescription + @property + def native_unit_of_measurement(self) -> str | None: + """Return the unit_of_measurement of the sensor.""" + if self.entity_description.device_class != SensorDeviceClass.VOLUME: + return self.entity_description.native_unit_of_measurement + return ( + UnitOfVolume.GALLONS + if self.coordinator.data.user.units.units_name == "imperial" + else UnitOfVolume.LITERS + ) + @property def icon(self) -> str | None: """Icon of the entity based on the value.""" diff --git a/tests/components/hydrawise/conftest.py b/tests/components/hydrawise/conftest.py index 550e944db36..e1d0db47ebc 100644 --- a/tests/components/hydrawise/conftest.py +++ b/tests/components/hydrawise/conftest.py @@ -15,6 +15,7 @@ from pydrawise.schema import ( Sensor, SensorModel, SensorStatus, + UnitsSummary, User, Zone, ) @@ -84,7 +85,11 @@ def mock_auth() -> Generator[AsyncMock, None, None]: @pytest.fixture def user() -> User: """Hydrawise User fixture.""" - return User(customer_id=12345, email="asdf@asdf.com") + return User( + customer_id=12345, + email="asdf@asdf.com", + units=UnitsSummary(units_name="imperial"), + ) @pytest.fixture diff --git a/tests/components/hydrawise/test_sensor.py b/tests/components/hydrawise/test_sensor.py index fcbc47c41f4..af75ad69ade 100644 --- a/tests/components/hydrawise/test_sensor.py +++ b/tests/components/hydrawise/test_sensor.py @@ -3,13 +3,18 @@ from collections.abc import Awaitable, Callable from unittest.mock import patch -from pydrawise.schema import Controller, Zone +from pydrawise.schema import Controller, User, Zone import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.util.unit_system import ( + METRIC_SYSTEM, + US_CUSTOMARY_SYSTEM, + UnitSystem, +) from tests.common import MockConfigEntry, snapshot_platform @@ -45,7 +50,7 @@ async def test_suspended_state( assert next_cycle.state == "unknown" -async def test_no_sensor_and_water_state2( +async def test_no_sensor_and_water_state( hass: HomeAssistant, controller: Controller, mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]], @@ -63,3 +68,30 @@ async def test_no_sensor_and_water_state2( sensor = hass.states.get("binary_sensor.home_controller_connectivity") assert sensor is not None assert sensor.state == "on" + + +@pytest.mark.parametrize( + ("hydrawise_unit_system", "unit_system", "expected_state"), + [ + ("imperial", METRIC_SYSTEM, "454.6279552584"), + ("imperial", US_CUSTOMARY_SYSTEM, "120.1"), + ("metric", METRIC_SYSTEM, "120.1"), + ("metric", US_CUSTOMARY_SYSTEM, "31.7270634882136"), + ], +) +async def test_volume_unit_conversion( + hass: HomeAssistant, + unit_system: UnitSystem, + hydrawise_unit_system: str, + expected_state: str, + user: User, + mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]], +) -> None: + """Test volume unit conversion.""" + hass.config.units = unit_system + user.units.units_name = hydrawise_unit_system + await mock_add_config_entry() + + daily_active_water_use = hass.states.get("sensor.zone_one_daily_active_water_use") + assert daily_active_water_use is not None + assert daily_active_water_use.state == expected_state From 5b322f1af530b55b1e625f27fb07cc18a7824a56 Mon Sep 17 00:00:00 2001 From: BestPig Date: Thu, 20 Jun 2024 13:06:30 +0200 Subject: [PATCH 18/26] Fix songpal crash for soundbars without sound modes (#119999) Getting soundField on soundbar that doesn't support it crash raise an exception, so it make the whole components unavailable. As there is no simple way to know if soundField is supported, I just get all sound settings, and then pick soundField one if present. If not present, then return None to make it continue, it will just have to effect to display no sound mode and not able to select one (Exactly what we want). --- .../components/songpal/media_player.py | 7 +++- tests/components/songpal/__init__.py | 13 ++++++- tests/components/songpal/test_media_player.py | 37 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/songpal/media_player.py b/homeassistant/components/songpal/media_player.py index c6d6524cefb..9f828591a08 100644 --- a/homeassistant/components/songpal/media_player.py +++ b/homeassistant/components/songpal/media_player.py @@ -140,7 +140,12 @@ class SongpalEntity(MediaPlayerEntity): async def _get_sound_modes_info(self): """Get available sound modes and the active one.""" - settings = await self._dev.get_sound_settings("soundField") + for settings in await self._dev.get_sound_settings(): + if settings.target == "soundField": + break + else: + return None, {} + if isinstance(settings, Setting): settings = [settings] diff --git a/tests/components/songpal/__init__.py b/tests/components/songpal/__init__.py index ab585c5a6d5..15bf0c530d3 100644 --- a/tests/components/songpal/__init__.py +++ b/tests/components/songpal/__init__.py @@ -23,7 +23,9 @@ CONF_DATA = { } -def _create_mocked_device(throw_exception=False, wired_mac=MAC, wireless_mac=None): +def _create_mocked_device( + throw_exception=False, wired_mac=MAC, wireless_mac=None, no_soundfield=False +): mocked_device = MagicMock() type(mocked_device).get_supported_methods = AsyncMock( @@ -101,7 +103,14 @@ def _create_mocked_device(throw_exception=False, wired_mac=MAC, wireless_mac=Non soundField = MagicMock() soundField.currentValue = "sound_mode2" soundField.candidate = [sound_mode1, sound_mode2, sound_mode3] - type(mocked_device).get_sound_settings = AsyncMock(return_value=[soundField]) + + settings = MagicMock() + settings.target = "soundField" + settings.__iter__.return_value = [soundField] + + type(mocked_device).get_sound_settings = AsyncMock( + return_value=[] if no_soundfield else [settings] + ) type(mocked_device).set_power = AsyncMock() type(mocked_device).set_sound_settings = AsyncMock() diff --git a/tests/components/songpal/test_media_player.py b/tests/components/songpal/test_media_player.py index 2393a5a9086..8f56170b839 100644 --- a/tests/components/songpal/test_media_player.py +++ b/tests/components/songpal/test_media_player.py @@ -159,6 +159,43 @@ async def test_state( assert entity.unique_id == MAC +async def test_state_nosoundmode( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, +) -> None: + """Test state of the entity with no soundField in sound settings.""" + mocked_device = _create_mocked_device(no_soundfield=True) + entry = MockConfigEntry(domain=songpal.DOMAIN, data=CONF_DATA) + entry.add_to_hass(hass) + + with _patch_media_player_device(mocked_device): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.name == FRIENDLY_NAME + assert state.state == STATE_ON + attributes = state.as_dict()["attributes"] + assert attributes["volume_level"] == 0.5 + assert attributes["is_volume_muted"] is False + assert attributes["source_list"] == ["title1", "title2"] + assert attributes["source"] == "title2" + assert "sound_mode_list" not in attributes + assert "sound_mode" not in attributes + assert attributes["supported_features"] == SUPPORT_SONGPAL + + device = device_registry.async_get_device(identifiers={(songpal.DOMAIN, MAC)}) + assert device.connections == {(dr.CONNECTION_NETWORK_MAC, MAC)} + assert device.manufacturer == "Sony Corporation" + assert device.name == FRIENDLY_NAME + assert device.sw_version == SW_VERSION + assert device.model == MODEL + + entity = entity_registry.async_get(ENTITY_ID) + assert entity.unique_id == MAC + + async def test_state_wireless( hass: HomeAssistant, device_registry: dr.DeviceRegistry, From 39f67afa64621e9a783212ba3d15a11e0efe24da Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 21 Jun 2024 10:36:52 +0200 Subject: [PATCH 19/26] Make UniFi services handle unloaded config entry (#120028) --- homeassistant/components/unifi/services.py | 15 +++++--- tests/components/unifi/test_services.py | 41 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/unifi/services.py b/homeassistant/components/unifi/services.py index 5dcc0e9719c..ce726a0f5d0 100644 --- a/homeassistant/components/unifi/services.py +++ b/homeassistant/components/unifi/services.py @@ -6,6 +6,7 @@ from typing import Any from aiounifi.models.client import ClientReconnectRequest, ClientRemoveRequest import voluptuous as vol +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ATTR_DEVICE_ID from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers import device_registry as dr @@ -66,9 +67,9 @@ async def async_reconnect_client(hass: HomeAssistant, data: Mapping[str, Any]) - if mac == "": return - for entry in hass.config_entries.async_entries(UNIFI_DOMAIN): - if ( - (hub := entry.runtime_data) + for config_entry in hass.config_entries.async_entries(UNIFI_DOMAIN): + if config_entry.state is not ConfigEntryState.LOADED or ( + (hub := config_entry.runtime_data) and not hub.available or (client := hub.api.clients.get(mac)) is None or client.is_wired @@ -85,8 +86,12 @@ async def async_remove_clients(hass: HomeAssistant, data: Mapping[str, Any]) -> - Total time between first seen and last seen is less than 15 minutes. - Neither IP, hostname nor name is configured. """ - for entry in hass.config_entries.async_entries(UNIFI_DOMAIN): - if (hub := entry.runtime_data) and not hub.available: + for config_entry in hass.config_entries.async_entries(UNIFI_DOMAIN): + if ( + config_entry.state is not ConfigEntryState.LOADED + or (hub := config_entry.runtime_data) + and not hub.available + ): continue clients_to_remove = [] diff --git a/tests/components/unifi/test_services.py b/tests/components/unifi/test_services.py index 8cd029b1cf5..c4ccdc8b902 100644 --- a/tests/components/unifi/test_services.py +++ b/tests/components/unifi/test_services.py @@ -281,3 +281,44 @@ async def test_remove_clients_no_call_on_empty_list( await hass.services.async_call(UNIFI_DOMAIN, SERVICE_REMOVE_CLIENTS, blocking=True) assert aioclient_mock.call_count == 0 + + +@pytest.mark.parametrize( + "clients_all_payload", + [ + [ + { + "first_seen": 100, + "last_seen": 500, + "mac": "00:00:00:00:00:01", + } + ] + ], +) +async def test_services_handle_unloaded_config_entry( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + device_registry: dr.DeviceRegistry, + config_entry_setup: ConfigEntry, + clients_all_payload, +) -> None: + """Verify no call is made if config entry is unloaded.""" + await hass.config_entries.async_unload(config_entry_setup.entry_id) + await hass.async_block_till_done() + + aioclient_mock.clear_requests() + + await hass.services.async_call(UNIFI_DOMAIN, SERVICE_REMOVE_CLIENTS, blocking=True) + assert aioclient_mock.call_count == 0 + + device_entry = device_registry.async_get_or_create( + config_entry_id=config_entry_setup.entry_id, + connections={(dr.CONNECTION_NETWORK_MAC, clients_all_payload[0]["mac"])}, + ) + await hass.services.async_call( + UNIFI_DOMAIN, + SERVICE_RECONNECT_CLIENT, + service_data={ATTR_DEVICE_ID: device_entry.id}, + blocking=True, + ) + assert aioclient_mock.call_count == 0 From 75a469f4d6862b1b575d24817e01d236da8bb440 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Fri, 21 Jun 2024 04:37:51 -0400 Subject: [PATCH 20/26] Bump env-canada to 0.6.3 (#120035) Co-authored-by: J. Nick Koston --- homeassistant/components/environment_canada/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/environment_canada/manifest.json b/homeassistant/components/environment_canada/manifest.json index f29c8177dfd..a0bdd5d4919 100644 --- a/homeassistant/components/environment_canada/manifest.json +++ b/homeassistant/components/environment_canada/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/environment_canada", "iot_class": "cloud_polling", "loggers": ["env_canada"], - "requirements": ["env-canada==0.6.2"] + "requirements": ["env-canada==0.6.3"] } diff --git a/requirements_all.txt b/requirements_all.txt index 0a97310fdd5..7b5d02fb9c4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -810,7 +810,7 @@ enocean==0.50 enturclient==0.2.4 # homeassistant.components.environment_canada -env-canada==0.6.2 +env-canada==0.6.3 # homeassistant.components.season ephem==4.1.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ace048b35ba..1f37062bfaa 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ energyzero==2.1.0 enocean==0.50 # homeassistant.components.environment_canada -env-canada==0.6.2 +env-canada==0.6.3 # homeassistant.components.season ephem==4.1.5 From 59b2f4e56fd3b9f5bfa7e69b669a05a3522cc11b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 21 Jun 2024 08:47:50 +0200 Subject: [PATCH 21/26] Bump aioimaplib to 1.1.0 (#120045) --- homeassistant/components/imap/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/imap/manifest.json b/homeassistant/components/imap/manifest.json index 3c35d00f714..b058a3d50f4 100644 --- a/homeassistant/components/imap/manifest.json +++ b/homeassistant/components/imap/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/imap", "iot_class": "cloud_push", "loggers": ["aioimaplib"], - "requirements": ["aioimaplib==1.0.1"] + "requirements": ["aioimaplib==1.1.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 7b5d02fb9c4..66dd81ff9af 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -261,7 +261,7 @@ aiohomekit==3.1.5 aiohue==4.7.1 # homeassistant.components.imap -aioimaplib==1.0.1 +aioimaplib==1.1.0 # homeassistant.components.apache_kafka aiokafka==0.10.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1f37062bfaa..15cbe079531 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -237,7 +237,7 @@ aiohomekit==3.1.5 aiohue==4.7.1 # homeassistant.components.imap -aioimaplib==1.0.1 +aioimaplib==1.1.0 # homeassistant.components.apache_kafka aiokafka==0.10.0 From 53a21dcb6b6c3c7c266c9906725094a099147c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Fri, 21 Jun 2024 10:34:39 +0200 Subject: [PATCH 22/26] Update AEMET-OpenData to v0.5.2 (#120065) --- homeassistant/components/aemet/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/aemet/manifest.json b/homeassistant/components/aemet/manifest.json index b8a19bcd27a..8a22385f82b 100644 --- a/homeassistant/components/aemet/manifest.json +++ b/homeassistant/components/aemet/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/aemet", "iot_class": "cloud_polling", "loggers": ["aemet_opendata"], - "requirements": ["AEMET-OpenData==0.5.1"] + "requirements": ["AEMET-OpenData==0.5.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index 66dd81ff9af..0a63d43b593 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -4,7 +4,7 @@ -r requirements.txt # homeassistant.components.aemet -AEMET-OpenData==0.5.1 +AEMET-OpenData==0.5.2 # homeassistant.components.honeywell AIOSomecomfort==0.0.25 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 15cbe079531..02d2f23d61a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -4,7 +4,7 @@ -r requirements_test.txt # homeassistant.components.aemet -AEMET-OpenData==0.5.1 +AEMET-OpenData==0.5.2 # homeassistant.components.honeywell AIOSomecomfort==0.0.25 From 92c12fdf0ac0f87acf10800ffb7ed8c6358e1d78 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Fri, 21 Jun 2024 11:19:52 -0500 Subject: [PATCH 23/26] Bump intents to 2024.6.21 (#120106) --- homeassistant/components/conversation/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../components/conversation/snapshots/test_init.ambr | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/conversation/manifest.json b/homeassistant/components/conversation/manifest.json index a3af6607aba..ee0b29f22fc 100644 --- a/homeassistant/components/conversation/manifest.json +++ b/homeassistant/components/conversation/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/conversation", "integration_type": "system", "quality_scale": "internal", - "requirements": ["hassil==1.7.1", "home-assistant-intents==2024.6.5"] + "requirements": ["hassil==1.7.1", "home-assistant-intents==2024.6.21"] } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 8a64a42cc9d..5a64438116f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -33,7 +33,7 @@ hass-nabucasa==0.81.1 hassil==1.7.1 home-assistant-bluetooth==1.12.0 home-assistant-frontend==20240610.1 -home-assistant-intents==2024.6.5 +home-assistant-intents==2024.6.21 httpx==0.27.0 ifaddr==0.2.0 Jinja2==3.1.4 diff --git a/requirements_all.txt b/requirements_all.txt index 0a63d43b593..4ee7c3518b1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1090,7 +1090,7 @@ holidays==0.51 home-assistant-frontend==20240610.1 # homeassistant.components.conversation -home-assistant-intents==2024.6.5 +home-assistant-intents==2024.6.21 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 02d2f23d61a..495aec2eec0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -892,7 +892,7 @@ holidays==0.51 home-assistant-frontend==20240610.1 # homeassistant.components.conversation -home-assistant-intents==2024.6.5 +home-assistant-intents==2024.6.21 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/tests/components/conversation/snapshots/test_init.ambr b/tests/components/conversation/snapshots/test_init.ambr index 6264e61863f..403c72aaa10 100644 --- a/tests/components/conversation/snapshots/test_init.ambr +++ b/tests/components/conversation/snapshots/test_init.ambr @@ -563,7 +563,7 @@ 'speech': dict({ 'plain': dict({ 'extra_data': None, - 'speech': 'Sorry, I am not aware of any device called kitchen light', + 'speech': 'Sorry, I am not aware of any device called kitchen', }), }), }), @@ -703,7 +703,7 @@ 'speech': dict({ 'plain': dict({ 'extra_data': None, - 'speech': 'Sorry, I am not aware of any device called late added light', + 'speech': 'Sorry, I am not aware of any device called late added', }), }), }), @@ -783,7 +783,7 @@ 'speech': dict({ 'plain': dict({ 'extra_data': None, - 'speech': 'Sorry, I am not aware of any device called kitchen light', + 'speech': 'Sorry, I am not aware of any device called kitchen', }), }), }), @@ -803,7 +803,7 @@ 'speech': dict({ 'plain': dict({ 'extra_data': None, - 'speech': 'Sorry, I am not aware of any device called my cool light', + 'speech': 'Sorry, I am not aware of any device called my cool', }), }), }), @@ -943,7 +943,7 @@ 'speech': dict({ 'plain': dict({ 'extra_data': None, - 'speech': 'Sorry, I am not aware of any device called kitchen light', + 'speech': 'Sorry, I am not aware of any device called kitchen', }), }), }), @@ -993,7 +993,7 @@ 'speech': dict({ 'plain': dict({ 'extra_data': None, - 'speech': 'Sorry, I am not aware of any device called renamed light', + 'speech': 'Sorry, I am not aware of any device called renamed', }), }), }), From c1dc6fd51155d71b3fe4f04def01066b5ba98e3f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 21 Jun 2024 18:42:30 +0200 Subject: [PATCH 24/26] Bump version to 2024.6.4 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index cd340cd5079..25c5df8a136 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -24,7 +24,7 @@ if TYPE_CHECKING: APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2024 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "3" +PATCH_VERSION: Final = "4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0) diff --git a/pyproject.toml b/pyproject.toml index a03f3533a80..4c11317242e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2024.6.3" +version = "2024.6.4" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From e62268d5eac922597eb19e6fb4408172c8db7803 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 21 Jun 2024 19:59:25 +0200 Subject: [PATCH 25/26] Revert "Make UniFi services handle unloaded config entry (#120028)" This reverts commit 39f67afa64621e9a783212ba3d15a11e0efe24da. --- homeassistant/components/unifi/services.py | 15 +++----- tests/components/unifi/test_services.py | 41 ---------------------- 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/unifi/services.py b/homeassistant/components/unifi/services.py index ce726a0f5d0..5dcc0e9719c 100644 --- a/homeassistant/components/unifi/services.py +++ b/homeassistant/components/unifi/services.py @@ -6,7 +6,6 @@ from typing import Any from aiounifi.models.client import ClientReconnectRequest, ClientRemoveRequest import voluptuous as vol -from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ATTR_DEVICE_ID from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers import device_registry as dr @@ -67,9 +66,9 @@ async def async_reconnect_client(hass: HomeAssistant, data: Mapping[str, Any]) - if mac == "": return - for config_entry in hass.config_entries.async_entries(UNIFI_DOMAIN): - if config_entry.state is not ConfigEntryState.LOADED or ( - (hub := config_entry.runtime_data) + for entry in hass.config_entries.async_entries(UNIFI_DOMAIN): + if ( + (hub := entry.runtime_data) and not hub.available or (client := hub.api.clients.get(mac)) is None or client.is_wired @@ -86,12 +85,8 @@ async def async_remove_clients(hass: HomeAssistant, data: Mapping[str, Any]) -> - Total time between first seen and last seen is less than 15 minutes. - Neither IP, hostname nor name is configured. """ - for config_entry in hass.config_entries.async_entries(UNIFI_DOMAIN): - if ( - config_entry.state is not ConfigEntryState.LOADED - or (hub := config_entry.runtime_data) - and not hub.available - ): + for entry in hass.config_entries.async_entries(UNIFI_DOMAIN): + if (hub := entry.runtime_data) and not hub.available: continue clients_to_remove = [] diff --git a/tests/components/unifi/test_services.py b/tests/components/unifi/test_services.py index c4ccdc8b902..8cd029b1cf5 100644 --- a/tests/components/unifi/test_services.py +++ b/tests/components/unifi/test_services.py @@ -281,44 +281,3 @@ async def test_remove_clients_no_call_on_empty_list( await hass.services.async_call(UNIFI_DOMAIN, SERVICE_REMOVE_CLIENTS, blocking=True) assert aioclient_mock.call_count == 0 - - -@pytest.mark.parametrize( - "clients_all_payload", - [ - [ - { - "first_seen": 100, - "last_seen": 500, - "mac": "00:00:00:00:00:01", - } - ] - ], -) -async def test_services_handle_unloaded_config_entry( - hass: HomeAssistant, - aioclient_mock: AiohttpClientMocker, - device_registry: dr.DeviceRegistry, - config_entry_setup: ConfigEntry, - clients_all_payload, -) -> None: - """Verify no call is made if config entry is unloaded.""" - await hass.config_entries.async_unload(config_entry_setup.entry_id) - await hass.async_block_till_done() - - aioclient_mock.clear_requests() - - await hass.services.async_call(UNIFI_DOMAIN, SERVICE_REMOVE_CLIENTS, blocking=True) - assert aioclient_mock.call_count == 0 - - device_entry = device_registry.async_get_or_create( - config_entry_id=config_entry_setup.entry_id, - connections={(dr.CONNECTION_NETWORK_MAC, clients_all_payload[0]["mac"])}, - ) - await hass.services.async_call( - UNIFI_DOMAIN, - SERVICE_RECONNECT_CLIENT, - service_data={ATTR_DEVICE_ID: device_entry.id}, - blocking=True, - ) - assert aioclient_mock.call_count == 0 From febcb335457526bce4b1115d31a551842140c92a Mon Sep 17 00:00:00 2001 From: Thomas Kistler Date: Tue, 18 Jun 2024 01:26:31 -0700 Subject: [PATCH 26/26] Update pydrawise to 2024.6.4 (#119868) --- homeassistant/components/hydrawise/manifest.json | 2 +- homeassistant/components/hydrawise/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/hydrawise/manifest.json b/homeassistant/components/hydrawise/manifest.json index dc6408407e7..b85ddca042e 100644 --- a/homeassistant/components/hydrawise/manifest.json +++ b/homeassistant/components/hydrawise/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/hydrawise", "iot_class": "cloud_polling", "loggers": ["pydrawise"], - "requirements": ["pydrawise==2024.6.3"] + "requirements": ["pydrawise==2024.6.4"] } diff --git a/homeassistant/components/hydrawise/sensor.py b/homeassistant/components/hydrawise/sensor.py index 4b377108b16..fe4b33d5851 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -48,7 +48,7 @@ def _get_zone_daily_active_water_use(sensor: HydrawiseSensor) -> float: return float(daily_water_summary.active_use_by_zone_id.get(sensor.zone.id, 0.0)) -def _get_controller_daily_active_water_use(sensor: HydrawiseSensor) -> float: +def _get_controller_daily_active_water_use(sensor: HydrawiseSensor) -> float | None: """Get active water use for the controller.""" daily_water_summary = sensor.coordinator.data.daily_water_use[sensor.controller.id] return daily_water_summary.total_active_use diff --git a/requirements_all.txt b/requirements_all.txt index 4ee7c3518b1..1ccf5efbdc9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1794,7 +1794,7 @@ pydiscovergy==3.0.1 pydoods==1.0.2 # homeassistant.components.hydrawise -pydrawise==2024.6.3 +pydrawise==2024.6.4 # homeassistant.components.android_ip_webcam pydroid-ipcam==2.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 495aec2eec0..328f660547f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1405,7 +1405,7 @@ pydexcom==0.2.3 pydiscovergy==3.0.1 # homeassistant.components.hydrawise -pydrawise==2024.6.3 +pydrawise==2024.6.4 # homeassistant.components.android_ip_webcam pydroid-ipcam==2.0.0