Improve handling of cloud hook registration (#65664)

Signed-off-by: cgtobi <cgtobi@gmail.com>
This commit is contained in:
Tobias Sauerwein 2022-02-16 16:42:45 +01:00 committed by GitHub
parent de2734bd0e
commit 3d1cad9f67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 50 deletions

View File

@ -1,6 +1,8 @@
"""Component to integrate the Home Assistant cloud."""
from __future__ import annotations
import asyncio
from collections.abc import Callable
from collections.abc import Awaitable, Callable
from enum import Enum
from hass_nabucasa import Cloud
@ -152,7 +154,8 @@ def async_is_connected(hass: HomeAssistant) -> bool:
@callback
def async_listen_connection_change(
hass: HomeAssistant, target: Callable[[CloudConnectionState], None]
hass: HomeAssistant,
target: Callable[[CloudConnectionState], Awaitable[None] | None],
) -> Callable[[], None]:
"""Notify on connection state changes."""
return async_dispatcher_connect(hass, SIGNAL_CLOUD_CONNECTION_STATE, target)

View File

@ -31,10 +31,7 @@ from homeassistant.helpers import (
config_entry_oauth2_flow,
config_validation as cv,
)
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.typing import ConfigType
@ -54,7 +51,6 @@ from .const import (
OAUTH2_AUTHORIZE,
OAUTH2_TOKEN,
PLATFORMS,
WEBHOOK_ACTIVATION,
WEBHOOK_DEACTIVATION,
WEBHOOK_PUSH_TYPE,
)
@ -150,8 +146,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
_webhook_retries = 0
async def unregister_webhook(
call_or_event_or_dt: ServiceCall | Event | datetime | None,
) -> None:
@ -171,11 +165,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"No webhook to be dropped for %s", entry.data[CONF_WEBHOOK_ID]
)
nonlocal _webhook_retries
if _webhook_retries < MAX_WEBHOOK_RETRIES:
_webhook_retries += 1
async_call_later(hass, 30, register_webhook)
async def register_webhook(
call_or_event_or_dt: ServiceCall | Event | datetime | None,
) -> None:
@ -184,14 +173,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.config_entries.async_update_entry(entry, data=data)
if cloud.async_active_subscription(hass):
if CONF_CLOUDHOOK_URL not in entry.data:
webhook_url = await cloud.async_create_cloudhook(
hass, entry.data[CONF_WEBHOOK_ID]
)
data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url}
hass.config_entries.async_update_entry(entry, data=data)
else:
webhook_url = entry.data[CONF_CLOUDHOOK_URL]
webhook_url = await async_cloudhook_generate_url(hass, entry)
else:
webhook_url = webhook_generate_url(hass, entry.data[CONF_WEBHOOK_ID])
@ -204,32 +186,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
return
webhook_register(
hass,
DOMAIN,
"Netatmo",
entry.data[CONF_WEBHOOK_ID],
async_handle_webhook,
)
try:
webhook_register(
hass,
DOMAIN,
"Netatmo",
entry.data[CONF_WEBHOOK_ID],
async_handle_webhook,
)
async def handle_event(event: dict) -> None:
"""Handle webhook events."""
if event["data"][WEBHOOK_PUSH_TYPE] == WEBHOOK_ACTIVATION:
if activation_listener is not None:
activation_listener()
if activation_timeout is not None:
activation_timeout()
activation_listener = async_dispatcher_connect(
hass,
f"signal-{DOMAIN}-webhook-None",
handle_event,
)
activation_timeout = async_call_later(hass, 30, unregister_webhook)
await hass.data[DOMAIN][entry.entry_id][AUTH].async_addwebhook(webhook_url)
_LOGGER.info("Register Netatmo webhook: %s", webhook_url)
except pyatmo.ApiError as err:
@ -239,10 +204,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
)
if hass.state == CoreState.running:
await register_webhook(None)
async def manage_cloudhook(state: cloud.CloudConnectionState) -> None:
if state is cloud.CloudConnectionState.CLOUD_CONNECTED:
await register_webhook(None)
if state is cloud.CloudConnectionState.CLOUD_DISCONNECTED:
await unregister_webhook(None)
async_call_later(hass, 30, register_webhook)
if cloud.async_active_subscription(hass):
if cloud.async_is_connected(hass):
await register_webhook(None)
cloud.async_listen_connection_change(hass, manage_cloudhook)
else:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, register_webhook)
if hass.state == CoreState.running:
await register_webhook(None)
else:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, register_webhook)
hass.services.async_register(DOMAIN, "register_webhook", register_webhook)
hass.services.async_register(DOMAIN, "unregister_webhook", unregister_webhook)
@ -252,6 +231,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True
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:
webhook_url = await cloud.async_create_cloudhook(
hass, entry.data[CONF_WEBHOOK_ID]
)
data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url}
hass.config_entries.async_update_entry(entry, data=data)
return webhook_url
return str(entry.data[CONF_CLOUDHOOK_URL])
async def async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle signals of config entry being updated."""
async_dispatcher_send(hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}")

View File

@ -182,6 +182,8 @@ async def test_setup_with_cloud(hass, config_entry):
with patch(
"homeassistant.components.cloud.async_is_logged_in", return_value=True
), patch(
"homeassistant.components.cloud.async_is_connected", return_value=True
), patch(
"homeassistant.components.cloud.async_active_subscription", return_value=True
), patch(
@ -203,6 +205,7 @@ async def test_setup_with_cloud(hass, config_entry):
hass, "netatmo", {"netatmo": {"client_id": "123", "client_secret": "abc"}}
)
assert hass.components.cloud.async_active_subscription() is True
assert hass.components.cloud.async_is_connected() is True
fake_create_cloudhook.assert_called_once()
assert (
@ -245,6 +248,8 @@ async def test_setup_with_cloudhook(hass):
with patch(
"homeassistant.components.cloud.async_is_logged_in", return_value=True
), patch(
"homeassistant.components.cloud.async_is_connected", return_value=True
), patch(
"homeassistant.components.cloud.async_active_subscription", return_value=True
), patch(