1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-27 06:28:31 +00:00

Handle missing Miele status codes gracefully (#159124)

This commit is contained in:
Åke Strandberg
2025-12-15 20:58:02 +01:00
committed by GitHub
parent e292a67692
commit f85a684e31
6 changed files with 87 additions and 65 deletions

View File

@@ -98,50 +98,28 @@ DEVICE_TYPE_TAGS = {
}
class StateStatus(IntEnum):
class StateStatus(MieleEnum, missing_to_none=True):
"""Define appliance states."""
RESERVED = 0
OFF = 1
ON = 2
PROGRAMMED = 3
WAITING_TO_START = 4
IN_USE = 5
PAUSE = 6
PROGRAM_ENDED = 7
FAILURE = 8
PROGRAM_INTERRUPTED = 9
IDLE = 10
RINSE_HOLD = 11
SERVICE = 12
SUPERFREEZING = 13
SUPERCOOLING = 14
SUPERHEATING = 15
SUPERCOOLING_SUPERFREEZING = 146
AUTOCLEANING = 147
NOT_CONNECTED = 255
STATE_STATUS_TAGS = {
StateStatus.OFF: "off",
StateStatus.ON: "on",
StateStatus.PROGRAMMED: "programmed",
StateStatus.WAITING_TO_START: "waiting_to_start",
StateStatus.IN_USE: "in_use",
StateStatus.PAUSE: "pause",
StateStatus.PROGRAM_ENDED: "program_ended",
StateStatus.FAILURE: "failure",
StateStatus.PROGRAM_INTERRUPTED: "program_interrupted",
StateStatus.IDLE: "idle",
StateStatus.RINSE_HOLD: "rinse_hold",
StateStatus.SERVICE: "service",
StateStatus.SUPERFREEZING: "superfreezing",
StateStatus.SUPERCOOLING: "supercooling",
StateStatus.SUPERHEATING: "superheating",
StateStatus.SUPERCOOLING_SUPERFREEZING: "supercooling_superfreezing",
StateStatus.AUTOCLEANING: "autocleaning",
StateStatus.NOT_CONNECTED: "not_connected",
}
reserved = 0
off = 1
on = 2
programmed = 3
waiting_to_start = 4
in_use = 5
pause = 6
program_ended = 7
failure = 8
program_interrupted = 9
idle = 10
rinse_hold = 11
service = 12
superfreezing = 13
supercooling = 14
superheating = 15
supercooling_superfreezing = 146
autocleaning = 147
not_connected = 255
class MieleActions(IntEnum):

View File

@@ -73,5 +73,5 @@ class MieleEntity(CoordinatorEntity[MieleDataUpdateCoordinator]):
return (
super().available
and self._device_id in self.coordinator.data.devices
and (self.device.state_status is not StateStatus.NOT_CONNECTED)
and (self.device.state_status is not StateStatus.not_connected)
)

View File

@@ -38,7 +38,6 @@ from .const import (
DOMAIN,
PROGRAM_IDS,
PROGRAM_PHASE,
STATE_STATUS_TAGS,
MieleAppliance,
PlatePowerStep,
StateDryingStep,
@@ -195,7 +194,7 @@ SENSOR_TYPES: Final[tuple[MieleSensorDefinition, ...]] = (
translation_key="status",
value_fn=lambda value: value.state_status,
device_class=SensorDeviceClass.ENUM,
options=sorted(set(STATE_STATUS_TAGS.values())),
options=sorted(set(StateStatus.keys())),
),
),
MieleSensorDefinition(
@@ -930,7 +929,7 @@ class MieleStatusSensor(MieleSensor):
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return STATE_STATUS_TAGS.get(StateStatus(self.device.state_status))
return StateStatus(self.device.state_status).name
@property
def available(self) -> bool:
@@ -998,11 +997,11 @@ class MieleTimeSensor(MieleRestorableSensor):
"""Update the last value of the sensor."""
current_value = self.entity_description.value_fn(self.device)
current_status = StateStatus(self.device.state_status)
current_status = StateStatus(self.device.state_status).name
# report end-specific value when program ends (some devices are immediately reporting 0...)
if (
current_status == StateStatus.PROGRAM_ENDED
current_status == StateStatus.program_ended.name
and self.entity_description.end_value_fn is not None
):
self._attr_native_value = self.entity_description.end_value_fn(
@@ -1010,11 +1009,15 @@ class MieleTimeSensor(MieleRestorableSensor):
)
# keep value when program ends if no function is specified
elif current_status == StateStatus.PROGRAM_ENDED:
elif current_status == StateStatus.program_ended.name:
pass
# force unknown when appliance is not working (some devices are keeping last value until a new cycle starts)
elif current_status in (StateStatus.OFF, StateStatus.ON, StateStatus.IDLE):
elif current_status in (
StateStatus.off.name,
StateStatus.on.name,
StateStatus.idle.name,
):
self._attr_native_value = None
# otherwise, cache value and return it
@@ -1030,7 +1033,7 @@ class MieleAbsoluteTimeSensor(MieleRestorableSensor):
def _update_native_value(self) -> None:
"""Update the last value of the sensor."""
current_value = self.entity_description.value_fn(self.device)
current_status = StateStatus(self.device.state_status)
current_status = StateStatus(self.device.state_status).name
# The API reports with minute precision, to avoid changing
# the value too often, we keep the cached value if it differs
@@ -1043,11 +1046,15 @@ class MieleAbsoluteTimeSensor(MieleRestorableSensor):
< current_value
< self._previous_value + timedelta(seconds=90)
)
) or current_status == StateStatus.PROGRAM_ENDED:
) or current_status == StateStatus.program_ended.name:
return
# force unknown when appliance is not working (some devices are keeping last value until a new cycle starts)
if current_status in (StateStatus.OFF, StateStatus.ON, StateStatus.IDLE):
if current_status in (
StateStatus.off.name,
StateStatus.on.name,
StateStatus.idle.name,
):
self._attr_native_value = None
# otherwise, cache value and return it
@@ -1064,7 +1071,7 @@ class MieleConsumptionSensor(MieleRestorableSensor):
def _update_native_value(self) -> None:
"""Update the last value of the sensor."""
current_value = self.entity_description.value_fn(self.device)
current_status = StateStatus(self.device.state_status)
current_status = StateStatus(self.device.state_status).name
# Guard for corrupt restored value
restored_value = (
self._attr_native_value
@@ -1079,12 +1086,12 @@ class MieleConsumptionSensor(MieleRestorableSensor):
# Force unknown when appliance is not able to report consumption
if current_status in (
StateStatus.ON,
StateStatus.OFF,
StateStatus.PROGRAMMED,
StateStatus.WAITING_TO_START,
StateStatus.IDLE,
StateStatus.SERVICE,
StateStatus.on.name,
StateStatus.off.name,
StateStatus.programmed.name,
StateStatus.waiting_to_start.name,
StateStatus.idle.name,
StateStatus.service.name,
):
self._is_reporting = False
self._attr_native_value = None
@@ -1093,7 +1100,7 @@ class MieleConsumptionSensor(MieleRestorableSensor):
# only after a while, so it is necessary to force 0 until we see the 0 value coming from API, unless
# we already saw a valid value in this cycle from cache
elif (
current_status in (StateStatus.IN_USE, StateStatus.PAUSE)
current_status in (StateStatus.in_use.name, StateStatus.pause.name)
and not self._is_reporting
and last_value > 0
):
@@ -1101,7 +1108,7 @@ class MieleConsumptionSensor(MieleRestorableSensor):
self._is_reporting = True
elif (
current_status in (StateStatus.IN_USE, StateStatus.PAUSE)
current_status in (StateStatus.in_use.name, StateStatus.pause.name)
and not self._is_reporting
and current_value is not None
and cast(int, current_value) > 0
@@ -1109,7 +1116,7 @@ class MieleConsumptionSensor(MieleRestorableSensor):
self._attr_native_value = 0
# keep value when program ends
elif current_status == StateStatus.PROGRAM_ENDED:
elif current_status == StateStatus.program_ended.name:
pass
else:

View File

@@ -1061,6 +1061,7 @@
"program_ended": "Program ended",
"program_interrupted": "Program interrupted",
"programmed": "Programmed",
"reserved": "Reserved",
"rinse_hold": "Rinse hold",
"service": "Service",
"supercooling": "Supercooling",

View File

@@ -58,7 +58,7 @@ SWITCH_TYPES: Final[tuple[MieleSwitchDefinition, ...]] = (
description=MieleSwitchDescription(
key="supercooling",
value_fn=lambda value: value.state_status,
on_value=StateStatus.SUPERCOOLING,
on_value=StateStatus.supercooling,
translation_key="supercooling",
on_cmd_data={PROCESS_ACTION: MieleActions.START_SUPERCOOL},
off_cmd_data={PROCESS_ACTION: MieleActions.STOP_SUPERCOOL},
@@ -73,7 +73,7 @@ SWITCH_TYPES: Final[tuple[MieleSwitchDefinition, ...]] = (
description=MieleSwitchDescription(
key="superfreezing",
value_fn=lambda value: value.state_status,
on_value=StateStatus.SUPERFREEZING,
on_value=StateStatus.superfreezing,
translation_key="superfreezing",
on_cmd_data={PROCESS_ACTION: MieleActions.START_SUPERFREEZE},
off_cmd_data={PROCESS_ACTION: MieleActions.STOP_SUPERFREEZE},

View File

@@ -17,6 +17,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -73,6 +74,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -367,6 +369,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -423,6 +426,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -458,6 +462,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -514,6 +519,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1431,6 +1437,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1487,6 +1494,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1522,6 +1530,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1578,6 +1587,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1613,6 +1623,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1669,6 +1680,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1872,6 +1884,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -1928,6 +1941,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -2453,6 +2467,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -2509,6 +2524,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -2600,6 +2616,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -2656,6 +2673,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -2691,6 +2709,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -2747,6 +2766,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -3706,6 +3726,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -3762,6 +3783,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -3853,6 +3875,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -3909,6 +3932,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -4823,6 +4847,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -4879,6 +4904,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -4970,6 +4996,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -5026,6 +5053,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -5061,6 +5089,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -5117,6 +5146,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -6076,6 +6106,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -6132,6 +6163,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -6223,6 +6255,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -6279,6 +6312,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -7193,6 +7227,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',
@@ -7249,6 +7284,7 @@
'program_ended',
'program_interrupted',
'programmed',
'reserved',
'rinse_hold',
'service',
'supercooling',