From 42cf4e8db755fb43c3f8ffce14e8332e71f8006c Mon Sep 17 00:00:00 2001 From: hanwg Date: Tue, 22 Jul 2025 13:42:40 +0800 Subject: [PATCH 1/6] Fix multiple webhook secrets for Telegram bot (#149103) --- homeassistant/components/telegram_bot/webhooks.py | 14 ++++++++++---- tests/components/telegram_bot/test_telegram_bot.py | 12 ++++++------ tests/components/telegram_bot/test_webhooks.py | 5 +++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/telegram_bot/webhooks.py b/homeassistant/components/telegram_bot/webhooks.py index 0bfad34681a62..29c3305858bb8 100644 --- a/homeassistant/components/telegram_bot/webhooks.py +++ b/homeassistant/components/telegram_bot/webhooks.py @@ -82,7 +82,7 @@ def __init__( self.base_url = config.data.get(CONF_URL) or get_url( hass, require_ssl=True, allow_internal=False ) - self.webhook_url = f"{self.base_url}{TELEGRAM_WEBHOOK_URL}" + self.webhook_url = self.base_url + _get_webhook_url(bot) async def shutdown(self) -> None: """Shutdown the app.""" @@ -98,9 +98,11 @@ async def _try_to_set_webhook(self) -> bool: api_kwargs={"secret_token": self.secret_token}, connect_timeout=5, ) - except TelegramError: + except TelegramError as err: retry_num += 1 - _LOGGER.warning("Error trying to set webhook (retry #%d)", retry_num) + _LOGGER.warning( + "Error trying to set webhook (retry #%d)", retry_num, exc_info=err + ) return False @@ -143,7 +145,6 @@ class PushBotView(HomeAssistantView): """View for handling webhook calls from Telegram.""" requires_auth = False - url = TELEGRAM_WEBHOOK_URL name = "telegram_webhooks" def __init__( @@ -160,6 +161,7 @@ def __init__( self.application = application self.trusted_networks = trusted_networks self.secret_token = secret_token + self.url = _get_webhook_url(bot) async def post(self, request: HomeAssistantRequest) -> Response | None: """Accept the POST from telegram.""" @@ -183,3 +185,7 @@ async def post(self, request: HomeAssistantRequest) -> Response | None: await self.application.process_update(update) return None + + +def _get_webhook_url(bot: Bot) -> str: + return f"{TELEGRAM_WEBHOOK_URL}_{bot.id}" diff --git a/tests/components/telegram_bot/test_telegram_bot.py b/tests/components/telegram_bot/test_telegram_bot.py index 73dd9e277634d..80b9859ceabdd 100644 --- a/tests/components/telegram_bot/test_telegram_bot.py +++ b/tests/components/telegram_bot/test_telegram_bot.py @@ -364,7 +364,7 @@ async def test_webhook_endpoint_generates_telegram_text_event( events = async_capture_events(hass, "telegram_text") response = await client.post( - TELEGRAM_WEBHOOK_URL, + f"{TELEGRAM_WEBHOOK_URL}_123456", json=update_message_text, headers={"X-Telegram-Bot-Api-Secret-Token": mock_generate_secret_token}, ) @@ -391,7 +391,7 @@ async def test_webhook_endpoint_generates_telegram_command_event( events = async_capture_events(hass, "telegram_command") response = await client.post( - TELEGRAM_WEBHOOK_URL, + f"{TELEGRAM_WEBHOOK_URL}_123456", json=update_message_command, headers={"X-Telegram-Bot-Api-Secret-Token": mock_generate_secret_token}, ) @@ -418,7 +418,7 @@ async def test_webhook_endpoint_generates_telegram_callback_event( events = async_capture_events(hass, "telegram_callback") response = await client.post( - TELEGRAM_WEBHOOK_URL, + f"{TELEGRAM_WEBHOOK_URL}_123456", json=update_callback_query, headers={"X-Telegram-Bot-Api-Secret-Token": mock_generate_secret_token}, ) @@ -594,7 +594,7 @@ async def test_webhook_endpoint_unauthorized_update_doesnt_generate_telegram_tex events = async_capture_events(hass, "telegram_text") response = await client.post( - TELEGRAM_WEBHOOK_URL, + f"{TELEGRAM_WEBHOOK_URL}_123456", json=unauthorized_update_message_text, headers={"X-Telegram-Bot-Api-Secret-Token": mock_generate_secret_token}, ) @@ -618,7 +618,7 @@ async def test_webhook_endpoint_without_secret_token_is_denied( async_capture_events(hass, "telegram_text") response = await client.post( - TELEGRAM_WEBHOOK_URL, + f"{TELEGRAM_WEBHOOK_URL}_123456", json=update_message_text, ) assert response.status == 401 @@ -636,7 +636,7 @@ async def test_webhook_endpoint_invalid_secret_token_is_denied( async_capture_events(hass, "telegram_text") response = await client.post( - TELEGRAM_WEBHOOK_URL, + f"{TELEGRAM_WEBHOOK_URL}_123456", json=update_message_text, headers={"X-Telegram-Bot-Api-Secret-Token": incorrect_secret_token}, ) diff --git a/tests/components/telegram_bot/test_webhooks.py b/tests/components/telegram_bot/test_webhooks.py index 3419d33074dea..a02bb3e335860 100644 --- a/tests/components/telegram_bot/test_webhooks.py +++ b/tests/components/telegram_bot/test_webhooks.py @@ -7,6 +7,7 @@ from telegram import WebhookInfo from telegram.error import TimedOut +from homeassistant.components.telegram_bot.webhooks import TELEGRAM_WEBHOOK_URL from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -115,7 +116,7 @@ async def test_webhooks_update_invalid_json( client = await hass_client() response = await client.post( - "/api/telegram_webhooks", + f"{TELEGRAM_WEBHOOK_URL}_123456", headers={"X-Telegram-Bot-Api-Secret-Token": mock_generate_secret_token}, ) assert response.status == 400 @@ -139,7 +140,7 @@ async def test_webhooks_unauthorized_network( return_value=IPv4Network("1.2.3.4"), ) as mock_remote: response = await client.post( - "/api/telegram_webhooks", + f"{TELEGRAM_WEBHOOK_URL}_123456", json="mock json", headers={"X-Telegram-Bot-Api-Secret-Token": mock_generate_secret_token}, ) From 48b8827390502fa992e1bb28e74369bc82f5fe8d Mon Sep 17 00:00:00 2001 From: David Ferguson Date: Tue, 22 Jul 2025 02:56:54 -0400 Subject: [PATCH 2/6] Bump asyncsleepiq to 1.5.3 (#149215) --- homeassistant/components/sleepiq/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sleepiq/manifest.json b/homeassistant/components/sleepiq/manifest.json index db29e5ab5867a..5082e2313dfd5 100644 --- a/homeassistant/components/sleepiq/manifest.json +++ b/homeassistant/components/sleepiq/manifest.json @@ -11,5 +11,5 @@ "documentation": "https://www.home-assistant.io/integrations/sleepiq", "iot_class": "cloud_polling", "loggers": ["asyncsleepiq"], - "requirements": ["asyncsleepiq==1.5.2"] + "requirements": ["asyncsleepiq==1.5.3"] } diff --git a/requirements_all.txt b/requirements_all.txt index 074b68773da4f..710a729685005 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -542,7 +542,7 @@ asyncinotify==4.2.0 asyncpysupla==0.0.5 # homeassistant.components.sleepiq -asyncsleepiq==1.5.2 +asyncsleepiq==1.5.3 # homeassistant.components.aten_pe # atenpdu==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 10be46583566c..c224132c2d7cb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -500,7 +500,7 @@ async-upnp-client==0.45.0 asyncarve==0.1.1 # homeassistant.components.sleepiq -asyncsleepiq==1.5.2 +asyncsleepiq==1.5.3 # homeassistant.components.aurora auroranoaa==0.0.5 From 3e7974a63864f7ec37a360562e9810eece04e605 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Tue, 22 Jul 2025 08:58:32 +0200 Subject: [PATCH 3/6] Add missing hyphen to "post-processing" in `nzbget` (#149205) --- homeassistant/components/nzbget/strings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nzbget/strings.json b/homeassistant/components/nzbget/strings.json index 3b41e798d2252..358be131c938f 100644 --- a/homeassistant/components/nzbget/strings.json +++ b/homeassistant/components/nzbget/strings.json @@ -43,10 +43,10 @@ "name": "Disk free" }, "post_processing_jobs": { - "name": "Post processing jobs" + "name": "Post-processing jobs" }, "post_processing_paused": { - "name": "Post processing paused" + "name": "Post-processing paused" }, "queue_size": { "name": "Queue size" From df4e1411cc8041a3e7e9d91c3216df60568f4e40 Mon Sep 17 00:00:00 2001 From: Raphael Hehl <7577984+RaHehl@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:00:25 +0200 Subject: [PATCH 4/6] Bump uiprotect to version 7.18.1 (#149209) --- homeassistant/components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index e5b017e0ab661..5beb4ca059d2a 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -40,7 +40,7 @@ "integration_type": "hub", "iot_class": "local_push", "loggers": ["uiprotect", "unifi_discovery"], - "requirements": ["uiprotect==7.16.0", "unifi-discovery==1.2.0"], + "requirements": ["uiprotect==7.18.1", "unifi-discovery==1.2.0"], "ssdp": [ { "manufacturer": "Ubiquiti Networks", diff --git a/requirements_all.txt b/requirements_all.txt index 710a729685005..47e753be1f361 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -3001,7 +3001,7 @@ typedmonarchmoney==0.4.4 uasiren==0.0.1 # homeassistant.components.unifiprotect -uiprotect==7.16.0 +uiprotect==7.18.1 # homeassistant.components.landisgyr_heat_meter ultraheat-api==0.5.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c224132c2d7cb..d74eb8270b56e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2475,7 +2475,7 @@ typedmonarchmoney==0.4.4 uasiren==0.0.1 # homeassistant.components.unifiprotect -uiprotect==7.16.0 +uiprotect==7.18.1 # homeassistant.components.landisgyr_heat_meter ultraheat-api==0.5.7 From 2315bcbfe3cff9f838113351f98adf9b124a16b3 Mon Sep 17 00:00:00 2001 From: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:02:15 +0200 Subject: [PATCH 5/6] Set has_entity_name in Onkyo (#149223) --- homeassistant/components/onkyo/media_player.py | 1 + homeassistant/components/onkyo/quality_scale.yaml | 2 +- tests/components/onkyo/snapshots/test_media_player.ambr | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index 2965388236d68..05374bfe6cfc6 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -152,6 +152,7 @@ class OnkyoMediaPlayer(MediaPlayerEntity): """Onkyo Receiver Media Player (one per each zone).""" _attr_should_poll = False + _attr_has_entity_name = True _supports_volume: bool = False # None means no technical possibility of support diff --git a/homeassistant/components/onkyo/quality_scale.yaml b/homeassistant/components/onkyo/quality_scale.yaml index caf0d33fafc7c..1e8bf07e66ae5 100644 --- a/homeassistant/components/onkyo/quality_scale.yaml +++ b/homeassistant/components/onkyo/quality_scale.yaml @@ -22,7 +22,7 @@ rules: comment: | Currently we store created entities in hass.data. That should be removed in the future. entity-unique-id: done - has-entity-name: todo + has-entity-name: done runtime-data: done test-before-configure: done test-before-setup: done diff --git a/tests/components/onkyo/snapshots/test_media_player.ambr b/tests/components/onkyo/snapshots/test_media_player.ambr index 1504952a86d50..32717a8af4328 100644 --- a/tests/components/onkyo/snapshots/test_media_player.ambr +++ b/tests/components/onkyo/snapshots/test_media_player.ambr @@ -22,7 +22,7 @@ 'domain': 'media_player', 'entity_category': None, 'entity_id': 'media_player.tx_nr7100', - 'has_entity_name': False, + 'has_entity_name': True, 'hidden_by': None, 'icon': None, 'id': , @@ -98,7 +98,7 @@ 'domain': 'media_player', 'entity_category': None, 'entity_id': 'media_player.tx_nr7100_zone_2', - 'has_entity_name': False, + 'has_entity_name': True, 'hidden_by': None, 'icon': None, 'id': , @@ -162,7 +162,7 @@ 'domain': 'media_player', 'entity_category': None, 'entity_id': 'media_player.tx_nr7100_zone_3', - 'has_entity_name': False, + 'has_entity_name': True, 'hidden_by': None, 'icon': None, 'id': , From f5d68a4ea40e76358ea29d09839d093cdfd8c9c7 Mon Sep 17 00:00:00 2001 From: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:10:59 +0200 Subject: [PATCH 6/6] Simplify getting domains to resolve in bootstrap (#145829) --- homeassistant/bootstrap.py | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 493b9b1eab697..4e49d6cec7ee0 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -695,10 +695,10 @@ async def async_mount_local_lib_path(config_dir: str) -> str: def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]: """Get domains of components to set up.""" - # Filter out the repeating and common config section [homeassistant] - domains = { - domain for key in config if (domain := cv.domain_key(key)) != core.DOMAIN - } + # The common config section [homeassistant] could be filtered here, + # but that is not necessary, since it corresponds to the core integration, + # that is always unconditionally loaded. + domains = {cv.domain_key(key) for key in config} # Add config entry and default domains if not hass.config.recovery_mode: @@ -726,34 +726,28 @@ async def _async_resolve_domains_and_preload( together with all their dependencies. """ domains_to_setup = _get_domains(hass, config) - platform_integrations = conf_util.extract_platform_integrations( - config, BASE_PLATFORMS - ) - # Ensure base platforms that have platform integrations are added to `domains`, - # so they can be setup first instead of discovering them later when a config - # entry setup task notices that it's needed and there is already a long line - # to use the import executor. + + # Also process all base platforms since we do not require the manifest + # to list them as dependencies. + # We want to later avoid lock contention when multiple integrations try to load + # their manifests at once. # + # Additionally process integrations that are defined under base platforms + # to speed things up. # For example if we have # sensor: # - platform: template # - # `template` has to be loaded to validate the config for sensor - # so we want to start loading `sensor` as soon as we know - # it will be needed. The more platforms under `sensor:`, the longer + # `template` has to be loaded to validate the config for sensor. + # The more platforms under `sensor:`, the longer # it will take to finish setup for `sensor` because each of these # platforms has to be imported before we can validate the config. # # Thankfully we are migrating away from the platform pattern # so this will be less of a problem in the future. - domains_to_setup.update(platform_integrations) - - # Additionally process base platforms since we do not require the manifest - # to list them as dependencies. - # We want to later avoid lock contention when multiple integrations try to load - # their manifests at once. - # Also process integrations that are defined under base platforms - # to speed things up. + platform_integrations = conf_util.extract_platform_integrations( + config, BASE_PLATFORMS + ) additional_domains_to_process = { *BASE_PLATFORMS, *chain.from_iterable(platform_integrations.values()),