Deduplicate shared logic in Matter vacuum commands (#147578)

Get the run mode by tag in a single place to avoid code duplication.
Also raise an error if the run mode (unexpectedly) is not found.
This commit is contained in:
Stefan Agner 2025-06-26 11:54:52 +02:00 committed by GitHub
parent d55ecd885e
commit be49296547
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 83 additions and 17 deletions

View File

@ -17,6 +17,7 @@ from homeassistant.components.vacuum import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .entity import MatterEntity
@ -67,20 +68,31 @@ class MatterVacuum(MatterEntity, StateVacuumEntity):
entity_description: StateVacuumEntityDescription
_platform_translation_key = "vacuum"
def _get_run_mode_by_tag(
self, tag: ModeTag
) -> clusters.RvcRunMode.Structs.ModeOptionStruct | None:
"""Get the run mode by tag."""
supported_run_modes = self._supported_run_modes or {}
for mode in supported_run_modes.values():
for t in mode.modeTags:
if t.value == tag.value:
return mode
return None
async def async_stop(self, **kwargs: Any) -> None:
"""Stop the vacuum cleaner."""
# We simply set the RvcRunMode to the first runmode
# that has the idle tag to stop the vacuum cleaner.
# this is compatible with both Matter 1.2 and 1.3+ devices.
supported_run_modes = self._supported_run_modes or {}
for mode in supported_run_modes.values():
for tag in mode.modeTags:
if tag.value == ModeTag.IDLE:
# stop the vacuum by changing the run mode to idle
await self.send_device_command(
clusters.RvcRunMode.Commands.ChangeToMode(newMode=mode.mode)
)
return
mode = self._get_run_mode_by_tag(ModeTag.IDLE)
if mode is None:
raise HomeAssistantError(
"No supported run mode found to stop the vacuum cleaner."
)
await self.send_device_command(
clusters.RvcRunMode.Commands.ChangeToMode(newMode=mode.mode)
)
async def async_return_to_base(self, **kwargs: Any) -> None:
"""Set the vacuum cleaner to return to the dock."""
@ -110,14 +122,15 @@ class MatterVacuum(MatterEntity, StateVacuumEntity):
# We simply set the RvcRunMode to the first runmode
# that has the cleaning tag to start the vacuum cleaner.
# this is compatible with both Matter 1.2 and 1.3+ devices.
supported_run_modes = self._supported_run_modes or {}
for mode in supported_run_modes.values():
for tag in mode.modeTags:
if tag.value == ModeTag.CLEANING:
await self.send_device_command(
clusters.RvcRunMode.Commands.ChangeToMode(newMode=mode.mode)
)
return
mode = self._get_run_mode_by_tag(ModeTag.CLEANING)
if mode is None:
raise HomeAssistantError(
"No supported run mode found to start the vacuum cleaner."
)
await self.send_device_command(
clusters.RvcRunMode.Commands.ChangeToMode(newMode=mode.mode)
)
async def async_pause(self) -> None:
"""Pause the cleaning task."""

View File

@ -9,6 +9,7 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
@ -238,3 +239,55 @@ async def test_vacuum_updates(
state = hass.states.get(entity_id)
assert state
assert state.state == "error"
@pytest.mark.parametrize("node_fixture", ["vacuum_cleaner"])
async def test_vacuum_actions_no_supported_run_modes(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test vacuum entity actions when no supported run modes are available."""
# Fetch translations
await async_setup_component(hass, "homeassistant", {})
entity_id = "vacuum.mock_vacuum"
state = hass.states.get(entity_id)
assert state
# Set empty supported modes to simulate no available run modes
# RvcRunMode cluster ID is 84, SupportedModes attribute ID is 0
set_node_attribute(matter_node, 1, 84, 0, [])
# RvcOperationalState cluster ID is 97, AcceptedCommandList attribute ID is 65529
set_node_attribute(matter_node, 1, 97, 65529, [])
await trigger_subscription_callback(hass, matter_client)
# test start action fails when no supported run modes
with pytest.raises(
HomeAssistantError,
match="No supported run mode found to start the vacuum cleaner",
):
await hass.services.async_call(
"vacuum",
"start",
{
"entity_id": entity_id,
},
blocking=True,
)
# test stop action fails when no supported run modes
with pytest.raises(
HomeAssistantError,
match="No supported run mode found to stop the vacuum cleaner",
):
await hass.services.async_call(
"vacuum",
"stop",
{
"entity_id": entity_id,
},
blocking=True,
)
# Ensure no commands were sent to the device
assert matter_client.send_device_command.call_count == 0