From 524c2129eb52ad2bbb913204ea592e442379b2d3 Mon Sep 17 00:00:00 2001 From: Daniel Jolly Date: Wed, 1 Apr 2026 10:28:59 -0400 Subject: [PATCH] Fix ToDo List Intents item casing (#160177) Co-authored-by: Erik Montnemery Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com> --- homeassistant/components/todo/intent.py | 13 ++++++++----- tests/components/todo/test_intent.py | 12 ++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/todo/intent.py b/homeassistant/components/todo/intent.py index 883f7fac6f1..cc86b7a095f 100644 --- a/homeassistant/components/todo/intent.py +++ b/homeassistant/components/todo/intent.py @@ -91,7 +91,7 @@ class ListAddItemIntentHandler(ListBaseIntentHandler): # Add to list await target_list.async_create_todo_item( - TodoItem(summary=item, status=TodoItemStatus.NEEDS_ACTION) + TodoItem(summary=item.capitalize(), status=TodoItemStatus.NEEDS_ACTION) ) @@ -108,9 +108,9 @@ class ListCompleteItemIntentHandler(ListBaseIntentHandler): matching_item = None for todo_item in target_list.todo_items or (): if ( - item in (todo_item.uid, todo_item.summary) - and todo_item.status == TodoItemStatus.NEEDS_ACTION - ): + item == todo_item.uid + or item.casefold() == (todo_item.summary or "").casefold() + ) and todo_item.status == TodoItemStatus.NEEDS_ACTION: matching_item = todo_item break if not matching_item or not matching_item.uid: @@ -140,7 +140,10 @@ class ListRemoveItemIntentHandler(ListBaseIntentHandler): # Find item in list matching_item = None for todo_item in target_list.todo_items or (): - if item in (todo_item.uid, todo_item.summary): + if ( + item == todo_item.uid + or item.casefold() == (todo_item.summary or "").casefold() + ): matching_item = todo_item break if not matching_item or not matching_item.uid: diff --git a/tests/components/todo/test_intent.py b/tests/components/todo/test_intent.py index 0b7ba0c628d..78953c47acc 100644 --- a/tests/components/todo/test_intent.py +++ b/tests/components/todo/test_intent.py @@ -66,7 +66,7 @@ async def test_add_item_intent( assert len(entity1.items) == 1 assert len(entity2.items) == 0 - assert entity1.items[0].summary == "beer" # summary is trimmed + assert entity1.items[0].summary == "Beer" # summary is trimmed and capitalized assert entity1.items[0].status == TodoItemStatus.NEEDS_ACTION entity1.items.clear() @@ -82,7 +82,7 @@ async def test_add_item_intent( assert len(entity1.items) == 0 assert len(entity2.items) == 1 - assert entity2.items[0].summary == "cheese" + assert entity2.items[0].summary == "Cheese" assert entity2.items[0].status == TodoItemStatus.NEEDS_ACTION # List name is case insensitive @@ -97,7 +97,7 @@ async def test_add_item_intent( assert len(entity1.items) == 0 assert len(entity2.items) == 2 - assert entity2.items[1].summary == "wine" + assert entity2.items[1].summary == "Wine" assert entity2.items[1].status == TodoItemStatus.NEEDS_ACTION # Should fail if lists are not exposed @@ -187,8 +187,8 @@ async def test_complete_item_intent( """Test the complete item intent.""" entity1 = MockTodoListEntity( [ - TodoItem(summary="beer", uid="1", status=TodoItemStatus.NEEDS_ACTION), - TodoItem(summary="wine", uid="2", status=TodoItemStatus.NEEDS_ACTION), + TodoItem(summary="Beer", uid="1", status=TodoItemStatus.NEEDS_ACTION), + TodoItem(summary="Wine", uid="2", status=TodoItemStatus.NEEDS_ACTION), ] ) entity1._attr_name = "List 1" @@ -298,7 +298,7 @@ async def test_remove_item_intent( """Test the remove item intent.""" entity1 = MockTodoListEntity( [ - TodoItem(summary="beer", uid="1", status=TodoItemStatus.NEEDS_ACTION), + TodoItem(summary="Beer", uid="1", status=TodoItemStatus.NEEDS_ACTION), TodoItem(summary="wine", uid="2", status=TodoItemStatus.NEEDS_ACTION), TodoItem(summary="beer", uid="3", status=TodoItemStatus.COMPLETED), ]