diff --git a/tests/conftest.py b/tests/conftest.py index de9b260..e995a59 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -36,3 +36,8 @@ def dishwasher_resources() -> dict[str, dict]: @pytest.fixture def fridge_resources() -> dict[str, dict]: return _load_device('refrigerator') + + +@pytest.fixture +def washer_resources() -> dict[str, dict]: + return _load_device('washer') diff --git a/tests/fixtures/golden/washer.json b/tests/fixtures/golden/washer.json new file mode 100644 index 0000000..bf80007 --- /dev/null +++ b/tests/fixtures/golden/washer.json @@ -0,0 +1,25 @@ +{ + "state_keys": [ + "alarm_code", + "buzzer_sound", + "child_lock", + "cycle", + "cycle_active", + "delay_start_hours", + "diagnosis_status", + "energy_kwh", + "finish_time", + "firmware_update", + "job_beginning_status", + "machine_state", + "power_switch", + "power_watts", + "progress", + "progress_percentage", + "remote_control", + "rinse_cycles", + "spin_speed", + "wash_temperature", + "water_liters" + ] +} diff --git a/tests/fixtures/washer_device.json b/tests/fixtures/washer_device.json new file mode 100644 index 0000000..2f2eece --- /dev/null +++ b/tests/fixtures/washer_device.json @@ -0,0 +1,473 @@ +{ + "device0": [ + { + "rt": [ + "x.com.samsung.devcol", + "oic.wk.col" + ], + "if": [ + "oic.if.baseline", + "oic.if.ll", + "oic.if.b" + ] + }, + { + "href": "/realtimenotiforclient/vs/0", + "rep": { + "x.com.samsung.da.timeforshortnoti": "0", + "x.com.samsung.da.periodicnotisubscription": "true" + } + }, + { + "href": "/alarms/vs/0", + "rep": {} + }, + { + "href": "/diagnosis/vs/0", + "rep": { + "x.com.samsung.da.diagnosisStart": "Ready" + } + }, + { + "href": "/energy/consumption/vs/0", + "rep": { + "x.com.samsung.da.instantaneousPower": "-500", + "x.com.samsung.da.instantaneousPowerUnit": "W", + "x.com.samsung.da.cumulativePower": "18800", + "x.com.samsung.da.cumulativeUnit": "Wh", + "x.com.samsung.da.cumulativeDate": "1783774800", + "x.com.samsung.da.cumulativeDateUTC": "1783767600", + "x.com.samsung.da.cumulativeSavedPower": "0" + } + }, + { + "href": "/energy/consumption/0", + "rep": {} + }, + { + "href": "/course/vs/0", + "rep": { + "x.com.samsung.da.supportedModes": [ + "HOMECARE_WIZARD_V2" + ], + "x.com.samsung.da.options": [ + "DeviceType_0167", + "UpdateAllow_NotAllowed", + "Course_1C", + "AiOption_On", + "LaundryOutTime_0", + "SteamWash_Disable", + "SeamlessControl_Disable", + "KidsLockBypass_On", + "WashingTimes_3", + "DrumCleanProposal_40", + "DetergentOnce_1", + "DetergentLeft_0", + "DetergentBase_5", + "DetergentAlarm_Off", + "DetergentType_2", + "DetergentTotal_0", + "SoftenerOnce_0", + "SoftenerLeft_0", + "SoftenerBase_0", + "SoftenerAlarm_Off", + "SoftenerType_0", + "SoftenerTotal_0", + "SpecialFunction_4", + "AvailableDelayTime_163", + "BubbleSoak_Off", + "LaundryPlannerUserSetTime_0", + "ProgressTimeSet_4201048201A4A200D4", + "SendToDevice_Off", + "GMT_04", + "PreWashSetting_Off", + "IntensiveSetting_Off", + "BubbleSoakSet_00F000F00000F000F0F0F0F00000F000F0F0F0F0000000", + "EnergyLevelSet_050304010402010402040402050302020202050303050101", + "MostUsed_1C8410923FA67F00000000000000", + "TextureLevel_None", + "SavingModeCondition_010102151B1D1C1E8F202122232425261C2D30323396662E2F030149035E2830", + "SavingMode_Off", + "DetergentUserSet_0000", + "SoftenerUserSet_0000", + "DetergentSetting_01", + "DetergentSettingList_01", + "SoftenerCustomSetting_01", + "SoftenerCustomSettingList_0102", + "DetergentDefaultCourseList_030422232F2604012E", + "LaundryManageFinish_Off", + "UserLocation_Indoor", + "GeoFenceAlarm", + "UsagesDB_ok", + "EnergyKW_396", + "DrumCleanLog_2026-07-01T20:18:07", + "TimeSync_NotSupported" + ], + "x.com.samsung.da.supportedOptions": [ + "31C8410923FA67F1B847E923FA67F1E811E933FA33F1D841E923FA67F96841E920FA37F8F8102923FA57F25843E933FA57F26831E920FA20733857E933FA67F24841E930FA30F32833E923FA37F20857E943FA67F22841E920FA30F23831E930FA57F2F831E933FA30F21841E943FA57F66831E933FA30F2E867E943FA67F2D841E923FA30F30843E923FA67F2985209204A520278000913FA67F2880009000A67E" + ] + } + }, + { + "href": "/power/vs/0", + "rep": { + "x.com.samsung.da.power": "On" + } + }, + { + "href": "/power/0", + "rep": { + "value": true + } + }, + { + "href": "/cycleinterface/vs/0", + "rep": {} + }, + { + "href": "/kidslock/vs/0", + "rep": { + "x.com.samsung.da.kidsLock": "Ready" + } + }, + { + "href": "/kidslock/0", + "rep": { + "value": false + } + }, + { + "href": "/operational/state/vs/0", + "rep": { + "x.com.samsung.da.state": "Ready", + "x.com.samsung.da.remainingTime": "02:43: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", + "Weightsensing", + "Wash", + "Rinse", + "Spin", + "Finish" + ] + } + }, + { + "href": "/operational/state/0", + "rep": { + "currentMachineState": "**REDACTED**", + "machineStates": "**REDACTED**", + "jobStates": [ + "None", + "Weightsensing", + "Wash", + "Rinse", + "Spin", + "Finish" + ], + "currentJobState": "None", + "remainingTime": "02:43:00", + "progressPercentage": "1" + } + }, + { + "href": "/information/vs/0", + "rep": { + "x.com.samsung.da.modelNum": "TEST-MODEL", + "x.com.samsung.da.description": "DA_WM_TP1_21_COMMON_WD7000B/DC92-03724A_004D", + "x.com.samsung.da.serialNum": "**REDACTED**", + "x.com.samsung.da.otnDUID": "**REDACTED**", + "x.com.samsung.da.diagProtocolType": "BLE_OCF", + "x.com.samsung.da.diagLogType": [ + "errCode", + "dump" + ], + "x.com.samsung.da.diagDumpType": "file", + "x.com.samsung.da.diagEndPoint": "SSM", + "x.com.samsung.da.diagMnid": "0AJT", + "x.com.samsung.da.diagSetupid": "WFC", + "x.com.samsung.da.diagMinVersion": "3.0", + "x.com.samsung.da.diagTsId": "DA01", + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "DA_WM_TP1_21_COMMON|20375141|20010002001811424AA30217008A0000", + "x.com.samsung.da.type": "Software", + "x.com.samsung.da.number": "02986A260118(A182)", + "x.com.samsung.da.newVersionAvailable": "0" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Firmware_1_DB_20375141240308040FFFFF203724412507152904FFFF(01672037514120372441_30000000)(FileDown:0)(Type:0)", + "x.com.samsung.da.type": "Firmware", + "x.com.samsung.da.number": "03751A24030804,03724A25071529", + "x.com.samsung.da.newVersionAvailable": "0" + }, + { + "x.com.samsung.da.id": "2", + "x.com.samsung.da.description": "Firmware_2_DB_2036284224030602042FFFFFFFFFFFFFFFFFFFFFFFFE(016720362842FFFFFFFF_30000000)(FileDown:0)(Type:0)", + "x.com.samsung.da.type": "Firmware", + "x.com.samsung.da.number": "03628B24030602,FFFFFFFFFFFFFF" + } + ] + } + }, + { + "href": "/file/information/vs/0", + "rep": { + "x.com.samsung.timeoffset": "+02:00" + } + }, + { + "href": "/washer/vs/0", + "rep": { + "x.com.samsung.da.waterTemperature": "40", + "x.com.samsung.da.supportedWaterTemperature": [ + "None", + "Cold", + "20", + "30", + "40", + "60", + "90" + ], + "x.com.samsung.da.spinLevel": "1400", + "x.com.samsung.da.supportedSpinLevel": [ + "RinseHold", + "NoSpin", + "400", + "800", + "1000", + "1200", + "1400" + ], + "x.com.samsung.da.rinseCycles": "2", + "x.com.samsung.da.supportedRinseCycles": [ + "0", + "1", + "2", + "3", + "4", + "5" + ], + "x.com.samsung.da.currentAvailableTemperature": "00", + "x.com.samsung.da.currentAvailableRinse": "3F", + "x.com.samsung.da.currentAvailableSpin": "7F", + "x.com.samsung.da.currentAvailableSubOptions": "00000000000000000000" + } + }, + { + "href": "/st/washercourse/vs/0", + "rep": { + "x.com.samsung.da.st.washerMode": "Table_02_Course_1C", + "x.com.samsung.da.st.courseTable": "Table_02" + } + }, + { + "href": "/water/consumption/vs/0", + "rep": { + "x.com.samsung.da.cumulativeWater": "1966500" + } + }, + { + "href": "/setting/vs/0", + "rep": { + "x.com.samsung.da.supportedSetLanguage": [ + "ko_KR", + "en_US" + ], + "x.com.samsung.da.setLanguage": "sk_SK" + } + }, + { + "href": "/wm/editcourse/vs/0", + "rep": { + "x.com.samsung.da.editCourseList": "EditCourseList_1C1D211B1E29243328262722202325322F2E30662D8F96", + "x.com.samsung.da.fixedCourseList": "FixedCourseList_1C29" + } + }, + { + "href": "/wm/setinfo/vs/0", + "rep": { + "x.com.samsung.da.isModelSettingWithoutSC": "true", + "x.com.samsung.da.aiCourse": "true", + "x.com.samsung.da.isModelSettingPowerOnOff": "false", + "x.com.samsung.da.modelCode": "M(None),W(WW90DG6U25LEU4)" + } + }, + { + "href": "/wm/jobbeginingstatus/vs/0", + "rep": { + "x.com.samsung.da.currentStatus": "None" + } + }, + { + "href": "/wm/welcomemsg/vs/0", + "rep": {} + }, + { + "href": "/wm/personalcourse/vs/0", + "rep": { + "x.com.samsung.da.courses": [ + "F1_00", + "F2_00", + "F3_00", + "F4_00", + "F5_00", + "F6_00", + "F7_00", + "F8_00", + "F9_00", + "FA_00" + ], + "x.com.samsung.da.maxCourseNum": "10", + "x.com.samsung.da.labelScanCourse": "FB_00" + } + }, + { + "href": "/otninformation/vs/0", + "rep": { + "x.com.samsung.da.target": "", + "x.com.samsung.da.newVersionAvailable": "false", + "x.com.samsung.da.newVersionNo": "00000000", + "x.com.samsung.da.currentVersionInfo": "00000000", + "otnStatus": "None", + "flashingProgress": "", + "otnTarget": "main", + "otnCompleteDate": "2026-03-22", + "otnList": [ + { + "type": "WIFI", + "modelId": "DA_WM_TP1_21_COMMON", + "versions": [ + "30260118" + ], + "visVersion": "260118" + }, + { + "type": "Micom", + "modelId": "01672037514120372441", + "versions": [ + "24030804", + "25071529" + ], + "visVersion": "250715" + }, + { + "type": "Micom", + "modelId": "016720362842FFFFFFFF", + "versions": [ + "24030602", + "FFFFFFFF" + ], + "visVersion": "240306" + } + ] + } + }, + { + "href": "/buzzersound/vs/0", + "rep": { + "supportedBuzzerSound": [ + "Off", + "On" + ], + "setBuzzerSound": "On" + } + }, + { + "href": "/remotectrl/vs/0", + "rep": { + "x.com.samsung.da.remoteControlEnabled": "false" + } + }, + { + "href": "/remotectrl/0", + "rep": { + "value": false + } + }, + { + "href": "/configuration/vs/0", + "rep": { + "x.com.samsung.da.region": "0000000000", + "x.com.samsung.da.countryCode": "SK" + } + }, + { + "href": "/drlc/0", + "rep": { + "DRLevel": 0, + "start": "0000-00-00T00:00:00Z", + "duration": 0, + "override": false + } + }, + { + "href": "/drlc/vs/0", + "rep": { + "x.com.samsung.da.drlcLevel": "0", + "x.com.samsung.da.durationminutes": "0", + "x.com.samsung.da.start": "0000-00-00T00:00:00Z", + "x.com.samsung.da.override": "Off", + "x.com.samsung.da.realSaving": "Off" + } + }, + { + "href": "/timezone/vs/0", + "rep": { + "timezoneid": "Europe/Bratislava", + "offset": "+02:00", + "DST": "ON" + } + }, + { + "href": "/connectionconfig/vs/0", + "rep": { + "autoReconnectionMinVersion": "1.0", + "autoReconnection": "true", + "autoReconnectionProtocolType": [ + "helper_hotspot", + "ble_ocf" + ], + "supportedWiFiAuthType": [ + "OPEN", + "WEP", + "WPA-PSK", + "WPA2-PSK", + "SAE" + ], + "supportedWiFiCryptoType": [ + "TKIP", + "AES", + "WEP-64", + "WEP-128" + ], + "supportedWiFiFreq": [ + "2.4G" + ], + "calmConnectionCare": { + "version": "1.0", + "role": [ + "things" + ] + } + } + }, + { + "href": "/wirelessinfo/vs/0", + "rep": { + "macaddressWiFi": "**REDACTED**", + "macaddressBLE": "**REDACTED**" + } + }, + { + "href": "/quickcontrol/info/vs/0", + "rep": { + "supportedVersion": "1.0" + } + } + ] +} diff --git a/tests/test_golden_regression.py b/tests/test_golden_regression.py index 23b1b7b..98884b8 100644 --- a/tests/test_golden_regression.py +++ b/tests/test_golden_regression.py @@ -7,12 +7,18 @@ GOLDEN = Path(__file__).parent / 'fixtures' / 'golden' def _new_state_keys(name, resources): - from custom_components.localthings.registry.by_type import for_device + from custom_components.localthings.registry.by_type import for_device, for_device_by_model from custom_components.localthings.registry.discovery import discover from custom_components.localthings.registry.adapter import flatten otn = resources.get('/otninformation/vs/0', {}) one_ui = otn.get('swVersionInfo', {}).get('oneUiVersion', '') + info = resources.get('/information/vs/0', {}) reg = for_device(one_ui) if one_ui else None + if reg is None: + reg = for_device_by_model( + info.get('x.com.samsung.da.modelNum', ''), + info.get('x.com.samsung.da.description', ''), + ) if reg is None: from custom_components.localthings.registry.registry import CAPABILITIES caps, pats = CAPABILITIES, [] @@ -39,6 +45,18 @@ def test_registry_reproduces_golden_state_keys(name, ip, request): ) +def test_registry_reproduces_golden_state_keys_for_washer(): + from tests.conftest import _load_device + resources = _load_device('washer') + golden = json.loads((GOLDEN / 'washer.json').read_text()) + state_keys = _new_state_keys('washer', resources) + 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_resources_from_batch_preferred_over_flat(): from tests.conftest import _resources_from_dump dump = {