mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 11:47:06 +00:00
Improve handling of cloud hook registration (#65664)
Signed-off-by: cgtobi <cgtobi@gmail.com>
This commit is contained in:
parent
de2734bd0e
commit
3d1cad9f67
@ -1,6 +1,8 @@
|
|||||||
"""Component to integrate the Home Assistant cloud."""
|
"""Component to integrate the Home Assistant cloud."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Awaitable, Callable
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from hass_nabucasa import Cloud
|
from hass_nabucasa import Cloud
|
||||||
@ -152,7 +154,8 @@ def async_is_connected(hass: HomeAssistant) -> bool:
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_listen_connection_change(
|
def async_listen_connection_change(
|
||||||
hass: HomeAssistant, target: Callable[[CloudConnectionState], None]
|
hass: HomeAssistant,
|
||||||
|
target: Callable[[CloudConnectionState], Awaitable[None] | None],
|
||||||
) -> Callable[[], None]:
|
) -> Callable[[], None]:
|
||||||
"""Notify on connection state changes."""
|
"""Notify on connection state changes."""
|
||||||
return async_dispatcher_connect(hass, SIGNAL_CLOUD_CONNECTION_STATE, target)
|
return async_dispatcher_connect(hass, SIGNAL_CLOUD_CONNECTION_STATE, target)
|
||||||
|
@ -31,10 +31,7 @@ from homeassistant.helpers import (
|
|||||||
config_entry_oauth2_flow,
|
config_entry_oauth2_flow,
|
||||||
config_validation as cv,
|
config_validation as cv,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
async_dispatcher_connect,
|
|
||||||
async_dispatcher_send,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.event import async_call_later
|
from homeassistant.helpers.event import async_call_later
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
@ -54,7 +51,6 @@ from .const import (
|
|||||||
OAUTH2_AUTHORIZE,
|
OAUTH2_AUTHORIZE,
|
||||||
OAUTH2_TOKEN,
|
OAUTH2_TOKEN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
WEBHOOK_ACTIVATION,
|
|
||||||
WEBHOOK_DEACTIVATION,
|
WEBHOOK_DEACTIVATION,
|
||||||
WEBHOOK_PUSH_TYPE,
|
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)
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
_webhook_retries = 0
|
|
||||||
|
|
||||||
async def unregister_webhook(
|
async def unregister_webhook(
|
||||||
call_or_event_or_dt: ServiceCall | Event | datetime | None,
|
call_or_event_or_dt: ServiceCall | Event | datetime | None,
|
||||||
) -> 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]
|
"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(
|
async def register_webhook(
|
||||||
call_or_event_or_dt: ServiceCall | Event | datetime | None,
|
call_or_event_or_dt: ServiceCall | Event | datetime | None,
|
||||||
) -> 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)
|
hass.config_entries.async_update_entry(entry, data=data)
|
||||||
|
|
||||||
if cloud.async_active_subscription(hass):
|
if cloud.async_active_subscription(hass):
|
||||||
if CONF_CLOUDHOOK_URL not in entry.data:
|
webhook_url = await async_cloudhook_generate_url(hass, entry)
|
||||||
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]
|
|
||||||
else:
|
else:
|
||||||
webhook_url = webhook_generate_url(hass, entry.data[CONF_WEBHOOK_ID])
|
webhook_url = webhook_generate_url(hass, entry.data[CONF_WEBHOOK_ID])
|
||||||
|
|
||||||
@ -204,7 +186,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
webhook_register(
|
webhook_register(
|
||||||
hass,
|
hass,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -213,23 +194,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
async_handle_webhook,
|
async_handle_webhook,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def handle_event(event: dict) -> None:
|
try:
|
||||||
"""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)
|
await hass.data[DOMAIN][entry.entry_id][AUTH].async_addwebhook(webhook_url)
|
||||||
_LOGGER.info("Register Netatmo webhook: %s", webhook_url)
|
_LOGGER.info("Register Netatmo webhook: %s", webhook_url)
|
||||||
except pyatmo.ApiError as err:
|
except pyatmo.ApiError as err:
|
||||||
@ -239,6 +204,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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:
|
||||||
if hass.state == CoreState.running:
|
if hass.state == CoreState.running:
|
||||||
await register_webhook(None)
|
await register_webhook(None)
|
||||||
else:
|
else:
|
||||||
@ -252,6 +231,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
return True
|
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:
|
async def async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
"""Handle signals of config entry being updated."""
|
"""Handle signals of config entry being updated."""
|
||||||
async_dispatcher_send(hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}")
|
async_dispatcher_send(hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}")
|
||||||
|
@ -182,6 +182,8 @@ async def test_setup_with_cloud(hass, config_entry):
|
|||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.cloud.async_is_logged_in", return_value=True
|
"homeassistant.components.cloud.async_is_logged_in", return_value=True
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.cloud.async_is_connected", return_value=True
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
||||||
), patch(
|
), patch(
|
||||||
@ -203,6 +205,7 @@ async def test_setup_with_cloud(hass, config_entry):
|
|||||||
hass, "netatmo", {"netatmo": {"client_id": "123", "client_secret": "abc"}}
|
hass, "netatmo", {"netatmo": {"client_id": "123", "client_secret": "abc"}}
|
||||||
)
|
)
|
||||||
assert hass.components.cloud.async_active_subscription() is True
|
assert hass.components.cloud.async_active_subscription() is True
|
||||||
|
assert hass.components.cloud.async_is_connected() is True
|
||||||
fake_create_cloudhook.assert_called_once()
|
fake_create_cloudhook.assert_called_once()
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
@ -245,6 +248,8 @@ async def test_setup_with_cloudhook(hass):
|
|||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.cloud.async_is_logged_in", return_value=True
|
"homeassistant.components.cloud.async_is_logged_in", return_value=True
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.cloud.async_is_connected", return_value=True
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
||||||
), patch(
|
), patch(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user