mirror of
https://github.com/home-assistant/core.git
synced 2026-09-27 09:58:07 -04:00
Add timer_list in right place
This commit is contained in:
@@ -26,19 +26,10 @@ from homeassistant.components.assist_pipeline import (
|
||||
async_pipeline_from_audio_stream,
|
||||
vad,
|
||||
)
|
||||
from homeassistant.components.local_timer_list import LocalTimerListEntity
|
||||
from homeassistant.components.media_player import async_process_play_media_url
|
||||
from homeassistant.components.timer_list import (
|
||||
DATA_COMPONENT as TIMER_LIST_DATA_COMPONENT,
|
||||
)
|
||||
from homeassistant.core import Context, HomeAssistant, callback
|
||||
from homeassistant.core import Context, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import (
|
||||
chat_session,
|
||||
device_registry as dr,
|
||||
entity,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.helpers import chat_session, entity
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
|
||||
from .const import PREANNOUNCE_URL, AssistSatelliteEntityFeature
|
||||
@@ -160,12 +151,6 @@ class AssistSatelliteEntity(entity.Entity):
|
||||
"""Return state of the entity."""
|
||||
return self.__assist_satellite_state
|
||||
|
||||
@override
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Run when entity about to be added to hass."""
|
||||
await super().async_added_to_hass()
|
||||
await _async_ensure_timer_list_entity(self.hass, self.registry_entry)
|
||||
|
||||
@property
|
||||
def pipeline_entity_id(self) -> str | None:
|
||||
"""Entity ID of the pipeline to use for the next conversation."""
|
||||
@@ -744,35 +729,6 @@ class AssistSatelliteEntity(entity.Entity):
|
||||
)
|
||||
|
||||
|
||||
async def _async_ensure_timer_list_entity(
|
||||
hass: HomeAssistant, registry_entry: er.RegistryEntry | None
|
||||
) -> None:
|
||||
"""Create a timer_list entity for this satellite's device, if it lacks one.
|
||||
|
||||
Always attempts to add the entity: the entity registry reconciles by
|
||||
``unique_id`` (the device id), so this safely re-attaches to the existing
|
||||
registry entry on restart rather than creating a duplicate. Checking for
|
||||
an existing *registry* entry first would be wrong, since registry entries
|
||||
persist across restarts even though the live entity does not.
|
||||
"""
|
||||
if registry_entry is None or registry_entry.device_id is None:
|
||||
return
|
||||
|
||||
device_id = registry_entry.device_id
|
||||
device_registry = dr.async_get(hass)
|
||||
device = device_registry.async_get(device_id)
|
||||
device_name = device.name_by_user or device.name if device else None
|
||||
|
||||
await hass.data[TIMER_LIST_DATA_COMPONENT].async_add_entities(
|
||||
[
|
||||
LocalTimerListEntity(
|
||||
name=f"{device_name} Timers" if device_name else "Timers",
|
||||
unique_id=device_id,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
|
||||
"""Collect list reference names recursively."""
|
||||
if isinstance(expression, Group):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "assist_satellite",
|
||||
"name": "Assist Satellite",
|
||||
"codeowners": ["@home-assistant/core", "@synesthesiam", "@arturpragacz"],
|
||||
"dependencies": ["assist_pipeline", "http", "local_timer_list", "stt", "tts"],
|
||||
"dependencies": ["assist_pipeline", "http", "stt", "tts"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/assist_satellite",
|
||||
"integration_type": "entity",
|
||||
"quality_scale": "internal",
|
||||
|
||||
@@ -287,6 +287,7 @@ class RuntimeEntryData:
|
||||
if self.device_info.voice_assistant_feature_flags_compat(self.api_version):
|
||||
needed_platforms.add(Platform.BINARY_SENSOR)
|
||||
needed_platforms.add(Platform.SELECT)
|
||||
needed_platforms.add(Platform.TIMER_LIST)
|
||||
|
||||
# Make a dict of the EntityInfo by type and send
|
||||
# them to the listeners for each specific EntityInfo type
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
"after_dependencies": ["hassio", "tag", "usb", "zeroconf"],
|
||||
"codeowners": ["@jesserockz", "@kbx81", "@bdraco"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["assist_pipeline", "bluetooth", "intent", "ffmpeg", "http"],
|
||||
"dependencies": [
|
||||
"assist_pipeline",
|
||||
"bluetooth",
|
||||
"intent",
|
||||
"ffmpeg",
|
||||
"http",
|
||||
"timer_list"
|
||||
],
|
||||
"dhcp": [
|
||||
{
|
||||
"registered_devices": true
|
||||
|
||||
@@ -195,10 +195,22 @@ class TimerManager:
|
||||
|
||||
@callback
|
||||
def _get_entity(self, device_id: str) -> TimerListEntity | None:
|
||||
"""Return the timer_list entity for a device, if it has one."""
|
||||
"""Return the timer_list entity for a device, if it has one.
|
||||
|
||||
The list is provided by the device's own integration, so it may live on
|
||||
any platform; resolve it by device association rather than assuming the
|
||||
``timer_list`` platform owns it.
|
||||
"""
|
||||
entity_registry = er.async_get(self.hass)
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
TIMER_LIST_DOMAIN, TIMER_LIST_DOMAIN, device_id
|
||||
entity_id = next(
|
||||
(
|
||||
entry.entity_id
|
||||
for entry in er.async_entries_for_device(
|
||||
entity_registry, device_id, include_disabled_entities=True
|
||||
)
|
||||
if entry.domain == TIMER_LIST_DOMAIN
|
||||
),
|
||||
None,
|
||||
)
|
||||
if entity_id is None:
|
||||
return None
|
||||
@@ -400,17 +412,17 @@ def _timer_info_from_item(
|
||||
def _all_timer_infos(hass: HomeAssistant) -> list[TimerInfo]:
|
||||
"""Return snapshots of all active/paused timers across satellite devices.
|
||||
|
||||
Only considers timer_list entities auto-created for a satellite device
|
||||
(registry platform == "timer_list"), not a user's manually-created
|
||||
local_timer_list helper (registry platform == "local_timer_list").
|
||||
Only device-linked timer lists (each provided by a satellite's own
|
||||
integration) are considered, not a user's standalone local_timer_list
|
||||
helper, which has no associated device.
|
||||
"""
|
||||
component = hass.data[TIMER_LIST_DATA_COMPONENT]
|
||||
infos: list[TimerInfo] = []
|
||||
for timer_entity in component.entities:
|
||||
registry_entry = timer_entity.registry_entry
|
||||
if registry_entry is None or registry_entry.platform != TIMER_LIST_DOMAIN:
|
||||
if registry_entry is None or registry_entry.device_id is None:
|
||||
continue
|
||||
device_id = registry_entry.unique_id
|
||||
device_id = registry_entry.device_id
|
||||
for item in timer_entity.timers:
|
||||
if item.status not in (TimerStatus.ACTIVE, TimerStatus.PAUSED):
|
||||
continue
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
"""Local timer list platform."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
from typing import override
|
||||
|
||||
from homeassistant.components.timer_list import (
|
||||
DOMAIN as TIMER_LIST_DOMAIN,
|
||||
TimerItem,
|
||||
TimerListEntity,
|
||||
TimerListEntityFeature,
|
||||
TimerListEventType,
|
||||
TimerStatus,
|
||||
)
|
||||
from homeassistant.components.timer_list import InMemoryTimerListEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.util import dt as dt_util, ulid as ulid_util
|
||||
|
||||
from .const import CONF_TIMER_LIST_NAME
|
||||
|
||||
_FINISHED_STATUSES = (TimerStatus.FINISHED, TimerStatus.CANCELLED)
|
||||
MAX_ARCHIVED_TIMERS = 10
|
||||
|
||||
# Kept as a named subclass so UI-created lists have a stable, distinct type.
|
||||
class LocalTimerListEntity(InMemoryTimerListEntity):
|
||||
"""A standalone, UI-created in-memory timer list."""
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -39,175 +27,3 @@ async def async_setup_entry(
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class LocalTimerListEntity(TimerListEntity):
|
||||
"""A local, in-memory timer list."""
|
||||
|
||||
_attr_supported_features = (
|
||||
TimerListEntityFeature.START_TIMER
|
||||
| TimerListEntityFeature.PAUSE_TIMER
|
||||
| TimerListEntityFeature.CANCEL_TIMER
|
||||
| TimerListEntityFeature.ADD_TIME
|
||||
)
|
||||
|
||||
def __init__(self, *, name: str, unique_id: str) -> None:
|
||||
"""Initialize the timer list."""
|
||||
super().__init__()
|
||||
self._attr_name = name
|
||||
self._attr_unique_id = unique_id
|
||||
self._timers: dict[str, TimerItem] = {}
|
||||
self._cancel_callbacks: dict[str, CALLBACK_TYPE] = {}
|
||||
|
||||
@property
|
||||
@override
|
||||
def timers(self) -> list[TimerItem]:
|
||||
"""Return the timers in the list."""
|
||||
return list(self._timers.values())
|
||||
|
||||
@override
|
||||
async def async_start_timer(self, *, name: str | None, duration: timedelta) -> str:
|
||||
"""Create and start a new timer, returning its id."""
|
||||
now = dt_util.utcnow()
|
||||
timer_id = ulid_util.ulid_now()
|
||||
timer = TimerItem(
|
||||
timer_id=timer_id,
|
||||
name=name,
|
||||
status=TimerStatus.ACTIVE,
|
||||
duration=duration,
|
||||
created_at=now,
|
||||
finishes_at=now + duration,
|
||||
)
|
||||
self._timers[timer_id] = timer
|
||||
self._schedule(timer)
|
||||
self._notify(TimerListEventType.STARTED, timer)
|
||||
return timer_id
|
||||
|
||||
@override
|
||||
async def async_pause_timer(self, timer_id: str) -> None:
|
||||
"""Pause an active timer."""
|
||||
timer = self._get_timer(timer_id)
|
||||
if timer.status != TimerStatus.ACTIVE or timer.finishes_at is None:
|
||||
return
|
||||
timer.remaining = max(timedelta(0), timer.finishes_at - dt_util.utcnow())
|
||||
timer.finishes_at = None
|
||||
timer.status = TimerStatus.PAUSED
|
||||
self._unschedule(timer_id)
|
||||
self._notify(TimerListEventType.UPDATED, timer)
|
||||
|
||||
@override
|
||||
async def async_unpause_timer(self, timer_id: str) -> None:
|
||||
"""Resume a paused timer."""
|
||||
timer = self._get_timer(timer_id)
|
||||
if timer.status != TimerStatus.PAUSED or timer.remaining is None:
|
||||
return
|
||||
timer.finishes_at = dt_util.utcnow() + timer.remaining
|
||||
timer.remaining = None
|
||||
timer.status = TimerStatus.ACTIVE
|
||||
self._schedule(timer)
|
||||
self._notify(TimerListEventType.UPDATED, timer)
|
||||
|
||||
@override
|
||||
async def async_cancel_timer(self, timer_id: str) -> None:
|
||||
"""Cancel a timer, archiving it in the ``cancelled`` state."""
|
||||
timer = self._get_timer(timer_id)
|
||||
self._unschedule(timer_id)
|
||||
timer.status = TimerStatus.CANCELLED
|
||||
timer.finishes_at = None
|
||||
timer.remaining = None
|
||||
timer.finished_at = dt_util.utcnow()
|
||||
self._notify(TimerListEventType.CANCELLED, timer)
|
||||
self._enforce_archive_limit()
|
||||
|
||||
@override
|
||||
async def async_add_time(self, timer_id: str, duration: timedelta) -> None:
|
||||
"""Add (or, with a negative duration, remove) time on a timer."""
|
||||
timer = self._get_timer(timer_id)
|
||||
if timer.status == TimerStatus.ACTIVE and timer.finishes_at is not None:
|
||||
now = dt_util.utcnow()
|
||||
finishes_at = timer.finishes_at + duration
|
||||
if finishes_at <= now:
|
||||
self._unschedule(timer_id)
|
||||
self._async_timer_finished(timer_id, now)
|
||||
return
|
||||
timer.finishes_at = finishes_at
|
||||
self._schedule(timer)
|
||||
elif timer.status == TimerStatus.PAUSED and timer.remaining is not None:
|
||||
timer.remaining = max(timedelta(0), timer.remaining + duration)
|
||||
else:
|
||||
return
|
||||
self._notify(TimerListEventType.UPDATED, timer)
|
||||
|
||||
@override
|
||||
async def async_remove_timer(self, timer_id: str) -> None:
|
||||
"""Remove a timer from the list regardless of its status."""
|
||||
timer = self._get_timer(timer_id)
|
||||
self._unschedule(timer_id)
|
||||
del self._timers[timer_id]
|
||||
self._notify(TimerListEventType.REMOVED, timer)
|
||||
|
||||
def _get_timer(self, timer_id: str) -> TimerItem:
|
||||
"""Return a timer by id or raise if it does not exist."""
|
||||
if (timer := self._timers.get(timer_id)) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=TIMER_LIST_DOMAIN,
|
||||
translation_key="timer_not_found",
|
||||
translation_placeholders={"timer_id": timer_id},
|
||||
)
|
||||
return timer
|
||||
|
||||
@callback
|
||||
def _schedule(self, timer: TimerItem) -> None:
|
||||
"""Schedule (or reschedule) the finish callback for a timer."""
|
||||
self._unschedule(timer.timer_id)
|
||||
assert timer.finishes_at is not None
|
||||
self._cancel_callbacks[timer.timer_id] = async_track_point_in_utc_time(
|
||||
self.hass,
|
||||
partial(self._async_timer_finished, timer.timer_id),
|
||||
timer.finishes_at,
|
||||
)
|
||||
|
||||
@callback
|
||||
def _unschedule(self, timer_id: str) -> None:
|
||||
"""Cancel a pending finish callback, if any."""
|
||||
if cancel := self._cancel_callbacks.pop(timer_id, None):
|
||||
cancel()
|
||||
|
||||
@callback
|
||||
def _async_timer_finished(self, timer_id: str, now: datetime) -> None:
|
||||
"""Handle a timer reaching its finish time, archiving it."""
|
||||
self._cancel_callbacks.pop(timer_id, None)
|
||||
if (timer := self._timers.get(timer_id)) is None:
|
||||
return
|
||||
|
||||
timer.status = TimerStatus.FINISHED
|
||||
timer.finishes_at = None
|
||||
timer.remaining = None
|
||||
timer.finished_at = dt_util.utcnow()
|
||||
self._notify(TimerListEventType.FINISHED, timer)
|
||||
self._enforce_archive_limit()
|
||||
|
||||
@callback
|
||||
def _enforce_archive_limit(self) -> None:
|
||||
"""Evict the oldest archived timers beyond ``MAX_ARCHIVED_TIMERS``."""
|
||||
archived = sorted(
|
||||
(
|
||||
timer
|
||||
for timer in self._timers.values()
|
||||
if timer.status in _FINISHED_STATUSES
|
||||
),
|
||||
key=lambda timer: timer.finished_at or dt_util.utcnow(),
|
||||
)
|
||||
excess = len(archived) - MAX_ARCHIVED_TIMERS
|
||||
if excess <= 0:
|
||||
return
|
||||
for timer in archived[:excess]:
|
||||
del self._timers[timer.timer_id]
|
||||
self._notify(TimerListEventType.REMOVED, timer)
|
||||
|
||||
@override
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Cancel all pending finish callbacks."""
|
||||
for cancel in self._cancel_callbacks.values():
|
||||
cancel()
|
||||
self._cancel_callbacks.clear()
|
||||
|
||||
@@ -270,6 +270,10 @@ class TimerListEntity(Entity):
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
# Imported at the end so the reusable entity can subclass TimerListEntity above.
|
||||
from .local import InMemoryTimerListEntity as InMemoryTimerListEntity # noqa: E402
|
||||
|
||||
|
||||
async def _async_start_timer(
|
||||
entity: TimerListEntity, call: ServiceCall
|
||||
) -> dict[str, Any]:
|
||||
|
||||
@@ -48,6 +48,17 @@ async def init_components(hass: HomeAssistant) -> None:
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
|
||||
def _make_timer_device_id(hass: HomeAssistant) -> str:
|
||||
"""Create a real device to host a timer_list entity and return its id."""
|
||||
entry = MockConfigEntry(domain="test")
|
||||
entry.add_to_hass(hass)
|
||||
device = dr.async_get(hass).async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={("test", entry.entry_id)},
|
||||
)
|
||||
return device.id
|
||||
|
||||
|
||||
async def _register_timer_device(
|
||||
hass: HomeAssistant, device_id: str, handler: TimerHandler
|
||||
) -> Callable[[], None]:
|
||||
@@ -60,6 +71,13 @@ async def _register_timer_device(
|
||||
await component.async_add_entities(
|
||||
[LocalTimerListEntity(name=f"{device_id} Timers", unique_id=device_id)]
|
||||
)
|
||||
# The list is resolved by device association, so link it to the device.
|
||||
entity_registry = er.async_get(hass)
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
TIMER_LIST_DOMAIN, TIMER_LIST_DOMAIN, device_id
|
||||
)
|
||||
assert entity_id is not None
|
||||
entity_registry.async_update_entity(entity_id, device_id=device_id)
|
||||
return async_register_timer_handler(hass, device_id, handler)
|
||||
|
||||
|
||||
@@ -77,7 +95,7 @@ def _get_timer_entity(hass: HomeAssistant, device_id: str) -> TimerListEntity:
|
||||
|
||||
async def test_start_finish_timer(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test starting a timer and having it finish."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
timer_name = "test timer"
|
||||
started_event = asyncio.Event()
|
||||
finished_event = asyncio.Event()
|
||||
@@ -125,7 +143,7 @@ async def test_start_finish_timer(hass: HomeAssistant, init_components) -> None:
|
||||
|
||||
async def test_cancel_timer(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test cancelling a timer."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
timer_name: str | None = None
|
||||
started_event = asyncio.Event()
|
||||
cancelled_event = asyncio.Event()
|
||||
@@ -251,7 +269,7 @@ async def test_cancel_timer(hass: HomeAssistant, init_components) -> None:
|
||||
|
||||
async def test_increase_timer(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test increasing the time of a running timer."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
started_event = asyncio.Event()
|
||||
updated_event = asyncio.Event()
|
||||
cancelled_event = asyncio.Event()
|
||||
@@ -368,7 +386,7 @@ async def test_increase_timer(hass: HomeAssistant, init_components) -> None:
|
||||
|
||||
async def test_decrease_timer(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test decreasing the time of a running timer."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
started_event = asyncio.Event()
|
||||
updated_event = asyncio.Event()
|
||||
cancelled_event = asyncio.Event()
|
||||
@@ -471,7 +489,7 @@ async def test_decrease_timer_below_zero(hass: HomeAssistant, init_components) -
|
||||
started_event = asyncio.Event()
|
||||
finished_event = asyncio.Event()
|
||||
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
timer_id: str | None = None
|
||||
original_total_seconds = 0
|
||||
|
||||
@@ -538,7 +556,7 @@ async def test_decrease_timer_below_zero(hass: HomeAssistant, init_components) -
|
||||
|
||||
async def test_find_timer_failed(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test finding a timer with the wrong info."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
|
||||
# No device id
|
||||
with pytest.raises(TimersNotSupportedError):
|
||||
@@ -908,7 +926,7 @@ async def test_disambiguation(
|
||||
|
||||
async def test_pause_unpause_timer(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test pausing and unpausing a running timer."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
|
||||
started_event = asyncio.Event()
|
||||
updated_event = asyncio.Event()
|
||||
@@ -992,7 +1010,7 @@ async def test_timer_manager_pause_unpause(
|
||||
# Start a timer
|
||||
handle_timer = MagicMock()
|
||||
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
await _register_timer_device(hass, device_id, handle_timer)
|
||||
|
||||
timer_id = await timer_manager.start_timer(
|
||||
@@ -1046,7 +1064,7 @@ async def test_timers_not_supported(hass: HomeAssistant, init_components) -> Non
|
||||
def handle_timer(event_type: TimerEventType, timer: TimerInfo) -> None:
|
||||
pass
|
||||
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
unregister = await _register_timer_device(hass, device_id, handle_timer)
|
||||
|
||||
timer_id = await timer_manager.start_timer(
|
||||
@@ -1070,7 +1088,7 @@ async def test_timers_not_supported(hass: HomeAssistant, init_components) -> Non
|
||||
|
||||
async def test_timer_status_with_names(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test getting the status of named timers."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
|
||||
started_event = asyncio.Event()
|
||||
num_started = 0
|
||||
@@ -1454,7 +1472,7 @@ async def test_pause_unpause_timer_disambiguate(
|
||||
hass: HomeAssistant, init_components
|
||||
) -> None:
|
||||
"""Test disamgibuating timers by their paused state."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
started_timer_ids: list[str] = []
|
||||
paused_timer_ids: list[str] = []
|
||||
unpaused_timer_ids: list[str] = []
|
||||
@@ -1557,7 +1575,7 @@ async def test_pause_unpause_timer_disambiguate(
|
||||
|
||||
async def test_async_device_supports_timers(hass: HomeAssistant) -> None:
|
||||
"""Test async_device_supports_timers function."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
|
||||
# Before intent initialization
|
||||
assert not async_device_supports_timers(hass, device_id)
|
||||
@@ -1578,7 +1596,7 @@ async def test_async_device_supports_timers(hass: HomeAssistant) -> None:
|
||||
|
||||
async def test_cancel_all_timers(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test cancelling all timers."""
|
||||
device_id = "test_device"
|
||||
device_id = _make_timer_device_id(hass)
|
||||
|
||||
started_event = asyncio.Event()
|
||||
num_started = 0
|
||||
|
||||
Reference in New Issue
Block a user