# To-do list entity

> Build a to-do list entity for a shopping list, a task list, or any other ordered list of items with a status.

Source: https://contributors.home-assistant.io/core/entities/todo/

A to-do list entity represents a list of items, each with a status: open or done. Think of a shopping list, the tasks in a task manager, or the chores a robot vacuum app keeps track of. Inherit from `TodoListEntity` in `homeassistant.components.todo`.

People see the list in the to-do panel, and automations, scripts, and Assist can read it and add to it. What they can change depends on the [supported features](https://contributors.home-assistant.io/core/entities/todo/#supported-features) you set.

## Properties

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `todo_items` | `list[TodoItem] \| None` | `None` | The items on the list, in order. `None` means the list is not known. |

The generic properties every entity has, like `name`, `available`, and `unique_id`, are on [the entity class](https://contributors.home-assistant.io/core/entities/entity-class/).

## Supported features

Combine the flags of `TodoListEntityFeature` with `|`, and set them with `_attr_supported_features`. Without any, the list is read only. See [supported features](https://contributors.home-assistant.io/glossary/supported-features/).

| Constant | What it enables |
| --- | --- |
| `CREATE_TODO_ITEM` | Adding items. Implement `async_create_todo_item`. |
| `DELETE_TODO_ITEM` | Removing items, and removing all completed items at once. Implement `async_delete_todo_items`. |
| `UPDATE_TODO_ITEM` | Changing items, like renaming them or marking them as done. Implement `async_update_todo_item`. |
| `MOVE_TODO_ITEM` | Reordering items. Implement `async_move_todo_item`. |
| `SET_DUE_DATE_ON_ITEM` | Setting `due` to a date when an item is created or updated. |
| `SET_DUE_DATETIME_ON_ITEM` | Setting `due` to a date and time when an item is created or updated. |
| `SET_DESCRIPTION_ON_ITEM` | Setting `description` when an item is created or updated. |

When an action uses a field that you don't support, like a due date, the action fails with a validation error before your method is called.

## To-do items

Each item is a `TodoItem`, a dataclass from `homeassistant.components.todo`.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `uid` | `str \| None` | `None` | A unique ID for the item. Needed to update, delete, or move it. |
| `summary` | `str \| None` | `None` | The text of the item. |
| `status` | `TodoItemStatus \| None` | `None` | `TodoItemStatus.NEEDS_ACTION` or `TodoItemStatus.COMPLETED`. |
| `due` | `datetime.date \| datetime.datetime \| None` | `None` | When the item is due. A `datetime` must have a time zone. |
| `description` | `str \| None` | `None` | A longer description than the summary. |
| `completed` | `datetime.datetime \| None` | `None` | When the item was last marked as completed. |

Every item that you return needs a `uid`, a `summary`, and a `status`.

## Methods

All methods are async only. Each one is needed only for the matching feature.

### Create an item

```python
async def async_create_todo_item(self, item: TodoItem) -> None:
    """Add an item to the list."""
```

The new item has a `summary` and the status `NEEDS_ACTION`, plus the optional fields you support. It has no `uid` yet: your service or your integration gives it one.

### Update an item

```python
async def async_update_todo_item(self, item: TodoItem) -> None:
    """Update an item on the list."""
```

The item always has its `uid`. It is a copy of the item as you last returned it, with the changed fields filled in. A field set to `None` was cleared on purpose, so clear it on your side too.

### Delete items

```python
async def async_delete_todo_items(self, uids: list[str]) -> None:
    """Delete items from the list."""
```

You get a list of IDs. Delete all of them, not only the first.

### Move an item

```python
async def async_move_todo_item(
    self, uid: str, previous_uid: str | None = None
) -> None:
    """Move an item on the list."""
```

Move the item with `uid` so it comes right after the item with `previous_uid`. When `previous_uid` is `None`, move it to the top of the list.

After any change, update `todo_items` and write the state, or refresh it from your service. The base class sends the new items to the frontend each time the state is written.

## States

The state is the number of items with the status `NEEDS_ACTION`. It is `unknown` when `todo_items` is `None`.

## Example

```python
from homeassistant.components.todo import (
    TodoItem,
    TodoItemStatus,
    TodoListEntity,
    TodoListEntityFeature,
)


class ExampleShoppingList(TodoListEntity):
    """A shopping list kept by a cloud service."""

    _attr_has_entity_name = True
    _attr_translation_key = "shopping_list"
    _attr_supported_features = (
        TodoListEntityFeature.CREATE_TODO_ITEM
        | TodoListEntityFeature.DELETE_TODO_ITEM
        | TodoListEntityFeature.UPDATE_TODO_ITEM
    )

    def __init__(self, client: ExampleClient) -> None:
        """Initialize the list."""
        self._client = client
        self._attr_unique_id = f"{client.account_id}_shopping_list"

    async def async_update(self) -> None:
        """Fetch the items from the service."""
        self._attr_todo_items = [
            TodoItem(
                uid=entry.id,
                summary=entry.text,
                status=(
                    TodoItemStatus.COMPLETED
                    if entry.done
                    else TodoItemStatus.NEEDS_ACTION
                ),
            )
            for entry in await self._client.get_items()
        ]

    async def async_create_todo_item(self, item: TodoItem) -> None:
        """Add an item to the list."""
        await self._client.add_item(item.summary)
        await self.async_update_ha_state(force_refresh=True)

    async def async_update_todo_item(self, item: TodoItem) -> None:
        """Update an item on the list."""
        done = item.status == TodoItemStatus.COMPLETED
        await self._client.update_item(item.uid, item.summary, done)
        await self.async_update_ha_state(force_refresh=True)

    async def async_delete_todo_items(self, uids: list[str]) -> None:
        """Delete items from the list."""
        await self._client.delete_items(uids)
        await self.async_update_ha_state(force_refresh=True)
```

This example polls. When your service pushes changes, or you use a [coordinator](https://contributors.home-assistant.io/glossary/coordinator/), refresh through that instead. See [updating state](https://contributors.home-assistant.io/core/entities/state-updates/).
