diff --git a/custom_components/localthings/registry/capabilities/water_purifier.py b/custom_components/localthings/registry/capabilities/water_purifier.py index 159f11a..b04e2e9 100644 --- a/custom_components/localthings/registry/capabilities/water_purifier.py +++ b/custom_components/localthings/registry/capabilities/water_purifier.py @@ -175,11 +175,23 @@ FAVORITE_HOTWATER = Capability( write_fn=lambda p, rep, href=None: ( ['favorite', 'hotwater', 'vs', '0'], {'x.com.samsung.da.switchHotwater': 'Locked' if p == 'On' else 'Unlocked'})), + # Issue #196: `supportedList` is only the four *fixed* presets + # (e.g. ['40', '75', '85', '90']) -- the SmartThings app also lets + # the user add one custom value to their own display list via its + # "temperatures to display" editor (a wheel picker bounded by + # /setting/waterpurifier/vs/0's hotwaterRange, separate resource), + # and that custom value shows up in `showList` + # (['40', '50', '75', '85', '90'] here) but never in + # `supportedList`. Reading options from `supportedList` meant a + # unit whose current default *was* that custom value rendered as + # "unknown" -- not a coverage gap, just the wrong field. `showList` + # is a superset of `supportedList` that always includes whatever + # the current default actually is, custom or not. SelectDesc(key='favorite_hotwater_temperature', field='x.com.samsung.da.favorite.defaultTemperature', icon='mdi:thermometer', entity_category='config', - options_field='x.com.samsung.da.favorite.supportedList', + options_field='x.com.samsung.da.favorite.showList', write_fn=lambda p, rep, href=None: ( ['favorite', 'hotwater', 'vs', '0'], {'x.com.samsung.da.favorite.defaultTemperature': p})), diff --git a/tests/test_water_purifier_capabilities.py b/tests/test_water_purifier_capabilities.py index 432eb8a..5721fd7 100644 --- a/tests/test_water_purifier_capabilities.py +++ b/tests/test_water_purifier_capabilities.py @@ -281,9 +281,17 @@ def test_hotwater_lock_fallback_gating_across_status_lock_states(): assert fallback.exists_fn({}, {}) is False -def test_favorite_hotwater_temperature_options_come_from_live_supported_list(): +def test_favorite_hotwater_temperature_options_come_from_live_show_list(): + """`supportedList` is only the four fixed presets -- `showList` is a + superset that also carries the one custom value (if any) the + SmartThings app's "temperatures to display" editor let the user add, + and always includes whatever the current default actually is. Reading + `supportedList` meant a unit whose default *was* that custom value + rendered as 'unknown' in HA (issue #196) -- see the ailite fixture's + version of this test below for the case that actually exercises it + (this fixture's showList == supportedList, no custom value set).""" desc = _desc_coffee('favorite_hotwater_temperature') - assert desc.options_field == 'x.com.samsung.da.favorite.supportedList' + assert desc.options_field == 'x.com.samsung.da.favorite.showList' def test_coffee_recipe_hrefs_are_ignored_not_guessed(): @@ -361,6 +369,24 @@ def test_ailite_hot_water_temperature_gated_off_without_supported_list(): assert 'hot_water_temperature' not in state +def test_ailite_favorite_hotwater_temperature_options_include_the_custom_value(): + """This board's real dump (issue #196) is the concrete case: the user + added a custom 50C value via the SmartThings app's "temperatures to + display" editor, so favorite.showList is + ['40', '50', '75', '85', '90'] while favorite.supportedList stays the + fixed ['40', '75', '85', '90'] -- '50' only ever appears in showList. + Whichever of the two the descriptor reads from, a default temperature + of '50' would render as HA's 'unknown' state unless that field's raw + option list actually contains '50'.""" + desc = _desc_ailite('favorite_hotwater_temperature') + assert desc.options_field == 'x.com.samsung.da.favorite.showList' + _, resources = _water_purifier_ailite() + rep = resources['/favorite/hotwater/vs/0'] + assert rep['x.com.samsung.da.favorite.defaultTemperature'] in rep[desc.options_field] + assert '50' not in rep['x.com.samsung.da.favorite.supportedList'] + assert '50' in rep['x.com.samsung.da.favorite.showList'] + + def test_ailite_sound_mode_options_come_from_live_supported_modes(): """This board's supportedModes (voice/fixedTone/mute) differs from both laundry.SOUND_MODE's hardcoded voice/tone/mute and air_purifier.SOUND_MODE's