Apply ruff format to code merged from PR #251/#275
PR #251 and PR #275 predate this repo's ruff-format adoption on those files; running the formatter (single->double quotes, line wrapping, trailing-comma cleanup) keeps the merged code consistent with the rest of the codebase. No behavior change.
This commit is contained in:
@@ -658,7 +658,8 @@ class LocalThingsCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def _subdevice_probe_priority(
|
def _subdevice_probe_priority(
|
||||||
self, resources: dict[str, dict],
|
self,
|
||||||
|
resources: dict[str, dict],
|
||||||
) -> tuple[str, ...]:
|
) -> tuple[str, ...]:
|
||||||
"""Return live primary-entity hrefs in hot/warm-first order.
|
"""Return live primary-entity hrefs in hot/warm-first order.
|
||||||
|
|
||||||
@@ -676,23 +677,17 @@ class LocalThingsCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
if registry is None:
|
if registry is None:
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
tier_rank = {'hot': 0, 'warm': 1, 'cold': 2}
|
tier_rank = {"hot": 0, "warm": 1, "cold": 2}
|
||||||
ranked = []
|
ranked = []
|
||||||
for order, href in enumerate(resources):
|
for order, href in enumerate(resources):
|
||||||
primary_caps = [
|
primary_caps = [
|
||||||
capability
|
capability
|
||||||
for capability in registry.capabilities.get(href, ())
|
for capability in registry.capabilities.get(href, ())
|
||||||
if any(
|
if any(desc.entity_category is None for desc in capability.entities)
|
||||||
desc.entity_category is None
|
|
||||||
for desc in capability.entities
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
if not primary_caps:
|
if not primary_caps:
|
||||||
continue
|
continue
|
||||||
rank = min(
|
rank = min(tier_rank.get(capability.poll_tier, 2) for capability in primary_caps)
|
||||||
tier_rank.get(capability.poll_tier, 2)
|
|
||||||
for capability in primary_caps
|
|
||||||
)
|
|
||||||
ranked.append((rank, order, href))
|
ranked.append((rank, order, href))
|
||||||
return tuple(href for _, _, href in sorted(ranked))
|
return tuple(href for _, _, href in sorted(ranked))
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ Door-LED keys use NO `x.com.samsung.da.` prefix -- `setBrightness` /
|
|||||||
`setNightLight` -- preserved exactly as they appear in the OCF resource rep.
|
`setNightLight` -- preserved exactly as they appear in the OCF resource rep.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import string
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from datetime import time as dt_time
|
from datetime import time as dt_time
|
||||||
import string
|
|
||||||
|
|
||||||
from ...catalog import has_entity_translation
|
from ...catalog import has_entity_translation
|
||||||
from ..capability import Capability
|
from ..capability import Capability
|
||||||
@@ -324,7 +324,7 @@ def cycle_write(p, rep, href=None):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def personal_course_labels(resources, href='/wm/personalcourse/vs/0'):
|
def personal_course_labels(resources, href="/wm/personalcourse/vs/0"):
|
||||||
"""Return device-provided personal course names keyed by course code.
|
"""Return device-provided personal course names keyed by course code.
|
||||||
|
|
||||||
Populated entries use a small TLV payload. The leading field is
|
Populated entries use a small TLV payload. The leading field is
|
||||||
@@ -335,10 +335,10 @@ def personal_course_labels(resources, href='/wm/personalcourse/vs/0'):
|
|||||||
"""
|
"""
|
||||||
rep = resources.get(href) or {}
|
rep = resources.get(href) or {}
|
||||||
labels = {}
|
labels = {}
|
||||||
for entry in rep.get('x.com.samsung.da.courses') or []:
|
for entry in rep.get("x.com.samsung.da.courses") or []:
|
||||||
if not isinstance(entry, str) or '_' not in entry:
|
if not isinstance(entry, str) or "_" not in entry:
|
||||||
continue
|
continue
|
||||||
code, encoded = entry.split('_', 1)
|
code, encoded = entry.split("_", 1)
|
||||||
try:
|
try:
|
||||||
payload = bytes.fromhex(encoded)
|
payload = bytes.fromhex(encoded)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@@ -349,7 +349,7 @@ def personal_course_labels(resources, href='/wm/personalcourse/vs/0'):
|
|||||||
if name_length == 0 or len(payload) < 2 + name_length:
|
if name_length == 0 or len(payload) < 2 + name_length:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
name = payload[2:2 + name_length].decode('utf-8')
|
name = payload[2 : 2 + name_length].decode("utf-8")
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
continue
|
continue
|
||||||
if name.strip() and name.isprintable():
|
if name.strip() and name.isprintable():
|
||||||
@@ -364,7 +364,7 @@ def washer_cycle_fallback(value, resources):
|
|||||||
if label := personal_course_labels(resources).get(value.upper()):
|
if label := personal_course_labels(resources).get(value.upper()):
|
||||||
return label
|
return label
|
||||||
if len(value) == 2 and all(char in string.hexdigits for char in value):
|
if len(value) == 2 and all(char in string.hexdigits for char in value):
|
||||||
return f'Unknown (0x{value.upper()})'
|
return f"Unknown (0x{value.upper()})"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,9 @@ def _get_raw(sess, path_segs: tuple[str, ...], timeout: float = 10.0):
|
|||||||
|
|
||||||
|
|
||||||
def _get_batch(
|
def _get_batch(
|
||||||
sess, path_segs: tuple[str, ...], timeout: float = 10.0,
|
sess,
|
||||||
|
path_segs: tuple[str, ...],
|
||||||
|
timeout: float = 10.0,
|
||||||
) -> dict[str, dict]:
|
) -> dict[str, dict]:
|
||||||
"""GET a Samsung Collection resource and parse it the same way
|
"""GET a Samsung Collection resource and parse it the same way
|
||||||
/device/0 itself is parsed (parse_device0_batch): a [devcol-rep,
|
/device/0 itself is parsed (parse_device0_batch): a [devcol-rep,
|
||||||
@@ -246,7 +248,9 @@ def _get_batch(
|
|||||||
|
|
||||||
|
|
||||||
def _get_property(
|
def _get_property(
|
||||||
sess, path_segs: tuple[str, ...], timeout: float = 10.0,
|
sess,
|
||||||
|
path_segs: tuple[str, ...],
|
||||||
|
timeout: float = 10.0,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""GET a plain OCF Property-map resource (a bare dict, not a Collection
|
"""GET a plain OCF Property-map resource (a bare dict, not a Collection
|
||||||
batch). Used for `/multidevice/vs/0` (issue #177 follow-up): listed in
|
batch). Used for `/multidevice/vs/0` (issue #177 follow-up): listed in
|
||||||
@@ -301,10 +305,7 @@ def enumerate_subdevices(
|
|||||||
# casing, and probing it twice would materialize the same physical
|
# casing, and probing it twice would materialize the same physical
|
||||||
# subdevice as two Subdevice candidates.
|
# subdevice as two Subdevice candidates.
|
||||||
probed_ids: set[str] = set()
|
probed_ids: set[str] = set()
|
||||||
deadline = (
|
deadline = time.monotonic() + max(0.0, time_budget) if time_budget is not None else None
|
||||||
time.monotonic() + max(0.0, time_budget)
|
|
||||||
if time_budget is not None else None
|
|
||||||
)
|
|
||||||
budget_exhausted = False
|
budget_exhausted = False
|
||||||
|
|
||||||
def _next_timeout(maximum: float) -> float | None:
|
def _next_timeout(maximum: float) -> float | None:
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ class LocalThingsSelect(LocalThingsEntity, SelectEntity):
|
|||||||
"""Normalize both current state and options through one path."""
|
"""Normalize both current state and options through one path."""
|
||||||
display_fn = self._bound.desc.display_fn
|
display_fn = self._bound.desc.display_fn
|
||||||
fallback_fn = (
|
fallback_fn = (
|
||||||
(lambda raw: display_fn(raw, self._resources))
|
(lambda raw: display_fn(raw, self._resources)) if display_fn is not None else None
|
||||||
if display_fn is not None else None
|
|
||||||
)
|
)
|
||||||
return _display(value, self.translation_key, fallback_fn)
|
return _display(value, self.translation_key, fallback_fn)
|
||||||
|
|
||||||
|
|||||||
@@ -67,28 +67,28 @@ class TestCourseHelpers:
|
|||||||
assigning an inferred meaning to any standard course code.
|
assigning an inferred meaning to any standard course code.
|
||||||
"""
|
"""
|
||||||
resources = {
|
resources = {
|
||||||
'/wm/personalcourse/vs/0': {
|
"/wm/personalcourse/vs/0": {
|
||||||
'x.com.samsung.da.courses': [
|
"x.com.samsung.da.courses": [
|
||||||
'F1_0106EC868DEC98B7021EED8CACED8BB020EB93B1',
|
"F1_0106EC868DEC98B7021EED8CACED8BB020EB93B1",
|
||||||
'F2_00',
|
"F2_00",
|
||||||
'F3_0109EC9A94EAB8B0EBB3B40220ECA084EC9AA9',
|
"F3_0109EC9A94EAB8B0EBB3B40220ECA084EC9AA9",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
first_name = bytes.fromhex('EC868DEC98B7').decode('utf-8')
|
first_name = bytes.fromhex("EC868DEC98B7").decode("utf-8")
|
||||||
second_name = bytes.fromhex('EC9A94EAB8B0EBB3B4').decode('utf-8')
|
second_name = bytes.fromhex("EC9A94EAB8B0EBB3B4").decode("utf-8")
|
||||||
assert laundry.personal_course_labels(resources) == {
|
assert laundry.personal_course_labels(resources) == {
|
||||||
'F1': first_name,
|
"F1": first_name,
|
||||||
'F3': second_name,
|
"F3": second_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_personal_course_names_reject_malformed_payloads(self):
|
def test_personal_course_names_reject_malformed_payloads(self):
|
||||||
resources = {
|
resources = {
|
||||||
'/wm/personalcourse/vs/0': {
|
"/wm/personalcourse/vs/0": {
|
||||||
'x.com.samsung.da.courses': [
|
"x.com.samsung.da.courses": [
|
||||||
'F1_not-hex',
|
"F1_not-hex",
|
||||||
'F2_0106AA',
|
"F2_0106AA",
|
||||||
'F3_020141',
|
"F3_020141",
|
||||||
None,
|
None,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -97,15 +97,15 @@ class TestCourseHelpers:
|
|||||||
|
|
||||||
def test_washer_cycle_fallback_uses_labels_then_safe_unknown_text(self):
|
def test_washer_cycle_fallback_uses_labels_then_safe_unknown_text(self):
|
||||||
resources = {
|
resources = {
|
||||||
'/wm/personalcourse/vs/0': {
|
"/wm/personalcourse/vs/0": {
|
||||||
'x.com.samsung.da.courses': ['F1_0106EC868DEC98B7'],
|
"x.com.samsung.da.courses": ["F1_0106EC868DEC98B7"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expected = bytes.fromhex('EC868DEC98B7').decode('utf-8')
|
expected = bytes.fromhex("EC868DEC98B7").decode("utf-8")
|
||||||
assert laundry.washer_cycle_fallback('F1', resources) == expected
|
assert laundry.washer_cycle_fallback("F1", resources) == expected
|
||||||
assert laundry.washer_cycle_fallback('69', resources) == 'Unknown (0x69)'
|
assert laundry.washer_cycle_fallback("69", resources) == "Unknown (0x69)"
|
||||||
assert laundry.washer_cycle_fallback('6f', resources) == 'Unknown (0x6F)'
|
assert laundry.washer_cycle_fallback("6f", resources) == "Unknown (0x6F)"
|
||||||
assert laundry.washer_cycle_fallback('Normal', resources) is None
|
assert laundry.washer_cycle_fallback("Normal", resources) is None
|
||||||
|
|
||||||
|
|
||||||
class TestCourseCodesFromSupportedOptions:
|
class TestCourseCodesFromSupportedOptions:
|
||||||
|
|||||||
@@ -49,6 +49,4 @@ def test_display_passes_through_non_string_values():
|
|||||||
|
|
||||||
|
|
||||||
def test_display_uses_fallback_when_translation_has_no_state_table():
|
def test_display_uses_fallback_when_translation_has_no_state_table():
|
||||||
assert _display('69', 'cycle', lambda value: f'Unknown (0x{value})') == (
|
assert _display("69", "cycle", lambda value: f"Unknown (0x{value})") == ("Unknown (0x69)")
|
||||||
'Unknown (0x69)'
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ async def test_fac_bora_2in1_unique_ids_include_subdevice_prefix(hass: HomeAssis
|
|||||||
# capture instead of the synthetic sessions test_subdevices.py uses.
|
# capture instead of the synthetic sessions test_subdevices.py uses.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
async def test_flat_probe_priority_puts_live_climate_state_before_cold_metrics(
|
async def test_flat_probe_priority_puts_live_climate_state_before_cold_metrics(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
):
|
):
|
||||||
|
|||||||
+22
-20
@@ -360,6 +360,7 @@ def test_enumeration_budget_bounds_silent_prefixed_fallback_and_uses_priority(
|
|||||||
live-state href is then attempted first, and the last Property probe is
|
live-state href is then attempted first, and the last Property probe is
|
||||||
clamped to exactly the time left in the shared enumeration budget.
|
clamped to exactly the time left in the shared enumeration budget.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Clock:
|
class Clock:
|
||||||
now = 0.0
|
now = 0.0
|
||||||
|
|
||||||
@@ -381,20 +382,20 @@ def test_enumeration_budget_bounds_silent_prefixed_fallback_and_uses_priority(
|
|||||||
|
|
||||||
clock = Clock()
|
clock = Clock()
|
||||||
session = SilentSession(clock)
|
session = SilentSession(clock)
|
||||||
monkeypatch.setattr(subdevices_module.time, 'monotonic', clock.monotonic)
|
monkeypatch.setattr(subdevices_module.time, "monotonic", clock.monotonic)
|
||||||
resources = {
|
resources = {
|
||||||
'/subdevices/vs/0': {
|
"/subdevices/vs/0": {
|
||||||
'x.com.samsung.da.subdeviceIdList': [_UUID],
|
"x.com.samsung.da.subdeviceIdList": [_UUID],
|
||||||
},
|
},
|
||||||
'/power/vs/0': {'power': 'On'},
|
"/power/vs/0": {"power": "On"},
|
||||||
'/mode/vs/0': {'mode': 'Cool'},
|
"/mode/vs/0": {"mode": "Cool"},
|
||||||
}
|
}
|
||||||
|
|
||||||
found, extra = enumerate_subdevices(
|
found, extra = enumerate_subdevices(
|
||||||
session,
|
session,
|
||||||
resources,
|
resources,
|
||||||
oic_res_links=[],
|
oic_res_links=[],
|
||||||
preferred_hrefs=('/mode/vs/0',),
|
preferred_hrefs=("/mode/vs/0",),
|
||||||
time_budget=7.0,
|
time_budget=7.0,
|
||||||
collection_timeout=4.0,
|
collection_timeout=4.0,
|
||||||
property_timeout=2.0,
|
property_timeout=2.0,
|
||||||
@@ -404,9 +405,9 @@ def test_enumeration_budget_bounds_silent_prefixed_fallback_and_uses_priority(
|
|||||||
assert extra == {}
|
assert extra == {}
|
||||||
assert clock.now == 7.0
|
assert clock.now == 7.0
|
||||||
assert session.calls == [
|
assert session.calls == [
|
||||||
((_UUID, 'device', '0'), 4.0),
|
((_UUID, "device", "0"), 4.0),
|
||||||
((_UUID, 'mode', 'vs', '0'), 2.0),
|
((_UUID, "mode", "vs", "0"), 2.0),
|
||||||
((_UUID, 'power', 'vs', '0'), 1.0),
|
((_UUID, "power", "vs", "0"), 1.0),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -414,6 +415,7 @@ def test_enumeration_keeps_preferred_response_found_before_budget_expires(
|
|||||||
monkeypatch,
|
monkeypatch,
|
||||||
):
|
):
|
||||||
"""A useful early response survives later silent probes hitting the cap."""
|
"""A useful early response survives later silent probes hitting the cap."""
|
||||||
|
|
||||||
class Clock:
|
class Clock:
|
||||||
now = 0.0
|
now = 0.0
|
||||||
|
|
||||||
@@ -425,8 +427,8 @@ def test_enumeration_keeps_preferred_response_found_before_budget_expires(
|
|||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
|
||||||
def get(self, path, timeout=10.0):
|
def get(self, path, timeout=10.0):
|
||||||
if tuple(path) == (_UUID, 'mode', 'vs', '0'):
|
if tuple(path) == (_UUID, "mode", "vs", "0"):
|
||||||
return 0x45, cbor2.dumps({'mode': 'Cool'})
|
return 0x45, cbor2.dumps({"mode": "Cool"})
|
||||||
self.clock.now += timeout
|
self.clock.now += timeout
|
||||||
raise TimeoutError
|
raise TimeoutError
|
||||||
|
|
||||||
@@ -434,30 +436,30 @@ def test_enumeration_keeps_preferred_response_found_before_budget_expires(
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
clock = Clock()
|
clock = Clock()
|
||||||
monkeypatch.setattr(subdevices_module.time, 'monotonic', clock.monotonic)
|
monkeypatch.setattr(subdevices_module.time, "monotonic", clock.monotonic)
|
||||||
resources = {
|
resources = {
|
||||||
'/subdevices/vs/0': {
|
"/subdevices/vs/0": {
|
||||||
'x.com.samsung.da.subdeviceIdList': [_UUID],
|
"x.com.samsung.da.subdeviceIdList": [_UUID],
|
||||||
},
|
},
|
||||||
'/power/vs/0': {'power': 'On'},
|
"/power/vs/0": {"power": "On"},
|
||||||
'/mode/vs/0': {'mode': 'Cool'},
|
"/mode/vs/0": {"mode": "Cool"},
|
||||||
}
|
}
|
||||||
|
|
||||||
found, extra = enumerate_subdevices(
|
found, extra = enumerate_subdevices(
|
||||||
PartlyResponsiveSession(clock),
|
PartlyResponsiveSession(clock),
|
||||||
resources,
|
resources,
|
||||||
oic_res_links=[],
|
oic_res_links=[],
|
||||||
preferred_hrefs=('/mode/vs/0',),
|
preferred_hrefs=("/mode/vs/0",),
|
||||||
time_budget=7.0,
|
time_budget=7.0,
|
||||||
collection_timeout=4.0,
|
collection_timeout=4.0,
|
||||||
property_timeout=2.0,
|
property_timeout=2.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert [(subdevice.kind, subdevice.key) for subdevice in found] == [
|
assert [(subdevice.kind, subdevice.key) for subdevice in found] == [
|
||||||
('prefixed', _UUID),
|
("prefixed", _UUID),
|
||||||
]
|
]
|
||||||
assert found[0].flat_hrefs == ('/mode/vs/0',)
|
assert found[0].flat_hrefs == ("/mode/vs/0",)
|
||||||
assert extra == {f'/{_UUID}/mode/vs/0': {'mode': 'Cool'}}
|
assert extra == {f"/{_UUID}/mode/vs/0": {"mode": "Cool"}}
|
||||||
assert clock.now == 7.0
|
assert clock.now == 7.0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+138
-64
@@ -115,90 +115,164 @@ def test_no_catalog_carries_unresolved_core_references():
|
|||||||
|
|
||||||
|
|
||||||
def test_confirmed_korean_table_02_washer_course_names():
|
def test_confirmed_korean_table_02_washer_course_names():
|
||||||
states = _load('en')['entity']['select']['washer_cycle_table_02']['state']
|
states = _load("en")["entity"]["select"]["washer_cycle_table_02"]["state"]
|
||||||
assert {code: states[code] for code in (
|
assert {
|
||||||
'69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71',
|
code: states[code]
|
||||||
'72', '73', '74', '75', '76', '77', '78', '79', '88',
|
for code in (
|
||||||
)} == {
|
"69",
|
||||||
'69': 'AI Wash',
|
"6a",
|
||||||
'6a': 'Wool',
|
"6b",
|
||||||
'6b': 'Denim',
|
"6c",
|
||||||
'6c': 'Blouses',
|
"6d",
|
||||||
'6d': 'Delicates',
|
"6e",
|
||||||
'6e': 'Active Wear',
|
"6f",
|
||||||
'6f': 'Bedding',
|
"70",
|
||||||
'70': 'Towels',
|
"71",
|
||||||
'71': 'Quick Wash',
|
"72",
|
||||||
'72': 'Shirts',
|
"73",
|
||||||
'73': 'Sanitize',
|
"74",
|
||||||
'74': 'Drum Clean',
|
"75",
|
||||||
'75': 'Outdoor',
|
"76",
|
||||||
'76': 'Baby Care',
|
"77",
|
||||||
'77': 'Cottons',
|
"78",
|
||||||
'78': 'Rinse + Spin',
|
"79",
|
||||||
'79': 'Spin Only',
|
"88",
|
||||||
'88': 'Pet Care',
|
)
|
||||||
|
} == {
|
||||||
|
"69": "AI Wash",
|
||||||
|
"6a": "Wool",
|
||||||
|
"6b": "Denim",
|
||||||
|
"6c": "Blouses",
|
||||||
|
"6d": "Delicates",
|
||||||
|
"6e": "Active Wear",
|
||||||
|
"6f": "Bedding",
|
||||||
|
"70": "Towels",
|
||||||
|
"71": "Quick Wash",
|
||||||
|
"72": "Shirts",
|
||||||
|
"73": "Sanitize",
|
||||||
|
"74": "Drum Clean",
|
||||||
|
"75": "Outdoor",
|
||||||
|
"76": "Baby Care",
|
||||||
|
"77": "Cottons",
|
||||||
|
"78": "Rinse + Spin",
|
||||||
|
"79": "Spin Only",
|
||||||
|
"88": "Pet Care",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_confirmed_dishwasher_course_names():
|
def test_confirmed_dishwasher_course_names():
|
||||||
states = _load('en')['entity']['select']['dishwasher_cycle']['state']
|
states = _load("en")["entity"]["select"]["dishwasher_cycle"]["state"]
|
||||||
assert {code: states[code] for code in (
|
assert {
|
||||||
'82', '8a', 'a7', 'a8', '8c', '88',
|
code: states[code]
|
||||||
)} == {
|
for code in (
|
||||||
'82': 'Auto',
|
"82",
|
||||||
'8a': 'Normal',
|
"8a",
|
||||||
'a7': 'Heavy',
|
"a7",
|
||||||
'a8': 'Express',
|
"a8",
|
||||||
'8c': 'Extra Silence',
|
"8c",
|
||||||
'88': 'Self Clean',
|
"88",
|
||||||
|
)
|
||||||
|
} == {
|
||||||
|
"82": "Auto",
|
||||||
|
"8a": "Normal",
|
||||||
|
"a7": "Heavy",
|
||||||
|
"a8": "Express",
|
||||||
|
"8c": "Extra Silence",
|
||||||
|
"88": "Self Clean",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_confirmed_course_names_are_localized():
|
def test_confirmed_course_names_are_localized():
|
||||||
washer_codes = (
|
washer_codes = (
|
||||||
'69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71',
|
"69",
|
||||||
'72', '73', '74', '75', '76', '77', '78', '79', '88',
|
"6a",
|
||||||
|
"6b",
|
||||||
|
"6c",
|
||||||
|
"6d",
|
||||||
|
"6e",
|
||||||
|
"6f",
|
||||||
|
"70",
|
||||||
|
"71",
|
||||||
|
"72",
|
||||||
|
"73",
|
||||||
|
"74",
|
||||||
|
"75",
|
||||||
|
"76",
|
||||||
|
"77",
|
||||||
|
"78",
|
||||||
|
"79",
|
||||||
|
"88",
|
||||||
)
|
)
|
||||||
dishwasher_codes = ('82', '8a', 'a7', 'a8', '8c', '88')
|
dishwasher_codes = ("82", "8a", "a7", "a8", "8c", "88")
|
||||||
expected = {
|
expected = {
|
||||||
'cs': {
|
"cs": {
|
||||||
'washer': (
|
"washer": (
|
||||||
'AI praní', 'Vlna', 'Džíny', 'Halenky', 'Jemné prádlo',
|
"AI praní",
|
||||||
'Sportovní oblečení', 'Ložní prádlo', 'Ručníky',
|
"Vlna",
|
||||||
'Rychlé praní', 'Košile', 'Dezinfekce', 'Čištění bubnu',
|
"Džíny",
|
||||||
'Outdoor', 'Dětské potřeby', 'Bavlna',
|
"Halenky",
|
||||||
'Máchání + odstřeďování', 'Pouze odstřeďování',
|
"Jemné prádlo",
|
||||||
'Péče o domácí mazlíčky',
|
"Sportovní oblečení",
|
||||||
|
"Ložní prádlo",
|
||||||
|
"Ručníky",
|
||||||
|
"Rychlé praní",
|
||||||
|
"Košile",
|
||||||
|
"Dezinfekce",
|
||||||
|
"Čištění bubnu",
|
||||||
|
"Outdoor",
|
||||||
|
"Dětské potřeby",
|
||||||
|
"Bavlna",
|
||||||
|
"Máchání + odstřeďování",
|
||||||
|
"Pouze odstřeďování",
|
||||||
|
"Péče o domácí mazlíčky",
|
||||||
),
|
),
|
||||||
'dishwasher': (
|
"dishwasher": (
|
||||||
'Automatický', 'Normální', 'Intenzivní', 'Expresní',
|
"Automatický",
|
||||||
'Extra tichý', 'Samočištění',
|
"Normální",
|
||||||
|
"Intenzivní",
|
||||||
|
"Expresní",
|
||||||
|
"Extra tichý",
|
||||||
|
"Samočištění",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
'nl': {
|
"nl": {
|
||||||
'washer': (
|
"washer": (
|
||||||
'AI Wash', 'Wol', 'Spijkergoed', 'Blouses', 'Fijne was',
|
"AI Wash",
|
||||||
'Sportkleding', 'Beddengoed', 'Handdoeken', 'Snelle was',
|
"Wol",
|
||||||
'Overhemden', 'Hygiëne', 'Trommel reinigen', 'Outdoor',
|
"Spijkergoed",
|
||||||
'Babyverzorging', 'Katoen', 'Spoelen + centrifugeren',
|
"Blouses",
|
||||||
'Alleen centrifugeren', 'Pet Care',
|
"Fijne was",
|
||||||
|
"Sportkleding",
|
||||||
|
"Beddengoed",
|
||||||
|
"Handdoeken",
|
||||||
|
"Snelle was",
|
||||||
|
"Overhemden",
|
||||||
|
"Hygiëne",
|
||||||
|
"Trommel reinigen",
|
||||||
|
"Outdoor",
|
||||||
|
"Babyverzorging",
|
||||||
|
"Katoen",
|
||||||
|
"Spoelen + centrifugeren",
|
||||||
|
"Alleen centrifugeren",
|
||||||
|
"Pet Care",
|
||||||
),
|
),
|
||||||
'dishwasher': (
|
"dishwasher": (
|
||||||
'Auto', 'Normaal', 'Intensief', 'Express', 'Extra stil',
|
"Auto",
|
||||||
'Zelfreiniging',
|
"Normaal",
|
||||||
|
"Intensief",
|
||||||
|
"Express",
|
||||||
|
"Extra stil",
|
||||||
|
"Zelfreiniging",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for language, translations in expected.items():
|
for language, translations in expected.items():
|
||||||
catalog = _load(language)['entity']['select']
|
catalog = _load(language)["entity"]["select"]
|
||||||
washer = catalog['washer_cycle_table_02']['state']
|
washer = catalog["washer_cycle_table_02"]["state"]
|
||||||
dishwasher = catalog['dishwasher_cycle']['state']
|
dishwasher = catalog["dishwasher_cycle"]["state"]
|
||||||
assert tuple(washer[code] for code in washer_codes) == translations['washer']
|
assert tuple(washer[code] for code in washer_codes) == translations["washer"]
|
||||||
assert tuple(dishwasher[code] for code in dishwasher_codes) == (
|
assert tuple(dishwasher[code] for code in dishwasher_codes) == (translations["dishwasher"])
|
||||||
translations['dishwasher']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# The hood fan is its device's primary feature: fan.py sets _attr_name = None
|
# The hood fan is its device's primary feature: fan.py sets _attr_name = None
|
||||||
|
|||||||
@@ -125,10 +125,26 @@ class TestWasherCourse:
|
|||||||
from custom_components.localthings.catalog import translated_states
|
from custom_components.localthings.catalog import translated_states
|
||||||
|
|
||||||
confirmed = {
|
confirmed = {
|
||||||
'69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71',
|
"69",
|
||||||
'72', '73', '74', '75', '76', '77', '78', '79', '88',
|
"6a",
|
||||||
|
"6b",
|
||||||
|
"6c",
|
||||||
|
"6d",
|
||||||
|
"6e",
|
||||||
|
"6f",
|
||||||
|
"70",
|
||||||
|
"71",
|
||||||
|
"72",
|
||||||
|
"73",
|
||||||
|
"74",
|
||||||
|
"75",
|
||||||
|
"76",
|
||||||
|
"77",
|
||||||
|
"78",
|
||||||
|
"79",
|
||||||
|
"88",
|
||||||
}
|
}
|
||||||
assert confirmed <= translated_states('select', 'washer_cycle_table_02')
|
assert confirmed <= translated_states("select", "washer_cycle_table_02")
|
||||||
|
|
||||||
def test_missing_course_option_returns_none(self):
|
def test_missing_course_option_returns_none(self):
|
||||||
desc = next(e for e in washer.WASHER_COURSE.entities if e.key == "cycle")
|
desc = next(e for e in washer.WASHER_COURSE.entities if e.key == "cycle")
|
||||||
|
|||||||
Reference in New Issue
Block a user