laundry: add DV6800N's Table_00 dryer courses (issue #394)
A DV6800N (DA_WM_A51_20_COMMON) reports the same /st/dryercourse/vs/0 courseTable 'Table_00' as issue #357's DVE45R6300W/A3 -- also DA_WM_A51_20_COMMON -- and its reporter listed 14 selectable courses read straight off the appliance's own menu, in the same order /course/vs/0's supportedOptions enumerates them. Initially treated this as a second, incompatible Table_00 code family and added a device-model-keyed disambiguation layer to laundry.cycle_select. That was an unproven assumption: the two reporters' confirmed sets are mostly non-overlapping subsets (11 vs 14 codes), which is exactly what you'd expect from two models on a shared board exposing different subsets of one course table via their own /course/vs/0 supportedOptions -- not evidence of two different code dictionaries. The one code both reporters confirmed, 'a5', means Bedding on both, which is corroborating, not neutral. No confirmed code conflicts between the two sets, so this folds #394's 14 codes straight into the existing dryer_cycle_table_00 catalog entry (mirrored to all shipped languages), same shape as #357's original translations-only change. Adds a scrubbed fixture + golden for the DV6800N dump and tests confirming zero unbound hrefs and that its course codes resolve through the shared, now-larger dryer_cycle_table_00 catalog.
This commit is contained in:
@@ -63,6 +63,12 @@ DRYER_SETTINGS = Capability(
|
||||
# code. It shares no codes with Table_03 above -- 'a5' Bedding here and
|
||||
# '01' Normal are both table-scoped, so a Table_03 dryer never picks up a
|
||||
# Table_00 label or vice versa (see laundry.cycle_select's table_href).
|
||||
# A DV6800N -- same DA_WM_A51_20_COMMON board, also Table_00 -- confirmed
|
||||
# 14 more courses the same way (issue #394); its /course/vs/0 supportedOptions
|
||||
# only advertises a different subset of this same table (each model exposes
|
||||
# whichever courses its hardware supports), not a conflicting code family --
|
||||
# the one code both reporters confirmed, 'a5', means Bedding on both. Folded
|
||||
# into the same catalog entry below rather than a new one.
|
||||
#
|
||||
# Drum Clean+ maintenance tracking (issue #258) reuses washer.py's
|
||||
# DrumCleanProposal_/WashingTimes_/DrumCleanLog_ tokens on this same
|
||||
|
||||
@@ -344,7 +344,20 @@
|
||||
"a4": "Časové sušení",
|
||||
"a6": "Rychlé sušení",
|
||||
"a3": "Sportovní oblečení",
|
||||
"a2": "Jemné prádlo"
|
||||
"a2": "Jemné prádlo",
|
||||
"9a": "Bavlna",
|
||||
"ca": "Provětrání",
|
||||
"db": "Super Speed",
|
||||
"99": "Smíšená náplň",
|
||||
"93": "Žehlení",
|
||||
"b5": "Vlna",
|
||||
"d7": "Outdoor péče",
|
||||
"96": "Studený vzduch",
|
||||
"97": "Teplý vzduch",
|
||||
"7f": "Časové sušení",
|
||||
"98": "Rychlé sušení 35",
|
||||
"eb": "Jemné prádlo",
|
||||
"b6": "Syntetika"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
@@ -344,7 +344,20 @@
|
||||
"a4": "Zeittrocknen",
|
||||
"a6": "Schnelltrocknen",
|
||||
"a3": "Sportkleidung",
|
||||
"a2": "Feinwäsche"
|
||||
"a2": "Feinwäsche",
|
||||
"9a": "Baumwolle",
|
||||
"ca": "Frischluft",
|
||||
"db": "Super Speed",
|
||||
"99": "Mischwäsche",
|
||||
"93": "Bügeltrocken",
|
||||
"b5": "Wolle",
|
||||
"d7": "Outdoor-Pflege",
|
||||
"96": "Kaltluft",
|
||||
"97": "Warmluft",
|
||||
"7f": "Zeittrocknen",
|
||||
"98": "Schnelltrocknen 35",
|
||||
"eb": "Feinwäsche",
|
||||
"b6": "Synthetik"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
@@ -344,7 +344,20 @@
|
||||
"a4": "Time Dry",
|
||||
"a6": "Quick Dry",
|
||||
"a3": "Active Wear",
|
||||
"a2": "Delicates"
|
||||
"a2": "Delicates",
|
||||
"9a": "Cotton",
|
||||
"ca": "Air Wash",
|
||||
"db": "Super Speed",
|
||||
"99": "Mixed Load",
|
||||
"93": "Iron Dry",
|
||||
"b5": "Wool",
|
||||
"d7": "Outdoor Care",
|
||||
"96": "Cool Air",
|
||||
"97": "Warm Air",
|
||||
"7f": "Time Dry",
|
||||
"98": "Quick Dry 35",
|
||||
"eb": "Delicates",
|
||||
"b6": "Synthetics"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
@@ -541,7 +541,20 @@
|
||||
"a4": "Secado por tiempo",
|
||||
"a6": "Secado rápido",
|
||||
"a3": "Ropa deportiva",
|
||||
"a2": "Delicados"
|
||||
"a2": "Delicados",
|
||||
"9a": "Algodón",
|
||||
"ca": "Aireación",
|
||||
"db": "Súper velocidad",
|
||||
"99": "Carga mixta",
|
||||
"93": "Planchado fácil",
|
||||
"b5": "Lana",
|
||||
"d7": "Cuidado para exterior",
|
||||
"96": "Aire frío",
|
||||
"97": "Aire caliente",
|
||||
"7f": "Secado por tiempo",
|
||||
"98": "Secado rápido 35",
|
||||
"eb": "Delicados",
|
||||
"b6": "Sintéticos"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
@@ -344,7 +344,20 @@
|
||||
"a4": "Asciugatura a tempo",
|
||||
"a6": "Asciugatura rapida",
|
||||
"a3": "Abbigliamento sportivo",
|
||||
"a2": "Delicati"
|
||||
"a2": "Delicati",
|
||||
"9a": "Cotone",
|
||||
"ca": "Arieggiatura",
|
||||
"db": "Super Speed",
|
||||
"99": "Carico misto",
|
||||
"93": "Pronto da stirare",
|
||||
"b5": "Lana",
|
||||
"d7": "Cura outdoor",
|
||||
"96": "Aria fredda",
|
||||
"97": "Aria calda",
|
||||
"7f": "Asciugatura a tempo",
|
||||
"98": "Asciugatura rapida 35",
|
||||
"eb": "Delicati",
|
||||
"b6": "Sintetici"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
@@ -344,7 +344,20 @@
|
||||
"a4": "시간건조",
|
||||
"a6": "쾌속건조",
|
||||
"a3": "운동복",
|
||||
"a2": "섬세의류"
|
||||
"a2": "섬세의류",
|
||||
"9a": "면의류",
|
||||
"ca": "송풍",
|
||||
"db": "쾌속건조",
|
||||
"99": "혼합",
|
||||
"93": "다림질건조",
|
||||
"b5": "울",
|
||||
"d7": "아웃도어케어",
|
||||
"96": "송풍건조",
|
||||
"97": "온풍건조",
|
||||
"7f": "시간건조",
|
||||
"98": "쾌속건조 35분",
|
||||
"eb": "섬세의류",
|
||||
"b6": "합성섬유"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
@@ -344,7 +344,20 @@
|
||||
"a4": "Tijdprogramma",
|
||||
"a6": "Snel drogen",
|
||||
"a3": "Sportkleding",
|
||||
"a2": "Fijne was"
|
||||
"a2": "Fijne was",
|
||||
"9a": "Katoen",
|
||||
"ca": "Luchtverfrissing",
|
||||
"db": "Super Speed",
|
||||
"99": "Gemengde was",
|
||||
"93": "Strijkdroog",
|
||||
"b5": "Wol",
|
||||
"d7": "Outdoorverzorging",
|
||||
"96": "Koude lucht",
|
||||
"97": "Warme lucht",
|
||||
"7f": "Tijdprogramma",
|
||||
"98": "Snel drogen 35",
|
||||
"eb": "Fijne was",
|
||||
"b6": "Synthetisch"
|
||||
}
|
||||
},
|
||||
"dryer_cycle_table_03": {
|
||||
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
{
|
||||
"device0": [
|
||||
{
|
||||
"rt": [
|
||||
"x.com.samsung.devcol",
|
||||
"oic.wk.col"
|
||||
],
|
||||
"if": [
|
||||
"oic.if.baseline",
|
||||
"oic.if.ll",
|
||||
"oic.if.b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href": "/alarms/vs/0",
|
||||
"rep": {}
|
||||
},
|
||||
{
|
||||
"href": "/configuration/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.region": "0000000000",
|
||||
"x.com.samsung.da.countryCode": "US\u0001"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/course/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.supportedModes": [
|
||||
"HOMECARE_WIZARD_V2"
|
||||
],
|
||||
"x.com.samsung.da.options": [
|
||||
"DeviceType_0165",
|
||||
"UpdateAllow_NotAllowed",
|
||||
"Course_9A",
|
||||
"MixedLoadBell_Disable",
|
||||
"MixedLoadBellNoti_Nothing",
|
||||
"LaundryOutTime_0",
|
||||
"SeamlessControl_Disable",
|
||||
"KidsLockBypass_On",
|
||||
"DetergentOnce_0",
|
||||
"DetergentLeft_0",
|
||||
"DetergentBase_0",
|
||||
"DetergentAlarm_Off",
|
||||
"DetergentType_0",
|
||||
"DetergentTotal_0",
|
||||
"SpecialFunction_4",
|
||||
"AvailableDelayTime_188",
|
||||
"LaundryPlannerUserSetTime_0",
|
||||
"ProgressTimeSet_B22D00B4003C",
|
||||
"WrinklePreventRunning_Off",
|
||||
"EnergyLevelSet_050502050503020304010204030203",
|
||||
"MostUsed_9AD20EE000",
|
||||
"MixedLoadBellSet_02FFFF02FFFFFFFFFFFFFFFFFF02",
|
||||
"UsagesDB_ok",
|
||||
"EnergyKW_396",
|
||||
"TimeSync_NotSupported"
|
||||
],
|
||||
"x.com.samsung.da.supportedOptions": [
|
||||
"29AD20EE000CAD10EE000DBD204E00099D20EE00093D102E000B5D102E000D7D204E000A5D204E00096D000E10E97D000E10E7FD000E33E98D000E000EBD204E000B6D20EE000"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/cycleinterface/vs/0",
|
||||
"rep": {}
|
||||
},
|
||||
{
|
||||
"href": "/diagnosis/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.diagnosisStart": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/energy/consumption/0",
|
||||
"rep": {}
|
||||
},
|
||||
{
|
||||
"href": "/energy/consumption/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.instantaneousPowerUnit": "W",
|
||||
"x.com.samsung.da.instantaneousPower": "-500",
|
||||
"x.com.samsung.da.cumulativePower": "692600",
|
||||
"x.com.samsung.da.cumulativeUnit": "Wh",
|
||||
"x.com.samsung.da.cumulativeDate": "1786964400",
|
||||
"x.com.samsung.da.cumulativeDateUTC": "1786960800"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/file/information/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.timeoffset": "+01:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/information/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.modelNum": "DA_WM_A51_20_COMMON|20221341|30010102001211000103000000000000",
|
||||
"x.com.samsung.da.description": "DA_WM_A51_20_COMMON_DV6800N/DC92-01967B_0404",
|
||||
"x.com.samsung.da.serialNum": "**REDACTED**",
|
||||
"x.com.samsung.da.otnDUID": "**REDACTED**",
|
||||
"x.com.samsung.da.items": [
|
||||
{
|
||||
"x.com.samsung.da.id": "0",
|
||||
"x.com.samsung.da.description": "DA_WM_A51_20_COMMON|20221341|30010102001211000103000000000000",
|
||||
"x.com.samsung.da.type": "Software",
|
||||
"x.com.samsung.da.number": "02198A230708(E257)",
|
||||
"x.com.samsung.da.newVersionAvailable": "0"
|
||||
},
|
||||
{
|
||||
"x.com.samsung.da.id": "1",
|
||||
"x.com.samsung.da.description": "DA_WM_A51_20_COMMON",
|
||||
"x.com.samsung.da.type": "Firmware",
|
||||
"x.com.samsung.da.number": "17111305,17122616",
|
||||
"x.com.samsung.da.newVersionAvailable": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/kidslock/0",
|
||||
"rep": {
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/kidslock/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.kidsLock": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/operational/state/0",
|
||||
"rep": {
|
||||
"currentMachineState": "**REDACTED**",
|
||||
"machineStates": "**REDACTED**",
|
||||
"jobStates": [
|
||||
"None",
|
||||
"Drying",
|
||||
"Cooling",
|
||||
"Finish"
|
||||
],
|
||||
"currentJobState": "None",
|
||||
"remainingTime": "03:08:00",
|
||||
"progressPercentage": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/operational/state/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.state": "Ready",
|
||||
"x.com.samsung.da.remainingTime": "03:08:00",
|
||||
"x.com.samsung.da.progressPercentage": "1",
|
||||
"x.com.samsung.da.progress": "None",
|
||||
"x.com.samsung.da.delayEndTime": "00:00:00",
|
||||
"x.com.samsung.da.supportedProgress": [
|
||||
"None",
|
||||
"Drying",
|
||||
"Cooling",
|
||||
"Finish"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/power/0",
|
||||
"rep": {
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/power/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.power": "Off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/realtimenotiforclient/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.timeforshortnoti": "0",
|
||||
"x.com.samsung.da.periodicnotisubscription": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/remotectrl/0",
|
||||
"rep": {
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/remotectrl/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.remoteControlEnabled": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/setting/vs/0",
|
||||
"rep": {}
|
||||
},
|
||||
{
|
||||
"href": "/st/dryercourse/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.st.dryerMode": "Table_00_Course_9A",
|
||||
"x.com.samsung.da.st.courseTable": "Table_00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/washer/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.wrinklePrevent": "Off",
|
||||
"x.com.samsung.da.dryLevel": "2",
|
||||
"x.com.samsung.da.supportedDryLevel": [
|
||||
"None",
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"x.com.samsung.da.dryTime": "00:00:00",
|
||||
"x.com.samsung.da.supportedDryTime": [
|
||||
"00:00:00",
|
||||
"00:30:00",
|
||||
"01:00:00",
|
||||
"01:30:00",
|
||||
"02:00:00",
|
||||
"02:30:00"
|
||||
],
|
||||
"x.com.samsung.da.dryerType": "Electricity"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "/wm/editcourse/vs/0",
|
||||
"rep": {}
|
||||
},
|
||||
{
|
||||
"href": "/wm/jobbeginingstatus/vs/0",
|
||||
"rep": {}
|
||||
},
|
||||
{
|
||||
"href": "/wm/setinfo/vs/0",
|
||||
"rep": {
|
||||
"x.com.samsung.da.isModelSettingWithoutSC": "false",
|
||||
"x.com.samsung.da.isModelSettingPowerOnOff": "false"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"state_keys": [
|
||||
"alarm_code",
|
||||
"child_lock",
|
||||
"completion_minutes",
|
||||
"cycle",
|
||||
"cycle_active",
|
||||
"delay_start_hours",
|
||||
"diagnosis",
|
||||
"dry_level",
|
||||
"dry_time",
|
||||
"dryer_type",
|
||||
"energy_kwh",
|
||||
"finish_time",
|
||||
"job_beginning_status",
|
||||
"machine_state",
|
||||
"power_switch",
|
||||
"progress",
|
||||
"progress_percentage",
|
||||
"remote_control",
|
||||
"wrinkle_prevent"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Tests for dryer support and washer/dryer consistency (issue #14)."""
|
||||
|
||||
from custom_components.localthings.registry.adapter import flatten
|
||||
from custom_components.localthings.registry.by_type import for_device_by_model
|
||||
from custom_components.localthings.registry.by_type import for_device_by_model, resolve
|
||||
from custom_components.localthings.registry.capabilities import dryer, ignored
|
||||
from custom_components.localthings.registry.discovery import discover
|
||||
from custom_components.localthings.registry.entities import SelectDesc
|
||||
@@ -90,8 +90,12 @@ def test_course_bound_to_shared_course_vs_0():
|
||||
|
||||
|
||||
def test_reported_table_00_course_codes_are_translated():
|
||||
"""The reporter confirmed these codes on a DVE45R6300W/A3 by selecting
|
||||
each cycle and reading back the raw course code (issue #357)."""
|
||||
"""The DVE45R6300W/A3 reporter confirmed these codes by selecting each
|
||||
cycle and reading back the raw course code (issue #357). A DV6800N --
|
||||
same DA_WM_A51_20_COMMON board, also Table_00 -- later confirmed 14 more
|
||||
(issue #394): a different subset of the same table, not a conflicting
|
||||
code family (its one code in common with #357, 'a5', means Bedding on
|
||||
both), so both sets share the one dryer_cycle_table_00 catalog entry."""
|
||||
from custom_components.localthings.catalog import translated_states
|
||||
|
||||
desc = next(
|
||||
@@ -99,7 +103,32 @@ def test_reported_table_00_course_codes_are_translated():
|
||||
)
|
||||
table_00 = {"/st/dryercourse/vs/0": {"x.com.samsung.da.st.courseTable": "Table_00"}}
|
||||
assert desc.translation_key(table_00) == "dryer_cycle_table_00"
|
||||
confirmed = {"01", "9c", "a5", "9e", "9b", "27", "a0", "a4", "a6", "a3", "a2"}
|
||||
confirmed = {
|
||||
"01",
|
||||
"9c",
|
||||
"a5",
|
||||
"9e",
|
||||
"9b",
|
||||
"27",
|
||||
"a0",
|
||||
"a4",
|
||||
"a6",
|
||||
"a3",
|
||||
"a2", # issue #357
|
||||
"9a",
|
||||
"ca",
|
||||
"db",
|
||||
"99",
|
||||
"93",
|
||||
"b5",
|
||||
"d7",
|
||||
"96",
|
||||
"97",
|
||||
"7f",
|
||||
"98",
|
||||
"eb",
|
||||
"b6", # issue #394
|
||||
}
|
||||
assert confirmed <= translated_states("select", "dryer_cycle_table_00")
|
||||
|
||||
|
||||
@@ -109,3 +138,31 @@ def test_st_dryercourse_is_ignored():
|
||||
ignored_hrefs = {c.href for c in ignored.IGNORED}
|
||||
assert "/st/dryercourse/vs/0" in ignored_hrefs
|
||||
assert "/st/washercourse/vs/0" in ignored_hrefs
|
||||
|
||||
|
||||
def _dv6800n():
|
||||
resources = _load_device("dryer_dv6800n")
|
||||
reg = resolve(resources, device_types=("oic.wk.d", "oic.d.dryer"))
|
||||
return reg, resources
|
||||
|
||||
|
||||
def test_dv6800n_no_unbound_hrefs():
|
||||
"""Every resource in the issue #394 dump binds or is ignored."""
|
||||
reg, resources = _dv6800n()
|
||||
unbound = []
|
||||
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
|
||||
assert unbound == []
|
||||
|
||||
|
||||
def test_dv6800n_course_codes_read_from_dump():
|
||||
"""A DV6800N (DA_WM_A51_20_COMMON, issue #394) reports 'Table_00' same
|
||||
as #357's DVE45R6300W/A3, so it resolves to the same catalog entry --
|
||||
its /course/vs/0 supportedOptions just advertises a different subset of
|
||||
the same table (see test_reported_table_00_course_codes_are_translated)."""
|
||||
_, resources = _dv6800n()
|
||||
desc = next(
|
||||
e for e in dryer.DRYER_COURSE.entities if e.key == "cycle" and isinstance(e, SelectDesc)
|
||||
)
|
||||
assert desc.translation_key(resources) == "dryer_cycle_table_00"
|
||||
confirmed = ["9A", "CA", "DB", "99", "93", "B5", "D7", "A5", "96", "97", "7F", "98", "EB", "B6"]
|
||||
assert desc.options(resources) == confirmed
|
||||
|
||||
@@ -121,6 +121,25 @@ def test_registry_reproduces_golden_state_keys_for_dryer_dve50a8600():
|
||||
)
|
||||
|
||||
|
||||
def test_registry_reproduces_golden_state_keys_for_dryer_dv6800n():
|
||||
"""DA_WM_A51_20_COMMON/DV6800N (issue #394) resolves via /oic/d's
|
||||
'oic.d.dryer' device type, not board-token guessing -- its modelNum's
|
||||
board tokens ('DA', 'WM', 'COMMON') are all deliberately excluded from
|
||||
_BOARD_TOKEN_TO_KEY (see registry/by_type's module docstring)."""
|
||||
from tests.conftest import _load_device
|
||||
|
||||
resources = _load_device("dryer_dv6800n")
|
||||
golden = json.loads((GOLDEN / "dryer_dv6800n.json").read_text())
|
||||
state_keys = _new_state_keys(
|
||||
"dryer_dv6800n", resources, device_types=("oic.wk.d", "oic.d.dryer")
|
||||
)
|
||||
assert set(state_keys) == set(golden["state_keys"]), (
|
||||
f"state_keys mismatch:\n"
|
||||
f" extra: {sorted(set(state_keys) - set(golden['state_keys']))}\n"
|
||||
f" missing: {sorted(set(golden['state_keys']) - set(state_keys))}"
|
||||
)
|
||||
|
||||
|
||||
def test_registry_reproduces_golden_state_keys_for_airconditioner():
|
||||
from tests.conftest import _load_device
|
||||
|
||||
|
||||
Reference in New Issue
Block a user