From a6eb1c62d444f3e52ac7c3e3ad57c7b121c4a3b3 Mon Sep 17 00:00:00 2001 From: Marc Billow Date: Wed, 29 Jul 2026 02:55:47 +0000 Subject: [PATCH] feat(microwave): add filter reminder / end signal reminder switches (#181) Both FilterRemind_*/RemindBeep_* option-array tokens are already present -- and both On and Off already confirmed -- on the existing ME7500D fixtures (issue #152), so this is a straight sibling of the Sound/Lamp switches rather than new discovery work. Gated with exists_fn like Lamp since the MW7300B combi dump has neither token. Doesn't address the rest of issue #181 (power-level slider, non-reported cooking modes, 3-level light, child lock, send-to- microwave) -- those need write-contract confirmation this dump doesn't carry. --- .../registry/capabilities/microwave.py | 49 ++++++++++++++ .../localthings/translations/en.json | 6 ++ .../localthings/translations/nl.json | 6 ++ tests/fixtures/golden/microwave_me7500d.json | 2 + .../golden/microwave_me7500d_lamp_high.json | 2 + tests/test_microwave_capabilities.py | 66 +++++++++++++++++++ 6 files changed, 131 insertions(+) diff --git a/custom_components/localthings/registry/capabilities/microwave.py b/custom_components/localthings/registry/capabilities/microwave.py index 7323c60..6910d9f 100644 --- a/custom_components/localthings/registry/capabilities/microwave.py +++ b/custom_components/localthings/registry/capabilities/microwave.py @@ -27,6 +27,10 @@ different from an oven, and defined fresh here: (a brightness level, not literally 'On') -- the switch now treats any non-Off/non-None value as "on" for reads, and writes back 'High'/'Off' (the two confirmed tokens) rather than the never-confirmed 'On'. + * Filter reminder / end signal reminder: bare 'FilterRemind'/'RemindBeep' + option-array tokens (issue #181), both with On and Off observed live + (issue #152's ME7500D fixtures) -- gated with exists_fn like Lamp since + the MW7300B combi dump has neither. Note: cooking-mode writes are unproven here, same caveat as oven.py's OVEN_MODE -- exposed as a SelectDesc for fidelity, first real-world write @@ -145,6 +149,14 @@ def _lamp_exists(rep, resources): return option_value(rep.get('x.com.samsung.da.options'), 'Lamp') is not None +def _filter_remind_exists(rep, resources): + return option_value(rep.get('x.com.samsung.da.options'), 'FilterRemind') is not None + + +def _remind_beep_exists(rep, resources): + return option_value(rep.get('x.com.samsung.da.options'), 'RemindBeep') is not None + + def _lamp_write(p, rep, href=None): if p not in ('On', 'Off'): return None @@ -159,6 +171,26 @@ def _lamp_write(p, rep, href=None): } +def _filter_remind_write(p, rep, href=None): + if p not in ('On', 'Off'): + return None + if not rep.get('x.com.samsung.da.options'): + return None + return ['mode', 'vs', '0'], { + 'x.com.samsung.da.options': option_write('FilterRemind', p), + } + + +def _remind_beep_write(p, rep, href=None): + if p not in ('On', 'Off'): + return None + if not rep.get('x.com.samsung.da.options'): + return None + return ['mode', 'vs', '0'], { + 'x.com.samsung.da.options': option_write('RemindBeep', p), + } + + # --------------------------------------------------------------------------- # Capabilities # --------------------------------------------------------------------------- @@ -213,5 +245,22 @@ MICROWAVE_MODE = Capability( exists_fn=_lamp_exists, value_fn=lambda opts: option_value(opts, 'Lamp') not in (None, 'Off'), write_fn=_lamp_write), + # issue #181: Filter Reminder / End Signal Reminder toggles, + # confirmed present (both On and Off observed across dumps -- see + # issue #152's ME7500D fixtures) but only on boards that carry the + # FilterRemind_*/RemindBeep_* tokens; gated off elsewhere (e.g. the + # MW7300B combi dump has neither) rather than assumed universal. + SwitchDesc(key='filter_remind', field='x.com.samsung.da.options', + icon='mdi:air-filter', + entity_category='config', + exists_fn=_filter_remind_exists, + value_fn=lambda opts: option_value(opts, 'FilterRemind') == 'On', + write_fn=_filter_remind_write), + SwitchDesc(key='remind_beep', field='x.com.samsung.da.options', + icon='mdi:bell-ring', + entity_category='config', + exists_fn=_remind_beep_exists, + value_fn=lambda opts: option_value(opts, 'RemindBeep') == 'On', + write_fn=_remind_beep_write), ), ) diff --git a/custom_components/localthings/translations/en.json b/custom_components/localthings/translations/en.json index c8f573f..6b273bc 100644 --- a/custom_components/localthings/translations/en.json +++ b/custom_components/localthings/translations/en.json @@ -985,6 +985,9 @@ "favorite_coffee_enabled": { "name": "Favorite coffee" }, + "filter_remind": { + "name": "Filter reminder" + }, "fridge_sound": { "name": "Sound" }, @@ -1027,6 +1030,9 @@ "rapid_fridge": { "name": "Rapid fridge" }, + "remind_beep": { + "name": "End signal reminder" + }, "sabbath_mode": { "name": "Sabbath mode" }, diff --git a/custom_components/localthings/translations/nl.json b/custom_components/localthings/translations/nl.json index 93e497c..f4f2a86 100644 --- a/custom_components/localthings/translations/nl.json +++ b/custom_components/localthings/translations/nl.json @@ -985,6 +985,9 @@ "favorite_coffee_enabled": { "name": "Favoriete koffie" }, + "filter_remind": { + "name": "Filterherinnering" + }, "fridge_sound": { "name": "Geluid" }, @@ -1027,6 +1030,9 @@ "rapid_fridge": { "name": "Snelkoelen" }, + "remind_beep": { + "name": "Eindsignaal herinnering" + }, "sabbath_mode": { "name": "Sabbatmodus" }, diff --git a/tests/fixtures/golden/microwave_me7500d.json b/tests/fixtures/golden/microwave_me7500d.json index 40df21d..0ca4592 100644 --- a/tests/fixtures/golden/microwave_me7500d.json +++ b/tests/fixtures/golden/microwave_me7500d.json @@ -10,6 +10,7 @@ "door_open", "energy_kwh", "fan", + "filter_remind", "finish_time", "firmware_update", "lamp", @@ -17,6 +18,7 @@ "operation_time_minutes", "power_level", "progress_percentage", + "remind_beep", "sound" ] } diff --git a/tests/fixtures/golden/microwave_me7500d_lamp_high.json b/tests/fixtures/golden/microwave_me7500d_lamp_high.json index 40df21d..0ca4592 100644 --- a/tests/fixtures/golden/microwave_me7500d_lamp_high.json +++ b/tests/fixtures/golden/microwave_me7500d_lamp_high.json @@ -10,6 +10,7 @@ "door_open", "energy_kwh", "fan", + "filter_remind", "finish_time", "firmware_update", "lamp", @@ -17,6 +18,7 @@ "operation_time_minutes", "power_level", "progress_percentage", + "remind_beep", "sound" ] } diff --git a/tests/test_microwave_capabilities.py b/tests/test_microwave_capabilities.py index f01d096..7253609 100644 --- a/tests/test_microwave_capabilities.py +++ b/tests/test_microwave_capabilities.py @@ -217,3 +217,69 @@ def test_lamp_reads_any_non_off_level_as_true(): just a literal 'On'.""" desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'lamp') assert desc.value_fn(['Lamp_High']) is True + + +# --------------------------------------------------------------------------- +# MICROWAVE_MODE — filter_remind/remind_beep options-array writes (issue #181) +# --------------------------------------------------------------------------- + +def test_filter_remind_gated_absent_when_no_option(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'filter_remind') + rep = {'x.com.samsung.da.options': ['DeviceType_MW7300B-/EU1', 'Sound_Off']} + assert desc.exists_fn(rep, {}) is False + + +def test_filter_remind_gated_present_when_option_reported(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'filter_remind') + rep = {'x.com.samsung.da.options': ['FilterRemind_Off']} + assert desc.exists_fn(rep, {}) is True + + +def test_filter_remind_reads_on_off(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'filter_remind') + assert desc.value_fn(['FilterRemind_On']) is True + assert desc.value_fn(['FilterRemind_Off']) is False + + +def test_filter_remind_write_is_single_token(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'filter_remind') + rep = {'x.com.samsung.da.options': ['FilterRemind_Off']} + path, body = desc.write_fn('On', rep) + assert path == ['mode', 'vs', '0'] + assert body == {'x.com.samsung.da.options': ['FilterRemind_On']} + + +def test_filter_remind_write_requires_existing_options(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'filter_remind') + assert desc.write_fn('On', {}) is None + + +def test_remind_beep_gated_absent_when_no_option(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'remind_beep') + rep = {'x.com.samsung.da.options': ['DeviceType_MW7300B-/EU1', 'Sound_Off']} + assert desc.exists_fn(rep, {}) is False + + +def test_remind_beep_gated_present_when_option_reported(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'remind_beep') + rep = {'x.com.samsung.da.options': ['RemindBeep_On']} + assert desc.exists_fn(rep, {}) is True + + +def test_remind_beep_reads_on_off(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'remind_beep') + assert desc.value_fn(['RemindBeep_On']) is True + assert desc.value_fn(['RemindBeep_Off']) is False + + +def test_remind_beep_write_is_single_token(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'remind_beep') + rep = {'x.com.samsung.da.options': ['RemindBeep_On']} + path, body = desc.write_fn('Off', rep) + assert path == ['mode', 'vs', '0'] + assert body == {'x.com.samsung.da.options': ['RemindBeep_Off']} + + +def test_remind_beep_write_requires_existing_options(): + desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'remind_beep') + assert desc.write_fn('On', {}) is None