Scope washer/dryer cycle translations to the device's own course table

Course codes on the shared /course/vs/0 contract aren't guaranteed
consistent across board generations: washer/combo devices report course
table Table_02, dryer devices Table_03 (x.com.samsung.da.st.courseTable,
previously fully ignored), and every code in washer_cycle/dryer_cycle was
confirmed exclusively against those. FlexWash's older DA_WM_A51 board
reports Table_00 instead -- applying the same translations there risked
showing a wrong name for any code that happens to numerically collide
between tables, not just an untranslated one.

SelectDesc.translation_key can now be a callable (resources -> key or
None), mirroring the existing pattern for `options`. laundry.cycle_select
gains optional table_href/validated_table params: when given, the renamed
washer_cycle_table_02/dryer_cycle_table_03 keys only apply when the
device's own course table matches exactly -- a different table, or no
table id at all, gets no translation_key (raw code display) rather than
a guess. dishwasher's call site is unchanged (static key, unconditional):
no equivalent table-id resource exists in any dump seen, and no evidence
its course codes vary by table the way washer/dryer's do.

entity.py and select.py resolve a callable translation_key once (via
coordinator.last_resources) and reuse that resolved value everywhere
_display() needs it, rather than re-checking the raw descriptor field.
This commit is contained in:
Marc Billow committed 2026-07-24 05:42:12 +00:00
1 parent b2c0517115
commit b2c32a90b6
12 files changed
+124 -27

No files matched your search

+40
View File
@@ -145,6 +145,46 @@ class TestCycleSelect:
assert desc.write_fn('1D', {}) is None
class TestCycleSelectTableGating:
"""translation_key becomes a resolver, not a plain string, once
table_href/validated_table are given -- washer/dryer's real call sites
(issue: course codes aren't guaranteed consistent across board
generations sharing the same /course/vs/0 contract; FlexWash's older
board reports a different course table than every device the shipped
translations were confirmed against)."""
def _desc(self):
return laundry.cycle_select(
translation_key='washer_cycle_table_02', icon='x',
table_href='/st/washercourse/vs/0', validated_table='Table_02',
)
def test_static_string_when_no_table_params_given(self):
"""dishwasher's call site -- no equivalent table-id resource in any
dump seen, no evidence of the same cross-board risk -- keeps the
plain static key unconditionally."""
desc = laundry.cycle_select(translation_key='dishwasher_cycle', icon='x')
assert desc.translation_key == 'dishwasher_cycle'
def test_resolves_to_the_key_when_table_matches(self):
desc = self._desc()
resources = {'/st/washercourse/vs/0': {'x.com.samsung.da.st.courseTable': 'Table_02'}}
assert callable(desc.translation_key)
assert desc.translation_key(resources) == 'washer_cycle_table_02'
def test_resolves_to_none_for_a_different_table(self):
desc = self._desc()
resources = {'/st/washercourse/vs/0': {'x.com.samsung.da.st.courseTable': 'Table_00'}}
assert desc.translation_key(resources) is None
def test_resolves_to_none_when_table_id_is_unknown(self):
"""Absence isn't evidence of a match -- no href, or an empty rep,
gets no translation_key either, not the family default."""
desc = self._desc()
assert desc.translation_key({}) is None
assert desc.translation_key({'/st/washercourse/vs/0': {}}) is None
class TestBuzzerSound:
def test_href(self):
assert laundry.BUZZER_SOUND.href == '/buzzersound/vs/0'