mirror of
https://github.com/home-assistant/core.git
synced 2026-02-14 23:28:42 +00:00
Add subscribe preview feature helper to labs (#161778)
This commit is contained in:
@@ -73,31 +73,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
started = False
|
||||
|
||||
async def _async_handle_labs_update(
|
||||
event: Event[labs.EventLabsUpdatedData],
|
||||
event_data: labs.EventLabsUpdatedData,
|
||||
) -> None:
|
||||
"""Handle labs feature toggle."""
|
||||
await analytics.save_preferences({ATTR_SNAPSHOTS: event.data["enabled"]})
|
||||
await analytics.save_preferences({ATTR_SNAPSHOTS: event_data["enabled"]})
|
||||
if started:
|
||||
await analytics.async_schedule()
|
||||
|
||||
@callback
|
||||
def _async_labs_event_filter(event_data: labs.EventLabsUpdatedData) -> bool:
|
||||
"""Filter labs events for this integration's snapshot feature."""
|
||||
return (
|
||||
event_data["domain"] == DOMAIN
|
||||
and event_data["preview_feature"] == LABS_SNAPSHOT_FEATURE
|
||||
)
|
||||
|
||||
async def start_schedule(_event: Event) -> None:
|
||||
"""Start the send schedule after the started event."""
|
||||
nonlocal started
|
||||
started = True
|
||||
await analytics.async_schedule()
|
||||
|
||||
hass.bus.async_listen(
|
||||
labs.EVENT_LABS_UPDATED,
|
||||
_async_handle_labs_update,
|
||||
event_filter=_async_labs_event_filter,
|
||||
labs.async_subscribe_preview_feature(
|
||||
hass, DOMAIN, LABS_SNAPSHOT_FEATURE, _async_handle_labs_update
|
||||
)
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, start_schedule)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components import labs, websocket_api
|
||||
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
|
||||
from homeassistant.components.labs import async_listen as async_labs_listen
|
||||
from homeassistant.components.labs import async_subscribe_preview_feature
|
||||
from homeassistant.const import (
|
||||
ATTR_AREA_ID,
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -386,14 +386,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
schema=vol.Schema({vol.Optional(CONF_ID): str}),
|
||||
)
|
||||
|
||||
@callback
|
||||
def new_triggers_conditions_listener() -> None:
|
||||
async def new_triggers_conditions_listener(
|
||||
_event_data: labs.EventLabsUpdatedData,
|
||||
) -> None:
|
||||
"""Handle new_triggers_conditions flag change."""
|
||||
hass.async_create_task(
|
||||
reload_helper.execute_service(ServiceCall(hass, DOMAIN, SERVICE_RELOAD))
|
||||
)
|
||||
await reload_helper.execute_service(ServiceCall(hass, DOMAIN, SERVICE_RELOAD))
|
||||
|
||||
async_labs_listen(
|
||||
async_subscribe_preview_feature(
|
||||
hass,
|
||||
DOMAIN,
|
||||
NEW_TRIGGERS_CONDITIONS_FEATURE_FLAG,
|
||||
|
||||
@@ -21,6 +21,7 @@ from .const import DOMAIN, LABS_DATA, STORAGE_KEY, STORAGE_VERSION
|
||||
from .helpers import (
|
||||
async_is_preview_feature_enabled,
|
||||
async_listen,
|
||||
async_subscribe_preview_feature,
|
||||
async_update_preview_feature,
|
||||
)
|
||||
from .models import (
|
||||
@@ -41,6 +42,7 @@ __all__ = [
|
||||
"EventLabsUpdatedData",
|
||||
"async_is_preview_feature_enabled",
|
||||
"async_listen",
|
||||
"async_subscribe_preview_feature",
|
||||
"async_update_preview_feature",
|
||||
]
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import EVENT_LABS_UPDATED
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
@@ -32,6 +33,43 @@ def async_is_preview_feature_enabled(
|
||||
return (domain, preview_feature) in labs_data.data.preview_feature_status
|
||||
|
||||
|
||||
@callback
|
||||
def async_subscribe_preview_feature(
|
||||
hass: HomeAssistant,
|
||||
domain: str,
|
||||
preview_feature: str,
|
||||
listener: Callable[[EventLabsUpdatedData], Coroutine[Any, Any, None]],
|
||||
) -> Callable[[], None]:
|
||||
"""Listen for changes to a specific preview feature.
|
||||
|
||||
Args:
|
||||
hass: HomeAssistant instance
|
||||
domain: Integration domain
|
||||
preview_feature: Preview feature name
|
||||
listener: Coroutine function to invoke when the preview feature
|
||||
is toggled. Receives the event data as argument. Runs eagerly.
|
||||
|
||||
Returns:
|
||||
Callable to unsubscribe from the listener
|
||||
"""
|
||||
|
||||
@callback
|
||||
def _async_event_filter(event_data: EventLabsUpdatedData) -> bool:
|
||||
"""Filter labs events for this integration's preview feature."""
|
||||
return (
|
||||
event_data["domain"] == domain
|
||||
and event_data["preview_feature"] == preview_feature
|
||||
)
|
||||
|
||||
async def _handler(event: Event[EventLabsUpdatedData]) -> None:
|
||||
"""Handle labs feature update event."""
|
||||
await listener(event.data)
|
||||
|
||||
return hass.bus.async_listen(
|
||||
EVENT_LABS_UPDATED, _handler, event_filter=_async_event_filter
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_listen(
|
||||
hass: HomeAssistant,
|
||||
@@ -51,16 +89,10 @@ def async_listen(
|
||||
Callable to unsubscribe from the listener
|
||||
"""
|
||||
|
||||
@callback
|
||||
def _async_feature_updated(event: Event[EventLabsUpdatedData]) -> None:
|
||||
"""Handle labs feature update event."""
|
||||
if (
|
||||
event.data["domain"] == domain
|
||||
and event.data["preview_feature"] == preview_feature
|
||||
):
|
||||
listener()
|
||||
async def _listener(_event_data: EventLabsUpdatedData) -> None:
|
||||
listener()
|
||||
|
||||
return hass.bus.async_listen(EVENT_LABS_UPDATED, _async_feature_updated)
|
||||
return async_subscribe_preview_feature(hass, domain, preview_feature, _listener)
|
||||
|
||||
|
||||
async def async_update_preview_feature(
|
||||
|
||||
@@ -13,9 +13,10 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from .const import LABS_DATA
|
||||
from .helpers import (
|
||||
async_is_preview_feature_enabled,
|
||||
async_listen,
|
||||
async_subscribe_preview_feature,
|
||||
async_update_preview_feature,
|
||||
)
|
||||
from .models import EventLabsUpdatedData
|
||||
|
||||
|
||||
@callback
|
||||
@@ -132,20 +133,27 @@ def websocket_subscribe_feature(
|
||||
|
||||
preview_feature = labs_data.preview_features[preview_feature_id]
|
||||
|
||||
@callback
|
||||
def send_event() -> None:
|
||||
async def send_event(event_data: EventLabsUpdatedData) -> None:
|
||||
"""Send feature state to client."""
|
||||
enabled = async_is_preview_feature_enabled(hass, domain, preview_feature_key)
|
||||
connection.send_message(
|
||||
websocket_api.event_message(
|
||||
msg["id"],
|
||||
preview_feature.to_dict(enabled=enabled),
|
||||
preview_feature.to_dict(enabled=event_data["enabled"]),
|
||||
)
|
||||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_listen(
|
||||
connection.subscriptions[msg["id"]] = async_subscribe_preview_feature(
|
||||
hass, domain, preview_feature_key, send_event
|
||||
)
|
||||
|
||||
connection.send_result(msg["id"])
|
||||
send_event()
|
||||
connection.send_message(
|
||||
websocket_api.event_message(
|
||||
msg["id"],
|
||||
preview_feature.to_dict(
|
||||
enabled=async_is_preview_feature_enabled(
|
||||
hass, domain, preview_feature_key
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -13,7 +13,10 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components import automation, websocket_api
|
||||
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
|
||||
from homeassistant.components.labs import async_listen as async_labs_listen
|
||||
from homeassistant.components.labs import (
|
||||
EventLabsUpdatedData,
|
||||
async_subscribe_preview_feature,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_MODE,
|
||||
@@ -282,14 +285,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
DOMAIN, SERVICE_TOGGLE, toggle_service, schema=SCRIPT_TURN_ONOFF_SCHEMA
|
||||
)
|
||||
|
||||
@callback
|
||||
def new_triggers_conditions_listener() -> None:
|
||||
async def new_triggers_conditions_listener(
|
||||
_event_data: EventLabsUpdatedData,
|
||||
) -> None:
|
||||
"""Handle new_triggers_conditions flag change."""
|
||||
hass.async_create_task(
|
||||
reload_service(ServiceCall(hass, DOMAIN, SERVICE_RELOAD))
|
||||
)
|
||||
await reload_service(ServiceCall(hass, DOMAIN, SERVICE_RELOAD))
|
||||
|
||||
async_labs_listen(
|
||||
async_subscribe_preview_feature(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
automation.NEW_TRIGGERS_CONDITIONS_FEATURE_FLAG,
|
||||
|
||||
@@ -173,14 +173,15 @@ async def async_setup(hass: HomeAssistant) -> None:
|
||||
hass.data[CONDITION_PLATFORM_SUBSCRIPTIONS] = []
|
||||
hass.data[CONDITIONS] = {}
|
||||
|
||||
@callback
|
||||
def new_triggers_conditions_listener() -> None:
|
||||
async def new_triggers_conditions_listener(
|
||||
_event_data: labs.EventLabsUpdatedData,
|
||||
) -> None:
|
||||
"""Handle new_triggers_conditions flag change."""
|
||||
# Invalidate the cache
|
||||
hass.data[CONDITION_DESCRIPTION_CACHE] = {}
|
||||
hass.data[CONDITION_DISABLED_CONDITIONS] = set()
|
||||
|
||||
labs.async_listen(
|
||||
labs.async_subscribe_preview_feature(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
automation.NEW_TRIGGERS_CONDITIONS_FEATURE_FLAG,
|
||||
|
||||
@@ -149,14 +149,15 @@ async def async_setup(hass: HomeAssistant) -> None:
|
||||
hass.data[TRIGGER_PLATFORM_SUBSCRIPTIONS] = []
|
||||
hass.data[TRIGGERS] = {}
|
||||
|
||||
@callback
|
||||
def new_triggers_conditions_listener() -> None:
|
||||
async def new_triggers_conditions_listener(
|
||||
_event_data: labs.EventLabsUpdatedData,
|
||||
) -> None:
|
||||
"""Handle new_triggers_conditions flag change."""
|
||||
# Invalidate the cache
|
||||
hass.data[TRIGGER_DESCRIPTION_CACHE] = {}
|
||||
hass.data[TRIGGER_DISABLED_TRIGGERS] = set()
|
||||
|
||||
labs.async_listen(
|
||||
labs.async_subscribe_preview_feature(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
automation.NEW_TRIGGERS_CONDITIONS_FEATURE_FLAG,
|
||||
|
||||
@@ -9,8 +9,10 @@ import pytest
|
||||
|
||||
from homeassistant.components.labs import (
|
||||
EVENT_LABS_UPDATED,
|
||||
EventLabsUpdatedData,
|
||||
async_is_preview_feature_enabled,
|
||||
async_listen,
|
||||
async_subscribe_preview_feature,
|
||||
async_update_preview_feature,
|
||||
)
|
||||
from homeassistant.components.labs.const import DOMAIN, LABS_DATA
|
||||
@@ -441,6 +443,96 @@ async def test_async_listen_helper(hass: HomeAssistant) -> None:
|
||||
assert len(listener_calls) == 1
|
||||
|
||||
|
||||
async def test_async_subscribe_preview_feature_helper(hass: HomeAssistant) -> None:
|
||||
"""Test async_subscribe_preview_feature helper."""
|
||||
hass.config.components.add("kitchen_sink")
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
calls: list[EventLabsUpdatedData] = []
|
||||
|
||||
async def listener(event_data: EventLabsUpdatedData) -> None:
|
||||
"""Test listener callback."""
|
||||
calls.append(event_data)
|
||||
|
||||
unsub = async_subscribe_preview_feature(
|
||||
hass,
|
||||
domain="kitchen_sink",
|
||||
preview_feature="special_repair",
|
||||
listener=listener,
|
||||
)
|
||||
|
||||
# Fire event for the subscribed feature
|
||||
hass.bus.async_fire(
|
||||
EVENT_LABS_UPDATED,
|
||||
{
|
||||
"domain": "kitchen_sink",
|
||||
"preview_feature": "special_repair",
|
||||
"enabled": True,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
assert calls[0]["enabled"] is True
|
||||
|
||||
# Fire event for a different feature - should not trigger listener
|
||||
hass.bus.async_fire(
|
||||
EVENT_LABS_UPDATED,
|
||||
{
|
||||
"domain": "kitchen_sink",
|
||||
"preview_feature": "other_feature",
|
||||
"enabled": True,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
|
||||
# Fire event for a different domain - should not trigger listener
|
||||
hass.bus.async_fire(
|
||||
EVENT_LABS_UPDATED,
|
||||
{
|
||||
"domain": "other_domain",
|
||||
"preview_feature": "special_repair",
|
||||
"enabled": True,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
|
||||
# Fire event with enabled=False
|
||||
hass.bus.async_fire(
|
||||
EVENT_LABS_UPDATED,
|
||||
{
|
||||
"domain": "kitchen_sink",
|
||||
"preview_feature": "special_repair",
|
||||
"enabled": False,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 2
|
||||
assert calls[1]["enabled"] is False
|
||||
|
||||
# Test unsubscribe
|
||||
unsub()
|
||||
|
||||
hass.bus.async_fire(
|
||||
EVENT_LABS_UPDATED,
|
||||
{
|
||||
"domain": "kitchen_sink",
|
||||
"preview_feature": "special_repair",
|
||||
"enabled": True,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 2
|
||||
|
||||
|
||||
async def test_async_update_preview_feature(
|
||||
hass: HomeAssistant, hass_storage: dict[str, Any]
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user