PR #68 restated its own translation data in Python: a 60-line TRANSLATED_SELECT_STATES table of frozensets duplicating every entity.select.*.state key, a second _TRANSLATED_COURSE_TABLES table naming which course tables have translations, and a strings.json that was a 835-line byte-for-byte copy of translations/en.json save 43 [%key:...%] references. Each needed hand-syncing, and one was already drifting. Home Assistant loads exactly one file per language for a custom integration -- translations/<lang>.json. It never reads strings.json and never resolves [%key:...%]; both belong to Core's build tooling, which custom integrations don't run through (hassfest skips a missing strings.json and validates translations/en.json instead). So en.json is the source, and the new catalog.py reads the keys and states back out of it for the two decisions Python genuinely has to make: - select._display() normalizes a raw Samsung option to a lowercase state key only when the catalog knows it, else leaves the vendor's casing alone. Derived sets are identical to the removed literals. - laundry.cycle_select() keys off a device-reported course table only when that table has an entry, else falls back to the name-only 'cycle' key. Translating Table_00 is now a translations-only change. Also fixes six names that had already drifted between the Python descriptors and the catalog, restoring HA's sentence case for two generic ones (Auto release dry, Bubble soak) and taking the catalog's wording for the rest, and adds a test so the vestigial descriptor names can't silently disagree with the UI again. Claude-Session: https://claude.ai/code/session_01GiibJZZLWVvyxq7mc7EDNp
48 lines
1.9 KiB
Python
48 lines
1.9 KiB
Python
"""Tests for select-option display casing (custom_components/localthings/select.py)."""
|
|
from custom_components.localthings.select import _display
|
|
|
|
_UNTRANSLATED = None
|
|
_TRANSLATED = 'ice_type'
|
|
|
|
|
|
def test_display_titlecases_a_fully_lowercase_device_native_token():
|
|
"""Samsung's sound-mode field is genuinely lowercase on the wire
|
|
('voice'/'tone'/'mute') -- these have no other casing signal to key
|
|
off, so title-case them for display."""
|
|
assert _display('voice', _UNTRANSLATED) == 'Voice'
|
|
assert _display('mute', _UNTRANSLATED) == 'Mute'
|
|
|
|
|
|
def test_display_inserts_a_space_at_a_camelcase_boundary():
|
|
"""'ExtraHigh' (from supportedHeatedDry) should read as two words."""
|
|
assert _display('ExtraHigh', _UNTRANSLATED) == 'Extra High'
|
|
|
|
|
|
def test_display_passes_through_an_already_human_friendly_value():
|
|
"""'AI Wash' etc. (dishwasher cycle names) already read fine and
|
|
have no camelCase boundary or all-lowercase pattern -- must not be
|
|
mangled."""
|
|
assert _display('AI Wash', _UNTRANSLATED) == 'AI Wash'
|
|
assert _display('Low', _UNTRANSLATED) == 'Low'
|
|
assert _display('Off', _UNTRANSLATED) == 'Off'
|
|
|
|
|
|
def test_display_lowercases_for_translation_key_lookup():
|
|
"""An entity with a translation_key must match the catalog's
|
|
lowercase keys exactly -- unlike the untranslated cases above, this
|
|
is not a cosmetic transform."""
|
|
assert _display('Whiskey_IceBall_3', _TRANSLATED) == 'whiskey_iceball_3'
|
|
|
|
|
|
def test_unknown_translated_vendor_value_keeps_readable_fallback():
|
|
"""A firmware-added value must remain readable instead of being mangled."""
|
|
assert _display('FutureVendorMode', _TRANSLATED) == 'Future Vendor Mode'
|
|
|
|
|
|
def test_known_camel_case_state_uses_snake_case_translation_key():
|
|
assert _display('ExtraHigh', 'heated_dry') == 'extra_high'
|
|
|
|
|
|
def test_display_passes_through_non_string_values():
|
|
assert _display(None, _UNTRANSLATED) is None
|