1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-17 15:44:52 +01:00

Make TODO subscriptions use TodoItem instead of JSON (#165802)

This commit is contained in:
Abílio Costa
2026-03-17 19:09:13 +00:00
committed by GitHub
parent 836353015b
commit 14545660e2
2 changed files with 58 additions and 14 deletions

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
from collections.abc import Callable, Iterable
import copy
import dataclasses
import datetime
import logging
@@ -28,7 +29,6 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util
from homeassistant.util.json import JsonValueType
from .const import (
ATTR_DESCRIPTION,
@@ -240,7 +240,7 @@ class TodoListEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""An entity that represents a To-do list."""
_attr_todo_items: list[TodoItem] | None = None
_update_listeners: list[Callable[[list[JsonValueType] | None], None]] | None = None
_update_listeners: list[Callable[[list[TodoItem]], None]] | None = None
@property
def state(self) -> int | None:
@@ -281,13 +281,9 @@ class TodoListEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@final
@callback
def async_subscribe_updates(
self,
listener: Callable[[list[JsonValueType] | None], None],
self, listener: Callable[[list[TodoItem]], None]
) -> CALLBACK_TYPE:
"""Subscribe to To-do list item updates.
Called by websocket API.
"""
"""Subscribe to To-do list item updates."""
if self._update_listeners is None:
self._update_listeners = []
self._update_listeners.append(listener)
@@ -306,9 +302,7 @@ class TodoListEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if not self._update_listeners:
return
todo_items: list[JsonValueType] = [
dataclasses.asdict(item) for item in self.todo_items or ()
]
todo_items = [copy.copy(item) for item in self.todo_items or []]
for listener in self._update_listeners:
listener(todo_items)
@@ -341,13 +335,13 @@ async def websocket_handle_subscribe_todo_items(
return
@callback
def todo_item_listener(todo_items: list[JsonValueType] | None) -> None:
def todo_item_listener(todo_items: list[TodoItem]) -> None:
"""Push updated To-do list items to websocket."""
connection.send_message(
websocket_api.event_message(
msg["id"],
{
"items": todo_items,
"items": [dataclasses.asdict(item) for item in todo_items],
},
)
)
@@ -357,7 +351,7 @@ async def websocket_handle_subscribe_todo_items(
)
connection.send_result(msg["id"])
# Push an initial forecast update
# Push an initial list update
entity.async_update_listeners()

View File

@@ -1231,3 +1231,53 @@ async def test_list_todo_items_extended_fields(
]
}
}
async def test_async_subscribe_updates(
hass: HomeAssistant, test_entity: TodoListEntity
) -> None:
"""Test async_subscribe_updates delivers list updates to listeners."""
await create_mock_platform(hass, [test_entity])
received_updates: list[list[TodoItem]] = []
def listener(items: list[TodoItem]) -> None:
received_updates.append(items)
unsub = test_entity.async_subscribe_updates(listener)
# Trigger an update
test_entity.async_write_ha_state()
assert len(received_updates) == 1
items = received_updates[0]
assert len(items) == 2
assert isinstance(items[0], TodoItem)
assert items[0].summary == "Item #1"
assert items[0].uid == "1"
assert items[0].status == TodoItemStatus.NEEDS_ACTION
assert isinstance(items[1], TodoItem)
assert items[1].summary == "Item #2"
assert items[1].uid == "2"
assert items[1].status == TodoItemStatus.COMPLETED
# Verify items are copies (not the same objects)
assert items[0] is not test_entity.todo_items[0]
assert items[1] is not test_entity.todo_items[1]
# Add a new item and trigger update
test_entity._attr_todo_items = [
*test_entity._attr_todo_items,
TodoItem(summary="Item #3", uid="3", status=TodoItemStatus.NEEDS_ACTION),
]
test_entity.async_write_ha_state()
assert len(received_updates) == 2
items = received_updates[1]
assert len(items) == 3
assert items[2].summary == "Item #3"
# Unsubscribe and verify no more updates
unsub()
test_entity.async_write_ha_state()
assert len(received_updates) == 2