Files
localthings/tests/test_air_dresser_tp2_20_capabilities.py
Marc Billow a3d9cce164 Extend AirDresser support to DA_DF_TP2_20_COMMON (#157)
A different board generation from issue #162's DA_DF_A51_20_COMMON, also
carrying the '_DF_' modelNum token and so already routed into the
air_dresser registry -- but reporting two resources #162's board doesn't:

  /st/airdressercourse/vs/0 -- the course table id (Table_00), read the
    same way washer/dryer read /st/washercourse|dryercourse/vs/0. Wired up
    as AIR_DRESSER_COURSE's table_href and added to the global ignore list,
    mirroring that existing pair exactly.
  /airdresseroption/sanitize/vs/0 -- a genuine on/off setting (not covered
    by any existing capability), added as AIR_DRESSER_SANITIZE.

Introducing table_href means the course select's translation_key is now
always the table-lookup callable, so the bare 'air_dresser_cycle' catalog
entry added for #162 (only ever used when no table_href was passed) is
unreachable in every case and is removed -- both boards fall back to the
shared 'cycle' entry until their course tables get named, same as
washer/dryer's own precedent for an unrecognized table.
2026-07-28 13:50:59 +00:00

83 lines
3.2 KiB
Python

"""Tests for the DA_DF_TP2_20_COMMON AirDresser (model DF9500A, issue #157).
A different board generation than issue #162's DA_DF_A51_20_COMMON, routed
into the same air_dresser registry (both carry the '_DF_' modelNum token).
Exercises the two things this board does differently: a populated
/wm/editcourse/vs/0 (so cycle_options() never needs the supportedOptions
fallback #162 relies on) and the new /airdresseroption/sanitize/vs/0
capability #162's dump doesn't report at all.
"""
from custom_components.localthings.registry.adapter import flatten
from custom_components.localthings.registry.by_type import air_dresser, for_device_by_model
from custom_components.localthings.registry.discovery import discover
from tests.conftest import _load_device
def _air_dresser():
resources = _load_device('air_dresser_tp2_20')
info = resources['/information/vs/0']
reg = for_device_by_model(
info['x.com.samsung.da.modelNum'], info['x.com.samsung.da.description'])
return reg, resources
def _state():
reg, resources = _air_dresser()
bound = discover(resources, reg.capabilities, reg.pattern_capabilities)
return flatten(bound, resources)
def test_resolves_to_air_dresser_registry():
reg, _ = _air_dresser()
assert reg is not None and reg.name == 'air_dresser'
def test_no_unbound_hrefs():
"""Confirms /st/airdressercourse/vs/0 and /airdresseroption/sanitize/vs/0
-- the two hrefs this board reports that #162's dump doesn't -- are
covered (ignored.py and AIR_DRESSER_SANITIZE respectively)."""
reg, resources = _air_dresser()
unbound = []
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
assert unbound == []
def test_course_options_come_from_edit_course_list_not_the_fallback():
"""Unlike issue #162's board, this one populates /wm/editcourse/vs/0
directly, so cycle_options() should never reach the
supportedOptions-decode fallback."""
from custom_components.localthings.registry.capabilities.laundry import cycle_options
_, resources = _air_dresser()
codes = cycle_options(resources)
assert codes[:3] == ['22', '23', '0C']
assert codes == cycle_options({'/wm/editcourse/vs/0': resources['/wm/editcourse/vs/0']})
def test_course_select_reads_current_selection():
state = _state()
assert state['cycle'] == '22'
def test_course_translation_key_falls_back_to_cycle_for_unidentified_table():
"""Table_00's course codes aren't identified yet, so the select's
translation_key resolves to the generic 'cycle' catalog entry rather
than a table-specific one that doesn't exist."""
_, resources = _air_dresser()
desc = air_dresser.REGISTRY.capabilities['/course/vs/0'][0].entities[0]
assert desc.translation_key(resources) == 'cycle'
def test_sanitize_present_and_toggles():
state = _state()
assert state['sanitize'] is False
desc = next(
e for e in air_dresser.REGISTRY.capabilities['/airdresseroption/sanitize/vs/0'][0].entities
if e.key == 'sanitize'
)
path, body = desc.write_fn('On', {})
assert path == ['airdresseroption', 'sanitize', 'vs', '0']
assert body == {'x.com.samsung.da.sanitize': 'On'}
assert desc.write_fn('Sparkle', {}) is None