Todo
Manage Home Assistant todo lists with add, remove, update, and toggle operations.
Quick Example
// Component approach
<Todo entityId="todo.shopping_list">
{({ items, itemCount, addItem, toggleItem }) => (
<div>
<h3>Shopping List ({itemCount} items)</h3>
<button onClick={() => addItem('Milk')}>
Add Milk
</button>
{items.map(item => (
<div key={item.uid}>
<span>{item.summary}</span>
<span> - {item.status === 'completed' ? 'DONE' : 'PENDING'}</span>
<button onClick={() => toggleItem(item.uid)}>
Toggle
</button>
</div>
))}
</div>
)}
</Todo>
// Hook approach
const shoppingList = useTodo('todo.shopping_list')
<div>
<h3>Shopping List ({shoppingList.itemCount} items)</h3>
<button onClick={() => shoppingList.addItem('Milk')}>
Add Milk
</button>
{shoppingList.items.map(item => (
<div key={item.uid}>
<span>{item.summary}</span>
<span> - {item.status === 'completed' ? 'DONE' : 'PENDING'}</span>
<button onClick={() => shoppingList.toggleItem(item.uid)}>
Toggle
</button>
</div>
))}
</div>
Component API
Basic Usage
import { Todo } from 'hass-react'
<Todo entityId="todo.shopping_list">
{(todoProps) => (
// Your UI here
)}
</Todo>
Render Props
The Todo component provides these props to your render function:
State Properties
itemCount(number) - Total number of items in the listitems(TodoItem[]) - Array of todo itemsisLoadingItems(boolean | undefined) - Whether items are being loaded
Support Properties
supportsAddItem(boolean) - List supports adding new itemssupportsRemoveItem(boolean) - List supports removing itemssupportsUpdateItem(boolean) - List supports updating item statussupportsClearCompleted(boolean) - List supports bulk clearing completed items
Control Methods
addItem(summary: string)- Add a new item to the listremoveItem(uid: string)- Remove an item from the listupdateItem(uid: string, status: 'needs_action' | 'completed')- Update item statustoggleItem(uid: string)- Toggle item between completed and needs actionclearCompleted()- Remove all completed items
Todo Item Structure
interface TodoItem {
uid: string // Unique identifier
summary: string // Item description
status: 'needs_action' | 'completed' // Current status
due?: string // Due date (optional)
description?: string // Extended description (optional)
}
Entity Properties
entityId(string) - The entity IDstate(string) - Raw state value from Home Assistantattributes(object) - All entity attributeslastChanged(Date) - When the entity last changedlastUpdated(Date) - When the entity was last updated
Hook API
Basic Usage
import { useTodo } from 'hass-react'
function MyComponent() {
const todoList = useTodo('todo.shopping_list')
// All the same properties as component render props
return <div>{todoList.itemCount} items</div>
}
The useTodo hook returns an object with all the same properties and methods as the component's render props.
List All Todo Lists
Use the useTodos hook to retrieve all available todo list entities:
import { useTodos } from 'hass-react'
function TodoListList() {
const todos = useTodos()
return (
<div>
<h2>Available Todo Lists ({todos.length})</h2>
{todos.map(todo => (
<div key={todo.entity_id}>
{todo.attributes.friendly_name || todo.entity_id}
</div>
))}
</div>
)
}
The useTodos hook fetches all todo list entities from Home Assistant and returns an array of todo list objects.
Examples
Simple Todo List
<Todo entityId="todo.tasks">
{({ items, itemCount, addItem, toggleItem, removeItem, attributes }) => (
<div>
<h3>{attributes.friendly_name} ({itemCount} items)</h3>
<div>
<button onClick={() => addItem('New task')}>
Add Task
</button>
</div>
{items.map(item => (
<div key={item.uid}>
<span style={{
textDecoration: item.status === 'completed' ? 'line-through' : 'none'
}}>
{item.summary}
</span>
<button onClick={() => toggleItem(item.uid)}>
{item.status === 'completed' ? 'Undo' : 'Complete'}
</button>
<button onClick={() => removeItem(item.uid)}>
Remove
</button>
</div>
))}
{items.length === 0 && <p>No items in list</p>}
</div>
)}
</Todo>
Shopping List with Add Form
<Todo entityId="todo.shopping_list">
{({ items, addItem, toggleItem, removeItem, attributes }) => {
const [newItem, setNewItem] = useState('')
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (newItem.trim()) {
addItem(newItem.trim())
setNewItem('')
}
}
return (
<div>
<h3>{attributes.friendly_name}</h3>
<form onSubmit={handleSubmit}>
<input
type="text"
value={newItem}
onChange={(e) => setNewItem(e.target.value)}
placeholder="Add item to shopping list"
/>
<button type="submit">Add</button>
</form>
<div>
{items
.filter(item => item.status === 'needs_action')
.map(item => (
<div key={item.uid}>
<span>{item.summary}</span>
<button onClick={() => toggleItem(item.uid)}>
Mark as Bought
</button>
<button onClick={() => removeItem(item.uid)}>
Remove
</button>
</div>
))}
</div>
<h4>Completed</h4>
<div>
{items
.filter(item => item.status === 'completed')
.map(item => (
<div key={item.uid}>
<span style={{ textDecoration: 'line-through' }}>
{item.summary}
</span>
<button onClick={() => toggleItem(item.uid)}>
Uncheck
</button>
<button onClick={() => removeItem(item.uid)}>
Remove
</button>
</div>
))}
</div>
</div>
)
}}
</Todo>
Todo List with Categories
<Todo entityId="todo.daily_tasks">
{({ items, addItem, toggleItem, clearCompleted, supportsClearCompleted, attributes }) => {
const pendingItems = items.filter(item => item.status === 'needs_action')
const completedItems = items.filter(item => item.status === 'completed')
return (
<div>
<h3>{attributes.friendly_name}</h3>
<div>
<strong>Pending Tasks ({pendingItems.length})</strong>
{pendingItems.map(item => (
<div key={item.uid}>
<span>{item.summary}</span>
{item.due && <span> - Due: {new Date(item.due).toLocaleDateString()}</span>}
<button onClick={() => toggleItem(item.uid)}>
Complete
</button>
</div>
))}
{pendingItems.length === 0 && <p>All tasks completed!</p>}
</div>
<div>
<strong>Completed Tasks ({completedItems.length})</strong>
{supportsClearCompleted && completedItems.length > 0 && (
<button onClick={clearCompleted}>
Clear All Completed
</button>
)}
{completedItems.map(item => (
<div key={item.uid}>
<span style={{ color: 'gray' }}>{item.summary}</span>
<button onClick={() => toggleItem(item.uid)}>
Undo
</button>
</div>
))}
</div>
<button onClick={() => addItem('New task')}>
Add Task
</button>
</div>
)
}}
</Todo>
Multiple Todo Lists
function TodoDashboard() {
const todoLists = [
'todo.shopping_list',
'todo.work_tasks',
'todo.household_chores'
]
return (
<div>
<h2>Todo Dashboard</h2>
{todoLists.map(entityId => (
<Todo key={entityId} entityId={entityId}>
{({ items, itemCount, addItem, attributes }) => {
const pendingCount = items.filter(item => item.status === 'needs_action').length
const completedCount = items.filter(item => item.status === 'completed').length
return (
<div>
<h3>{attributes.friendly_name}</h3>
<p>Pending: {pendingCount}, Completed: {completedCount}</p>
<button onClick={() => addItem('New item')}>
Quick Add
</button>
</div>
)
}}
</Todo>
))}
</div>
)
}
Todo List with Progress
<Todo entityId="todo.project_tasks">
{({ items, itemCount, toggleItem, attributes }) => {
const completedCount = items.filter(item => item.status === 'completed').length
const progressPercent = itemCount > 0 ? Math.round((completedCount / itemCount) * 100) : 0
return (
<div>
<h3>{attributes.friendly_name}</h3>
<div>
Progress: {completedCount} / {itemCount} ({progressPercent}%)
</div>
<div>
{items.map(item => (
<div key={item.uid}>
<label>
<input
type="checkbox"
checked={item.status === 'completed'}
onChange={() => toggleItem(item.uid)}
/>
{item.summary}
</label>
{item.description && <p>{item.description}</p>}
</div>
))}
</div>
</div>
)
}}
</Todo>
Using Hooks
import { useTodo } from 'hass-react'
function TodoCard({ entityId }) {
const todo = useTodo(entityId)
const [newItemText, setNewItemText] = useState('')
const handleAddItem = () => {
if (newItemText.trim()) {
todo.addItem(newItemText.trim())
setNewItemText('')
}
}
const getStatusSummary = () => {
const pending = todo.items.filter(item => item.status === 'needs_action').length
const completed = todo.items.filter(item => item.status === 'completed').length
return { pending, completed }
}
const { pending, completed } = getStatusSummary()
return (
<div>
<h3>{todo.attributes.friendly_name}</h3>
<div>
Total: {todo.itemCount} | Pending: {pending} | Completed: {completed}
</div>
{todo.supportsAddItem && (
<div>
<input
type="text"
value={newItemText}
onChange={(e) => setNewItemText(e.target.value)}
placeholder="New item"
/>
<button onClick={handleAddItem}>
Add
</button>
</div>
)}
<div>
{todo.items.map(item => (
<div key={item.uid}>
<span style={{
textDecoration: item.status === 'completed' ? 'line-through' : 'none',
color: item.status === 'completed' ? 'gray' : 'black'
}}>
{item.summary}
</span>
{todo.supportsUpdateItem && (
<button onClick={() => todo.toggleItem(item.uid)}>
{item.status === 'completed' ? 'Undo' : 'Done'}
</button>
)}
{todo.supportsRemoveItem && (
<button onClick={() => todo.removeItem(item.uid)}>
Delete
</button>
)}
</div>
))}
</div>
{todo.supportsClearCompleted && completed > 0 && (
<button onClick={todo.clearCompleted}>
Clear {completed} Completed Items
</button>
)}
<p>Last updated: {todo.lastUpdated.toLocaleTimeString()}</p>
</div>
)
}