From 4e4ac795956b59bcbea91261c722c690b7e0f3ba Mon Sep 17 00:00:00 2001 From: MatthewFlamm <39341281+MatthewFlamm@users.noreply.github.com> Date: Fri, 3 May 2024 07:06:40 -0400 Subject: [PATCH 01/23] Fix nws forecast coordinators and remove legacy forecast handling (#115857) Co-authored-by: J. Nick Koston --- homeassistant/components/nws/__init__.py | 108 +++++++------------- homeassistant/components/nws/manifest.json | 2 +- homeassistant/components/nws/sensor.py | 9 +- homeassistant/components/nws/weather.py | 112 ++++++--------------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/nws/conftest.py | 1 + tests/components/nws/test_weather.py | 80 +-------------- 8 files changed, 82 insertions(+), 234 deletions(-) diff --git a/homeassistant/components/nws/__init__.py b/homeassistant/components/nws/__init__.py index 34157769b97..840d4d917f7 100644 --- a/homeassistant/components/nws/__init__.py +++ b/homeassistant/components/nws/__init__.py @@ -2,21 +2,18 @@ from __future__ import annotations -from collections.abc import Awaitable, Callable from dataclasses import dataclass import datetime import logging -from typing import TYPE_CHECKING -from pynws import SimpleNWS +from pynws import SimpleNWS, call_with_retry from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.helpers import debounce from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo -from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.update_coordinator import TimestampDataUpdateCoordinator from homeassistant.util.dt import utcnow @@ -27,8 +24,10 @@ _LOGGER = logging.getLogger(__name__) PLATFORMS = [Platform.SENSOR, Platform.WEATHER] DEFAULT_SCAN_INTERVAL = datetime.timedelta(minutes=10) -FAILED_SCAN_INTERVAL = datetime.timedelta(minutes=1) -DEBOUNCE_TIME = 60 # in seconds +RETRY_INTERVAL = datetime.timedelta(minutes=1) +RETRY_STOP = datetime.timedelta(minutes=10) + +DEBOUNCE_TIME = 10 * 60 # in seconds def base_unique_id(latitude: float, longitude: float) -> str: @@ -41,62 +40,9 @@ class NWSData: """Data for the National Weather Service integration.""" api: SimpleNWS - coordinator_observation: NwsDataUpdateCoordinator - coordinator_forecast: NwsDataUpdateCoordinator - coordinator_forecast_hourly: NwsDataUpdateCoordinator - - -class NwsDataUpdateCoordinator(TimestampDataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module - """NWS data update coordinator. - - Implements faster data update intervals for failed updates and exposes a last successful update time. - """ - - def __init__( - self, - hass: HomeAssistant, - logger: logging.Logger, - *, - name: str, - update_interval: datetime.timedelta, - failed_update_interval: datetime.timedelta, - update_method: Callable[[], Awaitable[None]] | None = None, - request_refresh_debouncer: debounce.Debouncer | None = None, - ) -> None: - """Initialize NWS coordinator.""" - super().__init__( - hass, - logger, - name=name, - update_interval=update_interval, - update_method=update_method, - request_refresh_debouncer=request_refresh_debouncer, - ) - self.failed_update_interval = failed_update_interval - - @callback - def _schedule_refresh(self) -> None: - """Schedule a refresh.""" - if self._unsub_refresh: - self._unsub_refresh() - self._unsub_refresh = None - - # We _floor_ utcnow to create a schedule on a rounded second, - # minimizing the time between the point and the real activation. - # That way we obtain a constant update frequency, - # as long as the update process takes less than a second - if self.last_update_success: - if TYPE_CHECKING: - # the base class allows None, but this one doesn't - assert self.update_interval is not None - update_interval = self.update_interval - else: - update_interval = self.failed_update_interval - self._unsub_refresh = async_track_point_in_utc_time( - self.hass, - self._handle_refresh_interval, - utcnow().replace(microsecond=0) + update_interval, - ) + coordinator_observation: TimestampDataUpdateCoordinator[None] + coordinator_forecast: TimestampDataUpdateCoordinator[None] + coordinator_forecast_hourly: TimestampDataUpdateCoordinator[None] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -114,39 +60,57 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def update_observation() -> None: """Retrieve recent observations.""" - await nws_data.update_observation(start_time=utcnow() - UPDATE_TIME_PERIOD) + await call_with_retry( + nws_data.update_observation, + RETRY_INTERVAL, + RETRY_STOP, + start_time=utcnow() - UPDATE_TIME_PERIOD, + ) - coordinator_observation = NwsDataUpdateCoordinator( + async def update_forecast() -> None: + """Retrieve twice-daily forecsat.""" + await call_with_retry( + nws_data.update_forecast, + RETRY_INTERVAL, + RETRY_STOP, + ) + + async def update_forecast_hourly() -> None: + """Retrieve hourly forecast.""" + await call_with_retry( + nws_data.update_forecast_hourly, + RETRY_INTERVAL, + RETRY_STOP, + ) + + coordinator_observation = TimestampDataUpdateCoordinator( hass, _LOGGER, name=f"NWS observation station {station}", update_method=update_observation, update_interval=DEFAULT_SCAN_INTERVAL, - failed_update_interval=FAILED_SCAN_INTERVAL, request_refresh_debouncer=debounce.Debouncer( hass, _LOGGER, cooldown=DEBOUNCE_TIME, immediate=True ), ) - coordinator_forecast = NwsDataUpdateCoordinator( + coordinator_forecast = TimestampDataUpdateCoordinator( hass, _LOGGER, name=f"NWS forecast station {station}", - update_method=nws_data.update_forecast, + update_method=update_forecast, update_interval=DEFAULT_SCAN_INTERVAL, - failed_update_interval=FAILED_SCAN_INTERVAL, request_refresh_debouncer=debounce.Debouncer( hass, _LOGGER, cooldown=DEBOUNCE_TIME, immediate=True ), ) - coordinator_forecast_hourly = NwsDataUpdateCoordinator( + coordinator_forecast_hourly = TimestampDataUpdateCoordinator( hass, _LOGGER, name=f"NWS forecast hourly station {station}", - update_method=nws_data.update_forecast_hourly, + update_method=update_forecast_hourly, update_interval=DEFAULT_SCAN_INTERVAL, - failed_update_interval=FAILED_SCAN_INTERVAL, request_refresh_debouncer=debounce.Debouncer( hass, _LOGGER, cooldown=DEBOUNCE_TIME, immediate=True ), diff --git a/homeassistant/components/nws/manifest.json b/homeassistant/components/nws/manifest.json index 4006a145db4..f68d76ee95b 100644 --- a/homeassistant/components/nws/manifest.json +++ b/homeassistant/components/nws/manifest.json @@ -7,5 +7,5 @@ "iot_class": "cloud_polling", "loggers": ["metar", "pynws"], "quality_scale": "platinum", - "requirements": ["pynws==1.6.0"] + "requirements": ["pynws[retry]==1.7.0"] } diff --git a/homeassistant/components/nws/sensor.py b/homeassistant/components/nws/sensor.py index 1d8c5ab045e..447c2dc5cf8 100644 --- a/homeassistant/components/nws/sensor.py +++ b/homeassistant/components/nws/sensor.py @@ -25,7 +25,10 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + TimestampDataUpdateCoordinator, +) from homeassistant.util.dt import utcnow from homeassistant.util.unit_conversion import ( DistanceConverter, @@ -34,7 +37,7 @@ from homeassistant.util.unit_conversion import ( ) from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM -from . import NWSData, NwsDataUpdateCoordinator, base_unique_id, device_info +from . import NWSData, base_unique_id, device_info from .const import ATTRIBUTION, CONF_STATION, DOMAIN, OBSERVATION_VALID_TIME PARALLEL_UPDATES = 0 @@ -158,7 +161,7 @@ async def async_setup_entry( ) -class NWSSensor(CoordinatorEntity[NwsDataUpdateCoordinator], SensorEntity): +class NWSSensor(CoordinatorEntity[TimestampDataUpdateCoordinator[None]], SensorEntity): """An NWS Sensor Entity.""" entity_description: NWSSensorEntityDescription diff --git a/homeassistant/components/nws/weather.py b/homeassistant/components/nws/weather.py index 89414f5acf1..c017d579c3a 100644 --- a/homeassistant/components/nws/weather.py +++ b/homeassistant/components/nws/weather.py @@ -2,6 +2,7 @@ from __future__ import annotations +from functools import partial from types import MappingProxyType from typing import TYPE_CHECKING, Any, cast @@ -34,7 +35,6 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.util.dt import utcnow from homeassistant.util.unit_conversion import SpeedConverter, TemperatureConverter from . import NWSData, base_unique_id, device_info @@ -46,7 +46,6 @@ from .const import ( DOMAIN, FORECAST_VALID_TIME, HOURLY, - OBSERVATION_VALID_TIME, ) PARALLEL_UPDATES = 0 @@ -140,96 +139,69 @@ class NWSWeather(CoordinatorWeatherEntity): self.nws = nws_data.api latitude = entry_data[CONF_LATITUDE] longitude = entry_data[CONF_LONGITUDE] - self.coordinator_forecast_legacy = nws_data.coordinator_forecast - self.station = self.nws.station - self.observation: dict[str, Any] | None = None - self._forecast_hourly: list[dict[str, Any]] | None = None - self._forecast_legacy: list[dict[str, Any]] | None = None - self._forecast_twice_daily: list[dict[str, Any]] | None = None + self.station = self.nws.station self._attr_unique_id = _calculate_unique_id(entry_data, DAYNIGHT) self._attr_device_info = device_info(latitude, longitude) self._attr_name = self.station async def async_added_to_hass(self) -> None: - """Set up a listener and load data.""" + """When entity is added to hass.""" await super().async_added_to_hass() - self.async_on_remove( - self.coordinator_forecast_legacy.async_add_listener( - self._handle_legacy_forecast_coordinator_update + self.async_on_remove(partial(self._remove_forecast_listener, "daily")) + self.async_on_remove(partial(self._remove_forecast_listener, "hourly")) + self.async_on_remove(partial(self._remove_forecast_listener, "twice_daily")) + + for forecast_type in ("twice_daily", "hourly"): + if (coordinator := self.forecast_coordinators[forecast_type]) is None: + continue + self.unsub_forecast[forecast_type] = coordinator.async_add_listener( + partial(self._handle_forecast_update, forecast_type) ) - ) - # Load initial data from coordinators - self._handle_coordinator_update() - self._handle_hourly_forecast_coordinator_update() - self._handle_twice_daily_forecast_coordinator_update() - self._handle_legacy_forecast_coordinator_update() - - @callback - def _handle_coordinator_update(self) -> None: - """Load data from integration.""" - self.observation = self.nws.observation - self.async_write_ha_state() - - @callback - def _handle_hourly_forecast_coordinator_update(self) -> None: - """Handle updated data from the hourly forecast coordinator.""" - self._forecast_hourly = self.nws.forecast_hourly - - @callback - def _handle_twice_daily_forecast_coordinator_update(self) -> None: - """Handle updated data from the twice daily forecast coordinator.""" - self._forecast_twice_daily = self.nws.forecast - - @callback - def _handle_legacy_forecast_coordinator_update(self) -> None: - """Handle updated data from the legacy forecast coordinator.""" - self._forecast_legacy = self.nws.forecast - self.async_write_ha_state() @property def native_temperature(self) -> float | None: """Return the current temperature.""" - if self.observation: - return self.observation.get("temperature") + if observation := self.nws.observation: + return observation.get("temperature") return None @property def native_pressure(self) -> int | None: """Return the current pressure.""" - if self.observation: - return self.observation.get("seaLevelPressure") + if observation := self.nws.observation: + return observation.get("seaLevelPressure") return None @property def humidity(self) -> float | None: """Return the name of the sensor.""" - if self.observation: - return self.observation.get("relativeHumidity") + if observation := self.nws.observation: + return observation.get("relativeHumidity") return None @property def native_wind_speed(self) -> float | None: """Return the current windspeed.""" - if self.observation: - return self.observation.get("windSpeed") + if observation := self.nws.observation: + return observation.get("windSpeed") return None @property def wind_bearing(self) -> int | None: """Return the current wind bearing (degrees).""" - if self.observation: - return self.observation.get("windDirection") + if observation := self.nws.observation: + return observation.get("windDirection") return None @property def condition(self) -> str | None: """Return current condition.""" weather = None - if self.observation: - weather = self.observation.get("iconWeather") - time = cast(str, self.observation.get("iconTime")) + if observation := self.nws.observation: + weather = observation.get("iconWeather") + time = cast(str, observation.get("iconTime")) if weather: return convert_condition(time, weather) @@ -238,8 +210,8 @@ class NWSWeather(CoordinatorWeatherEntity): @property def native_visibility(self) -> int | None: """Return visibility.""" - if self.observation: - return self.observation.get("visibility") + if observation := self.nws.observation: + return observation.get("visibility") return None def _forecast( @@ -302,33 +274,12 @@ class NWSWeather(CoordinatorWeatherEntity): @callback def _async_forecast_hourly(self) -> list[Forecast] | None: """Return the hourly forecast in native units.""" - return self._forecast(self._forecast_hourly, HOURLY) + return self._forecast(self.nws.forecast_hourly, HOURLY) @callback def _async_forecast_twice_daily(self) -> list[Forecast] | None: """Return the twice daily forecast in native units.""" - return self._forecast(self._forecast_twice_daily, DAYNIGHT) - - @property - def available(self) -> bool: - """Return if state is available.""" - last_success = ( - self.coordinator.last_update_success - and self.coordinator_forecast_legacy.last_update_success - ) - if ( - self.coordinator.last_update_success_time - and self.coordinator_forecast_legacy.last_update_success_time - ): - last_success_time = ( - utcnow() - self.coordinator.last_update_success_time - < OBSERVATION_VALID_TIME - and utcnow() - self.coordinator_forecast_legacy.last_update_success_time - < FORECAST_VALID_TIME - ) - else: - last_success_time = False - return last_success or last_success_time + return self._forecast(self.nws.forecast, DAYNIGHT) async def async_update(self) -> None: """Update the entity. @@ -336,4 +287,7 @@ class NWSWeather(CoordinatorWeatherEntity): Only used by the generic entity update service. """ await self.coordinator.async_request_refresh() - await self.coordinator_forecast_legacy.async_request_refresh() + + for forecast_type in ("twice_daily", "hourly"): + if (coordinator := self.forecast_coordinators[forecast_type]) is not None: + await coordinator.async_request_refresh() diff --git a/requirements_all.txt b/requirements_all.txt index f391511e607..c31b3d92b5e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2001,7 +2001,7 @@ pynobo==1.8.1 pynuki==1.6.3 # homeassistant.components.nws -pynws==1.6.0 +pynws[retry]==1.7.0 # homeassistant.components.nx584 pynx584==0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 140741518d1..8f1786020fb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1564,7 +1564,7 @@ pynobo==1.8.1 pynuki==1.6.3 # homeassistant.components.nws -pynws==1.6.0 +pynws[retry]==1.7.0 # homeassistant.components.nx584 pynx584==0.5 diff --git a/tests/components/nws/conftest.py b/tests/components/nws/conftest.py index ac2c281c57b..48401fe87ba 100644 --- a/tests/components/nws/conftest.py +++ b/tests/components/nws/conftest.py @@ -11,6 +11,7 @@ from .const import DEFAULT_FORECAST, DEFAULT_OBSERVATION @pytest.fixture def mock_simple_nws(): """Mock pynws SimpleNWS with default values.""" + with patch("homeassistant.components.nws.SimpleNWS") as mock_nws: instance = mock_nws.return_value instance.set_station = AsyncMock(return_value=None) diff --git a/tests/components/nws/test_weather.py b/tests/components/nws/test_weather.py index ad40b576a8a..87aae18be60 100644 --- a/tests/components/nws/test_weather.py +++ b/tests/components/nws/test_weather.py @@ -13,7 +13,6 @@ from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_SUNNY, DOMAIN as WEATHER_DOMAIN, - LEGACY_SERVICE_GET_FORECAST, SERVICE_GET_FORECASTS, ) from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN @@ -181,7 +180,7 @@ async def test_entity_refresh(hass: HomeAssistant, mock_simple_nws, no_sensor) - await hass.async_block_till_done() assert instance.update_observation.call_count == 2 assert instance.update_forecast.call_count == 2 - instance.update_forecast_hourly.assert_called_once() + assert instance.update_forecast_hourly.call_count == 2 async def test_error_observation( @@ -189,18 +188,8 @@ async def test_error_observation( ) -> None: """Test error during update observation.""" utc_time = dt_util.utcnow() - with ( - patch("homeassistant.components.nws.utcnow") as mock_utc, - patch("homeassistant.components.nws.weather.utcnow") as mock_utc_weather, - ): - - def increment_time(time): - mock_utc.return_value += time - mock_utc_weather.return_value += time - async_fire_time_changed(hass, mock_utc.return_value) - + with patch("homeassistant.components.nws.utcnow") as mock_utc: mock_utc.return_value = utc_time - mock_utc_weather.return_value = utc_time instance = mock_simple_nws.return_value # first update fails instance.update_observation.side_effect = aiohttp.ClientError @@ -219,68 +208,6 @@ async def test_error_observation( assert state assert state.state == STATE_UNAVAILABLE - # second update happens faster and succeeds - instance.update_observation.side_effect = None - increment_time(timedelta(minutes=1)) - await hass.async_block_till_done() - - assert instance.update_observation.call_count == 2 - - state = hass.states.get("weather.abc") - assert state - assert state.state == ATTR_CONDITION_SUNNY - - # third udate fails, but data is cached - instance.update_observation.side_effect = aiohttp.ClientError - - increment_time(timedelta(minutes=10)) - await hass.async_block_till_done() - - assert instance.update_observation.call_count == 3 - - state = hass.states.get("weather.abc") - assert state - assert state.state == ATTR_CONDITION_SUNNY - - # after 20 minutes data caching expires, data is no longer shown - increment_time(timedelta(minutes=10)) - await hass.async_block_till_done() - - state = hass.states.get("weather.abc") - assert state - assert state.state == STATE_UNAVAILABLE - - -async def test_error_forecast(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None: - """Test error during update forecast.""" - instance = mock_simple_nws.return_value - instance.update_forecast.side_effect = aiohttp.ClientError - - entry = MockConfigEntry( - domain=nws.DOMAIN, - data=NWS_CONFIG, - ) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - instance.update_forecast.assert_called_once() - - state = hass.states.get("weather.abc") - assert state - assert state.state == STATE_UNAVAILABLE - - instance.update_forecast.side_effect = None - - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=1)) - await hass.async_block_till_done() - - assert instance.update_forecast.call_count == 2 - - state = hass.states.get("weather.abc") - assert state - assert state.state == ATTR_CONDITION_SUNNY - async def test_new_config_entry(hass: HomeAssistant, no_sensor) -> None: """Test the expected entities are created.""" @@ -304,7 +231,6 @@ async def test_new_config_entry(hass: HomeAssistant, no_sensor) -> None: ("service"), [ SERVICE_GET_FORECASTS, - LEGACY_SERVICE_GET_FORECAST, ], ) async def test_forecast_service( @@ -355,7 +281,7 @@ async def test_forecast_service( assert instance.update_observation.call_count == 2 assert instance.update_forecast.call_count == 2 - assert instance.update_forecast_hourly.call_count == 1 + assert instance.update_forecast_hourly.call_count == 2 for forecast_type in ("twice_daily", "hourly"): response = await hass.services.async_call( From 624e4a2b483f55d6e4c8a50fe9193506b03ba8a0 Mon Sep 17 00:00:00 2001 From: GraceGRD <123941606+GraceGRD@users.noreply.github.com> Date: Wed, 1 May 2024 23:13:09 +0200 Subject: [PATCH 02/23] Bump opentherm_gw to 2.2.0 (#116527) --- homeassistant/components/opentherm_gw/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/opentherm_gw/manifest.json b/homeassistant/components/opentherm_gw/manifest.json index 50e0eab2643..b6ebef6e83c 100644 --- a/homeassistant/components/opentherm_gw/manifest.json +++ b/homeassistant/components/opentherm_gw/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/opentherm_gw", "iot_class": "local_push", "loggers": ["pyotgw"], - "requirements": ["pyotgw==2.1.3"] + "requirements": ["pyotgw==2.2.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index c31b3d92b5e..61789f0369a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2031,7 +2031,7 @@ pyoppleio-legacy==1.0.8 pyosoenergyapi==1.1.3 # homeassistant.components.opentherm_gw -pyotgw==2.1.3 +pyotgw==2.2.0 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8f1786020fb..8366bc7fb6d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1588,7 +1588,7 @@ pyopnsense==0.4.0 pyosoenergyapi==1.1.3 # homeassistant.components.opentherm_gw -pyotgw==2.1.3 +pyotgw==2.2.0 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp From 49de59432efad76608571d44bec57eb551255fc3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 May 2024 19:23:43 -0500 Subject: [PATCH 03/23] Add a lock to homekit_controller platform loads (#116539) --- .../homekit_controller/connection.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 78beb7bfffa..78190634aff 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -153,6 +153,7 @@ class HKDevice: self._subscriptions: dict[tuple[int, int], set[CALLBACK_TYPE]] = {} self._pending_subscribes: set[tuple[int, int]] = set() self._subscribe_timer: CALLBACK_TYPE | None = None + self._load_platforms_lock = asyncio.Lock() @property def entity_map(self) -> Accessories: @@ -327,7 +328,8 @@ class HKDevice: ) # BLE devices always get an RSSI sensor as well if "sensor" not in self.platforms: - await self._async_load_platforms({"sensor"}) + async with self._load_platforms_lock: + await self._async_load_platforms({"sensor"}) @callback def _async_start_polling(self) -> None: @@ -804,6 +806,7 @@ class HKDevice: async def _async_load_platforms(self, platforms: set[str]) -> None: """Load a group of platforms.""" + assert self._load_platforms_lock.locked(), "Must be called with lock held" if not (to_load := platforms - self.platforms): return self.platforms.update(to_load) @@ -813,22 +816,23 @@ class HKDevice: async def async_load_platforms(self) -> None: """Load any platforms needed by this HomeKit device.""" - to_load: set[str] = set() - for accessory in self.entity_map.accessories: - for service in accessory.services: - if service.type in HOMEKIT_ACCESSORY_DISPATCH: - platform = HOMEKIT_ACCESSORY_DISPATCH[service.type] - if platform not in self.platforms: - to_load.add(platform) - - for char in service.characteristics: - if char.type in CHARACTERISTIC_PLATFORMS: - platform = CHARACTERISTIC_PLATFORMS[char.type] + async with self._load_platforms_lock: + to_load: set[str] = set() + for accessory in self.entity_map.accessories: + for service in accessory.services: + if service.type in HOMEKIT_ACCESSORY_DISPATCH: + platform = HOMEKIT_ACCESSORY_DISPATCH[service.type] if platform not in self.platforms: to_load.add(platform) - if to_load: - await self._async_load_platforms(to_load) + for char in service.characteristics: + if char.type in CHARACTERISTIC_PLATFORMS: + platform = CHARACTERISTIC_PLATFORMS[char.type] + if platform not in self.platforms: + to_load.add(platform) + + if to_load: + await self._async_load_platforms(to_load) @callback def async_update_available_state(self, *_: Any) -> None: From ea6a9b83162eb8448056ffc1522ae67ad7cf3f2f Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 1 May 2024 21:19:55 +0200 Subject: [PATCH 04/23] Fix MQTT discovery cooldown too short with large setup (#116550) * Fix MQTT discovery cooldown too short with large setup * Set to 5 sec * Only change the discovery cooldown * Fire immediatly when teh debouncing period is over --- homeassistant/components/mqtt/client.py | 16 ++++++++++++---- homeassistant/components/mqtt/discovery.py | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index d79492ccb27..4fa9f4a1d49 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -83,7 +83,7 @@ if TYPE_CHECKING: _LOGGER = logging.getLogger(__name__) -DISCOVERY_COOLDOWN = 2 +DISCOVERY_COOLDOWN = 5 INITIAL_SUBSCRIBE_COOLDOWN = 1.0 SUBSCRIBE_COOLDOWN = 0.1 UNSUBSCRIBE_COOLDOWN = 0.1 @@ -349,6 +349,12 @@ class EnsureJobAfterCooldown: self._task = create_eager_task(self._async_job()) self._task.add_done_callback(self._async_task_done) + async def async_fire(self) -> None: + """Execute the job immediately.""" + if self._task: + await self._task + self._async_execute() + @callback def _async_cancel_timer(self) -> None: """Cancel any pending task.""" @@ -846,7 +852,7 @@ class MQTT: for topic, qos in subscriptions.items(): _LOGGER.debug("Subscribing to %s, mid: %s, qos: %s", topic, mid, qos) - self._last_subscribe = time.time() + self._last_subscribe = time.monotonic() if result == 0: await self._wait_for_mid(mid) @@ -876,6 +882,8 @@ class MQTT: await self._ha_started.wait() # Wait for Home Assistant to start await self._discovery_cooldown() # Wait for MQTT discovery to cool down # Update subscribe cooldown period to a shorter time + # and make sure we flush the debouncer + await self._subscribe_debouncer.async_fire() self._subscribe_debouncer.set_timeout(SUBSCRIBE_COOLDOWN) await self.async_publish( topic=birth_message.topic, @@ -1121,7 +1129,7 @@ class MQTT: async def _discovery_cooldown(self) -> None: """Wait until all discovery and subscriptions are processed.""" - now = time.time() + now = time.monotonic() # Reset discovery and subscribe cooldowns self._mqtt_data.last_discovery = now self._last_subscribe = now @@ -1133,7 +1141,7 @@ class MQTT: ) while now < wait_until: await asyncio.sleep(wait_until - now) - now = time.time() + now = time.monotonic() last_discovery = self._mqtt_data.last_discovery last_subscribe = ( now if self._pending_subscriptions else self._last_subscribe diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index e330cd9b44b..08d86c1a1a4 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -177,7 +177,7 @@ async def async_start( # noqa: C901 @callback def async_discovery_message_received(msg: ReceiveMessage) -> None: # noqa: C901 """Process the received message.""" - mqtt_data.last_discovery = time.time() + mqtt_data.last_discovery = time.monotonic() payload = msg.payload topic = msg.topic topic_trimmed = topic.replace(f"{discovery_topic}/", "", 1) @@ -370,7 +370,7 @@ async def async_start( # noqa: C901 ) ) - mqtt_data.last_discovery = time.time() + mqtt_data.last_discovery = time.monotonic() mqtt_integrations = await async_get_mqtt(hass) for integration, topics in mqtt_integrations.items(): From 5da6f83d10afbce4e1ff899223351bd05c46c289 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Wed, 1 May 2024 16:32:56 -0400 Subject: [PATCH 05/23] Bump upb_lib to 0.5.6 (#116558) --- homeassistant/components/upb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/upb/manifest.json b/homeassistant/components/upb/manifest.json index 240660ac89f..a5e32dd298e 100644 --- a/homeassistant/components/upb/manifest.json +++ b/homeassistant/components/upb/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/upb", "iot_class": "local_push", "loggers": ["upb_lib"], - "requirements": ["upb-lib==0.5.4"] + "requirements": ["upb-lib==0.5.6"] } diff --git a/requirements_all.txt b/requirements_all.txt index 61789f0369a..452de53e4e4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2779,7 +2779,7 @@ unifiled==0.11 universal-silabs-flasher==0.0.18 # homeassistant.components.upb -upb-lib==0.5.4 +upb-lib==0.5.6 # homeassistant.components.upcloud upcloud-api==2.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8366bc7fb6d..4f21b948bda 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2147,7 +2147,7 @@ unifi-discovery==1.1.8 universal-silabs-flasher==0.0.18 # homeassistant.components.upb -upb-lib==0.5.4 +upb-lib==0.5.6 # homeassistant.components.upcloud upcloud-api==2.0.0 From 65839067e33bc95fdc2d577b2fba2f0b01bdada5 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Wed, 1 May 2024 20:51:04 -0400 Subject: [PATCH 06/23] Bump elkm1_lib to 2.2.7 (#116564) Co-authored-by: J. Nick Koston --- homeassistant/components/elkm1/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/elkm1/manifest.json b/homeassistant/components/elkm1/manifest.json index 3ec5be46d41..5edab8463f7 100644 --- a/homeassistant/components/elkm1/manifest.json +++ b/homeassistant/components/elkm1/manifest.json @@ -15,5 +15,5 @@ "documentation": "https://www.home-assistant.io/integrations/elkm1", "iot_class": "local_push", "loggers": ["elkm1_lib"], - "requirements": ["elkm1-lib==2.2.6"] + "requirements": ["elkm1-lib==2.2.7"] } diff --git a/requirements_all.txt b/requirements_all.txt index 452de53e4e4..b72209ee772 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -777,7 +777,7 @@ elgato==5.1.2 eliqonline==1.2.2 # homeassistant.components.elkm1 -elkm1-lib==2.2.6 +elkm1-lib==2.2.7 # homeassistant.components.elmax elmax-api==0.0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4f21b948bda..e49c42a4594 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -637,7 +637,7 @@ electrickiwi-api==0.8.5 elgato==5.1.2 # homeassistant.components.elkm1 -elkm1-lib==2.2.6 +elkm1-lib==2.2.7 # homeassistant.components.elmax elmax-api==0.0.4 From 523de94184056760b1c69d422354a4b6c04e56a1 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 3 May 2024 13:27:01 +0200 Subject: [PATCH 07/23] Fix Matter startup when Matter bridge is present (#116569) --- homeassistant/components/matter/light.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/matter/light.py b/homeassistant/components/matter/light.py index 9d80ebc38f6..da72798dda1 100644 --- a/homeassistant/components/matter/light.py +++ b/homeassistant/components/matter/light.py @@ -398,6 +398,8 @@ class MatterLight(MatterEntity, LightEntity): def _check_transition_blocklist(self) -> None: """Check if this device is reported to have non working transitions.""" device_info = self._endpoint.device_info + if isinstance(device_info, clusters.BridgedDeviceBasicInformation): + return if ( device_info.vendorID, device_info.productID, From 99ab8d29561e51d30182e66933ba33522ad9d1ec Mon Sep 17 00:00:00 2001 From: Tomasz Date: Thu, 2 May 2024 00:21:40 +0200 Subject: [PATCH 08/23] Bump sanix to 1.0.6 (#116570) dependency version bump --- homeassistant/components/sanix/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sanix/manifest.json b/homeassistant/components/sanix/manifest.json index 4e1c6d56add..facf8f7a4dd 100644 --- a/homeassistant/components/sanix/manifest.json +++ b/homeassistant/components/sanix/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sanix", "iot_class": "cloud_polling", - "requirements": ["sanix==1.0.5"] + "requirements": ["sanix==1.0.6"] } diff --git a/requirements_all.txt b/requirements_all.txt index b72209ee772..7c90437ba27 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2499,7 +2499,7 @@ samsungctl[websocket]==0.7.1 samsungtvws[async,encrypted]==2.6.0 # homeassistant.components.sanix -sanix==1.0.5 +sanix==1.0.6 # homeassistant.components.satel_integra satel-integra==0.3.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e49c42a4594..05106fefc6d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1939,7 +1939,7 @@ samsungctl[websocket]==0.7.1 samsungtvws[async,encrypted]==2.6.0 # homeassistant.components.sanix -sanix==1.0.5 +sanix==1.0.6 # homeassistant.components.screenlogic screenlogicpy==0.10.0 From fabbe2f28fd18cfb35fc3038481b8eacbec2acda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A5le=20Stor=C3=B8=20Hauknes?= Date: Thu, 2 May 2024 02:19:40 +0200 Subject: [PATCH 09/23] Fix Airthings BLE model names (#116579) --- homeassistant/components/airthings_ble/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/airthings_ble/sensor.py b/homeassistant/components/airthings_ble/sensor.py index 8031b802eae..3b012ed7316 100644 --- a/homeassistant/components/airthings_ble/sensor.py +++ b/homeassistant/components/airthings_ble/sensor.py @@ -225,7 +225,7 @@ class AirthingsSensor( manufacturer=airthings_device.manufacturer, hw_version=airthings_device.hw_version, sw_version=airthings_device.sw_version, - model=airthings_device.model.name, + model=airthings_device.model.product_name, ) @property From 0e488ef50512a1d8e6b02182d9017ecc9d25561a Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Thu, 2 May 2024 15:57:47 +0200 Subject: [PATCH 10/23] Improve coordinator in Ondilo ico (#116596) * Improve coordinator in Ondilo ico * Improve coordinator in Ondilo ico --- .coveragerc | 1 + .../components/ondilo_ico/__init__.py | 10 ++- .../components/ondilo_ico/coordinator.py | 37 ++++++++++ homeassistant/components/ondilo_ico/sensor.py | 68 +++---------------- 4 files changed, 57 insertions(+), 59 deletions(-) create mode 100644 homeassistant/components/ondilo_ico/coordinator.py diff --git a/.coveragerc b/.coveragerc index 1ccb9e461df..10dedd43e81 100644 --- a/.coveragerc +++ b/.coveragerc @@ -939,6 +939,7 @@ omit = homeassistant/components/omnilogic/switch.py homeassistant/components/ondilo_ico/__init__.py homeassistant/components/ondilo_ico/api.py + homeassistant/components/ondilo_ico/coordinator.py homeassistant/components/ondilo_ico/sensor.py homeassistant/components/onkyo/media_player.py homeassistant/components/onvif/__init__.py diff --git a/homeassistant/components/ondilo_ico/__init__.py b/homeassistant/components/ondilo_ico/__init__.py index 5dccca54772..aa541c470f1 100644 --- a/homeassistant/components/ondilo_ico/__init__.py +++ b/homeassistant/components/ondilo_ico/__init__.py @@ -7,6 +7,7 @@ from homeassistant.helpers import config_entry_oauth2_flow from . import api, config_flow from .const import DOMAIN +from .coordinator import OndiloIcoCoordinator from .oauth_impl import OndiloOauth2Implementation PLATFORMS = [Platform.SENSOR] @@ -26,8 +27,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) ) - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = api.OndiloClient(hass, entry, implementation) + coordinator = OndiloIcoCoordinator( + hass, api.OndiloClient(hass, entry, implementation) + ) + + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/ondilo_ico/coordinator.py b/homeassistant/components/ondilo_ico/coordinator.py new file mode 100644 index 00000000000..d3e9b4a4e11 --- /dev/null +++ b/homeassistant/components/ondilo_ico/coordinator.py @@ -0,0 +1,37 @@ +"""Define an object to coordinate fetching Ondilo ICO data.""" + +from datetime import timedelta +import logging +from typing import Any + +from ondilo import OndiloError + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from . import DOMAIN +from .api import OndiloClient + +_LOGGER = logging.getLogger(__name__) + + +class OndiloIcoCoordinator(DataUpdateCoordinator[list[dict[str, Any]]]): + """Class to manage fetching Ondilo ICO data from API.""" + + def __init__(self, hass: HomeAssistant, api: OndiloClient) -> None: + """Initialize.""" + super().__init__( + hass, + logger=_LOGGER, + name=DOMAIN, + update_interval=timedelta(minutes=5), + ) + self.api = api + + async def _async_update_data(self) -> list[dict[str, Any]]: + """Fetch data from API endpoint.""" + try: + return await self.hass.async_add_executor_job(self.api.get_all_pools_data) + + except OndiloError as err: + raise UpdateFailed(f"Error communicating with API: {err}") from err diff --git a/homeassistant/components/ondilo_ico/sensor.py b/homeassistant/components/ondilo_ico/sensor.py index 17569fd784f..5f21fb6a909 100644 --- a/homeassistant/components/ondilo_ico/sensor.py +++ b/homeassistant/components/ondilo_ico/sensor.py @@ -2,12 +2,6 @@ from __future__ import annotations -from datetime import timedelta -import logging -from typing import Any - -from ondilo import OndiloError - from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, @@ -24,14 +18,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, - UpdateFailed, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .api import OndiloClient from .const import DOMAIN +from .coordinator import OndiloIcoCoordinator SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( @@ -78,66 +68,30 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ) -SCAN_INTERVAL = timedelta(minutes=5) -_LOGGER = logging.getLogger(__name__) - - async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the Ondilo ICO sensors.""" - api: OndiloClient = hass.data[DOMAIN][entry.entry_id] + coordinator: OndiloIcoCoordinator = hass.data[DOMAIN][entry.entry_id] - async def async_update_data() -> list[dict[str, Any]]: - """Fetch data from API endpoint. - - This is the place to pre-process the data to lookup tables - so entities can quickly look up their data. - """ - try: - return await hass.async_add_executor_job(api.get_all_pools_data) - - except OndiloError as err: - raise UpdateFailed(f"Error communicating with API: {err}") from err - - coordinator = DataUpdateCoordinator( - hass, - _LOGGER, - # Name of the data. For logging purposes. - name="sensor", - update_method=async_update_data, - # Polling interval. Will only be polled if there are subscribers. - update_interval=SCAN_INTERVAL, + async_add_entities( + OndiloICO(coordinator, poolidx, description) + for poolidx, pool in enumerate(coordinator.data) + for sensor in pool["sensors"] + for description in SENSOR_TYPES + if description.key == sensor["data_type"] ) - # Fetch initial data so we have data when entities subscribe - await coordinator.async_refresh() - entities = [] - for poolidx, pool in enumerate(coordinator.data): - entities.extend( - [ - OndiloICO(coordinator, poolidx, description) - for sensor in pool["sensors"] - for description in SENSOR_TYPES - if description.key == sensor["data_type"] - ] - ) - - async_add_entities(entities) - - -class OndiloICO( - CoordinatorEntity[DataUpdateCoordinator[list[dict[str, Any]]]], SensorEntity -): +class OndiloICO(CoordinatorEntity[OndiloIcoCoordinator], SensorEntity): """Representation of a Sensor.""" _attr_has_entity_name = True def __init__( self, - coordinator: DataUpdateCoordinator[list[dict[str, Any]]], + coordinator: OndiloIcoCoordinator, poolidx: int, description: SensorEntityDescription, ) -> None: From 575a3da772d7c4bec39dbdb519554372c00890cf Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Thu, 2 May 2024 11:44:32 +0200 Subject: [PATCH 11/23] Fix inheritance order for KNX notify (#116600) --- homeassistant/components/knx/notify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index e208e4fd646..f206ee62ece 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -97,7 +97,7 @@ def _create_notification_instance(xknx: XKNX, config: ConfigType) -> XknxNotific ) -class KNXNotify(NotifyEntity, KnxEntity): +class KNXNotify(KnxEntity, NotifyEntity): """Representation of a KNX notification entity.""" _device: XknxNotification From 7c1502fa059610290d4cb0e7391bd36a5bafcc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A5le=20Stor=C3=B8=20Hauknes?= Date: Thu, 2 May 2024 16:37:02 +0200 Subject: [PATCH 12/23] Bump Airthings BLE to 0.8.0 (#116616) Co-authored-by: J. Nick Koston --- homeassistant/components/airthings_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airthings_ble/manifest.json b/homeassistant/components/airthings_ble/manifest.json index 3f7bd02a33e..d93e3a0b8cb 100644 --- a/homeassistant/components/airthings_ble/manifest.json +++ b/homeassistant/components/airthings_ble/manifest.json @@ -24,5 +24,5 @@ "dependencies": ["bluetooth_adapters"], "documentation": "https://www.home-assistant.io/integrations/airthings_ble", "iot_class": "local_polling", - "requirements": ["airthings-ble==0.7.1"] + "requirements": ["airthings-ble==0.8.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 7c90437ba27..620a1aa2a15 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ aioymaps==1.2.2 airly==1.1.0 # homeassistant.components.airthings_ble -airthings-ble==0.7.1 +airthings-ble==0.8.0 # homeassistant.components.airthings airthings-cloud==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 05106fefc6d..8adb04408e1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -386,7 +386,7 @@ aioymaps==1.2.2 airly==1.1.0 # homeassistant.components.airthings_ble -airthings-ble==0.7.1 +airthings-ble==0.8.0 # homeassistant.components.airthings airthings-cloud==0.2.0 From 8193b82f4a37613e9ccfce62b616c7e27eee471e Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Thu, 2 May 2024 16:54:06 +0200 Subject: [PATCH 13/23] Bump pywaze to 1.0.1 (#116621) Co-authored-by: J. Nick Koston --- homeassistant/components/waze_travel_time/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/waze_travel_time/manifest.json b/homeassistant/components/waze_travel_time/manifest.json index 4fc08cf983d..ce7c9105781 100644 --- a/homeassistant/components/waze_travel_time/manifest.json +++ b/homeassistant/components/waze_travel_time/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/waze_travel_time", "iot_class": "cloud_polling", "loggers": ["pywaze", "homeassistant.helpers.location"], - "requirements": ["pywaze==1.0.0"] + "requirements": ["pywaze==1.0.1"] } diff --git a/requirements_all.txt b/requirements_all.txt index 620a1aa2a15..c5710500ad5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2370,7 +2370,7 @@ pyvlx==0.2.21 pyvolumio==0.1.5 # homeassistant.components.waze_travel_time -pywaze==1.0.0 +pywaze==1.0.1 # homeassistant.components.weatherflow pyweatherflowudp==1.4.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8adb04408e1..84f034d453f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1843,7 +1843,7 @@ pyvlx==0.2.21 pyvolumio==0.1.5 # homeassistant.components.waze_travel_time -pywaze==1.0.0 +pywaze==1.0.1 # homeassistant.components.weatherflow pyweatherflowudp==1.4.5 From c338f1b964e8f5727f284b9d51fddeaa7e42a3cc Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 2 May 2024 16:12:26 +0200 Subject: [PATCH 14/23] Add constraint for tuf (#116627) --- homeassistant/package_constraints.txt | 5 +++++ script/gen_requirements_all.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b1c0391022a..2c038ed3927 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -192,3 +192,8 @@ pycountry>=23.12.11 # scapy<2.5.0 will not work with python3.12 scapy>=2.5.0 + +# tuf isn't updated to deal with breaking changes in securesystemslib==1.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 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index a5db9997d9d..b611b050c7d 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -214,6 +214,11 @@ pycountry>=23.12.11 # scapy<2.5.0 will not work with python3.12 scapy>=2.5.0 + +# tuf isn't updated to deal with breaking changes in securesystemslib==1.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 """ GENERATED_MESSAGE = ( From 6be25c784d5141acd67256708e9149ff724f162c Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 2 May 2024 19:53:17 +0200 Subject: [PATCH 15/23] Bump aiounifi to v77 (#116639) --- homeassistant/components/unifi/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifi/manifest.json b/homeassistant/components/unifi/manifest.json index 982d654c8fe..504c2f505a7 100644 --- a/homeassistant/components/unifi/manifest.json +++ b/homeassistant/components/unifi/manifest.json @@ -8,7 +8,7 @@ "iot_class": "local_push", "loggers": ["aiounifi"], "quality_scale": "platinum", - "requirements": ["aiounifi==76"], + "requirements": ["aiounifi==77"], "ssdp": [ { "manufacturer": "Ubiquiti Networks", diff --git a/requirements_all.txt b/requirements_all.txt index c5710500ad5..c54606830dd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -386,7 +386,7 @@ aiotankerkoenig==0.4.1 aiotractive==0.5.6 # homeassistant.components.unifi -aiounifi==76 +aiounifi==77 # homeassistant.components.vlc_telnet aiovlc==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 84f034d453f..23730ab802e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -359,7 +359,7 @@ aiotankerkoenig==0.4.1 aiotractive==0.5.6 # homeassistant.components.unifi -aiounifi==76 +aiounifi==77 # homeassistant.components.vlc_telnet aiovlc==0.1.0 From c36fd5550b8cecf9e405c3de22253cebd9f8b37f Mon Sep 17 00:00:00 2001 From: Galorhallen <12990764+Galorhallen@users.noreply.github.com> Date: Fri, 3 May 2024 13:07:45 +0200 Subject: [PATCH 16/23] Bump govee-light-local library and fix wrong information for Govee lights (#116651) --- homeassistant/components/govee_light_local/light.py | 4 ++-- homeassistant/components/govee_light_local/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/govee_light_local/light.py b/homeassistant/components/govee_light_local/light.py index 836f48d2ea9..60bf07e8e19 100644 --- a/homeassistant/components/govee_light_local/light.py +++ b/homeassistant/components/govee_light_local/light.py @@ -17,7 +17,7 @@ from homeassistant.components.light import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo +from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -94,7 +94,7 @@ class GoveeLight(CoordinatorEntity[GoveeLocalApiCoordinator], LightEntity): name=device.sku, manufacturer=MANUFACTURER, model=device.sku, - connections={(CONNECTION_NETWORK_MAC, device.fingerprint)}, + serial_number=device.fingerprint, ) @property diff --git a/homeassistant/components/govee_light_local/manifest.json b/homeassistant/components/govee_light_local/manifest.json index cb7955f5407..df72a082190 100644 --- a/homeassistant/components/govee_light_local/manifest.json +++ b/homeassistant/components/govee_light_local/manifest.json @@ -6,5 +6,5 @@ "dependencies": ["network"], "documentation": "https://www.home-assistant.io/integrations/govee_light_local", "iot_class": "local_push", - "requirements": ["govee-local-api==1.4.4"] + "requirements": ["govee-local-api==1.4.5"] } diff --git a/requirements_all.txt b/requirements_all.txt index c54606830dd..362b8389df2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -983,7 +983,7 @@ gotailwind==0.2.2 govee-ble==0.31.2 # homeassistant.components.govee_light_local -govee-local-api==1.4.4 +govee-local-api==1.4.5 # homeassistant.components.remote_rpi_gpio gpiozero==1.6.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 23730ab802e..5672dd88dab 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -806,7 +806,7 @@ gotailwind==0.2.2 govee-ble==0.31.2 # homeassistant.components.govee_light_local -govee-local-api==1.4.4 +govee-local-api==1.4.5 # homeassistant.components.gpsd gps3==0.33.3 From abeb65e43d54136534c8c17d6eec14a07f89ee27 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 2 May 2024 20:31:28 -0400 Subject: [PATCH 17/23] Bump ZHA dependency bellows to 0.38.4 (#116660) Bump ZHA dependencies Co-authored-by: TheJulianJES --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index b1511b2f5bb..7a407a2eb33 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -21,7 +21,7 @@ "universal_silabs_flasher" ], "requirements": [ - "bellows==0.38.3", + "bellows==0.38.4", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.115", diff --git a/requirements_all.txt b/requirements_all.txt index 362b8389df2..8f15e0be921 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -541,7 +541,7 @@ beautifulsoup4==4.12.3 # beewi-smartclim==0.0.10 # homeassistant.components.zha -bellows==0.38.3 +bellows==0.38.4 # homeassistant.components.bmw_connected_drive bimmer-connected[china]==0.15.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5672dd88dab..a20a6a92811 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -466,7 +466,7 @@ base36==0.1.1 beautifulsoup4==4.12.3 # homeassistant.components.zha -bellows==0.38.3 +bellows==0.38.4 # homeassistant.components.bmw_connected_drive bimmer-connected[china]==0.15.2 From ac302f38b1926c404dc7c68039c27faee8f57422 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 2 May 2024 18:15:56 -0500 Subject: [PATCH 18/23] Bump habluetooth to 2.8.1 (#116661) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 4bb84ab6dc3..754e8faf996 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -20,6 +20,6 @@ "bluetooth-auto-recovery==1.4.2", "bluetooth-data-tools==1.19.0", "dbus-fast==2.21.1", - "habluetooth==2.8.0" + "habluetooth==2.8.1" ] } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 2c038ed3927..800e4d90009 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -28,7 +28,7 @@ dbus-fast==2.21.1 fnv-hash-fast==0.5.0 ha-av==10.1.1 ha-ffmpeg==3.2.0 -habluetooth==2.8.0 +habluetooth==2.8.1 hass-nabucasa==0.78.0 hassil==1.6.1 home-assistant-bluetooth==1.12.0 diff --git a/requirements_all.txt b/requirements_all.txt index 8f15e0be921..419c713347b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1035,7 +1035,7 @@ ha-philipsjs==3.1.1 habitipy==0.2.0 # homeassistant.components.bluetooth -habluetooth==2.8.0 +habluetooth==2.8.1 # homeassistant.components.cloud hass-nabucasa==0.78.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a20a6a92811..31832687250 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -849,7 +849,7 @@ ha-philipsjs==3.1.1 habitipy==0.2.0 # homeassistant.components.bluetooth -habluetooth==2.8.0 +habluetooth==2.8.1 # homeassistant.components.cloud hass-nabucasa==0.78.0 From 66bb3ecac905ae8ab0bd5265d272e9d714ae2b94 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Thu, 2 May 2024 19:17:41 -0400 Subject: [PATCH 19/23] Bump env_canada lib to 0.6.2 (#116662) --- 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 d0c34b0cf9a..f29c8177dfd 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.0"] + "requirements": ["env-canada==0.6.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index 419c713347b..6f741478f20 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -804,7 +804,7 @@ enocean==0.50 enturclient==0.2.4 # homeassistant.components.environment_canada -env-canada==0.6.0 +env-canada==0.6.2 # homeassistant.components.season ephem==4.1.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 31832687250..14951b8a6ec 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -658,7 +658,7 @@ energyzero==2.1.0 enocean==0.50 # homeassistant.components.environment_canada -env-canada==0.6.0 +env-canada==0.6.2 # homeassistant.components.season ephem==4.1.5 From 7a56ba1506fd4fec10d7ee6f02a9e9860896076b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 May 2024 05:17:01 -0500 Subject: [PATCH 20/23] Block dreame_vacuum versions older than 1.0.4 (#116673) --- homeassistant/loader.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 1a72c8eb351..89c3442be6a 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -90,7 +90,12 @@ class BlockedIntegration: BLOCKED_CUSTOM_INTEGRATIONS: dict[str, BlockedIntegration] = { # Added in 2024.3.0 because of https://github.com/home-assistant/core/issues/112464 - "start_time": BlockedIntegration(AwesomeVersion("1.1.7"), "breaks Home Assistant") + "start_time": BlockedIntegration(AwesomeVersion("1.1.7"), "breaks Home Assistant"), + # Added in 2024.5.1 because of + # https://community.home-assistant.io/t/psa-2024-5-upgrade-failure-and-dreame-vacuum-custom-integration/724612 + "dreame_vacuum": BlockedIntegration( + AwesomeVersion("1.0.4"), "crashes Home Assistant" + ), } DATA_COMPONENTS = "components" From a4f9a645889b628c82872102dc4706071d97dfe9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 3 May 2024 13:07:12 +0200 Subject: [PATCH 21/23] Fix fyta test timezone handling (#116689) --- tests/components/fyta/test_config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/fyta/test_config_flow.py b/tests/components/fyta/test_config_flow.py index 69478d04ca0..dedb468a617 100644 --- a/tests/components/fyta/test_config_flow.py +++ b/tests/components/fyta/test_config_flow.py @@ -21,7 +21,7 @@ from tests.common import MockConfigEntry USERNAME = "fyta_user" PASSWORD = "fyta_pass" ACCESS_TOKEN = "123xyz" -EXPIRATION = datetime.fromisoformat("2024-12-31T10:00:00").astimezone(UTC) +EXPIRATION = datetime.fromisoformat("2024-12-31T10:00:00").replace(tzinfo=UTC) async def test_user_flow( From 7e8cbafc6f4ac511c8d99f5264c8def76f4983db Mon Sep 17 00:00:00 2001 From: Felipe Martins Diel <41558831+felipediel@users.noreply.github.com> Date: Fri, 3 May 2024 08:11:22 -0300 Subject: [PATCH 22/23] Fix BroadlinkRemote._learn_command() (#116692) --- homeassistant/components/broadlink/remote.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/broadlink/remote.py b/homeassistant/components/broadlink/remote.py index 55368e5ff59..77c9ea0ff98 100644 --- a/homeassistant/components/broadlink/remote.py +++ b/homeassistant/components/broadlink/remote.py @@ -373,8 +373,11 @@ class BroadlinkRemote(BroadlinkEntity, RemoteEntity, RestoreEntity): start_time = dt_util.utcnow() while (dt_util.utcnow() - start_time) < LEARNING_TIMEOUT: await asyncio.sleep(1) - found = await device.async_request(device.api.check_frequency)[0] - if found: + is_found, frequency = await device.async_request( + device.api.check_frequency + ) + if is_found: + _LOGGER.info("Radiofrequency detected: %s MHz", frequency) break else: await device.async_request(device.api.cancel_sweep_frequency) From 9d2fd8217f1ed7e03afb2aca7014d7625c03cea3 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 3 May 2024 13:38:38 +0200 Subject: [PATCH 23/23] Bump version to 2024.5.1 --- 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 eb46817bd34..31dc771d966 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2024 MINOR_VERSION: Final = 5 -PATCH_VERSION: Final = "0" +PATCH_VERSION: Final = "1" __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 4dd5653f8ce..ac3c84d67f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2024.5.0" +version = "2024.5.1" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst"