diff --git a/homeassistant/components/todo/__init__.py b/homeassistant/components/todo/__init__.py index c7fd6236abc..f5110f41e59 100644 --- a/homeassistant/components/todo/__init__.py +++ b/homeassistant/components/todo/__init__.py @@ -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[TodoItem]], None]] | None = None + _update_listeners: list[Callable[[list[TodoItem] | None], None]] | None = None @property def state(self) -> int | None: @@ -281,7 +281,7 @@ class TodoListEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): @final @callback def async_subscribe_updates( - self, listener: Callable[[list[TodoItem]], None] + self, listener: Callable[[list[TodoItem] | None], None] ) -> CALLBACK_TYPE: """Subscribe to To-do list item updates.""" if self._update_listeners is None: @@ -302,7 +302,12 @@ class TodoListEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): if not self._update_listeners: return - todo_items = [copy.copy(item) for item in self.todo_items or []] + todo_items = ( + [copy.copy(item) for item in self.todo_items] + if self.todo_items is not None + else None + ) + for listener in self._update_listeners: listener(todo_items) @@ -335,14 +340,13 @@ async def websocket_handle_subscribe_todo_items( return @callback - def todo_item_listener(todo_items: list[TodoItem]) -> None: + def todo_item_listener(todo_items: list[TodoItem] | None) -> None: """Push updated To-do list items to websocket.""" + items = [dataclasses.asdict(item) for item in todo_items or []] connection.send_message( websocket_api.event_message( msg["id"], - { - "items": [dataclasses.asdict(item) for item in todo_items], - }, + {"items": items}, ) ) diff --git a/tests/components/todo/test_init.py b/tests/components/todo/test_init.py index b67fca49c4c..e7a9fd364f9 100644 --- a/tests/components/todo/test_init.py +++ b/tests/components/todo/test_init.py @@ -1239,9 +1239,9 @@ async def test_async_subscribe_updates( """Test async_subscribe_updates delivers list updates to listeners.""" await create_mock_platform(hass, [test_entity]) - received_updates: list[list[TodoItem]] = [] + received_updates: list[list[TodoItem] | None] = [] - def listener(items: list[TodoItem]) -> None: + def listener(items: list[TodoItem] | None) -> None: received_updates.append(items) unsub = test_entity.async_subscribe_updates(listener) @@ -1277,7 +1277,25 @@ async def test_async_subscribe_updates( assert len(items) == 3 assert items[2].summary == "Item #3" + # Set items to None and trigger update + test_entity._attr_todo_items = None + test_entity.async_write_ha_state() + assert len(received_updates) == 3 + assert received_updates[2] is None + + # Add a new item to make it available again and trigger update + test_entity._attr_todo_items = [ + TodoItem(summary="New item", uid="4", status=TodoItemStatus.NEEDS_ACTION) + ] + test_entity.async_write_ha_state() + assert len(received_updates) == 4 + items = received_updates[3] + assert len(items) == 1 + assert items[0].summary == "New item" + assert items[0].uid == "4" + assert items[0].status == TodoItemStatus.NEEDS_ACTION + # Unsubscribe and verify no more updates unsub() test_entity.async_write_ha_state() - assert len(received_updates) == 2 + assert len(received_updates) == 4