From 55c7b88a8f4283a181f9e64863476a1cbf39df25 Mon Sep 17 00:00:00 2001 From: perseus177 Date: Tue, 28 Jul 2026 02:38:28 +0200 Subject: [PATCH] fix(airconditioner): label the 2Step preset and test presets by their HA value The legacy Comode codes resolve through the same dynamic resolver as a real convenient resource, so Nano lands on the existing 'nano' preset (already labelled WindFree) rather than on a 'windfree' value of its own -- the new tests asserted the label instead of the value. 2Step had no catalog entry in either language and would have surfaced as the raw code. Also updates the existing five-percent-humidity test, which reached into the descriptor's field/value_fn directly, to the rep_fn the fallback needs, and covers the fallback itself. --- custom_components/localthings/translations/en.json | 3 ++- custom_components/localthings/translations/nl.json | 3 ++- tests/test_airconditioner_artik051_krac.py | 14 +++++++++----- tests/test_airconditioner_capabilities.py | 12 +++++++++++- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/custom_components/localthings/translations/en.json b/custom_components/localthings/translations/en.json index ca6ab24..bd41375 100644 --- a/custom_components/localthings/translations/en.json +++ b/custom_components/localthings/translations/en.json @@ -128,7 +128,8 @@ "longwind": "Long wind", "motionindirect": "Motion indirect", "motiondirect": "Motion direct", - "drycomfort": "Dry comfort" + "drycomfort": "Dry comfort", + "2step": "2-Step" } } } diff --git a/custom_components/localthings/translations/nl.json b/custom_components/localthings/translations/nl.json index 80becb6..b3779f5 100644 --- a/custom_components/localthings/translations/nl.json +++ b/custom_components/localthings/translations/nl.json @@ -128,7 +128,8 @@ "longwind": "Lange wind", "motionindirect": "Beweging indirect", "motiondirect": "Beweging direct", - "drycomfort": "Droog comfort" + "drycomfort": "Droog comfort", + "2step": "2-Step" } } } diff --git a/tests/test_airconditioner_artik051_krac.py b/tests/test_airconditioner_artik051_krac.py index 7c8f942..2e71e89 100644 --- a/tests/test_airconditioner_artik051_krac.py +++ b/tests/test_airconditioner_artik051_krac.py @@ -194,15 +194,19 @@ def test_preset_comes_from_the_comode_token(): entity = _climate(resources) assert entity.preset_mode == 'none' # Comode_Off in the fixture # Codes learned by driving this unit through its cloud integration and - # reading the token back: Nano is what the app calls WindFree. - assert 'windfree' in entity.preset_modes + # reading the token back. They go through the same dynamic resolver as a + # real convenient resource's supportedModes, so 'Nano' resolves to the + # existing 'nano' preset -- already labelled WindFree in the catalog. + assert entity.preset_modes == [ + 'none', 'nano', 'quiet', 'comfort', '2step', 'speed', + ] options = resources['/mode/vs/0']['x.com.samsung.da.options'] resources['/mode/vs/0']['x.com.samsung.da.options'] = [ 'Comode_Nano' if option.startswith('Comode_') else option for option in options ] - assert _climate(resources).preset_mode == 'windfree' + assert _climate(resources).preset_mode == 'nano' async def test_preset_write_uses_the_token_path(): @@ -210,7 +214,7 @@ async def test_preset_write_uses_the_token_path(): coordinator = _FakeCoordinator(resources) entity = _climate(resources, coordinator) - await entity.async_set_preset_mode('windfree') + await entity.async_set_preset_mode('nano') assert coordinator.commands[-1][1] == ('preset_legacy', 'Nano') @@ -224,7 +228,7 @@ async def test_newer_boards_keep_the_resource_paths(): await entity.async_set_fan_mode('high') await entity.async_set_swing_mode('off') - await entity.async_set_preset_mode('windfree') + await entity.async_set_preset_mode('quiet') # from its own supportedModes kinds = [payload[0] for _, payload in coordinator.commands] assert kinds == ['fan', 'swing', 'preset'] diff --git a/tests/test_airconditioner_capabilities.py b/tests/test_airconditioner_capabilities.py index 3a63492..f513be7 100644 --- a/tests/test_airconditioner_capabilities.py +++ b/tests/test_airconditioner_capabilities.py @@ -404,4 +404,14 @@ def test_current_temperature_vs_only_binds_when_ocf_href_absent(): def test_humidity_reads_five_percent_field_not_stuck_humidity_field(): desc = airconditioner.HUMIDITY.entities[0] rep = {'x.com.samsung.da.humidity': '0', 'x.com.samsung.da.fivepercentHumidity': '42'} - assert desc.value_fn(rep.get(desc.field)) == 42.0 + assert desc.rep_fn(rep) == 42.0 + + +def test_humidity_falls_back_to_the_plain_field_where_five_percent_is_absent(): + """ARTIK051 boards (issue #136) have no fivepercentHumidity field at all. + Their plain field is not stuck -- it carries a reading while Air monitoring + is on -- so 0 means "not measuring" on both generations, not 0% humidity.""" + desc = airconditioner.HUMIDITY.entities[0] + assert desc.rep_fn({'x.com.samsung.da.humidity': '51'}) == 51.0 + assert desc.rep_fn({'x.com.samsung.da.humidity': '0'}) is None + assert desc.rep_fn({}) is None