feat: mutable child lock, remote control, delay start; stale-data fallback on poll failure
Child lock and remote control promoted from read-only binary sensors to writable switches. Delay start time promoted from a raw string sensor to a writable TimeEntity. On persistent poll failure (both initial attempt and reconnect), fall back to the last known resource values instead of raising UpdateFailed. This keeps entities available with stale data rather than flooding history with Unavailable gaps whenever the device drops a /device/0 block fetch. UpdateFailed is still raised on the very first poll before any data exists.
This commit is contained in:
@@ -246,6 +246,10 @@ class LocalThingsCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
try:
|
||||
resources = await self.hass.async_add_executor_job(self._poll_once)
|
||||
except Exception as e2:
|
||||
_LOGGER.error("poll failed after reconnect: %s", e2)
|
||||
if self._last_resources:
|
||||
_LOGGER.debug("Full error:", exc_info=e2)
|
||||
return flatten(self.bound, self._last_resources)
|
||||
raise UpdateFailed(f"poll failed after reconnect: {e2}") from e2
|
||||
|
||||
self._last_resources = resources
|
||||
|
||||
@@ -11,7 +11,7 @@ against live device dumps:
|
||||
/filter/waterfilter/vs/0 -> x.com.samsung.da.filterUsage / filterStatus
|
||||
"""
|
||||
from ..capability import Capability
|
||||
from ..entities import BinarySensorDesc, SensorDesc, SwitchDesc
|
||||
from ..entities import SensorDesc, SwitchDesc
|
||||
|
||||
|
||||
def _num(v):
|
||||
@@ -46,19 +46,25 @@ def _active_alarm_codes(items):
|
||||
KIDS_LOCK = Capability(
|
||||
href='/kidslock/vs/0',
|
||||
entities=(
|
||||
BinarySensorDesc(key='child_lock', field='x.com.samsung.da.kidsLock',
|
||||
name='Child lock', device_class='lock',
|
||||
value_fn=lambda v: v != 'Ready'),
|
||||
SwitchDesc(key='child_lock', field='x.com.samsung.da.kidsLock',
|
||||
name='Child lock', device_class='lock',
|
||||
value_fn=lambda v: v != 'Ready',
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
['kidslock', 'vs', '0'],
|
||||
{'x.com.samsung.da.kidsLock': 'Enable' if p == 'On' else 'Ready'})),
|
||||
),
|
||||
)
|
||||
|
||||
REMOTE_CONTROL = Capability(
|
||||
href='/remotectrl/vs/0',
|
||||
entities=(
|
||||
BinarySensorDesc(key='remote_control',
|
||||
field='x.com.samsung.da.remoteControlEnabled',
|
||||
name='Remote control', device_class='connectivity',
|
||||
value_fn=lambda v: str(v).lower() == 'true'),
|
||||
SwitchDesc(key='remote_control',
|
||||
field='x.com.samsung.da.remoteControlEnabled',
|
||||
name='Remote control', device_class='connectivity',
|
||||
value_fn=lambda v: str(v).lower() == 'true',
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
['remotectrl', 'vs', '0'],
|
||||
{'x.com.samsung.da.remoteControlEnabled': 'true' if p == 'On' else 'false'})),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
Shared by dryer/dishwasher/oven/washer families.
|
||||
"""
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from datetime import datetime, timezone, timedelta, time as dt_time
|
||||
|
||||
from ..capability import Capability
|
||||
from ..entities import BinarySensorDesc, ButtonDesc, SensorDesc
|
||||
from ..entities import BinarySensorDesc, ButtonDesc, SensorDesc, TimeDesc
|
||||
|
||||
_SAMSUNG_STATE_TO_OCF = {
|
||||
'Ready': 'idle', 'Run': 'active', 'Running': 'active',
|
||||
@@ -32,6 +32,16 @@ def _active_when(rep):
|
||||
return _SAMSUNG_STATE_TO_OCF.get(rep.get('x.com.samsung.da.state')) == 'active'
|
||||
|
||||
|
||||
def _parse_hms(v):
|
||||
if not v:
|
||||
return None
|
||||
try:
|
||||
h, m, s = v.split(':')
|
||||
return dt_time(int(h) % 24, int(m), int(s))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _finish_time(remaining_str):
|
||||
if not remaining_str:
|
||||
return None
|
||||
@@ -66,8 +76,12 @@ OPERATIONAL_STATE = Capability(
|
||||
SensorDesc(key='finish_time', field='x.com.samsung.da.remainingTime',
|
||||
name='Estimated finish', device_class='timestamp',
|
||||
value_fn=_finish_time),
|
||||
SensorDesc(key='delay_start_time', field='x.com.samsung.da.delayStartTime',
|
||||
name='Delay start time', icon='mdi:timer-pause'),
|
||||
TimeDesc(key='delay_start_time', field='x.com.samsung.da.delayStartTime',
|
||||
name='Delay start',
|
||||
value_fn=_parse_hms,
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
['operational', 'state', 'vs', '0'],
|
||||
{'x.com.samsung.da.delayStartTime': f'{p.hour}:{p.minute:02d}:{p.second:02d}'})),
|
||||
ButtonDesc(key='start', field='', name='Start cycle', payload='Run',
|
||||
icon='mdi:play',
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
|
||||
Reference in New Issue
Block a user