Stop surfacing '_OFF'-suffixed placeholder alarm codes (#166)

Samsung pre-populates /alarms/vs/0 with one row per supported alarm type,
each carrying a '<Name>_OFF' placeholder code (no 'Deleted' state at all)
when that alarm isn't firing. common._active_alarm_codes only ever
filtered on 'Deleted' state, so every device using this shared capability
(including the AC family) showed these inert placeholders --
'ErrorCode_OFF', 'FilterAlarm_OFF' -- as if they were live alarms.

Confirmed the '_OFF' suffix convention holds across every alarm code seen
in this repo's fixtures so far (ErrorCode_OFF, FilterAlarm_OFF, OV_E_OFF,
CT_E_OFF, WaterTankFull_OFF, AC_V_0002_OFF, all placeholders; DoorA_Opened,
FilterAlarm, SNSF_Reached, all genuinely active with no suffix) -- issue
#166's own dump has both a FilterAlarm_OFF placeholder and, on a second
unit, a live FilterAlarm/state=Created alert, which is what motivated
generalizing range_hood.py's existing (but narrower, ErrorCode_OFF-only)
special case into the shared helper instead of duplicating it further.

The other three points in #166 (filter-usage percentage vs. filterStatus
disagreement, an "air purification" config toggle the reporter says has no
physical effect, a "beep on/off" control) don't have a confirmed code fix:
the percentage math already matches the device's own filterUsage/
filterCapacity fields (filterStatus is a separate device-computed field we
already relay verbatim, not something we derive), the air-purify resource
is correctly wired to what the board reports and its absence from the
official app's own options list suggests an inert shared-board-profile stub
rather than an integration bug, and a "Beep volume" NumberDesc keyed off
the same Volume_100 option both dumps report already exists (0 mutes it).
This commit is contained in:
Marc Billow
2026-07-28 14:43:21 +00:00
parent 5a73a25005
commit 6007505e6b
2 changed files with 51 additions and 3 deletions
@@ -91,12 +91,25 @@ def _ml_to_l(v):
def _active_alarm_codes(items):
"""Join active alarm codes; skip retained rows Samsung leaves as Deleted.
"""Join active alarm codes; skip retained rows Samsung leaves as Deleted,
and any code ending in '_OFF'.
Laundry boards keep a Deleted ErrorCode row in /alarms/vs/0 after the
condition clears (see WD7000B diagnostics). Surface only live alarms so
HA doesn't stick on a stale ErrorCode. Range-hood has its own stricter
helper that also drops ErrorCode_OFF.
HA doesn't stick on a stale ErrorCode.
Samsung pre-populates this array with one row per alarm *type* the board
supports, each carrying its own '<Name>_OFF' placeholder code when that
alarm isn't firing -- confirmed across independent device families
(ErrorCode_OFF, FilterAlarm_OFF, OV_E_OFF, CT_E_OFF, WaterTankFull_OFF,
AC_V_0002_OFF all appear in fixtures with no corresponding active
condition). An alarm that's actually firing instead reports a plain,
unsuffixed code (FilterAlarm, DoorA_Opened, SNSF_Reached) -- issue #166's
AC dump has both a FilterAlarm_OFF placeholder and shows what a live
filter alert looks like: code 'FilterAlarm' (no suffix), state
'Created'. Range-hood previously special-cased only the literal
'ErrorCode_OFF' string in its own stricter helper; this generalizes
the same rule to the whole '_OFF' suffix convention.
"""
if not items or not isinstance(items, list):
return 'none'
@@ -105,6 +118,7 @@ def _active_alarm_codes(items):
for i in items
if i.get('x.com.samsung.da.code')
and str(i.get('x.com.samsung.da.state', '')).lower() != 'deleted'
and not str(i.get('x.com.samsung.da.code', '')).lower().endswith('_off')
]
return ', '.join(codes) if codes else 'none'
+34
View File
@@ -184,6 +184,40 @@ class TestWmSetinfoFlags:
},
]) == 'LE'
def test_off_suffixed_placeholder_codes_filtered(self):
"""issue #166: Samsung pre-populates /alarms/vs/0 with one row per
supported alarm *type*, each carrying a '<Name>_OFF' placeholder
code (no 'Deleted' state at all) when that alarm isn't firing --
confirmed not just for 'ErrorCode_OFF' but also 'FilterAlarm_OFF'
on the same dump. These were previously shown as if they were
active alarms."""
assert common._active_alarm_codes([
{
'x.com.samsung.da.code': 'ErrorCode_OFF',
'x.com.samsung.da.triggeredTime': '2026-07-28T12:59:22',
},
{
'x.com.samsung.da.code': 'FilterAlarm_OFF',
'x.com.samsung.da.triggeredTime': '2026-07-28T12:59:22',
},
]) == 'none'
def test_real_filter_alarm_not_off_suffixed_still_shown(self):
"""issue #166's actual live filter alert: code 'FilterAlarm' (no
'_OFF' suffix), state 'Created' -- distinct from the 'FilterAlarm_OFF'
placeholder and must still surface."""
assert common._active_alarm_codes([
{
'x.com.samsung.da.code': 'ErrorCode_OFF',
'x.com.samsung.da.triggeredTime': '2026-07-28T12:59:22',
},
{
'x.com.samsung.da.code': 'FilterAlarm',
'x.com.samsung.da.state': 'Created',
'x.com.samsung.da.triggeredTime': '2026-07-28T12:59:22',
},
]) == 'FilterAlarm'
class TestKidsLockFallback:
def test_generic_read_write(self):