From 10fc40e415466967014a1e0b5c7d8200a512ae5a Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 10 Mar 2024 11:04:17 -0600 Subject: [PATCH 01/31] Streamline Notion config entry updates (refresh token and user ID) (#112832) --- homeassistant/components/notion/__init__.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/notion/__init__.py b/homeassistant/components/notion/__init__.py index 33a74990928..6d2b6c48e1b 100644 --- a/homeassistant/components/notion/__init__.py +++ b/homeassistant/components/notion/__init__.py @@ -165,9 +165,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except NotionError as err: raise ConfigEntryNotReady("Config entry failed to load") from err - # Always update the config entry with the latest refresh token and user UUID: - entry_updates["data"][CONF_REFRESH_TOKEN] = client.refresh_token - entry_updates["data"][CONF_USER_UUID] = client.user_uuid + # Update the Notion user UUID and refresh token if they've changed: + for key, value in ( + (CONF_REFRESH_TOKEN, client.refresh_token), + (CONF_USER_UUID, client.user_uuid), + ): + if entry.data[key] == value: + continue + entry_updates["data"][key] = value + + hass.config_entries.async_update_entry(entry, **entry_updates) @callback def async_save_refresh_token(refresh_token: str) -> None: @@ -180,12 +187,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Create a callback to save the refresh token when it changes: entry.async_on_unload(client.add_refresh_token_callback(async_save_refresh_token)) - # Save the client's refresh token if it's different than what we already have: - if (token := client.refresh_token) and token != entry.data[CONF_REFRESH_TOKEN]: - async_save_refresh_token(token) - - hass.config_entries.async_update_entry(entry, **entry_updates) - async def async_update() -> NotionData: """Get the latest data from the Notion API.""" data = NotionData(hass=hass, entry=entry) From 8b00229868fa4e67e0ea59ed63fb7f84b89b84ac Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:50:57 +0100 Subject: [PATCH 02/31] Bump aioautomower to 2024.3.2 (#113162) --- homeassistant/components/husqvarna_automower/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/husqvarna_automower/manifest.json b/homeassistant/components/husqvarna_automower/manifest.json index f00baad661d..49a554f2e0a 100644 --- a/homeassistant/components/husqvarna_automower/manifest.json +++ b/homeassistant/components/husqvarna_automower/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/husqvarna_automower", "iot_class": "cloud_push", "loggers": ["aioautomower"], - "requirements": ["aioautomower==2024.3.0"] + "requirements": ["aioautomower==2024.3.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index add077d5b82..b0ea4f3e246 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -206,7 +206,7 @@ aioaseko==0.0.2 aioasuswrt==1.4.0 # homeassistant.components.husqvarna_automower -aioautomower==2024.3.0 +aioautomower==2024.3.2 # homeassistant.components.azure_devops aioazuredevops==1.3.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a50fefa2290..c81a8fbb2dc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -185,7 +185,7 @@ aioaseko==0.0.2 aioasuswrt==1.4.0 # homeassistant.components.husqvarna_automower -aioautomower==2024.3.0 +aioautomower==2024.3.2 # homeassistant.components.azure_devops aioazuredevops==1.3.5 From a167b0acaf691b8411e2fff4e53b888186bf3aac Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:51:24 +0100 Subject: [PATCH 03/31] Bump aioautomower to 2024.3.3 (#113430) --- .../husqvarna_automower/manifest.json | 2 +- .../components/husqvarna_automower/sensor.py | 7 +++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../husqvarna_automower/test_sensor.py | 30 +++++++++++++++---- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/husqvarna_automower/manifest.json b/homeassistant/components/husqvarna_automower/manifest.json index 49a554f2e0a..ed013f2e0c2 100644 --- a/homeassistant/components/husqvarna_automower/manifest.json +++ b/homeassistant/components/husqvarna_automower/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/husqvarna_automower", "iot_class": "cloud_push", "loggers": ["aioautomower"], - "requirements": ["aioautomower==2024.3.2"] + "requirements": ["aioautomower==2024.3.3"] } diff --git a/homeassistant/components/husqvarna_automower/sensor.py b/homeassistant/components/husqvarna_automower/sensor.py index 970c444737c..31eebde9c81 100644 --- a/homeassistant/components/husqvarna_automower/sensor.py +++ b/homeassistant/components/husqvarna_automower/sensor.py @@ -69,6 +69,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, suggested_unit_of_measurement=UnitOfTime.HOURS, + exists_fn=lambda data: data.statistics.total_charging_time is not None, value_fn=lambda data: data.statistics.total_charging_time, ), AutomowerSensorEntityDescription( @@ -79,6 +80,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, suggested_unit_of_measurement=UnitOfTime.HOURS, + exists_fn=lambda data: data.statistics.total_cutting_time is not None, value_fn=lambda data: data.statistics.total_cutting_time, ), AutomowerSensorEntityDescription( @@ -89,6 +91,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, suggested_unit_of_measurement=UnitOfTime.HOURS, + exists_fn=lambda data: data.statistics.total_running_time is not None, value_fn=lambda data: data.statistics.total_running_time, ), AutomowerSensorEntityDescription( @@ -99,6 +102,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, suggested_unit_of_measurement=UnitOfTime.HOURS, + exists_fn=lambda data: data.statistics.total_searching_time is not None, value_fn=lambda data: data.statistics.total_searching_time, ), AutomowerSensorEntityDescription( @@ -107,6 +111,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( icon="mdi:battery-sync-outline", entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.TOTAL, + exists_fn=lambda data: data.statistics.number_of_charging_cycles is not None, value_fn=lambda data: data.statistics.number_of_charging_cycles, ), AutomowerSensorEntityDescription( @@ -115,6 +120,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( icon="mdi:counter", entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.TOTAL, + exists_fn=lambda data: data.statistics.number_of_collisions is not None, value_fn=lambda data: data.statistics.number_of_collisions, ), AutomowerSensorEntityDescription( @@ -125,6 +131,7 @@ SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.DISTANCE, native_unit_of_measurement=UnitOfLength.METERS, suggested_unit_of_measurement=UnitOfLength.KILOMETERS, + exists_fn=lambda data: data.statistics.total_drive_distance is not None, value_fn=lambda data: data.statistics.total_drive_distance, ), AutomowerSensorEntityDescription( diff --git a/requirements_all.txt b/requirements_all.txt index b0ea4f3e246..fb1f88b2975 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -206,7 +206,7 @@ aioaseko==0.0.2 aioasuswrt==1.4.0 # homeassistant.components.husqvarna_automower -aioautomower==2024.3.2 +aioautomower==2024.3.3 # homeassistant.components.azure_devops aioazuredevops==1.3.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c81a8fbb2dc..b8f639afa1a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -185,7 +185,7 @@ aioaseko==0.0.2 aioasuswrt==1.4.0 # homeassistant.components.husqvarna_automower -aioautomower==2024.3.2 +aioautomower==2024.3.3 # homeassistant.components.azure_devops aioazuredevops==1.3.5 diff --git a/tests/components/husqvarna_automower/test_sensor.py b/tests/components/husqvarna_automower/test_sensor.py index 1775caac7f8..feae870478e 100644 --- a/tests/components/husqvarna_automower/test_sensor.py +++ b/tests/components/husqvarna_automower/test_sensor.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, patch from aioautomower.model import MowerModes from aioautomower.utils import mower_list_to_dictionary_dataclass from freezegun.api import FrozenDateTimeFactory +import pytest from syrupy import SnapshotAssertion from homeassistant.components.husqvarna_automower.const import DOMAIN @@ -59,17 +60,36 @@ async def test_cutting_blade_usage_time_sensor( assert state is not None assert state.state == "0.034" - entry = hass.config_entries.async_entries(DOMAIN)[0] - await hass.config_entries.async_remove(entry.entry_id) - await hass.async_block_till_done() + +@pytest.mark.parametrize( + ("sensor_to_test"), + [ + ("cutting_blade_usage_time"), + ("number_of_charging_cycles"), + ("number_of_collisions"), + ("total_charging_time"), + ("total_cutting_time"), + ("total_running_time"), + ("total_searching_time"), + ("total_drive_distance"), + ], +) +async def test_statistics_not_available( + hass: HomeAssistant, + mock_automower_client: AsyncMock, + mock_config_entry: MockConfigEntry, + sensor_to_test: str, +) -> None: + """Test if this sensor is only added, if data is available.""" + values = mower_list_to_dictionary_dataclass( load_json_value_fixture("mower.json", DOMAIN) ) - delattr(values[TEST_MOWER_ID].statistics, "cutting_blade_usage_time") + delattr(values[TEST_MOWER_ID].statistics, sensor_to_test) mock_automower_client.get_status.return_value = values await setup_integration(hass, mock_config_entry) - state = hass.states.get("sensor.test_mower_1_cutting_blade_usage_time") + state = hass.states.get(f"sensor.test_mower_1_{sensor_to_test}") assert state is None From 273d01c0f40746d1e10ecc476095abb0ec76c87f Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Thu, 14 Mar 2024 15:07:54 -0400 Subject: [PATCH 04/31] Check for EA release channel for UniFi Protect (#113432) Co-authored-by: J. Nick Koston --- homeassistant/components/unifiprotect/__init__.py | 13 +++++++------ homeassistant/components/unifiprotect/const.py | 2 +- homeassistant/components/unifiprotect/repairs.py | 5 +++-- .../components/unifiprotect/strings.json | 12 ++++++------ tests/components/unifiprotect/test_config_flow.py | 2 +- tests/components/unifiprotect/test_repairs.py | 15 ++++++++++----- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index c4a6bc88068..076095a16b3 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -6,6 +6,7 @@ import logging from aiohttp.client_exceptions import ServerDisconnectedError from pyunifiprotect.data import Bootstrap +from pyunifiprotect.data.types import FirmwareReleaseChannel from pyunifiprotect.exceptions import ClientError, NotAuthorized # Import the test_util.anonymize module from the pyunifiprotect package @@ -111,19 +112,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, data_service.async_stop) ) - if ( - not entry.options.get(CONF_ALLOW_EA, False) - and await nvr_info.get_is_prerelease() + if not entry.options.get(CONF_ALLOW_EA, False) and ( + await nvr_info.get_is_prerelease() + or nvr_info.release_channel != FirmwareReleaseChannel.RELEASE ): ir.async_create_issue( hass, DOMAIN, - "ea_warning", + "ea_channel_warning", is_fixable=True, is_persistent=True, learn_more_url="https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access", severity=IssueSeverity.WARNING, - translation_key="ea_warning", + translation_key="ea_channel_warning", translation_placeholders={"version": str(nvr_info.version)}, data={"entry_id": entry.entry_id}, ) @@ -149,7 +150,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: "version": str(nvr_info.version), }, ) - ir.async_delete_issue(hass, DOMAIN, "ea_warning") + ir.async_delete_issue(hass, DOMAIN, "ea_channel_warning") _LOGGER.exception("Error setting up UniFi Protect integration: %s", err) raise diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index 2982ca29c4a..39be5f0e7cb 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -24,7 +24,7 @@ CONF_DISABLE_RTSP = "disable_rtsp" CONF_ALL_UPDATES = "all_updates" CONF_OVERRIDE_CHOST = "override_connection_host" CONF_MAX_MEDIA = "max_media" -CONF_ALLOW_EA = "allow_ea" +CONF_ALLOW_EA = "allow_ea_channel" CONFIG_OPTIONS = [ CONF_ALL_UPDATES, diff --git a/homeassistant/components/unifiprotect/repairs.py b/homeassistant/components/unifiprotect/repairs.py index ddc0a257c14..254984da515 100644 --- a/homeassistant/components/unifiprotect/repairs.py +++ b/homeassistant/components/unifiprotect/repairs.py @@ -6,6 +6,7 @@ import logging from typing import cast from pyunifiprotect import ProtectApiClient +from pyunifiprotect.data.types import FirmwareReleaseChannel import voluptuous as vol from homeassistant import data_entry_flow @@ -68,7 +69,7 @@ class EAConfirm(ProtectRepair): ) nvr = await self._api.get_nvr() - if await nvr.get_is_prerelease(): + if nvr.release_channel != FirmwareReleaseChannel.RELEASE: return await self.async_step_confirm() await self.hass.config_entries.async_reload(self._entry.entry_id) return self.async_create_entry(data={}) @@ -124,7 +125,7 @@ async def async_create_fix_flow( data: dict[str, str | int | float | None] | None, ) -> RepairsFlow: """Create flow.""" - if data is not None and issue_id == "ea_warning": + if data is not None and issue_id == "ea_channel_warning": entry_id = cast(str, data["entry_id"]) if (entry := hass.config_entries.async_get_entry(entry_id)) is not None: api = async_create_api_client(hass, entry) diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index eccf5829332..bdc46217ab5 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -61,16 +61,16 @@ } }, "issues": { - "ea_warning": { - "title": "UniFi Protect v{version} is an Early Access version", + "ea_channel_warning": { + "title": "UniFi Protect Early Access enabled", "fix_flow": { "step": { "start": { - "title": "v{version} is an Early Access version", - "description": "You are using v{version} of UniFi Protect which is an Early Access version. [Early Access versions are not supported by Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) and it is recommended to go back to a stable release as soon as possible.\n\nBy submitting this form you have either [downgraded UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) or you agree to run an unsupported version of UniFi Protect." + "title": "UniFi Protect Early Access enabled", + "description": "You are either running an Early Access version of UniFi Protect (v{version}) or opt-ed into a release channel that is not the Official Release Channel. [Home Assistant does not support Early Access versions](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access), so you should immediately switch to the Official Release Channel. Accidentally upgrading to an Early Access version can break your UniFi Protect integration.\n\nBy submitting this form, you have switched back to the Official Release Channel or agree to run an unsupported version of UniFi Protect, which may break your Home Assistant integration at any time." }, "confirm": { - "title": "[%key:component::unifiprotect::issues::ea_warning::fix_flow::step::start::title%]", + "title": "[%key:component::unifiprotect::issues::ea_channel_warning::fix_flow::step::start::title%]", "description": "Are you sure you want to run unsupported versions of UniFi Protect? This may cause your Home Assistant integration to break." } } @@ -78,7 +78,7 @@ }, "ea_setup_failed": { "title": "Setup error using Early Access version", - "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}" + "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please restore a backup of a stable release of UniFi Protect to continue using the integration.\n\nError: {error}" }, "cloud_user": { "title": "Ubiquiti Cloud Users are not Supported", diff --git a/tests/components/unifiprotect/test_config_flow.py b/tests/components/unifiprotect/test_config_flow.py index a9ff98fc681..04eee1b8319 100644 --- a/tests/components/unifiprotect/test_config_flow.py +++ b/tests/components/unifiprotect/test_config_flow.py @@ -318,7 +318,7 @@ async def test_form_options(hass: HomeAssistant, ufp_client: ProtectApiClient) - "disable_rtsp": True, "override_connection_host": True, "max_media": 1000, - "allow_ea": False, + "allow_ea_channel": False, } await hass.config_entries.async_unload(mock_config.entry_id) diff --git a/tests/components/unifiprotect/test_repairs.py b/tests/components/unifiprotect/test_repairs.py index 12701604306..0c939a9791d 100644 --- a/tests/components/unifiprotect/test_repairs.py +++ b/tests/components/unifiprotect/test_repairs.py @@ -45,12 +45,14 @@ async def test_ea_warning_ignore( assert len(msg["result"]["issues"]) > 0 issue = None for i in msg["result"]["issues"]: - if i["issue_id"] == "ea_warning": + if i["issue_id"] == "ea_channel_warning": issue = i assert issue is not None url = RepairsFlowIndexView.url - resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "ea_warning"}) + resp = await client.post( + url, json={"handler": DOMAIN, "issue_id": "ea_channel_warning"} + ) assert resp.status == HTTPStatus.OK data = await resp.json() @@ -103,12 +105,14 @@ async def test_ea_warning_fix( assert len(msg["result"]["issues"]) > 0 issue = None for i in msg["result"]["issues"]: - if i["issue_id"] == "ea_warning": + if i["issue_id"] == "ea_channel_warning": issue = i assert issue is not None url = RepairsFlowIndexView.url - resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "ea_warning"}) + resp = await client.post( + url, json={"handler": DOMAIN, "issue_id": "ea_channel_warning"} + ) assert resp.status == HTTPStatus.OK data = await resp.json() @@ -121,8 +125,9 @@ async def test_ea_warning_fix( new_nvr = copy(ufp.api.bootstrap.nvr) new_nvr.version = Version("2.2.6") + new_nvr.release_channel = "release" mock_msg = Mock() - mock_msg.changed_data = {"version": "2.2.6"} + mock_msg.changed_data = {"version": "2.2.6", "releaseChannel": "release"} mock_msg.new_obj = new_nvr ufp.api.bootstrap.nvr = new_nvr From 26b26a3b1f8897941e92132fc0dc2cbcbd56eb6e Mon Sep 17 00:00:00 2001 From: Lex Li <425130+lextm@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:19:18 -0400 Subject: [PATCH 05/31] Bump `pysnmp-lextudio` to version `6.0.11` (#113463) --- homeassistant/components/snmp/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/snmp/manifest.json b/homeassistant/components/snmp/manifest.json index cd6c1dd9152..c4aa82f2a74 100644 --- a/homeassistant/components/snmp/manifest.json +++ b/homeassistant/components/snmp/manifest.json @@ -5,5 +5,5 @@ "documentation": "https://www.home-assistant.io/integrations/snmp", "iot_class": "local_polling", "loggers": ["pyasn1", "pysmi", "pysnmp"], - "requirements": ["pysnmp-lextudio==6.0.9"] + "requirements": ["pysnmp-lextudio==6.0.11"] } diff --git a/requirements_all.txt b/requirements_all.txt index fb1f88b2975..b31beea3226 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2155,7 +2155,7 @@ pysmartthings==0.7.8 pysml==0.0.12 # homeassistant.components.snmp -pysnmp-lextudio==6.0.9 +pysnmp-lextudio==6.0.11 # homeassistant.components.snooz pysnooz==0.8.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b8f639afa1a..128db237cb8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1673,7 +1673,7 @@ pysmartthings==0.7.8 pysml==0.0.12 # homeassistant.components.snmp -pysnmp-lextudio==6.0.9 +pysnmp-lextudio==6.0.11 # homeassistant.components.snooz pysnooz==0.8.6 From a5994d1d5fff0fe2c2d06a473ebdb890a9aeafef Mon Sep 17 00:00:00 2001 From: Erwin Douna Date: Wed, 20 Mar 2024 11:29:15 +0100 Subject: [PATCH 06/31] Tado fix water heater (#113464) Co-authored-by: Joostlek --- homeassistant/components/tado/water_heater.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/tado/water_heater.py b/homeassistant/components/tado/water_heater.py index cdbc041f535..d3ec29ae356 100644 --- a/homeassistant/components/tado/water_heater.py +++ b/homeassistant/components/tado/water_heater.py @@ -2,7 +2,6 @@ import logging from typing import Any -import PyTado import voluptuous as vol from homeassistant.components.water_heater import ( @@ -29,8 +28,6 @@ from .const import ( DATA, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED, - TADO_DEFAULT_MAX_TEMP, - TADO_DEFAULT_MIN_TEMP, TYPE_HOT_WATER, ) from .entity import TadoZoneEntity @@ -133,8 +130,8 @@ class TadoWaterHeater(TadoZoneEntity, WaterHeaterEntity): zone_name: str, zone_id: int, supports_temperature_control: bool, - min_temp: float | None = None, - max_temp: float | None = None, + min_temp, + max_temp, ) -> None: """Initialize of Tado water heater entity.""" self._tado = tado @@ -146,8 +143,8 @@ class TadoWaterHeater(TadoZoneEntity, WaterHeaterEntity): self._device_is_active = False self._supports_temperature_control = supports_temperature_control - self._min_temperature = min_temp or TADO_DEFAULT_MIN_TEMP - self._max_temperature = max_temp or TADO_DEFAULT_MAX_TEMP + self._min_temperature = min_temp + self._max_temperature = max_temp self._target_temp: float | None = None @@ -157,7 +154,7 @@ class TadoWaterHeater(TadoZoneEntity, WaterHeaterEntity): self._current_tado_hvac_mode = CONST_MODE_SMART_SCHEDULE self._overlay_mode = CONST_MODE_SMART_SCHEDULE - self._tado_zone_data: PyTado.TadoZone = {} + self._tado_zone_data: Any = None async def async_added_to_hass(self) -> None: """Register for sensor updates.""" From 099c22816986cc941ee6f24435f9a37afd6071c4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 14 Mar 2024 13:09:58 -1000 Subject: [PATCH 07/31] Bump aiodhcpwatcher to 0.8.2 (#113466) --- homeassistant/components/dhcp/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/dhcp/manifest.json b/homeassistant/components/dhcp/manifest.json index 95c925a8d33..673a80b8d9f 100644 --- a/homeassistant/components/dhcp/manifest.json +++ b/homeassistant/components/dhcp/manifest.json @@ -15,7 +15,7 @@ ], "quality_scale": "internal", "requirements": [ - "aiodhcpwatcher==0.8.1", + "aiodhcpwatcher==0.8.2", "aiodiscover==1.6.1", "cached_ipaddress==0.3.0" ] diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 1b850316b91..c92dad2ae35 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -1,6 +1,6 @@ # Automatically generated by gen_requirements_all.py, do not edit -aiodhcpwatcher==0.8.1 +aiodhcpwatcher==0.8.2 aiodiscover==1.6.1 aiohttp-fast-url-dispatcher==0.3.0 aiohttp-zlib-ng==0.3.1 diff --git a/requirements_all.txt b/requirements_all.txt index b31beea3226..0b2240ff0b1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -221,7 +221,7 @@ aiobotocore==2.9.1 aiocomelit==0.9.0 # homeassistant.components.dhcp -aiodhcpwatcher==0.8.1 +aiodhcpwatcher==0.8.2 # homeassistant.components.dhcp aiodiscover==1.6.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 128db237cb8..1a52448e86a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -200,7 +200,7 @@ aiobotocore==2.9.1 aiocomelit==0.9.0 # homeassistant.components.dhcp -aiodhcpwatcher==0.8.1 +aiodhcpwatcher==0.8.2 # homeassistant.components.dhcp aiodiscover==1.6.1 From de966b0eb141d7f169f61316fca9aa3a9b9d0c0e Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 15 Mar 2024 09:38:47 +0100 Subject: [PATCH 08/31] Bump axis to v55 (#113479) --- homeassistant/components/axis/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/axis/manifest.json b/homeassistant/components/axis/manifest.json index f56df16918e..c1471d370a5 100644 --- a/homeassistant/components/axis/manifest.json +++ b/homeassistant/components/axis/manifest.json @@ -26,7 +26,7 @@ "iot_class": "local_push", "loggers": ["axis"], "quality_scale": "platinum", - "requirements": ["axis==54"], + "requirements": ["axis==55"], "ssdp": [ { "manufacturer": "AXIS" diff --git a/requirements_all.txt b/requirements_all.txt index 0b2240ff0b1..f9f61f9525e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -514,7 +514,7 @@ aurorapy==0.2.7 # avion==0.10 # homeassistant.components.axis -axis==54 +axis==55 # homeassistant.components.azure_event_hub azure-eventhub==5.11.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1a52448e86a..9ddb3e2fc06 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -454,7 +454,7 @@ auroranoaa==0.0.3 aurorapy==0.2.7 # homeassistant.components.axis -axis==54 +axis==55 # homeassistant.components.azure_event_hub azure-eventhub==5.11.1 From 8bae8fdd75ea6ee54d2a9aad3ccfdbf6a26a242c Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 15 Mar 2024 13:00:08 +0200 Subject: [PATCH 09/31] Bump croniter to 2.0.2 (#113494) --- homeassistant/components/utility_meter/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/utility_meter/manifest.json b/homeassistant/components/utility_meter/manifest.json index 11aaf5307c8..25e803e6a2d 100644 --- a/homeassistant/components/utility_meter/manifest.json +++ b/homeassistant/components/utility_meter/manifest.json @@ -8,5 +8,5 @@ "iot_class": "local_push", "loggers": ["croniter"], "quality_scale": "internal", - "requirements": ["croniter==1.0.6"] + "requirements": ["croniter==2.0.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index f9f61f9525e..dc6b3111fb9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -669,7 +669,7 @@ connect-box==0.2.8 construct==2.10.68 # homeassistant.components.utility_meter -croniter==1.0.6 +croniter==2.0.2 # homeassistant.components.crownstone crownstone-cloud==1.4.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9ddb3e2fc06..985fbdb7320 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -553,7 +553,7 @@ colorthief==0.2.1 construct==2.10.68 # homeassistant.components.utility_meter -croniter==1.0.6 +croniter==2.0.2 # homeassistant.components.crownstone crownstone-cloud==1.4.9 From 5163b5f888aeb5449ee954efe26e22e3e5870c75 Mon Sep 17 00:00:00 2001 From: On Freund Date: Fri, 15 Mar 2024 12:42:53 +0200 Subject: [PATCH 10/31] Revert setting communication delay in Risco init (#113497) --- homeassistant/components/risco/__init__.py | 34 +++++----------------- tests/components/risco/conftest.py | 10 ------- tests/components/risco/test_init.py | 21 ------------- 3 files changed, 8 insertions(+), 57 deletions(-) delete mode 100644 tests/components/risco/test_init.py diff --git a/homeassistant/components/risco/__init__.py b/homeassistant/components/risco/__init__.py index d1e1c4f430c..efc17c48a06 100644 --- a/homeassistant/components/risco/__init__.py +++ b/homeassistant/components/risco/__init__.py @@ -37,12 +37,10 @@ from homeassistant.helpers.storage import Store from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( - CONF_COMMUNICATION_DELAY, DATA_COORDINATOR, DEFAULT_SCAN_INTERVAL, DOMAIN, EVENTS_COORDINATOR, - MAX_COMMUNICATION_DELAY, TYPE_LOCAL, ) @@ -85,31 +83,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def _async_setup_local_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data = entry.data - comm_delay = initial_delay = data.get(CONF_COMMUNICATION_DELAY, 0) + risco = RiscoLocal(data[CONF_HOST], data[CONF_PORT], data[CONF_PIN]) - while True: - risco = RiscoLocal( - data[CONF_HOST], - data[CONF_PORT], - data[CONF_PIN], - communication_delay=comm_delay, - ) - try: - await risco.connect() - except CannotConnectError as error: - if comm_delay >= MAX_COMMUNICATION_DELAY: - raise ConfigEntryNotReady() from error - comm_delay += 1 - except UnauthorizedError: - _LOGGER.exception("Failed to login to Risco cloud") - return False - else: - break - - if comm_delay > initial_delay: - new_data = data.copy() - new_data[CONF_COMMUNICATION_DELAY] = comm_delay - hass.config_entries.async_update_entry(entry, data=new_data) + try: + await risco.connect() + except CannotConnectError as error: + raise ConfigEntryNotReady() from error + except UnauthorizedError: + _LOGGER.exception("Failed to login to Risco cloud") + return False async def _error(error: Exception) -> None: _LOGGER.error("Error in Risco library: %s", error) diff --git a/tests/components/risco/conftest.py b/tests/components/risco/conftest.py index a8a764cd502..e08e6b29852 100644 --- a/tests/components/risco/conftest.py +++ b/tests/components/risco/conftest.py @@ -171,16 +171,6 @@ def connect_with_error(exception): yield -@pytest.fixture -def connect_with_single_error(exception): - """Fixture to simulate error on connect.""" - with patch( - "homeassistant.components.risco.RiscoLocal.connect", - side_effect=[exception, None], - ): - yield - - @pytest.fixture async def setup_risco_local(hass, local_config_entry): """Set up a local Risco integration for testing.""" diff --git a/tests/components/risco/test_init.py b/tests/components/risco/test_init.py deleted file mode 100644 index a1a9e3bd6a7..00000000000 --- a/tests/components/risco/test_init.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for the Risco initialization.""" -import pytest - -from homeassistant.components.risco import CannotConnectError -from homeassistant.components.risco.const import CONF_COMMUNICATION_DELAY -from homeassistant.core import HomeAssistant - - -@pytest.mark.parametrize("exception", [CannotConnectError]) -async def test_single_error_on_connect( - hass: HomeAssistant, connect_with_single_error, local_config_entry -) -> None: - """Test single error on connect to validate communication delay update from 0 (default) to 1.""" - expected_data = { - **local_config_entry.data, - **{"type": "local", CONF_COMMUNICATION_DELAY: 1}, - } - - await hass.config_entries.async_setup(local_config_entry.entry_id) - await hass.async_block_till_done() - assert local_config_entry.data == expected_data From 05b900321b9cf06b5410d9bb93dbad6085197928 Mon Sep 17 00:00:00 2001 From: On Freund Date: Fri, 15 Mar 2024 14:38:39 +0200 Subject: [PATCH 11/31] Bump pyrisco to 0.5.10 (#113505) --- homeassistant/components/risco/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/risco/manifest.json b/homeassistant/components/risco/manifest.json index ca28af3d8e5..b5d8c4442fd 100644 --- a/homeassistant/components/risco/manifest.json +++ b/homeassistant/components/risco/manifest.json @@ -7,5 +7,5 @@ "iot_class": "local_push", "loggers": ["pyrisco"], "quality_scale": "platinum", - "requirements": ["pyrisco==0.5.8"] + "requirements": ["pyrisco==0.5.10"] } diff --git a/requirements_all.txt b/requirements_all.txt index dc6b3111fb9..b939d524ad9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2090,7 +2090,7 @@ pyrecswitch==1.0.2 pyrepetierng==0.1.0 # homeassistant.components.risco -pyrisco==0.5.8 +pyrisco==0.5.10 # homeassistant.components.rituals_perfume_genie pyrituals==0.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 985fbdb7320..a5b6dcd6077 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1617,7 +1617,7 @@ pyqwikswitch==0.93 pyrainbird==4.0.2 # homeassistant.components.risco -pyrisco==0.5.8 +pyrisco==0.5.10 # homeassistant.components.rituals_perfume_genie pyrituals==0.0.6 From d5864a40a87005955bb4dddf4a584d93623a28a1 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 16 Mar 2024 23:37:24 +0100 Subject: [PATCH 12/31] Fix missing context when running script from template entity (#113523) Co-authored-by: J. Nick Koston --- homeassistant/components/template/coordinator.py | 7 +++++-- tests/components/template/test_sensor.py | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/template/coordinator.py b/homeassistant/components/template/coordinator.py index 047d58d9208..a5a38bf7b1d 100644 --- a/homeassistant/components/template/coordinator.py +++ b/homeassistant/components/template/coordinator.py @@ -3,7 +3,7 @@ from collections.abc import Callable import logging from homeassistant.const import EVENT_HOMEASSISTANT_START -from homeassistant.core import CoreState, callback +from homeassistant.core import Context, CoreState, callback from homeassistant.helpers import discovery, trigger as trigger_helper from homeassistant.helpers.script import Script from homeassistant.helpers.typing import ConfigType @@ -90,7 +90,10 @@ class TriggerUpdateCoordinator(DataUpdateCoordinator): ) async def _handle_triggered_with_script(self, run_variables, context=None): - if script_result := await self._script.async_run(run_variables, context): + # Create a context referring to the trigger context. + trigger_context_id = None if context is None else context.id + script_context = Context(parent_id=trigger_context_id) + if script_result := await self._script.async_run(run_variables, script_context): run_variables = script_result.variables self._handle_triggered(run_variables, context) diff --git a/tests/components/template/test_sensor.py b/tests/components/template/test_sensor.py index 314218fc849..8026618e7cd 100644 --- a/tests/components/template/test_sensor.py +++ b/tests/components/template/test_sensor.py @@ -29,6 +29,7 @@ import homeassistant.util.dt as dt_util from tests.common import ( MockConfigEntry, assert_setup_component, + async_capture_events, async_fire_time_changed, mock_restore_cache_with_extra_data, ) @@ -1848,6 +1849,7 @@ async def test_trigger_entity_restore_state( "my_variable": "{{ trigger.event.data.beer + 1 }}" }, }, + {"event": "test_event2", "event_data": {"hello": "world"}}, ], "sensor": [ { @@ -1864,6 +1866,10 @@ async def test_trigger_action( hass: HomeAssistant, start_ha, entity_registry: er.EntityRegistry ) -> None: """Test trigger entity with an action works.""" + event = "test_event2" + context = Context() + events = async_capture_events(hass, event) + state = hass.states.get("sensor.hello_name") assert state is not None assert state.state == STATE_UNKNOWN @@ -1875,3 +1881,6 @@ async def test_trigger_action( state = hass.states.get("sensor.hello_name") assert state.state == "3" assert state.context is context + + assert len(events) == 1 + assert events[0].context.parent_id == context.id From 4a620e015f095fa1817675734e7dc0182f1ee0ae Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 16 Mar 2024 15:03:26 -0700 Subject: [PATCH 13/31] Bump ical to 7.0.3 to fix local-todo persisted with invalid DTSTART values (#113526) --- homeassistant/components/google/manifest.json | 2 +- .../components/local_calendar/manifest.json | 2 +- .../components/local_todo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../local_todo/snapshots/test_todo.ambr | 10 ++++++++ tests/components/local_todo/test_todo.py | 23 +++++++++++++++++++ 7 files changed, 38 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index a08daee8961..ec9fb7018d6 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/calendar.google", "iot_class": "cloud_polling", "loggers": ["googleapiclient"], - "requirements": ["gcal-sync==6.0.3", "oauth2client==4.1.3", "ical==7.0.1"] + "requirements": ["gcal-sync==6.0.3", "oauth2client==4.1.3", "ical==7.0.3"] } diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index 25ec9f2ccc6..1c13970503d 100644 --- a/homeassistant/components/local_calendar/manifest.json +++ b/homeassistant/components/local_calendar/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/local_calendar", "iot_class": "local_polling", "loggers": ["ical"], - "requirements": ["ical==7.0.1"] + "requirements": ["ical==7.0.3"] } diff --git a/homeassistant/components/local_todo/manifest.json b/homeassistant/components/local_todo/manifest.json index 81f0f9dc199..3bcb8af9f43 100644 --- a/homeassistant/components/local_todo/manifest.json +++ b/homeassistant/components/local_todo/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/local_todo", "iot_class": "local_polling", - "requirements": ["ical==7.0.1"] + "requirements": ["ical==7.0.3"] } diff --git a/requirements_all.txt b/requirements_all.txt index b939d524ad9..19a8c7794d9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1115,7 +1115,7 @@ ibmiotf==0.3.4 # homeassistant.components.google # homeassistant.components.local_calendar # homeassistant.components.local_todo -ical==7.0.1 +ical==7.0.3 # homeassistant.components.ping icmplib==3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a5b6dcd6077..79d9c357454 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -905,7 +905,7 @@ ibeacon-ble==1.2.0 # homeassistant.components.google # homeassistant.components.local_calendar # homeassistant.components.local_todo -ical==7.0.1 +ical==7.0.3 # homeassistant.components.ping icmplib==3.0 diff --git a/tests/components/local_todo/snapshots/test_todo.ambr b/tests/components/local_todo/snapshots/test_todo.ambr index db4403f301c..15a44ff8c27 100644 --- a/tests/components/local_todo/snapshots/test_todo.ambr +++ b/tests/components/local_todo/snapshots/test_todo.ambr @@ -22,6 +22,16 @@ list([ ]) # --- +# name: test_parse_existing_ics[invalid_dtstart_tzname] + list([ + dict({ + 'due': '2023-10-24T11:30:00', + 'status': 'needs_action', + 'summary': 'Task', + 'uid': '077cb7f2-6c89-11ee-b2a9-0242ac110002', + }), + ]) +# --- # name: test_parse_existing_ics[migrate_legacy_due] list([ dict({ diff --git a/tests/components/local_todo/test_todo.py b/tests/components/local_todo/test_todo.py index 231f56b0afb..760b0260dbb 100644 --- a/tests/components/local_todo/test_todo.py +++ b/tests/components/local_todo/test_todo.py @@ -671,6 +671,28 @@ async def test_move_item_previous_unknown( ), "1", ), + ( + textwrap.dedent( + """\ + BEGIN:VCALENDAR + PRODID:-//homeassistant.io//local_todo 2.0//EN + VERSION:2.0 + BEGIN:VTODO + DTSTAMP:20231024T014011 + UID:077cb7f2-6c89-11ee-b2a9-0242ac110002 + CREATED:20231017T010348 + LAST-MODIFIED:20231024T014011 + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task + DUE:20231024T113000 + DTSTART;TZID=CST:20231024T113000 + END:VTODO + END:VCALENDAR + """ + ), + "1", + ), ], ids=( "empty", @@ -679,6 +701,7 @@ async def test_move_item_previous_unknown( "needs_action", "migrate_legacy_due", "due", + "invalid_dtstart_tzname", ), ) async def test_parse_existing_ics( From a7908d82507e528050323f30cd0b9ebb098d5450 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 15 Mar 2024 23:48:47 +0100 Subject: [PATCH 14/31] Fix Airthings BLE illuminance sensor name (#113560) --- homeassistant/components/airthings_ble/sensor.py | 1 + homeassistant/components/airthings_ble/strings.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/homeassistant/components/airthings_ble/sensor.py b/homeassistant/components/airthings_ble/sensor.py index 2bc2d5e726a..6abe3e5d174 100644 --- a/homeassistant/components/airthings_ble/sensor.py +++ b/homeassistant/components/airthings_ble/sensor.py @@ -105,6 +105,7 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = { ), "illuminance": SensorEntityDescription( key="illuminance", + translation_key="illuminance", native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/airthings_ble/strings.json b/homeassistant/components/airthings_ble/strings.json index b7343377a2b..6f17b9a317e 100644 --- a/homeassistant/components/airthings_ble/strings.json +++ b/homeassistant/components/airthings_ble/strings.json @@ -33,6 +33,9 @@ }, "radon_longterm_level": { "name": "Radon longterm level" + }, + "illuminance": { + "name": "[%key:component::sensor::entity_component::illuminance::name%]" } } } From 0a64ae2f7a149003ea40656a5137014ae487da06 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sat, 16 Mar 2024 16:18:41 +0200 Subject: [PATCH 15/31] Ignore Shelly block update with cfgChanged None (#113587) --- homeassistant/components/shelly/coordinator.py | 2 +- tests/components/shelly/test_coordinator.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 4afe66199f0..d41282b1f0b 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -216,7 +216,7 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]): # Check for input events and config change cfg_changed = 0 for block in self.device.blocks: - if block.type == "device": + if block.type == "device" and block.cfgChanged is not None: cfg_changed = block.cfgChanged # Shelly TRV sends information about changing the configuration for no diff --git a/tests/components/shelly/test_coordinator.py b/tests/components/shelly/test_coordinator.py index 67df09a5adb..940ab2123f0 100644 --- a/tests/components/shelly/test_coordinator.py +++ b/tests/components/shelly/test_coordinator.py @@ -67,6 +67,18 @@ async def test_block_reload_on_cfg_change( mock_block_device.mock_update() await hass.async_block_till_done() + # Make sure cfgChanged with None is ignored + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", None) + mock_block_device.mock_update() + await hass.async_block_till_done() + + # Wait for debouncer + freezer.tick(timedelta(seconds=ENTRY_RELOAD_COOLDOWN)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is not None + # Generate config change from switch to light monkeypatch.setitem( mock_block_device.settings["relays"][RELAY_BLOCK_ID], "appliance_type", "light" From 2e2d303291486ed4e98d5cdc6e9139785397641a Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sat, 16 Mar 2024 16:01:48 +0100 Subject: [PATCH 16/31] Catch `TimeoutError` in `Brother` config flow (#113593) * Catch TimeoutError in Brother config flow * Update tests * Remove unnecessary parentheses --------- Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com> --- homeassistant/components/brother/config_flow.py | 4 ++-- tests/components/brother/test_config_flow.py | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/brother/config_flow.py b/homeassistant/components/brother/config_flow.py index 55d47bb0c2c..3c60ccba5f0 100644 --- a/homeassistant/components/brother/config_flow.py +++ b/homeassistant/components/brother/config_flow.py @@ -58,7 +58,7 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry(title=title, data=user_input) except InvalidHost: errors[CONF_HOST] = "wrong_host" - except ConnectionError: + except (ConnectionError, TimeoutError): errors["base"] = "cannot_connect" except SnmpError: errors["base"] = "snmp_error" @@ -88,7 +88,7 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await self.brother.async_update() except UnsupportedModelError: return self.async_abort(reason="unsupported_model") - except (ConnectionError, SnmpError): + except (ConnectionError, SnmpError, TimeoutError): return self.async_abort(reason="cannot_connect") # Check if already configured diff --git a/tests/components/brother/test_config_flow.py b/tests/components/brother/test_config_flow.py index f83f882b8a0..3d83ecfcb7c 100644 --- a/tests/components/brother/test_config_flow.py +++ b/tests/components/brother/test_config_flow.py @@ -93,10 +93,11 @@ async def test_invalid_hostname(hass: HomeAssistant) -> None: assert result["errors"] == {CONF_HOST: "wrong_host"} -async def test_connection_error(hass: HomeAssistant) -> None: +@pytest.mark.parametrize("exc", [ConnectionError, TimeoutError]) +async def test_connection_error(hass: HomeAssistant, exc: Exception) -> None: """Test connection to host error.""" with patch("brother.Brother.initialize"), patch( - "brother.Brother._get_data", side_effect=ConnectionError() + "brother.Brother._get_data", side_effect=exc ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG @@ -147,10 +148,11 @@ async def test_device_exists_abort(hass: HomeAssistant) -> None: assert result["reason"] == "already_configured" -async def test_zeroconf_snmp_error(hass: HomeAssistant) -> None: - """Test we abort zeroconf flow on SNMP error.""" +@pytest.mark.parametrize("exc", [ConnectionError, TimeoutError, SnmpError("error")]) +async def test_zeroconf_exception(hass: HomeAssistant, exc: Exception) -> None: + """Test we abort zeroconf flow on exception.""" with patch("brother.Brother.initialize"), patch( - "brother.Brother._get_data", side_effect=SnmpError("error") + "brother.Brother._get_data", side_effect=exc ): result = await hass.config_entries.flow.async_init( DOMAIN, From fa9f5bd6477b85de44aa2484409ce8fe1920a77f Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 16 Mar 2024 23:02:52 +0100 Subject: [PATCH 17/31] Bump axis to v56 (#113608) --- homeassistant/components/axis/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/axis/manifest.json b/homeassistant/components/axis/manifest.json index c1471d370a5..346afc4b4fe 100644 --- a/homeassistant/components/axis/manifest.json +++ b/homeassistant/components/axis/manifest.json @@ -26,7 +26,7 @@ "iot_class": "local_push", "loggers": ["axis"], "quality_scale": "platinum", - "requirements": ["axis==55"], + "requirements": ["axis==56"], "ssdp": [ { "manufacturer": "AXIS" diff --git a/requirements_all.txt b/requirements_all.txt index 19a8c7794d9..7ac465663c5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -514,7 +514,7 @@ aurorapy==0.2.7 # avion==0.10 # homeassistant.components.axis -axis==55 +axis==56 # homeassistant.components.azure_event_hub azure-eventhub==5.11.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 79d9c357454..9febcf2defd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -454,7 +454,7 @@ auroranoaa==0.0.3 aurorapy==0.2.7 # homeassistant.components.axis -axis==55 +axis==56 # homeassistant.components.azure_event_hub azure-eventhub==5.11.1 From 686487e59c588397a1f39cd8cfb7a2db23c219f0 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sat, 16 Mar 2024 18:26:56 -0400 Subject: [PATCH 18/31] Bump pyunifiprotect to 5.0.1 (#113630) --- homeassistant/components/unifiprotect/manifest.json | 2 +- homeassistant/components/unifiprotect/select.py | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index eba2b934e05..1654539d91d 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -42,7 +42,7 @@ "iot_class": "local_push", "loggers": ["pyunifiprotect", "unifi_discovery"], "quality_scale": "platinum", - "requirements": ["pyunifiprotect==4.23.3", "unifi-discovery==1.1.8"], + "requirements": ["pyunifiprotect==5.0.1", "unifi-discovery==1.1.8"], "ssdp": [ { "manufacturer": "Ubiquiti Networks", diff --git a/homeassistant/components/unifiprotect/select.py b/homeassistant/components/unifiprotect/select.py index 5611ba79eca..e07a174659c 100644 --- a/homeassistant/components/unifiprotect/select.py +++ b/homeassistant/components/unifiprotect/select.py @@ -45,6 +45,7 @@ INFRARED_MODES = [ {"id": IRLEDMode.AUTO.value, "name": "Auto"}, {"id": IRLEDMode.ON.value, "name": "Always Enable"}, {"id": IRLEDMode.AUTO_NO_LED.value, "name": "Auto (Filter Only, no LED's)"}, + {"id": IRLEDMode.CUSTOM.value, "name": "Auto (Custom Lux)"}, {"id": IRLEDMode.OFF.value, "name": "Always Disable"}, ] diff --git a/requirements_all.txt b/requirements_all.txt index 7ac465663c5..46ccc244b8e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2340,7 +2340,7 @@ pytrydan==0.4.0 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.23.3 +pyunifiprotect==5.0.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9febcf2defd..1d5c3bad1a8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1801,7 +1801,7 @@ pytrydan==0.4.0 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.23.3 +pyunifiprotect==5.0.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From eb8a8424a59257595dd067590cfff5ecc0faaf77 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sat, 16 Mar 2024 22:15:18 -0400 Subject: [PATCH 19/31] Bump pyunifiprotect to 5.0.2 (#113651) --- homeassistant/components/unifiprotect/data.py | 27 ++----------------- .../components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/unifiprotect/data.py b/homeassistant/components/unifiprotect/data.py index 2825c2a4f3c..b82e9ff37f1 100644 --- a/homeassistant/components/unifiprotect/data.py +++ b/homeassistant/components/unifiprotect/data.py @@ -19,6 +19,7 @@ from pyunifiprotect.data import ( WSSubscriptionMessage, ) from pyunifiprotect.exceptions import ClientError, NotAuthorized +from pyunifiprotect.utils import log_event from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback @@ -41,11 +42,6 @@ from .utils import async_dispatch_id as _ufpd, async_get_devices_by_type _LOGGER = logging.getLogger(__name__) ProtectDeviceType = ProtectAdoptableDeviceModel | NVR -SMART_EVENTS = { - EventType.SMART_DETECT, - EventType.SMART_AUDIO_DETECT, - EventType.SMART_DETECT_LINE, -} @callback @@ -230,26 +226,7 @@ class ProtectData: # trigger updates for camera that the event references elif isinstance(obj, Event): # type: ignore[unreachable] if _LOGGER.isEnabledFor(logging.DEBUG): - _LOGGER.debug("event WS msg: %s", obj.dict()) - if obj.type in SMART_EVENTS: - if obj.camera is not None: - if obj.end is None: - _LOGGER.debug( - "%s (%s): New smart detection started for %s (%s)", - obj.camera.name, - obj.camera.mac, - obj.smart_detect_types, - obj.id, - ) - else: - _LOGGER.debug( - "%s (%s): Smart detection ended for %s (%s)", - obj.camera.name, - obj.camera.mac, - obj.smart_detect_types, - obj.id, - ) - + log_event(obj) if obj.type is EventType.DEVICE_ADOPTED: if obj.metadata is not None and obj.metadata.device_id is not None: device = self.api.bootstrap.get_device_from_id( diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 1654539d91d..1eb37befca0 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -42,7 +42,7 @@ "iot_class": "local_push", "loggers": ["pyunifiprotect", "unifi_discovery"], "quality_scale": "platinum", - "requirements": ["pyunifiprotect==5.0.1", "unifi-discovery==1.1.8"], + "requirements": ["pyunifiprotect==5.0.2", "unifi-discovery==1.1.8"], "ssdp": [ { "manufacturer": "Ubiquiti Networks", diff --git a/requirements_all.txt b/requirements_all.txt index 46ccc244b8e..50e99d674e9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2340,7 +2340,7 @@ pytrydan==0.4.0 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==5.0.1 +pyunifiprotect==5.0.2 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1d5c3bad1a8..865fec1c2e1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1801,7 +1801,7 @@ pytrydan==0.4.0 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==5.0.1 +pyunifiprotect==5.0.2 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From 368586c9d17727d63ab0edec297e8bd85ec68285 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 17 Mar 2024 21:21:42 +0100 Subject: [PATCH 20/31] Add removal condition to Shelly battery sensor (#113703) Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com> --- homeassistant/components/shelly/sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index b88b6886b84..82fc4fe6d78 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -941,6 +941,7 @@ RPC_SENSORS: Final = { device_class=SensorDeviceClass.BATTERY, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, + removal_condition=lambda _config, status, key: (status[key]["battery"] is None), ), "voltmeter": RpcSensorDescription( key="voltmeter", From 6859bae0b13375c94483e6fde8102f3d28b4a253 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Wed, 20 Mar 2024 04:57:37 -0500 Subject: [PATCH 21/31] Bump aioraven to 0.5.2 (#113714) --- homeassistant/components/rainforest_raven/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rainforest_raven/manifest.json b/homeassistant/components/rainforest_raven/manifest.json index 3e463af9ba4..ad161d32201 100644 --- a/homeassistant/components/rainforest_raven/manifest.json +++ b/homeassistant/components/rainforest_raven/manifest.json @@ -6,7 +6,7 @@ "dependencies": ["usb"], "documentation": "https://www.home-assistant.io/integrations/rainforest_raven", "iot_class": "local_polling", - "requirements": ["aioraven==0.5.1"], + "requirements": ["aioraven==0.5.2"], "usb": [ { "vid": "0403", diff --git a/requirements_all.txt b/requirements_all.txt index 50e99d674e9..f11d17fc8cb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -350,7 +350,7 @@ aiopyarr==23.4.0 aioqsw==0.3.5 # homeassistant.components.rainforest_raven -aioraven==0.5.1 +aioraven==0.5.2 # homeassistant.components.recollect_waste aiorecollect==2023.09.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 865fec1c2e1..d11bba443cc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -323,7 +323,7 @@ aiopyarr==23.4.0 aioqsw==0.3.5 # homeassistant.components.rainforest_raven -aioraven==0.5.1 +aioraven==0.5.2 # homeassistant.components.recollect_waste aiorecollect==2023.09.0 From 33678ff5a47b6e7b91f1b959070ccb27d846504d Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:12:46 +0100 Subject: [PATCH 22/31] Fix unknown values in onewire (#113731) * Fix unknown values in onewire * Update tests --- homeassistant/components/onewire/binary_sensor.py | 4 +++- homeassistant/components/onewire/switch.py | 6 ++++-- tests/components/onewire/const.py | 8 ++++++-- .../components/onewire/snapshots/test_binary_sensor.ambr | 4 ++-- tests/components/onewire/snapshots/test_switch.ambr | 4 ++-- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/onewire/binary_sensor.py b/homeassistant/components/onewire/binary_sensor.py index e7e30588f8a..5cd3fa65a60 100644 --- a/homeassistant/components/onewire/binary_sensor.py +++ b/homeassistant/components/onewire/binary_sensor.py @@ -143,6 +143,8 @@ class OneWireBinarySensor(OneWireEntity, BinarySensorEntity): entity_description: OneWireBinarySensorEntityDescription @property - def is_on(self) -> bool: + def is_on(self) -> bool | None: """Return true if sensor is on.""" + if self._state is None: + return None return bool(self._state) diff --git a/homeassistant/components/onewire/switch.py b/homeassistant/components/onewire/switch.py index 00a3f8f65f4..c63198ccf05 100644 --- a/homeassistant/components/onewire/switch.py +++ b/homeassistant/components/onewire/switch.py @@ -204,8 +204,10 @@ class OneWireSwitch(OneWireEntity, SwitchEntity): entity_description: OneWireSwitchEntityDescription @property - def is_on(self) -> bool: - """Return true if sensor is on.""" + def is_on(self) -> bool | None: + """Return true if switch is on.""" + if self._state is None: + return None return bool(self._state) def turn_on(self, **kwargs: Any) -> None: diff --git a/tests/components/onewire/const.py b/tests/components/onewire/const.py index 3da37a72459..cb9fca54f3d 100644 --- a/tests/components/onewire/const.py +++ b/tests/components/onewire/const.py @@ -155,7 +155,9 @@ MOCK_OWPROXY_DEVICES = { {ATTR_INJECT_READS: b" 1"}, {ATTR_INJECT_READS: b" 0"}, {ATTR_INJECT_READS: b" 0"}, - {ATTR_INJECT_READS: b" 0"}, + { + ATTR_INJECT_READS: ProtocolError, + }, {ATTR_INJECT_READS: b" 0"}, {ATTR_INJECT_READS: b" 0"}, {ATTR_INJECT_READS: b" 0"}, @@ -165,7 +167,9 @@ MOCK_OWPROXY_DEVICES = { {ATTR_INJECT_READS: b" 1"}, {ATTR_INJECT_READS: b" 0"}, {ATTR_INJECT_READS: b" 1"}, - {ATTR_INJECT_READS: b" 0"}, + { + ATTR_INJECT_READS: ProtocolError, + }, {ATTR_INJECT_READS: b" 1"}, {ATTR_INJECT_READS: b" 0"}, {ATTR_INJECT_READS: b" 1"}, diff --git a/tests/components/onewire/snapshots/test_binary_sensor.ambr b/tests/components/onewire/snapshots/test_binary_sensor.ambr index 2aa415f0345..0523c969ade 100644 --- a/tests/components/onewire/snapshots/test_binary_sensor.ambr +++ b/tests/components/onewire/snapshots/test_binary_sensor.ambr @@ -851,13 +851,13 @@ 'attributes': ReadOnlyDict({ 'device_file': '/29.111111111111/sensed.3', 'friendly_name': '29.111111111111 Sensed 3', - 'raw_value': 0.0, + 'raw_value': None, }), 'context': , 'entity_id': 'binary_sensor.29_111111111111_sensed_3', 'last_changed': , 'last_updated': , - 'state': 'off', + 'state': 'unknown', }), StateSnapshot({ 'attributes': ReadOnlyDict({ diff --git a/tests/components/onewire/snapshots/test_switch.ambr b/tests/components/onewire/snapshots/test_switch.ambr index 8fbb977948b..4f6498419a9 100644 --- a/tests/components/onewire/snapshots/test_switch.ambr +++ b/tests/components/onewire/snapshots/test_switch.ambr @@ -1271,13 +1271,13 @@ 'attributes': ReadOnlyDict({ 'device_file': '/29.111111111111/PIO.3', 'friendly_name': '29.111111111111 Programmed input-output 3', - 'raw_value': 0.0, + 'raw_value': None, }), 'context': , 'entity_id': 'switch.29_111111111111_programmed_input_output_3', 'last_changed': , 'last_updated': , - 'state': 'off', + 'state': 'unknown', }), StateSnapshot({ 'attributes': ReadOnlyDict({ From d67cd2af0c8dc05e33d0fb8b195b5646d8904433 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Tue, 19 Mar 2024 09:37:36 +0100 Subject: [PATCH 23/31] Bump pymodbus v3.6.6 (#113796) --- homeassistant/components/modbus/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/modbus/manifest.json b/homeassistant/components/modbus/manifest.json index 6b072457144..14faad789fe 100644 --- a/homeassistant/components/modbus/manifest.json +++ b/homeassistant/components/modbus/manifest.json @@ -6,5 +6,5 @@ "iot_class": "local_polling", "loggers": ["pymodbus"], "quality_scale": "gold", - "requirements": ["pymodbus==3.6.5"] + "requirements": ["pymodbus==3.6.6"] } diff --git a/requirements_all.txt b/requirements_all.txt index f11d17fc8cb..c147e265ec2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1971,7 +1971,7 @@ pymitv==1.4.3 pymochad==0.2.0 # homeassistant.components.modbus -pymodbus==3.6.5 +pymodbus==3.6.6 # homeassistant.components.monoprice pymonoprice==0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d11bba443cc..7636d8c6b18 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1525,7 +1525,7 @@ pymeteoclimatic==0.1.0 pymochad==0.2.0 # homeassistant.components.modbus -pymodbus==3.6.5 +pymodbus==3.6.6 # homeassistant.components.monoprice pymonoprice==0.4 From 4132a3d2ea7abc91b3b00d65ee3d7a4f9a2e67a4 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 20 Mar 2024 13:10:35 +0100 Subject: [PATCH 24/31] Catch API errors in cast media_player service handlers (#113839) * Catch API errors in cast media_player service handlers * Remove left over debug code * Fix wrapping of coroutine function with api_error --- homeassistant/components/cast/media_player.py | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index b2893a54310..5e907b0a659 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -4,9 +4,10 @@ from __future__ import annotations from collections.abc import Callable from contextlib import suppress from datetime import datetime +from functools import wraps import json import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar import pychromecast from pychromecast.controllers.homeassistant import HomeAssistantController @@ -18,6 +19,7 @@ from pychromecast.controllers.media import ( ) from pychromecast.controllers.multizone import MultizoneManager from pychromecast.controllers.receiver import VOLUME_CONTROL_TYPE_FIXED +from pychromecast.error import PyChromecastError from pychromecast.quick_play import quick_play from pychromecast.socket_client import ( CONNECTION_STATUS_CONNECTED, @@ -83,6 +85,34 @@ APP_IDS_UNRELIABLE_MEDIA_INFO = ("Netflix",) CAST_SPLASH = "https://www.home-assistant.io/images/cast/splash.png" +_CastDeviceT = TypeVar("_CastDeviceT", bound="CastDevice") +_R = TypeVar("_R") +_P = ParamSpec("_P") + +_FuncType = Callable[Concatenate[_CastDeviceT, _P], _R] +_ReturnFuncType = Callable[Concatenate[_CastDeviceT, _P], _R] + + +def api_error( + func: _FuncType[_CastDeviceT, _P, _R], +) -> _ReturnFuncType[_CastDeviceT, _P, _R]: + """Handle PyChromecastError and reraise a HomeAssistantError.""" + + @wraps(func) + def wrapper(self: _CastDeviceT, *args: _P.args, **kwargs: _P.kwargs) -> _R: + """Wrap a CastDevice method.""" + try: + return_value = func(self, *args, **kwargs) + except PyChromecastError as err: + raise HomeAssistantError( + f"{self.__class__.__name__}.{func.__name__} Failed: {err}" + ) from err + + return return_value + + return wrapper + + @callback def _async_create_cast_device(hass: HomeAssistant, info: ChromecastInfo): """Create a CastDevice entity or dynamic group from the chromecast object. @@ -476,6 +506,21 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): return media_controller + @api_error + def _quick_play(self, app_name: str, data: dict[str, Any]) -> None: + """Launch the app `app_name` and start playing media defined by `data`.""" + quick_play(self._get_chromecast(), app_name, data) + + @api_error + def _quit_app(self) -> None: + """Quit the currently running app.""" + self._get_chromecast().quit_app() + + @api_error + def _start_app(self, app_id: str) -> None: + """Start an app.""" + self._get_chromecast().start_app(app_id) + def turn_on(self) -> None: """Turn on the cast device.""" @@ -486,52 +531,61 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): if chromecast.app_id is not None: # Quit the previous app before starting splash screen or media player - chromecast.quit_app() + self._quit_app() # The only way we can turn the Chromecast is on is by launching an app if chromecast.cast_type == pychromecast.const.CAST_TYPE_CHROMECAST: app_data = {"media_id": CAST_SPLASH, "media_type": "image/png"} - quick_play(chromecast, "default_media_receiver", app_data) + self._quick_play("default_media_receiver", app_data) else: - chromecast.start_app(pychromecast.config.APP_MEDIA_RECEIVER) + self._start_app(pychromecast.config.APP_MEDIA_RECEIVER) + @api_error def turn_off(self) -> None: """Turn off the cast device.""" self._get_chromecast().quit_app() + @api_error def mute_volume(self, mute: bool) -> None: """Mute the volume.""" self._get_chromecast().set_volume_muted(mute) + @api_error def set_volume_level(self, volume: float) -> None: """Set volume level, range 0..1.""" self._get_chromecast().set_volume(volume) + @api_error def media_play(self) -> None: """Send play command.""" media_controller = self._media_controller() media_controller.play() + @api_error def media_pause(self) -> None: """Send pause command.""" media_controller = self._media_controller() media_controller.pause() + @api_error def media_stop(self) -> None: """Send stop command.""" media_controller = self._media_controller() media_controller.stop() + @api_error def media_previous_track(self) -> None: """Send previous track command.""" media_controller = self._media_controller() media_controller.queue_prev() + @api_error def media_next_track(self) -> None: """Send next track command.""" media_controller = self._media_controller() media_controller.queue_next() + @api_error def media_seek(self, position: float) -> None: """Seek the media to a specific location.""" media_controller = self._media_controller() @@ -644,7 +698,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): if "app_id" in app_data: app_id = app_data.pop("app_id") _LOGGER.info("Starting Cast app by ID %s", app_id) - await self.hass.async_add_executor_job(chromecast.start_app, app_id) + await self.hass.async_add_executor_job(self._start_app, app_id) if app_data: _LOGGER.warning( "Extra keys %s were ignored. Please use app_name to cast media", @@ -655,7 +709,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): app_name = app_data.pop("app_name") try: await self.hass.async_add_executor_job( - quick_play, chromecast, app_name, app_data + self._quick_play, app_name, app_data ) except NotImplementedError: _LOGGER.error("App %s not supported", app_name) @@ -729,7 +783,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): app_data, ) await self.hass.async_add_executor_job( - quick_play, chromecast, "default_media_receiver", app_data + self._quick_play, "default_media_receiver", app_data ) def _media_status(self): From 14c4cdc08953e02539c3a066c6873e2e70d268bc Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 19 Mar 2024 22:41:10 +0100 Subject: [PATCH 25/31] Bump pychromecast to 14.0.1 (#113841) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index d02bcd3558a..1d06ae23ca2 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -14,6 +14,6 @@ "documentation": "https://www.home-assistant.io/integrations/cast", "iot_class": "local_polling", "loggers": ["casttube", "pychromecast"], - "requirements": ["PyChromecast==14.0.0"], + "requirements": ["PyChromecast==14.0.1"], "zeroconf": ["_googlecast._tcp.local."] } diff --git a/requirements_all.txt b/requirements_all.txt index c147e265ec2..b7e161445a0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -54,7 +54,7 @@ ProgettiHWSW==0.1.3 # PyBluez==0.22 # homeassistant.components.cast -PyChromecast==14.0.0 +PyChromecast==14.0.1 # homeassistant.components.flick_electric PyFlick==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7636d8c6b18..60439019cc3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -45,7 +45,7 @@ PlexAPI==4.15.10 ProgettiHWSW==0.1.3 # homeassistant.components.cast -PyChromecast==14.0.0 +PyChromecast==14.0.1 # homeassistant.components.flick_electric PyFlick==0.0.2 From 8056886c66a7f88e9d5027c9ecfcd13cb2902766 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 19 Mar 2024 22:37:29 +0100 Subject: [PATCH 26/31] Fix startup race in cast (#113843) --- homeassistant/components/cast/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/cast/__init__.py b/homeassistant/components/cast/__init__.py index 8c574e0792b..9aed870d9b4 100644 --- a/homeassistant/components/cast/__init__.py +++ b/homeassistant/components/cast/__init__.py @@ -24,9 +24,9 @@ PLATFORMS = [Platform.MEDIA_PLAYER] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Cast from a config entry.""" + hass.data[DOMAIN] = {"cast_platform": {}, "unknown_models": {}} await home_assistant_cast.async_setup_ha_cast(hass, entry) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - hass.data[DOMAIN] = {"cast_platform": {}, "unknown_models": {}} await async_process_integration_platforms(hass, DOMAIN, _register_cast_platform) return True From 1e57f52ba27a48814cf84c920c2547d113e401ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 20 Mar 2024 08:23:16 +0100 Subject: [PATCH 27/31] Redact the area of traccar server geofences (#113861) --- homeassistant/components/traccar_server/diagnostics.py | 7 ++++++- .../traccar_server/snapshots/test_diagnostics.ambr | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/traccar_server/diagnostics.py b/homeassistant/components/traccar_server/diagnostics.py index 15b94a2b880..f4b1cc799cb 100644 --- a/homeassistant/components/traccar_server/diagnostics.py +++ b/homeassistant/components/traccar_server/diagnostics.py @@ -12,7 +12,12 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er from .const import DOMAIN from .coordinator import TraccarServerCoordinator -TO_REDACT = {CONF_ADDRESS, CONF_LATITUDE, CONF_LONGITUDE} +TO_REDACT = { + CONF_ADDRESS, + CONF_LATITUDE, + CONF_LONGITUDE, + "area", # This is the polygon area of a geofence +} async def async_get_config_entry_diagnostics( diff --git a/tests/components/traccar_server/snapshots/test_diagnostics.ambr b/tests/components/traccar_server/snapshots/test_diagnostics.ambr index 1726f1c3d45..20d01e427ea 100644 --- a/tests/components/traccar_server/snapshots/test_diagnostics.ambr +++ b/tests/components/traccar_server/snapshots/test_diagnostics.ambr @@ -34,7 +34,7 @@ 'uniqueId': 'abc123', }), 'geofence': dict({ - 'area': 'string', + 'area': '**REDACTED**', 'attributes': dict({ }), 'calendarId': 0, @@ -134,7 +134,7 @@ 'uniqueId': 'abc123', }), 'geofence': dict({ - 'area': 'string', + 'area': '**REDACTED**', 'attributes': dict({ }), 'calendarId': 0, From 6c274abc5010ca5476b187cda24b1993086e836e Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:27:51 +0100 Subject: [PATCH 28/31] Bump pytedee_async to 0.2.17 (#113933) --- homeassistant/components/tedee/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tedee/manifest.json b/homeassistant/components/tedee/manifest.json index 1f2a2405a44..db3a88f3113 100644 --- a/homeassistant/components/tedee/manifest.json +++ b/homeassistant/components/tedee/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/tedee", "iot_class": "local_push", "loggers": ["pytedee_async"], - "requirements": ["pytedee-async==0.2.16"] + "requirements": ["pytedee-async==0.2.17"] } diff --git a/requirements_all.txt b/requirements_all.txt index b7e161445a0..f5a4c1da417 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2182,7 +2182,7 @@ pyswitchbee==1.8.0 pytautulli==23.1.1 # homeassistant.components.tedee -pytedee-async==0.2.16 +pytedee-async==0.2.17 # homeassistant.components.tfiac pytfiac==0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 60439019cc3..c0f77531e1b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1697,7 +1697,7 @@ pyswitchbee==1.8.0 pytautulli==23.1.1 # homeassistant.components.tedee -pytedee-async==0.2.16 +pytedee-async==0.2.17 # homeassistant.components.motionmount python-MotionMount==0.3.1 From 19ef92735c67e0abe1684456959355ddef2bec8f Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 21 Mar 2024 21:42:42 +0100 Subject: [PATCH 29/31] Bump axis to v57 (#113952) --- homeassistant/components/axis/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/axis/manifest.json b/homeassistant/components/axis/manifest.json index 346afc4b4fe..44d615bf534 100644 --- a/homeassistant/components/axis/manifest.json +++ b/homeassistant/components/axis/manifest.json @@ -26,7 +26,7 @@ "iot_class": "local_push", "loggers": ["axis"], "quality_scale": "platinum", - "requirements": ["axis==56"], + "requirements": ["axis==57"], "ssdp": [ { "manufacturer": "AXIS" diff --git a/requirements_all.txt b/requirements_all.txt index f5a4c1da417..fe927e87424 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -514,7 +514,7 @@ aurorapy==0.2.7 # avion==0.10 # homeassistant.components.axis -axis==56 +axis==57 # homeassistant.components.azure_event_hub azure-eventhub==5.11.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c0f77531e1b..6307d864186 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -454,7 +454,7 @@ auroranoaa==0.0.3 aurorapy==0.2.7 # homeassistant.components.axis -axis==56 +axis==57 # homeassistant.components.azure_event_hub azure-eventhub==5.11.1 From 91bb321d8f4d3cebc1852816e2c0671a2c0525fb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Mar 2024 20:25:19 -0400 Subject: [PATCH 30/31] Bump version to 2024.3.2 --- 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 847387e76ae..f5efc37f352 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -16,7 +16,7 @@ from .helpers.deprecation import ( APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2024 MINOR_VERSION: Final = 3 -PATCH_VERSION: Final = "1" +PATCH_VERSION: Final = "2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0) diff --git a/pyproject.toml b/pyproject.toml index d8a1545fbb3..496a29eb31c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2024.3.1" +version = "2024.3.2" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 93289c9f09d55195f12805c31e8b6d49b38db3f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:13:44 +0100 Subject: [PATCH 31/31] Bump home-assistant/builder from 2024.01.0 to 2024.03.5 (#113887) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/builder.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index 333c31ce841..6446d41fb71 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -207,7 +207,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build base image - uses: home-assistant/builder@2024.01.0 + uses: home-assistant/builder@2024.03.5 with: args: | $BUILD_ARGS \ @@ -284,7 +284,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build base image - uses: home-assistant/builder@2024.01.0 + uses: home-assistant/builder@2024.03.5 with: args: | $BUILD_ARGS \