mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
Subscribe to Withings webhooks outside of coordinator (#101759)
* Subscribe to Withings webhooks outside of coordinator * Subscribe to Withings webhooks outside of coordinator * Update homeassistant/components/withings/__init__.py Co-authored-by: J. Nick Koston <nick@koston.org> * Update homeassistant/components/withings/__init__.py Co-authored-by: J. Nick Koston <nick@koston.org> --------- Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
9b785ef766
commit
ffb752c804
@ -4,8 +4,10 @@ For more details about this platform, please refer to the documentation at
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
import contextlib
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from aiohttp.hdrs import METH_HEAD, METH_POST
|
||||
@ -78,6 +80,8 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
SUBSCRIBE_DELAY = timedelta(seconds=5)
|
||||
UNSUBSCRIBE_DELAY = timedelta(seconds=1)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
@ -141,7 +145,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
) -> None:
|
||||
LOGGER.debug("Unregister Withings webhook (%s)", entry.data[CONF_WEBHOOK_ID])
|
||||
webhook_unregister(hass, entry.data[CONF_WEBHOOK_ID])
|
||||
await hass.data[DOMAIN][entry.entry_id].async_unsubscribe_webhooks()
|
||||
await async_unsubscribe_webhooks(client)
|
||||
coordinator.webhook_subscription_listener(False)
|
||||
|
||||
async def register_webhook(
|
||||
_: Any,
|
||||
@ -170,7 +175,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
get_webhook_handler(coordinator),
|
||||
)
|
||||
|
||||
await hass.data[DOMAIN][entry.entry_id].async_subscribe_webhooks(webhook_url)
|
||||
await async_subscribe_webhooks(client, webhook_url)
|
||||
coordinator.webhook_subscription_listener(True)
|
||||
LOGGER.debug("Register Withings webhook: %s", webhook_url)
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
|
||||
@ -213,6 +219,53 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_subscribe_webhooks(
|
||||
client: ConfigEntryWithingsApi, webhook_url: str
|
||||
) -> None:
|
||||
"""Subscribe to Withings webhooks."""
|
||||
await async_unsubscribe_webhooks(client)
|
||||
|
||||
notification_to_subscribe = {
|
||||
NotifyAppli.WEIGHT,
|
||||
NotifyAppli.CIRCULATORY,
|
||||
NotifyAppli.ACTIVITY,
|
||||
NotifyAppli.SLEEP,
|
||||
NotifyAppli.BED_IN,
|
||||
NotifyAppli.BED_OUT,
|
||||
}
|
||||
|
||||
for notification in notification_to_subscribe:
|
||||
LOGGER.debug(
|
||||
"Subscribing %s for %s in %s seconds",
|
||||
webhook_url,
|
||||
notification,
|
||||
SUBSCRIBE_DELAY.total_seconds(),
|
||||
)
|
||||
# Withings will HTTP HEAD the callback_url and needs some downtime
|
||||
# between each call or there is a higher chance of failure.
|
||||
await asyncio.sleep(SUBSCRIBE_DELAY.total_seconds())
|
||||
await client.async_notify_subscribe(webhook_url, notification)
|
||||
|
||||
|
||||
async def async_unsubscribe_webhooks(client: ConfigEntryWithingsApi) -> None:
|
||||
"""Unsubscribe to all Withings webhooks."""
|
||||
current_webhooks = await client.async_notify_list()
|
||||
|
||||
for webhook_configuration in current_webhooks.profiles:
|
||||
LOGGER.debug(
|
||||
"Unsubscribing %s for %s in %s seconds",
|
||||
webhook_configuration.callbackurl,
|
||||
webhook_configuration.appli,
|
||||
UNSUBSCRIBE_DELAY.total_seconds(),
|
||||
)
|
||||
# Quick calls to Withings can result in the service returning errors.
|
||||
# Give them some time to cool down.
|
||||
await asyncio.sleep(UNSUBSCRIBE_DELAY.total_seconds())
|
||||
await client.async_notify_revoke(
|
||||
webhook_configuration.callbackurl, webhook_configuration.appli
|
||||
)
|
||||
|
||||
|
||||
async def async_cloudhook_generate_url(hass: HomeAssistant, entry: ConfigEntry) -> str:
|
||||
"""Generate the full URL for a webhook_id."""
|
||||
if CONF_CLOUDHOOK_URL not in entry.data:
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""Withings coordinator."""
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
@ -24,9 +23,6 @@ from homeassistant.util import dt as dt_util
|
||||
from .api import ConfigEntryWithingsApi
|
||||
from .const import LOGGER, Measurement
|
||||
|
||||
SUBSCRIBE_DELAY = timedelta(seconds=5)
|
||||
UNSUBSCRIBE_DELAY = timedelta(seconds=1)
|
||||
|
||||
WITHINGS_MEASURE_TYPE_MAP: dict[
|
||||
NotifyAppli | GetSleepSummaryField | MeasureType, Measurement
|
||||
] = {
|
||||
@ -84,55 +80,12 @@ class WithingsDataUpdateCoordinator(DataUpdateCoordinator[dict[Measurement, Any]
|
||||
super().__init__(hass, LOGGER, name="Withings", update_interval=UPDATE_INTERVAL)
|
||||
self._client = client
|
||||
|
||||
async def async_subscribe_webhooks(self, webhook_url: str) -> None:
|
||||
"""Subscribe to webhooks."""
|
||||
await self.async_unsubscribe_webhooks()
|
||||
|
||||
current_webhooks = await self._client.async_notify_list()
|
||||
|
||||
subscribed_notifications = frozenset(
|
||||
profile.appli
|
||||
for profile in current_webhooks.profiles
|
||||
if profile.callbackurl == webhook_url
|
||||
)
|
||||
|
||||
notification_to_subscribe = (
|
||||
set(NotifyAppli)
|
||||
- subscribed_notifications
|
||||
- {NotifyAppli.USER, NotifyAppli.UNKNOWN}
|
||||
)
|
||||
|
||||
for notification in notification_to_subscribe:
|
||||
LOGGER.debug(
|
||||
"Subscribing %s for %s in %s seconds",
|
||||
webhook_url,
|
||||
notification,
|
||||
SUBSCRIBE_DELAY.total_seconds(),
|
||||
)
|
||||
# Withings will HTTP HEAD the callback_url and needs some downtime
|
||||
# between each call or there is a higher chance of failure.
|
||||
await asyncio.sleep(SUBSCRIBE_DELAY.total_seconds())
|
||||
await self._client.async_notify_subscribe(webhook_url, notification)
|
||||
self.update_interval = None
|
||||
|
||||
async def async_unsubscribe_webhooks(self) -> None:
|
||||
"""Unsubscribe to webhooks."""
|
||||
current_webhooks = await self._client.async_notify_list()
|
||||
|
||||
for webhook_configuration in current_webhooks.profiles:
|
||||
LOGGER.debug(
|
||||
"Unsubscribing %s for %s in %s seconds",
|
||||
webhook_configuration.callbackurl,
|
||||
webhook_configuration.appli,
|
||||
UNSUBSCRIBE_DELAY.total_seconds(),
|
||||
)
|
||||
# Quick calls to Withings can result in the service returning errors.
|
||||
# Give them some time to cool down.
|
||||
await asyncio.sleep(UNSUBSCRIBE_DELAY.total_seconds())
|
||||
await self._client.async_notify_revoke(
|
||||
webhook_configuration.callbackurl, webhook_configuration.appli
|
||||
)
|
||||
self.update_interval = UPDATE_INTERVAL
|
||||
def webhook_subscription_listener(self, connected: bool) -> None:
|
||||
"""Call when webhook status changed."""
|
||||
if connected:
|
||||
self.update_interval = None
|
||||
else:
|
||||
self.update_interval = UPDATE_INTERVAL
|
||||
|
||||
async def _async_update_data(self) -> dict[Measurement, Any]:
|
||||
try:
|
||||
|
@ -160,10 +160,10 @@ def disable_webhook_delay():
|
||||
|
||||
mock = AsyncMock()
|
||||
with patch(
|
||||
"homeassistant.components.withings.coordinator.SUBSCRIBE_DELAY",
|
||||
"homeassistant.components.withings.SUBSCRIBE_DELAY",
|
||||
timedelta(seconds=0),
|
||||
), patch(
|
||||
"homeassistant.components.withings.coordinator.UNSUBSCRIBE_DELAY",
|
||||
"homeassistant.components.withings.UNSUBSCRIBE_DELAY",
|
||||
timedelta(seconds=0),
|
||||
):
|
||||
yield mock
|
||||
|
@ -126,7 +126,7 @@ async def test_data_manager_webhook_subscription(
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=1))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert withings.async_notify_subscribe.call_count == 4
|
||||
assert withings.async_notify_subscribe.call_count == 6
|
||||
|
||||
webhook_url = "https://example.local:8123/api/webhook/55a7335ea8dee830eed4ef8f84cda8f6d80b83af0847dc74032e86120bffed5e"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user