From 98e3b9962e2b25a589838aade700ea32286a35fc Mon Sep 17 00:00:00 2001 From: Paul Tarjan Date: Wed, 18 Mar 2026 13:21:38 -0600 Subject: [PATCH] Log Withings webhook URL warning only once (#164551) Co-authored-by: Claude Opus 4.6 --- homeassistant/components/withings/__init__.py | 21 +++++--- tests/components/withings/test_init.py | 54 +++++++++++++++++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/withings/__init__.py b/homeassistant/components/withings/__init__.py index bea4af3627a..31f6e61a463 100644 --- a/homeassistant/components/withings/__init__.py +++ b/homeassistant/components/withings/__init__.py @@ -214,6 +214,7 @@ class WithingsWebhookManager: """Manager that manages the Withings webhooks.""" _webhooks_registered = False + _webhook_url_invalid = False _register_lock = asyncio.Lock() def __init__(self, hass: HomeAssistant, entry: WithingsConfigEntry) -> None: @@ -260,16 +261,20 @@ class WithingsWebhookManager: ) url = URL(webhook_url) if url.scheme != "https": - LOGGER.warning( - "Webhook not registered - HTTPS is required. " - "See https://www.home-assistant.io/integrations/withings/#webhook-requirements" - ) + if not self._webhook_url_invalid: + LOGGER.warning( + "Webhook not registered - HTTPS is required. " + "See https://www.home-assistant.io/integrations/withings/#webhook-requirements" + ) + self._webhook_url_invalid = True return if url.port != 443: - LOGGER.warning( - "Webhook not registered - port 443 is required. " - "See https://www.home-assistant.io/integrations/withings/#webhook-requirements" - ) + if not self._webhook_url_invalid: + LOGGER.warning( + "Webhook not registered - port 443 is required. " + "See https://www.home-assistant.io/integrations/withings/#webhook-requirements" + ) + self._webhook_url_invalid = True return webhook_name = "Withings" diff --git a/tests/components/withings/test_init.py b/tests/components/withings/test_init.py index b5035df7afe..2496a02c40f 100644 --- a/tests/components/withings/test_init.py +++ b/tests/components/withings/test_init.py @@ -386,6 +386,60 @@ async def test_setup_no_webhook( mock_async_generate_url.assert_called_once() assert expected_message in caplog.text + assert caplog.text.count(expected_message) == 1 + + +@pytest.mark.parametrize( + ("url", "expected_message"), + [ + ("http://example.com", "HTTPS is required"), + ("https://example.com:444", "port 443 is required"), + ], +) +async def test_setup_no_webhook_logged_once( + hass: HomeAssistant, + webhook_config_entry: MockConfigEntry, + withings: AsyncMock, + caplog: pytest.LogCaptureFixture, + freezer: FrozenDateTimeFactory, + url: str, + expected_message: str, +) -> None: + """Test webhook warning is only logged once on repeated retries.""" + await mock_cloud(hass) + await hass.async_block_till_done() + + with ( + patch("homeassistant.components.cloud.async_is_logged_in", return_value=True), + patch.object(cloud, "async_is_connected", return_value=True), + patch.object(cloud, "async_active_subscription", return_value=True), + patch( + "homeassistant.components.cloud.async_create_cloudhook", + return_value=url, + ), + patch( + "homeassistant.components.withings.async_get_config_entry_implementation", + ), + patch( + "homeassistant.components.cloud.async_delete_cloudhook", + ), + patch("homeassistant.components.withings.webhook_generate_url"), + ): + await setup_integration(hass, webhook_config_entry) + await prepare_webhook_setup(hass, freezer) + await hass.async_block_till_done() + + assert caplog.text.count(expected_message) == 1 + + # Simulate cloud disconnect then reconnect, triggering register_webhook again + async_mock_cloud_connection_status(hass, False) + await hass.async_block_till_done() + + async_mock_cloud_connection_status(hass, True) + await hass.async_block_till_done() + + # Warning should still only be logged once + assert caplog.text.count(expected_message) == 1 async def test_cloud_disconnect(