Complete dryer coverage from issue #14 screenshots (DV90BB5245AES1)

Name the two remaining unidentified course codes (0x21 Hygiene Care,
0x4C Air Refresh) by matching the SmartThings app's course-list order
against editCourseList in the reporter's dump.

Also close three entity gaps the screenshots surfaced that v0.6.0
missed: dry level was read-only despite the app exposing a writable
Level 1/2/3 picker; the app's "AI pattern" toggle (AiOption_On/Off)
and "Auto cycle link" toggle (/cycleinterface/vs/0, previously
globally ignored as always-empty on washer dumps) were both unbound.
Scope that ignore to washer.py now that the dryer binds it for real.
This commit is contained in:
Marc Billow committed 2026-07-24 00:43:58 +00:00
1 parent cf18cc3ecc
commit e07d6ed706
10 files changed
+121 -18

No files matched your search

+2
View File
@@ -1,6 +1,8 @@
{
"state_keys": [
"ai_pattern",
"alarm_code",
"auto_cycle_link",
"buzzer_sound",
"child_lock",
"cycle",
+37 -1
View File
@@ -39,10 +39,46 @@ def test_no_unbound_hrefs():
def test_expected_entities_present():
state = _state()
for key in ('buzzer_sound', 'cycle', 'power_switch', 'child_lock',
'remote_control', 'dry_level', 'wrinkle_prevent', 'energy_kwh'):
'remote_control', 'dry_level', 'wrinkle_prevent', 'energy_kwh',
'ai_pattern', 'auto_cycle_link'):
assert key in state, key
def test_dry_level_is_a_writable_select():
"""dryLevel used to be a read-only sensor, but the app's "Dryness"
control (issue #14 screenshots) is a Level 1/2/3 picker, and the dump's
supportedDryLevel list confirms the device accepts writes -- it should
be a select, not a sensor."""
desc = next(e for e in dryer.DRYER_SETTINGS.entities if e.key == 'dry_level')
assert desc.options_field == 'x.com.samsung.da.supportedDryLevel'
assert desc.write_fn is not None
assert desc.write_fn('Less', {}) == (
['washer', 'vs', '0'], {'x.com.samsung.da.dryLevel': 'Less'})
assert _state()['dry_level'] == 'Normal'
def test_ai_pattern_reads_and_writes_options_token():
"""AiOption_On/Off on /course/vs/0's options[] array is the app's "AI
pattern" toggle (issue #14 screenshot: on)."""
assert _state()['ai_pattern'] is True
desc = next(e for e in dryer.DRYER_COURSE.entities if e.key == 'ai_pattern')
opts = ['Course_16', 'AiOption_On']
href, payload = desc.write_fn('Off', {'x.com.samsung.da.options': opts})
assert href == ['course', 'vs', '0']
assert 'AiOption_Off' in payload['x.com.samsung.da.options']
def test_auto_cycle_link_bound_to_cycleinterface():
"""cycleInterfaceEnabled on /cycleinterface/vs/0 is the app's "Auto cycle
link" toggle (issue #14 screenshot: off) -- previously globally ignored
as always-empty until this dump showed it populated on a dryer."""
assert dryer.AUTO_CYCLE_LINK.href == '/cycleinterface/vs/0'
assert _state()['auto_cycle_link'] is False
desc = next(e for e in dryer.AUTO_CYCLE_LINK.entities if e.key == 'auto_cycle_link')
assert desc.write_fn('On', {}) == (
['cycleinterface', 'vs', '0'], {'x.com.samsung.da.cycleInterfaceEnabled': 'On'})
def test_job_beginning_status_reads_current_status():
"""The dump carries x.com.samsung.da.currentStatus (not the old
jobBeginingStatus field the dryer descriptor used to read), so the sensor
+11 -1
View File
@@ -68,8 +68,18 @@ class TestWasherIgnoredHrefs:
'/wm/welcomemsg/vs/0',
'/wm/personalcourse/vs/0',
'/energy/consumption/0',
'/cycleinterface/vs/0',
'/drlc/0',
'/operational/state/0',
):
assert href in ignored_hrefs, f"{href} should be in ignored.IGNORED"
def test_cycleinterface_is_scoped_to_washer_registry(self):
"""/cycleinterface/vs/0 is empty on every washer dump but real on the
dryer (issue #14's AUTO_CYCLE_LINK), so it's a washer-scoped ignore
(washer.CYCLE_INTERFACE_IGNORED) rather than a global one -- a global
entry would collide with the dryer's real capability."""
from custom_components.localthings.registry.by_type import washer as washer_registry
from custom_components.localthings.registry.capabilities.ignored import IGNORED
assert '/cycleinterface/vs/0' not in {cap.href for cap in IGNORED}
assert '/cycleinterface/vs/0' in washer_registry.REGISTRY.capabilities