diff --git a/homeassistant/helpers/intent.py b/homeassistant/helpers/intent.py index df5e40911ff..f8c8b2c6d8c 100644 --- a/homeassistant/helpers/intent.py +++ b/homeassistant/helpers/intent.py @@ -169,10 +169,13 @@ def _fuzzymatch(name: str, items: Iterable[T], key: Callable[[T], str]) -> Optio for idx, item in enumerate(items): match = regex.search(key(item)) if match: - # Add index so we pick first match in case same group and start - matches.append((len(match.group()), match.start(), idx, item)) + # Add key length so we prefer shorter keys with the same group and start. + # Add index so we pick first match in case same group, start, and key length. + matches.append( + (len(match.group()), match.start(), len(key(item)), idx, item) + ) - return sorted(matches)[0][3] if matches else None + return sorted(matches)[0][4] if matches else None class ServiceIntentHandler(IntentHandler): diff --git a/tests/helpers/test_intent.py b/tests/helpers/test_intent.py index bbb6e394ee0..e328d30ab7a 100644 --- a/tests/helpers/test_intent.py +++ b/tests/helpers/test_intent.py @@ -38,3 +38,21 @@ def test_async_validate_slots(): handler1.async_validate_slots( {"name": {"value": "kitchen"}, "probability": {"value": "0.5"}} ) + + +def test_fuzzy_match(): + """Test _fuzzymatch.""" + state1 = State("light.living_room_northwest", "off") + state2 = State("light.living_room_north", "off") + state3 = State("light.living_room_northeast", "off") + state4 = State("light.living_room_west", "off") + state5 = State("light.living_room", "off") + states = [state1, state2, state3, state4, state5] + + state = intent._fuzzymatch("Living Room", states, lambda state: state.name) + assert state == state5 + + state = intent._fuzzymatch( + "Living Room Northwest", states, lambda state: state.name + ) + assert state == state1