From 6f53b3043bbe3d21eff5cfc5aade8c19b4d63db2 Mon Sep 17 00:00:00 2001 From: themaanda Date: Tue, 21 Jul 2026 16:58:42 -0500 Subject: [PATCH] Cover TP1X refrigerator native resource mirrors --- .../registry/by_type/refrigerator.py | 2 + .../registry/capabilities/fridge.py | 16 ++ .../golden/refrigerator_tp1x_ref_21k_us.json | 30 +++ .../refrigerator_tp1x_ref_21k_us_device.json | 253 ++++++++++++++++++ tests/test_fridge_capabilities.py | 32 +++ tests/test_golden_regression.py | 14 + 6 files changed, 347 insertions(+) create mode 100644 tests/fixtures/golden/refrigerator_tp1x_ref_21k_us.json create mode 100644 tests/fixtures/refrigerator_tp1x_ref_21k_us_device.json diff --git a/custom_components/localthings/registry/by_type/refrigerator.py b/custom_components/localthings/registry/by_type/refrigerator.py index ce2f1dc..003eb8d 100644 --- a/custom_components/localthings/registry/by_type/refrigerator.py +++ b/custom_components/localthings/registry/by_type/refrigerator.py @@ -28,11 +28,13 @@ REGISTRY = DeviceRegistry( fridge.BEVERAGE_ZONE, fridge.FIRMWARE_UPDATE, fridge.DEFROST_DELAY, + fridge.DEFROST_DELAY_NATIVE_DUPLICATE, fridge.DEFROST_BLOCK_STATUS, fridge.SELF_CHECK, fridge.DOORS_FALLBACK, fridge.TEMPERATURES_FALLBACK, fridge.ICEMAKER_STATUS_FALLBACK, + fridge.ICEMAKER_STATUS_NATIVE_DUPLICATE, fridge.REFRIGERATION_FALLBACK, ]), pattern_capabilities=[ diff --git a/custom_components/localthings/registry/capabilities/fridge.py b/custom_components/localthings/registry/capabilities/fridge.py index 1f056e7..c788fe1 100644 --- a/custom_components/localthings/registry/capabilities/fridge.py +++ b/custom_components/localthings/registry/capabilities/fridge.py @@ -209,6 +209,14 @@ DEFROST_DELAY = Capability( ), ) +# OCF-native boolean mirror of DEFROST_DELAY. The captured TP1X_REF_21K +# firmware publishes the same state on both hrefs, but only the vendor resource +# above has a confirmed write contract. Bind the native mirror without another +# entity so discovery records it as an intentional duplicate. +DEFROST_DELAY_NATIVE_DUPLICATE = Capability( + href='/defrost/delay/0', +) + DEFROST_BLOCK_STATUS = Capability( href='/defrost/block/vs/0', poll_tier='warm', @@ -655,6 +663,14 @@ ICEMAKER_STATUS_FALLBACK = Capability( ), ) +# OCF-native aggregate mirror of ICEMAKER_STATUS_FALLBACK. On the captured +# TP1X_REF_21K it duplicates both the vendor aggregate and the richer per-unit +# /icemaker/one|two/vs/0 resources. Its write contract is not advertised, so +# keep the proven per-unit/vendor controls and bind this as a duplicate only. +ICEMAKER_STATUS_NATIVE_DUPLICATE = Capability( + href='/icemaker/status/0', +) + # OCF-native /refrigeration/0 (issue #7's unbound_hrefs) -- the odd one out # in this section: its three fields duplicate two *different* richer # hrefs (REFRIGERATION's rapidFridge/rapidFreezing and diff --git a/tests/fixtures/golden/refrigerator_tp1x_ref_21k_us.json b/tests/fixtures/golden/refrigerator_tp1x_ref_21k_us.json new file mode 100644 index 0000000..962e01c --- /dev/null +++ b/tests/fixtures/golden/refrigerator_tp1x_ref_21k_us.json @@ -0,0 +1,30 @@ +{ + "state_keys": [ + "alarm_code", + "autofill", + "cooler_setpoint", + "cooler_temperature", + "defrost_active", + "defrost_delay", + "door_cooler_open", + "door_cvroom_open", + "door_freezer_open", + "energy_kwh", + "filter_status", + "filter_usage", + "firmware_update", + "flex_zone_mode", + "freezer_setpoint", + "freezer_temperature", + "icemaker_one_enabled", + "icemaker_one_making_status", + "icemaker_two_enabled", + "icemaker_two_making_status", + "power_watts", + "rapid_freezing", + "rapid_fridge", + "sabbath_mode", + "selfcheck_result", + "selfcheck_status" + ] +} diff --git a/tests/fixtures/refrigerator_tp1x_ref_21k_us_device.json b/tests/fixtures/refrigerator_tp1x_ref_21k_us_device.json new file mode 100644 index 0000000..13c824e --- /dev/null +++ b/tests/fixtures/refrigerator_tp1x_ref_21k_us_device.json @@ -0,0 +1,253 @@ +{ + "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": "/temperatures/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "Freezer", + "x.com.samsung.da.desired": "0", + "x.com.samsung.da.current": "0", + "x.com.samsung.da.maximum": "5", + "x.com.samsung.da.minimum": "-8", + "x.com.samsung.da.unit": "Fahrenheit" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Fridge", + "x.com.samsung.da.desired": "39", + "x.com.samsung.da.current": "39", + "x.com.samsung.da.maximum": "44", + "x.com.samsung.da.minimum": "34", + "x.com.samsung.da.unit": "Fahrenheit" + } + ] + } + }, + {"href": "/temperature/current/freezer/0", "rep": {"range": [-8.0, 5.0], "units": "F", "temperature": 0.0}}, + {"href": "/temperature/desired/freezer/0", "rep": {"range": [-8.0, 5.0], "units": "F", "temperature": 0.0}}, + {"href": "/temperature/current/cooler/0", "rep": {"range": [34.0, 44.0], "units": "F", "temperature": 39.0}}, + {"href": "/temperature/desired/cooler/0", "rep": {"range": [34.0, 44.0], "units": "F", "temperature": 39.0}}, + { + "href": "/selfcheck/vs/0", + "rep": { + "x.com.samsung.da.status": "Ready", + "x.com.samsung.da.result": "Success", + "x.com.samsung.da.error": ["DA_ERROR_NONE"], + "x.com.samsung.da.supportedActions": ["Start"] + } + }, + { + "href": "/energy/consumption/vs/0", + "rep": { + "x.com.samsung.da.cumulativeConsumption": "63", + "x.com.samsung.da.cumulativePower": "1831899", + "x.com.samsung.da.cumulativeUnit": "Wh", + "x.com.samsung.da.instantaneousPower": "3", + "x.com.samsung.da.instantaneousPowerUnit": "W", + "x.com.samsung.da.monthlyConsumption": "52000", + "x.com.samsung.da.thismonthlyConsumption": "38900", + "x.com.samsung.da.cumulativeSavedPower": "0" + } + }, + { + "href": "/mode/vs/0", + "rep": { + "x.com.samsung.da.supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "18K_REF_OUTDOOR_CONTROL_V2", + "SUPPORT_SABBATH_CONTROL" + ], + "x.com.samsung.da.modes": ["CVN_CONVERTIBLE_ZONE", "CV_FDR_WINE", "WATERFILTER_DISABLE"], + "x.com.samsung.da.supportedOptions": [ + "CV_FDR_WINE", + "CV_FDR_DELI", + "CV_FDR_BEVERAGE", + "CV_FDR_MEAT", + "CV_FDR_SOFT_FREEZER" + ] + } + }, + { + "href": "/mode/0", + "rep": { + "supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "18K_REF_OUTDOOR_CONTROL_V2", + "SUPPORT_SABBATH_CONTROL" + ], + "modes": ["CVN_CONVERTIBLE_ZONE", "CV_FDR_WINE", "WATERFILTER_DISABLE"] + } + }, + { + "href": "/defrost/block/vs/0", + "rep": { + "x.com.samsung.da.supportedModes": ["DEFROST_BLOCK_ON", "DEFROST_BLOCK_OFF"], + "x.com.samsung.da.modes": ["DEFROST_BLOCK_OFF"] + } + }, + {"href": "/sabbath/vs/0", "rep": {"x.com.samsung.da.sabbathMode": "Off"}}, + { + "href": "/realtimenotiforclient/vs/0", + "rep": { + "x.com.samsung.da.timeforshortnoti": "0", + "x.com.samsung.da.periodicnotisubscription": "true" + } + }, + { + "href": "/information/vs/0", + "rep": { + "x.com.samsung.da.modelNum": "TP1X_REF_21K|00148241|00020850031611204103000021010000", + "x.com.samsung.da.description": "TP1X_REF_21K", + "x.com.samsung.da.serialNum": "REDACTED", + "x.com.samsung.da.otnDUID": "REDACTED", + "x.com.samsung.da.diagProtocolType": "WIFI_HTTPS", + "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": "512", + "x.com.samsung.da.diagMinVersion": "1.0", + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "WiFi Module", + "x.com.samsung.da.type": "Software", + "x.com.samsung.da.number": "250420", + "x.com.samsung.da.newVersionAvailable": "0" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Micom", + "x.com.samsung.da.type": "Firmware", + "x.com.samsung.da.number": "23071923, FFFFFFFF", + "x.com.samsung.da.newVersionAvailable": "0" + } + ] + } + }, + {"href": "/file/information/vs/0", "rep": {"x.com.samsung.timeoffset": "-05:00", "x.com.samsung.supprtedtype": 1}}, + { + "href": "/doors/vs/0", + "rep": { + "x.com.samsung.da.items": [ + {"x.com.samsung.da.id": "0", "x.com.samsung.da.description": "Door", "x.com.samsung.da.openState": "Close"}, + {"x.com.samsung.da.id": "1", "x.com.samsung.da.description": "Door", "x.com.samsung.da.openState": "Close"}, + {"x.com.samsung.da.id": "2", "x.com.samsung.da.description": "Door", "x.com.samsung.da.openState": "Close"} + ] + } + }, + {"href": "/door/freezer/0", "rep": {"openState": "Close"}}, + {"href": "/door/cooler/0", "rep": {"openState": "Close"}}, + {"href": "/door/cvroom/0", "rep": {"openState": "Close"}}, + {"href": "/configuration/vs/0", "rep": {"x.com.samsung.da.countryCode": "", "x.com.samsung.da.region": ""}}, + {"href": "/defrost/delay/0", "rep": {"value": false}}, + {"href": "/defrost/delay/vs/0", "rep": {"x.com.samsung.da.delayDefrost": "Off"}}, + { + "href": "/defrost/reservation/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "Summer Season", + "x.com.samsung.da.startTime": "0000-05-01T15:00:00", + "x.com.samsung.da.period": "04:00:00", + "x.com.samsung.da.endTime": "0000-10-31T00:00:00" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Winter Season", + "x.com.samsung.da.startTime": "0000-11-01T06:00:00", + "x.com.samsung.da.period": "04:00:00", + "x.com.samsung.da.endTime": "0000-04-30T00:00:00" + } + ] + } + }, + {"href": "/icemaker/status/0", "rep": {"status": "On"}}, + {"href": "/icemaker/status/vs/0", "rep": {"x.com.samsung.da.iceMaker": "On"}}, + {"href": "/refrigeration/0", "rep": {"defrost": false, "rapidFreeze": false, "rapidCool": false}}, + { + "href": "/refrigeration/vs/0", + "rep": {"x.com.samsung.da.rapidFridge": "Off", "x.com.samsung.da.rapidFreezing": "Off"} + }, + {"href": "/drlc/0", "rep": {"DRLevel": 0, "override": false}}, + { + "href": "/drlc/vs/0", + "rep": { + "x.com.samsung.da.drlcLevel": "0", + "x.com.samsung.da.override": "Off", + "x.com.samsung.da.durationminutes": "0", + "x.com.samsung.da.realSaving": "Off" + } + }, + {"href": "/bespoke/vs/0", "rep": {"x.com.samsung.da.BespokeProduct": "On"}}, + { + "href": "/otninformation/vs/0", + "rep": { + "x.com.samsung.da.target": "", + "x.com.samsung.da.newVersionAvailable": "false", + "otnStatus": "None", + "flashingProgress": "", + "otnList": [ + {"type": "WIFI", "modelId": "A-RFWW-TP1-22-REV1", "versions": ["20250420"], "visVersion": "250420"}, + {"type": "Micom", "modelId": "024100148241FFFFFFFF", "versions": ["23071923", "FFFFFFFF"], "visVersion": "230719"} + ] + } + }, + { + "href": "/filter/waterfilter/vs/0", + "rep": { + "x.com.samsung.da.filterUsage": "31", + "x.com.samsung.da.filterUsageResolution": "1", + "x.com.samsung.da.filterStatus": "normal", + "x.com.samsung.da.filterResetType": ["replaceable"] + } + }, + {"href": "/autofill/vs/0", "rep": {"x.com.samsung.da.autofill": "Off"}}, + { + "href": "/icemaker/one/vs/0", + "rep": { + "x.com.samsung.da.iceMaker.name": "CUBED_ICE", + "x.com.samsung.da.iceMaker.state": "On", + "x.com.samsung.da.iceMaker.type": "toggle", + "x.com.samsung.da.iceMaker.iceMakingStatus": "ICESTATUS_STOP", + "x.com.samsung.da.iceType.desired": "NORMAL" + } + }, + { + "href": "/icemaker/two/vs/0", + "rep": { + "x.com.samsung.da.iceMaker.name": "ICE_BITES", + "x.com.samsung.da.iceMaker.state": "On", + "x.com.samsung.da.iceMaker.type": "toggle", + "x.com.samsung.da.iceMaker.iceMakingStatus": "ICESTATUS_STOP", + "x.com.samsung.da.iceType.desired": "On" + } + }, + {"href": "/energy/ailevel/vs/0", "rep": {"aiLevel": "1", "supportedAiLevel": ["1"]}}, + { + "href": "/connectionconfig/vs/0", + "rep": { + "autoReconnectionMinVersion": "1.0", + "autoReconnection": "true", + "autoReconnectionProtocolType": ["ble_ocf", null], + "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": "/timezone/vs/0", "rep": {"timezoneid": "America/Chicago", "offset": "-05:00", "DST": "ON"}}, + {"href": "/wirelessinfo/vs/0", "rep": {"macaddressWiFi": "REDACTED", "macaddressBLE": "REDACTED"}}, + {"href": "/quickcontrol/info/vs/0", "rep": {"supportedVersion": "1.0"}} + ] +} diff --git a/tests/test_fridge_capabilities.py b/tests/test_fridge_capabilities.py index a45e7f7..068956d 100644 --- a/tests/test_fridge_capabilities.py +++ b/tests/test_fridge_capabilities.py @@ -102,3 +102,35 @@ class TestRefrigerationFallback: for key in ('rapid_fridge', 'rapid_freezing'): desc = next(e for e in fridge.REFRIGERATION_FALLBACK.entities if e.key == key) assert desc.exists_fn({}, {'/refrigeration/0': {}}) is True + + +class TestTp1xNativeDuplicateResources: + """The US TP1X_REF_21K publishes two native mirrors in addition to the + richer vendor resources. They must count as covered without producing + duplicate entities or guessing unverified write contracts.""" + + def test_duplicate_capabilities_have_no_entities(self): + assert fridge.DEFROST_DELAY_NATIVE_DUPLICATE.entities == () + assert fridge.ICEMAKER_STATUS_NATIVE_DUPLICATE.entities == () + + def test_us_fixture_has_complete_coverage(self): + from custom_components.localthings.registry.adapter import flatten + from custom_components.localthings.registry.by_type import refrigerator + from custom_components.localthings.registry.discovery import discover + from tests.conftest import _load_device + + resources = _load_device('refrigerator_tp1x_ref_21k_us') + unbound = [] + bound = discover( + resources, + refrigerator.REGISTRY.capabilities, + refrigerator.REGISTRY.pattern_capabilities, + log=unbound.append, + ) + + assert unbound == [] + state = flatten(bound, resources) + assert state['defrost_delay'] is False + assert 'ice_maker_enabled' not in state + assert state['icemaker_one_enabled'] is True + assert state['icemaker_two_enabled'] is True diff --git a/tests/test_golden_regression.py b/tests/test_golden_regression.py index e9a6cfb..ee05af3 100644 --- a/tests/test_golden_regression.py +++ b/tests/test_golden_regression.py @@ -81,6 +81,20 @@ def test_registry_reproduces_golden_state_keys_for_airconditioner(): ) +def test_registry_reproduces_golden_state_keys_for_tp1x_ref_21k_us(): + from tests.conftest import _load_device + resources = _load_device('refrigerator_tp1x_ref_21k_us') + golden = json.loads( + (GOLDEN / 'refrigerator_tp1x_ref_21k_us.json').read_text() + ) + state_keys = _new_state_keys('refrigerator_tp1x_ref_21k_us', 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 = {