From 048fbba36d1727da62ae7c656ea929d6255fb70c Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Sat, 21 Feb 2026 08:54:49 +0100 Subject: [PATCH 1/4] Replace "add-on" with "app" in `matter` (#163695) --- homeassistant/components/matter/strings.json | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/matter/strings.json b/homeassistant/components/matter/strings.json index 8aaa64c2390507..42d8d1cc0f05ca 100644 --- a/homeassistant/components/matter/strings.json +++ b/homeassistant/components/matter/strings.json @@ -1,14 +1,14 @@ { "config": { "abort": { - "addon_get_discovery_info_failed": "Failed to get Matter Server add-on discovery info.", - "addon_info_failed": "Failed to get Matter Server add-on info.", - "addon_install_failed": "Failed to install the Matter Server add-on.", - "addon_start_failed": "Failed to start the Matter Server add-on.", + "addon_get_discovery_info_failed": "Failed to get Matter Server app discovery info.", + "addon_info_failed": "Failed to get Matter Server app info.", + "addon_install_failed": "Failed to install the Matter Server app.", + "addon_start_failed": "Failed to start the Matter Server app.", "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "not_matter_addon": "Discovered add-on is not the official Matter Server add-on.", + "not_matter_addon": "Discovered app is not the official Matter Server app.", "reconfiguration_successful": "Successfully reconfigured the Matter integration." }, "error": { @@ -18,15 +18,15 @@ }, "flow_title": "{name}", "progress": { - "install_addon": "Please wait while the Matter Server add-on installation finishes. This can take several minutes.", - "start_addon": "Please wait while the Matter Server add-on starts. This add-on is what powers Matter in Home Assistant. This may take some seconds." + "install_addon": "Please wait while the Matter Server app installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Matter Server app starts. This app is what powers Matter in Home Assistant. This may take some seconds." }, "step": { "hassio_confirm": { - "title": "Set up the Matter integration with the Matter Server add-on" + "title": "Set up the Matter integration with the Matter Server app" }, "install_addon": { - "title": "The add-on installation has started" + "title": "The app installation has started" }, "manual": { "data": { @@ -35,13 +35,13 @@ }, "on_supervisor": { "data": { - "use_addon": "Use the official Matter Server Supervisor add-on" + "use_addon": "Use the official Matter Server Supervisor app" }, - "description": "Do you want to use the official Matter Server Supervisor add-on?\n\nIf you are already running the Matter Server in another add-on, in a custom container, natively etc., then do not select this option.", + "description": "Do you want to use the official Matter Server Supervisor app?\n\nIf you are already running the Matter Server in another app, in a custom container, natively etc., then do not select this option.", "title": "Select connection method" }, "start_addon": { - "title": "Starting add-on." + "title": "Starting app." } } }, From 11f0cd690ea81e0c324e83415bcb42d2af8a480a Mon Sep 17 00:00:00 2001 From: Manu <4445816+tr4nt0r@users.noreply.github.com> Date: Sat, 21 Feb 2026 09:16:14 +0100 Subject: [PATCH 2/4] Bump aiontfy to 0.8.0 (#163693) --- homeassistant/components/ntfy/manifest.json | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/ntfy/manifest.json b/homeassistant/components/ntfy/manifest.json index 1be3c30ba49e2d..fd102dabca2a6a 100644 --- a/homeassistant/components/ntfy/manifest.json +++ b/homeassistant/components/ntfy/manifest.json @@ -6,7 +6,7 @@ "documentation": "https://www.home-assistant.io/integrations/ntfy", "integration_type": "service", "iot_class": "cloud_push", - "loggers": ["aionfty"], + "loggers": ["aiontfy"], "quality_scale": "platinum", - "requirements": ["aiontfy==0.7.0"] + "requirements": ["aiontfy==0.8.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 43c4e2e205fc9a..46a747ee11f216 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -339,7 +339,7 @@ aionanoleaf2==1.0.2 aionotion==2024.03.0 # homeassistant.components.ntfy -aiontfy==0.7.0 +aiontfy==0.8.0 # homeassistant.components.nut aionut==4.3.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e09b56cdb585a9..08c3a22f23ef57 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -324,7 +324,7 @@ aionanoleaf2==1.0.2 aionotion==2024.03.0 # homeassistant.components.ntfy -aiontfy==0.7.0 +aiontfy==0.8.0 # homeassistant.components.nut aionut==4.3.4 From c3b0f7ba55813bdb0b5d14c767bbc26ff2b954a5 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Sat, 21 Feb 2026 01:17:59 -0700 Subject: [PATCH 3/4] Bump pylitterbot to 2025.1.0 (#163691) --- homeassistant/components/litterrobot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index 99e30167cc490b..aeeb963ff5410e 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -13,5 +13,5 @@ "iot_class": "cloud_push", "loggers": ["pylitterbot"], "quality_scale": "bronze", - "requirements": ["pylitterbot==2025.0.0"] + "requirements": ["pylitterbot==2025.1.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 46a747ee11f216..26ebb1dabfa98e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2224,7 +2224,7 @@ pyliebherrhomeapi==0.3.0 pylitejet==0.6.3 # homeassistant.components.litterrobot -pylitterbot==2025.0.0 +pylitterbot==2025.1.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.26.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 08c3a22f23ef57..a9ce5898144584 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1895,7 +1895,7 @@ pyliebherrhomeapi==0.3.0 pylitejet==0.6.3 # homeassistant.components.litterrobot -pylitterbot==2025.0.0 +pylitterbot==2025.1.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.26.0 From 047d5735d88ce8a8088fa8c40c504a7e86cc37d4 Mon Sep 17 00:00:00 2001 From: hanwg Date: Sat, 21 Feb 2026 16:19:06 +0800 Subject: [PATCH 4/4] Cleanup error handling for Telegram bot (#163689) --- .../components/telegram_bot/__init__.py | 4 +- homeassistant/components/telegram_bot/bot.py | 91 +++++-------------- .../telegram_bot/test_telegram_bot.py | 14 +-- 3 files changed, 34 insertions(+), 75 deletions(-) diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index 442f2c5bb66adb..83e5ae37d71efb 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -468,7 +468,7 @@ async def _async_send_telegram_message(service: ServiceCall) -> ServiceResponse: targets = _build_targets(service) service_responses: JsonValueType = [] - errors: list[tuple[HomeAssistantError, str]] = [] + errors: list[tuple[Exception, str]] = [] # invoke the service for each target for target_config_entry, target_chat_id, target_notify_entity_id in targets: @@ -495,7 +495,7 @@ async def _async_send_telegram_message(service: ServiceCall) -> ServiceResponse: assert isinstance(service_responses, list) service_responses.extend(formatted_responses) - except HomeAssistantError as ex: + except (HomeAssistantError, TelegramError) as ex: target = target_notify_entity_id or str(target_chat_id) errors.append((ex, target)) diff --git a/homeassistant/components/telegram_bot/bot.py b/homeassistant/components/telegram_bot/bot.py index e4aae644b1a825..cd6f9c825451d9 100644 --- a/homeassistant/components/telegram_bot/bot.py +++ b/homeassistant/components/telegram_bot/bot.py @@ -433,7 +433,6 @@ def _make_row_inline_keyboard(row_keyboard: Any) -> list[InlineKeyboardButton]: async def _send_msgs( self, func_send: Callable, - msg_error: str, message_tag: str | None, *args_msg: Any, context: Context | None = None, @@ -459,12 +458,10 @@ async def _send_msgs( response: Message = await self._send_msg( func_send, - msg_error, message_tag, chat_id, *args_msg, context=context, - suppress_error=len(chat_ids) > 1, **kwargs_msg, ) if response: @@ -475,58 +472,39 @@ async def _send_msgs( async def _send_msg( self, func_send: Callable, - msg_error: str, message_tag: str | None, *args_msg: Any, context: Context | None = None, - suppress_error: bool = False, **kwargs_msg: Any, ) -> Any: """Send one message.""" - try: - out = await func_send(*args_msg, **kwargs_msg) - if isinstance(out, Message): - chat_id = out.chat_id - message_id = out.message_id - self._last_message_id[chat_id] = message_id - _LOGGER.debug( - "Last message ID: %s (from chat_id %s)", - self._last_message_id, - chat_id, - ) + out = await func_send(*args_msg, **kwargs_msg) + if isinstance(out, Message): + chat_id = out.chat_id + message_id = out.message_id + self._last_message_id[chat_id] = message_id + _LOGGER.debug( + "Last message ID: %s (from chat_id %s)", + self._last_message_id, + chat_id, + ) - event_data: dict[str, Any] = { - ATTR_CHAT_ID: chat_id, - ATTR_MESSAGEID: message_id, - } - if message_tag is not None: - event_data[ATTR_MESSAGE_TAG] = message_tag - if kwargs_msg.get(ATTR_MESSAGE_THREAD_ID) is not None: - event_data[ATTR_MESSAGE_THREAD_ID] = kwargs_msg[ - ATTR_MESSAGE_THREAD_ID - ] - - event_data["bot"] = _get_bot_info(self.bot, self.config) - - self.hass.bus.async_fire( - EVENT_TELEGRAM_SENT, event_data, context=context - ) - async_dispatcher_send( - self.hass, signal(self.bot), EVENT_TELEGRAM_SENT, event_data - ) - except TelegramError as exc: - if not suppress_error: - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="action_failed", - translation_placeholders={"error": str(exc)}, - ) from exc + event_data: dict[str, Any] = { + ATTR_CHAT_ID: chat_id, + ATTR_MESSAGEID: message_id, + } + if message_tag is not None: + event_data[ATTR_MESSAGE_TAG] = message_tag + if kwargs_msg.get(ATTR_MESSAGE_THREAD_ID) is not None: + event_data[ATTR_MESSAGE_THREAD_ID] = kwargs_msg[ATTR_MESSAGE_THREAD_ID] + + event_data["bot"] = _get_bot_info(self.bot, self.config) - _LOGGER.error( - "%s: %s. Args: %s, kwargs: %s", msg_error, exc, args_msg, kwargs_msg + self.hass.bus.async_fire(EVENT_TELEGRAM_SENT, event_data, context=context) + async_dispatcher_send( + self.hass, signal(self.bot), EVENT_TELEGRAM_SENT, event_data ) - return None return out async def send_message( @@ -542,7 +520,6 @@ async def send_message( params = self._get_msg_kwargs(kwargs) return await self._send_msgs( self.bot.send_message, - "Error sending message", params[ATTR_MESSAGE_TAG], text, chat_id=chat_id, @@ -567,7 +544,6 @@ async def delete_message( _LOGGER.debug("Delete message %s in chat ID %s", message_id, chat_id) deleted: bool = await self._send_msg( self.bot.delete_message, - "Error deleting message", None, chat_id, message_id, @@ -644,7 +620,6 @@ async def edit_message_media( return await self._send_msg( self.bot.edit_message_media, - "Error editing message media", params[ATTR_MESSAGE_TAG], media=media, chat_id=chat_id, @@ -678,7 +653,6 @@ async def edit_message( _LOGGER.debug("Editing message with ID %s", message_id or inline_message_id) return await self._send_msg( self.bot.edit_message_text, - "Error editing text message", params[ATTR_MESSAGE_TAG], text, chat_id=chat_id, @@ -693,7 +667,6 @@ async def edit_message( if type_edit == SERVICE_EDIT_CAPTION: return await self._send_msg( self.bot.edit_message_caption, - "Error editing message attributes", params[ATTR_MESSAGE_TAG], chat_id=chat_id, message_id=message_id, @@ -707,7 +680,6 @@ async def edit_message( return await self._send_msg( self.bot.edit_message_reply_markup, - "Error editing message attributes", params[ATTR_MESSAGE_TAG], chat_id=chat_id, message_id=message_id, @@ -735,7 +707,6 @@ async def answer_callback_query( ) await self._send_msg( self.bot.answer_callback_query, - "Error sending answer callback query", params[ATTR_MESSAGE_TAG], callback_query_id, text=message, @@ -756,7 +727,6 @@ async def send_chat_action( _LOGGER.debug("Send action %s in chat ID %s", chat_action, chat_id) is_successful = await self._send_msg( self.bot.send_chat_action, - "Error sending action", None, chat_id=chat_id, action=chat_action, @@ -791,7 +761,6 @@ async def send_file( if file_type == SERVICE_SEND_PHOTO: return await self._send_msgs( self.bot.send_photo, - "Error sending photo", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], photo=file_content, @@ -808,7 +777,6 @@ async def send_file( if file_type == SERVICE_SEND_STICKER: return await self._send_msgs( self.bot.send_sticker, - "Error sending sticker", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], sticker=file_content, @@ -823,7 +791,6 @@ async def send_file( if file_type == SERVICE_SEND_VIDEO: return await self._send_msgs( self.bot.send_video, - "Error sending video", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], video=file_content, @@ -840,7 +807,6 @@ async def send_file( if file_type == SERVICE_SEND_DOCUMENT: return await self._send_msgs( self.bot.send_document, - "Error sending document", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], document=file_content, @@ -857,7 +823,6 @@ async def send_file( if file_type == SERVICE_SEND_VOICE: return await self._send_msgs( self.bot.send_voice, - "Error sending voice", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], voice=file_content, @@ -873,7 +838,6 @@ async def send_file( # SERVICE_SEND_ANIMATION return await self._send_msgs( self.bot.send_animation, - "Error sending animation", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], animation=file_content, @@ -899,7 +863,6 @@ async def send_sticker( if stickerid: return await self._send_msgs( self.bot.send_sticker, - "Error sending sticker", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], sticker=stickerid, @@ -925,7 +888,6 @@ async def send_location( params = self._get_msg_kwargs(kwargs) return await self._send_msgs( self.bot.send_location, - "Error sending location", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], latitude=latitude, @@ -951,7 +913,6 @@ async def send_poll( openperiod = kwargs.get(ATTR_OPEN_PERIOD) return await self._send_msgs( self.bot.send_poll, - "Error sending poll", params[ATTR_MESSAGE_TAG], chat_id=kwargs[ATTR_CHAT_ID], question=question, @@ -974,9 +935,7 @@ async def leave_chat( ) -> Any: """Remove bot from chat.""" _LOGGER.debug("Leave from chat ID %s", chat_id) - return await self._send_msg( - self.bot.leave_chat, "Error leaving chat", None, chat_id, context=context - ) + return await self._send_msg(self.bot.leave_chat, None, chat_id, context=context) async def set_message_reaction( self, @@ -1000,7 +959,6 @@ async def set_message_reaction( await self._send_msg( self.bot.set_message_reaction, - "Error setting message reaction", params[ATTR_MESSAGE_TAG], chat_id, message_id, @@ -1023,7 +981,6 @@ async def download_file( directory_path = self.hass.config.path(DOMAIN) file: File = await self._send_msg( self.bot.get_file, - "Error getting file", None, file_id=file_id, context=context, diff --git a/tests/components/telegram_bot/test_telegram_bot.py b/tests/components/telegram_bot/test_telegram_bot.py index 7ac6ecd02a092f..a0a149713c9973 100644 --- a/tests/components/telegram_bot/test_telegram_bot.py +++ b/tests/components/telegram_bot/test_telegram_bot.py @@ -353,7 +353,7 @@ async def test_send_sticker_partial_error( assert mock_send_sticker.call_count == 2 assert err.value.translation_key == "multiple_errors" assert err.value.translation_placeholders == { - "errors": "`entity_id` notify.mock_title_mock_chat_1: Action failed. mock network error\n`entity_id` notify.mock_title_mock_chat_2: Action failed. mock network error" + "errors": "`entity_id` notify.mock_title_mock_chat_1: mock network error\n`entity_id` notify.mock_title_mock_chat_2: mock network error" } @@ -364,7 +364,7 @@ async def test_send_sticker_error(hass: HomeAssistant, webhook_bot) -> None: ) as mock_bot: mock_bot.side_effect = NetworkError("mock network error") - with pytest.raises(HomeAssistantError) as err: + with pytest.raises(TelegramError) as err: await hass.services.async_call( DOMAIN, SERVICE_SEND_STICKER, @@ -377,8 +377,8 @@ async def test_send_sticker_error(hass: HomeAssistant, webhook_bot) -> None: await hass.async_block_till_done() mock_bot.assert_called_once() - assert err.value.translation_domain == DOMAIN - assert err.value.translation_key == "action_failed" + assert err.typename == "NetworkError" + assert err.value.message == "mock network error" async def test_send_message_with_invalid_inline_keyboard( @@ -2264,7 +2264,7 @@ async def test_download_file_when_bot_failed_to_get_file( "homeassistant.components.telegram_bot.bot.Bot.get_file", AsyncMock(side_effect=TelegramError("failed to get file")), ), - pytest.raises(HomeAssistantError) as err, + pytest.raises(TelegramError) as err, ): await hass.services.async_call( DOMAIN, @@ -2273,7 +2273,9 @@ async def test_download_file_when_bot_failed_to_get_file( blocking=True, ) await hass.async_block_till_done() - assert err.value.translation_key == "action_failed" + + assert err.typename == "TelegramError" + assert err.value.message == "failed to get file" async def test_download_file_when_empty_file_path(