mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Fix operational state and vacuum state for matter vacuum (#147466)
This commit is contained in:
parent
c54ce7eabd
commit
977e8adbfb
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
|
|
||||||
@ -74,6 +74,11 @@ OPERATIONAL_STATE_MAP = {
|
|||||||
clusters.OperationalState.Enums.OperationalStateEnum.kRunning: "running",
|
clusters.OperationalState.Enums.OperationalStateEnum.kRunning: "running",
|
||||||
clusters.OperationalState.Enums.OperationalStateEnum.kPaused: "paused",
|
clusters.OperationalState.Enums.OperationalStateEnum.kPaused: "paused",
|
||||||
clusters.OperationalState.Enums.OperationalStateEnum.kError: "error",
|
clusters.OperationalState.Enums.OperationalStateEnum.kError: "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
RVC_OPERATIONAL_STATE_MAP = {
|
||||||
|
# enum with known Operation state values which we can translate
|
||||||
|
**OPERATIONAL_STATE_MAP,
|
||||||
clusters.RvcOperationalState.Enums.OperationalStateEnum.kSeekingCharger: "seeking_charger",
|
clusters.RvcOperationalState.Enums.OperationalStateEnum.kSeekingCharger: "seeking_charger",
|
||||||
clusters.RvcOperationalState.Enums.OperationalStateEnum.kCharging: "charging",
|
clusters.RvcOperationalState.Enums.OperationalStateEnum.kCharging: "charging",
|
||||||
clusters.RvcOperationalState.Enums.OperationalStateEnum.kDocked: "docked",
|
clusters.RvcOperationalState.Enums.OperationalStateEnum.kDocked: "docked",
|
||||||
@ -171,6 +176,10 @@ class MatterOperationalStateSensorEntityDescription(MatterSensorEntityDescriptio
|
|||||||
state_list_attribute: type[ClusterAttributeDescriptor] = (
|
state_list_attribute: type[ClusterAttributeDescriptor] = (
|
||||||
clusters.OperationalState.Attributes.OperationalStateList
|
clusters.OperationalState.Attributes.OperationalStateList
|
||||||
)
|
)
|
||||||
|
state_attribute: type[ClusterAttributeDescriptor] = (
|
||||||
|
clusters.OperationalState.Attributes.OperationalState
|
||||||
|
)
|
||||||
|
state_map: dict[int, str] = field(default_factory=lambda: OPERATIONAL_STATE_MAP)
|
||||||
|
|
||||||
|
|
||||||
class MatterSensor(MatterEntity, SensorEntity):
|
class MatterSensor(MatterEntity, SensorEntity):
|
||||||
@ -245,15 +254,15 @@ class MatterOperationalStateSensor(MatterSensor):
|
|||||||
for state in operational_state_list:
|
for state in operational_state_list:
|
||||||
# prefer translateable (known) state from mapping,
|
# prefer translateable (known) state from mapping,
|
||||||
# fallback to the raw state label as given by the device/manufacturer
|
# fallback to the raw state label as given by the device/manufacturer
|
||||||
states_map[state.operationalStateID] = OPERATIONAL_STATE_MAP.get(
|
states_map[state.operationalStateID] = (
|
||||||
state.operationalStateID, slugify(state.operationalStateLabel)
|
self.entity_description.state_map.get(
|
||||||
|
state.operationalStateID, slugify(state.operationalStateLabel)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.states_map = states_map
|
self.states_map = states_map
|
||||||
self._attr_options = list(states_map.values())
|
self._attr_options = list(states_map.values())
|
||||||
self._attr_native_value = states_map.get(
|
self._attr_native_value = states_map.get(
|
||||||
self.get_matter_attribute_value(
|
self.get_matter_attribute_value(self.entity_description.state_attribute)
|
||||||
clusters.OperationalState.Attributes.OperationalState
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -999,6 +1008,8 @@ DISCOVERY_SCHEMAS = [
|
|||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
translation_key="operational_state",
|
translation_key="operational_state",
|
||||||
state_list_attribute=clusters.RvcOperationalState.Attributes.OperationalStateList,
|
state_list_attribute=clusters.RvcOperationalState.Attributes.OperationalStateList,
|
||||||
|
state_attribute=clusters.RvcOperationalState.Attributes.OperationalState,
|
||||||
|
state_map=RVC_OPERATIONAL_STATE_MAP,
|
||||||
),
|
),
|
||||||
entity_class=MatterOperationalStateSensor,
|
entity_class=MatterOperationalStateSensor,
|
||||||
required_attributes=(
|
required_attributes=(
|
||||||
@ -1016,6 +1027,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
translation_key="operational_state",
|
translation_key="operational_state",
|
||||||
state_list_attribute=clusters.OvenCavityOperationalState.Attributes.OperationalStateList,
|
state_list_attribute=clusters.OvenCavityOperationalState.Attributes.OperationalStateList,
|
||||||
|
state_attribute=clusters.OvenCavityOperationalState.Attributes.OperationalState,
|
||||||
),
|
),
|
||||||
entity_class=MatterOperationalStateSensor,
|
entity_class=MatterOperationalStateSensor,
|
||||||
required_attributes=(
|
required_attributes=(
|
||||||
|
@ -30,10 +30,10 @@ class OperationalState(IntEnum):
|
|||||||
Combination of generic OperationalState and RvcOperationalState.
|
Combination of generic OperationalState and RvcOperationalState.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NO_ERROR = 0x00
|
STOPPED = 0x00
|
||||||
UNABLE_TO_START_OR_RESUME = 0x01
|
RUNNING = 0x01
|
||||||
UNABLE_TO_COMPLETE_OPERATION = 0x02
|
PAUSED = 0x02
|
||||||
COMMAND_INVALID_IN_STATE = 0x03
|
ERROR = 0x03
|
||||||
SEEKING_CHARGER = 0x40
|
SEEKING_CHARGER = 0x40
|
||||||
CHARGING = 0x41
|
CHARGING = 0x41
|
||||||
DOCKED = 0x42
|
DOCKED = 0x42
|
||||||
@ -95,7 +95,7 @@ class MatterVacuum(MatterEntity, StateVacuumEntity):
|
|||||||
|
|
||||||
async def async_pause(self) -> None:
|
async def async_pause(self) -> None:
|
||||||
"""Pause the cleaning task."""
|
"""Pause the cleaning task."""
|
||||||
await self.send_device_command(clusters.OperationalState.Commands.Pause())
|
await self.send_device_command(clusters.RvcOperationalState.Commands.Pause())
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _update_from_device(self) -> None:
|
def _update_from_device(self) -> None:
|
||||||
@ -120,11 +120,10 @@ class MatterVacuum(MatterEntity, StateVacuumEntity):
|
|||||||
state = VacuumActivity.DOCKED
|
state = VacuumActivity.DOCKED
|
||||||
elif operational_state == OperationalState.SEEKING_CHARGER:
|
elif operational_state == OperationalState.SEEKING_CHARGER:
|
||||||
state = VacuumActivity.RETURNING
|
state = VacuumActivity.RETURNING
|
||||||
elif operational_state in (
|
elif operational_state == OperationalState.ERROR:
|
||||||
OperationalState.UNABLE_TO_COMPLETE_OPERATION,
|
|
||||||
OperationalState.UNABLE_TO_START_OR_RESUME,
|
|
||||||
):
|
|
||||||
state = VacuumActivity.ERROR
|
state = VacuumActivity.ERROR
|
||||||
|
elif operational_state == OperationalState.PAUSED:
|
||||||
|
state = VacuumActivity.PAUSED
|
||||||
elif (run_mode := self._supported_run_modes.get(run_mode_raw)) is not None:
|
elif (run_mode := self._supported_run_modes.get(run_mode_raw)) is not None:
|
||||||
tags = {x.value for x in run_mode.modeTags}
|
tags = {x.value for x in run_mode.modeTags}
|
||||||
if ModeTag.CLEANING in tags:
|
if ModeTag.CLEANING in tags:
|
||||||
@ -201,7 +200,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
entity_class=MatterVacuum,
|
entity_class=MatterVacuum,
|
||||||
required_attributes=(
|
required_attributes=(
|
||||||
clusters.RvcRunMode.Attributes.CurrentMode,
|
clusters.RvcRunMode.Attributes.CurrentMode,
|
||||||
clusters.RvcOperationalState.Attributes.CurrentPhase,
|
clusters.RvcOperationalState.Attributes.OperationalState,
|
||||||
),
|
),
|
||||||
optional_attributes=(
|
optional_attributes=(
|
||||||
clusters.RvcCleanMode.Attributes.CurrentMode,
|
clusters.RvcCleanMode.Attributes.CurrentMode,
|
||||||
|
@ -3775,7 +3775,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unknown',
|
'state': 'running',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[oven][sensor.mock_oven_temperature_2-entry]
|
# name: test_sensors[oven][sensor.mock_oven_temperature_2-entry]
|
||||||
@ -6433,7 +6433,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unknown',
|
'state': 'stopped',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[window_covering_full][sensor.mock_full_window_covering_target_opening_position-entry]
|
# name: test_sensors[window_covering_full][sensor.mock_full_window_covering_target_opening_position-entry]
|
||||||
|
@ -93,7 +93,7 @@ async def test_vacuum_actions(
|
|||||||
assert matter_client.send_device_command.call_args == call(
|
assert matter_client.send_device_command.call_args == call(
|
||||||
node_id=matter_node.node_id,
|
node_id=matter_node.node_id,
|
||||||
endpoint_id=1,
|
endpoint_id=1,
|
||||||
command=clusters.OperationalState.Commands.Pause(),
|
command=clusters.RvcOperationalState.Commands.Pause(),
|
||||||
)
|
)
|
||||||
matter_client.send_device_command.reset_mock()
|
matter_client.send_device_command.reset_mock()
|
||||||
|
|
||||||
@ -168,19 +168,26 @@ async def test_vacuum_updates(
|
|||||||
assert state
|
assert state
|
||||||
assert state.state == "returning"
|
assert state.state == "returning"
|
||||||
|
|
||||||
# confirm state is 'error' by setting the operational state to 0x01
|
# confirm state is 'idle' by setting the operational state to 0x01 (running) but mode is idle
|
||||||
set_node_attribute(matter_node, 1, 97, 4, 0x01)
|
set_node_attribute(matter_node, 1, 97, 4, 0x01)
|
||||||
await trigger_subscription_callback(hass, matter_client)
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state
|
assert state
|
||||||
assert state.state == "error"
|
assert state.state == "idle"
|
||||||
|
|
||||||
# confirm state is 'error' by setting the operational state to 0x02
|
# confirm state is 'idle' by setting the operational state to 0x01 (running) but mode is cleaning
|
||||||
|
set_node_attribute(matter_node, 1, 97, 4, 0x01)
|
||||||
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "idle"
|
||||||
|
|
||||||
|
# confirm state is 'paused' by setting the operational state to 0x02
|
||||||
set_node_attribute(matter_node, 1, 97, 4, 0x02)
|
set_node_attribute(matter_node, 1, 97, 4, 0x02)
|
||||||
await trigger_subscription_callback(hass, matter_client)
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state
|
assert state
|
||||||
assert state.state == "error"
|
assert state.state == "paused"
|
||||||
|
|
||||||
# confirm state is 'cleaning' by setting;
|
# confirm state is 'cleaning' by setting;
|
||||||
# - the operational state to 0x00
|
# - the operational state to 0x00
|
||||||
@ -211,3 +218,11 @@ async def test_vacuum_updates(
|
|||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state
|
assert state
|
||||||
assert state.state == "unknown"
|
assert state.state == "unknown"
|
||||||
|
|
||||||
|
# confirm state is 'error' by setting;
|
||||||
|
# - the operational state to 0x03
|
||||||
|
set_node_attribute(matter_node, 1, 97, 4, 3)
|
||||||
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "error"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user