Close gas-cooktop coverage gap for TP2X_DA-KS-COOKTOP-000001 (issue #314)

/alarms/vs/0 and /kidslock/vs/0 were the dump's two unbound hrefs -- both
are the exact shapes common.UNIVERSAL already models elsewhere
(common.ALARMS, common.KIDS_LOCK_VS_FALLBACK), picked individually rather
than pulling in all of UNIVERSAL to match this registry's existing
hand-picked-common style.

The six-vs-three burner count the reporter originally asked about is
expected behavior (the board's own /mode/vs/0 options genuinely advertise
six OperationState slots on hardware with three physical burners, with no
per-device signal to tell real slots from phantom ones) -- already
explained on the issue; this commit is scoped to the coverage warning.
This commit is contained in:
Marc Billow
2026-08-07 14:18:08 +00:00
parent 26c9168fb7
commit df5b704f3e
5 changed files with 242 additions and 0 deletions
@@ -22,6 +22,12 @@ REGISTRY = DeviceRegistry(
cooktop.COOKTOP_CONNECTED, cooktop.COOKTOP_CONNECTED,
cooktop.PAIRED_HOOD_STATUS, cooktop.PAIRED_HOOD_STATUS,
common.FIRMWARE_UPDATE, common.FIRMWARE_UPDATE,
# issue #314: /alarms/vs/0 and /kidslock/vs/0 are the same
# generic shapes common.UNIVERSAL already models elsewhere --
# picked individually rather than pulling in all of UNIVERSAL,
# matching this registry's existing hand-picked-common style.
common.ALARMS,
common.KIDS_LOCK_VS_FALLBACK,
] ]
), ),
) )
+138
View File
@@ -0,0 +1,138 @@
{
"device0": [
{
"rt": [
"x.com.samsung.devcol",
"oic.wk.col"
],
"if": [
"oic.if.baseline",
"oic.if.ll",
"oic.if.b"
]
},
{
"href": "/alarms/vs/0",
"rep": {
"rt": [
"x.com.samsung.da.alarms"
],
"if": [
"oic.if.baseline",
"oic.if.a"
],
"x.com.samsung.da.items": [
{
"x.com.samsung.da.id": "0",
"x.com.samsung.da.description": "Alarm",
"x.com.samsung.da.alarmType": "Device",
"x.com.samsung.da.code": "CT_E",
"x.com.samsung.da.triggeredTime": "2026-08-06T14:24:02"
}
]
}
},
{
"href": "/bluetooth/hood/status/vs/0",
"rep": {
"connectionState": "disconnected",
"micomModelId": "",
"firmwareVersion": "",
"power": "off",
"fanSpeed": 0,
"lampState": "off",
"timer": {},
"rt": [
"bluetoothHoodStatus"
],
"if": [
"oic.if.baseline",
"oic.if.s"
]
}
},
{
"href": "/configuration/vs/0",
"rep": {}
},
{
"href": "/connected/vs/0",
"rep": {
"x.com.samsung.da.connected": "On",
"rt": [
"x.com.samsung.da.connected"
],
"if": [
"oic.if.baseline",
"oic.if.a"
]
}
},
{
"href": "/kidslock/vs/0",
"rep": {
"x.com.samsung.da.kidsLock": "Ready"
}
},
{
"href": "/mode/vs/0",
"rep": {
"x.com.samsung.da.options": [
"DeviceType_NV8000T-/KO0",
"Pause_Off",
"SyncFlex_Off",
"FlexCoil_0",
"MainTimerCurrent_0",
"MainTimerSet_0",
"MainTimerState_Ready",
"IndependentTimerCheck_Enable",
"OperationState0_Ready",
"HotSurface0_Normal",
"PowerLevel0_0",
"OperationState1_Ready",
"HotSurface1_Normal",
"PowerLevel1_0",
"OperationState2_Ready",
"HotSurface2_Normal",
"PowerLevel2_0",
"OperationState3_Ready",
"HotSurface3_Normal",
"PowerLevel3_0",
"OperationState4_Ready",
"HotSurface4_Normal",
"PowerLevel4_0",
"OperationState5_Ready",
"HotSurface5_Normal",
"PowerLevel5_0"
],
"rt": [
"x.com.samsung.da.mode"
],
"if": [
"oic.if.baseline",
"oic.if.a"
]
}
},
{
"href": "/otninformation/vs/0",
"rep": {
"x.com.samsung.da.target": "",
"x.com.samsung.da.newVersionAvailable": "false"
}
},
{
"href": "/power/vs/0",
"rep": {
"x.com.samsung.da.power": "Off",
"rt": [
"x.com.samsung.da.operation"
],
"if": [
"oic.if.baseline",
"oic.if.a"
]
}
}
]
}
+24
View File
@@ -0,0 +1,24 @@
{
"state_keys": [
"alarm_code",
"any_burner_active",
"burner_0_state",
"burner_1_state",
"burner_2_state",
"burner_3_state",
"burner_4_state",
"burner_5_state",
"child_lock",
"cloud_connected",
"firmware_update",
"main_timer_current",
"main_timer_state",
"paired_hood_connected",
"paired_hood_fan_speed",
"paired_hood_firmware",
"paired_hood_light",
"paired_hood_model",
"paired_hood_power",
"power_state"
]
}
@@ -0,0 +1,55 @@
"""Tests for the TP2X_DA-KS-COOKTOP-000001 gas cooktop (issue #314).
The reporter's actual complaint (six advertised burner slots when only
three are physically present) is expected: the registry declares a
generous static superset of slots and gates each on the live options array
per cooktop.py's own comment; there's no per-device signal that
distinguishes an advertised-but-nonexistent slot from a real one, so the
extra entities are disabled by the user, not filtered by the integration.
This file locks in the actual gap from that issue -- /alarms/vs/0 and
/kidslock/vs/0 -- now bound via common.ALARMS/KIDS_LOCK_VS_FALLBACK.
"""
from custom_components.localthings.registry.adapter import flatten
from custom_components.localthings.registry.by_type import resolve
from custom_components.localthings.registry.discovery import discover
from tests.conftest import _load_device
def _bound():
resources = _load_device("gas_cooktop_tp2x_ks")
reg = resolve(resources)
return discover(resources, reg.capabilities, reg.pattern_capabilities), resources
def test_resolves_to_gas_cooktop_registry():
resources = _load_device("gas_cooktop_tp2x_ks")
reg = resolve(resources)
assert reg is not None and reg.name == "gas_cooktop"
def test_no_unbound_hrefs():
_, resources = _bound()
reg = resolve(resources)
unbound = []
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
assert unbound == []
def test_alarm_code_and_child_lock_bound():
bound, resources = _bound()
state = flatten(bound, resources)
assert state["alarm_code"] == "CT_E"
# x.com.samsung.da.kidsLock == "Ready" reads as unlocked, same polarity
# as every other board KIDS_LOCK_VS_FALLBACK covers.
assert state["child_lock"] is True
def test_advertises_six_burner_slots_only_three_of_which_are_real():
"""Not a bug to fix -- the board's own /mode/vs/0 options genuinely
list six OperationState slots (issue #314); the reporter's physical
cooktop only has three. Locks in the current, documented behavior."""
bound, resources = _bound()
state = flatten(bound, resources)
for i in range(6):
assert f"burner_{i}_state" in state
+19
View File
@@ -231,6 +231,25 @@ def test_registry_reproduces_golden_state_keys_for_water_purifier_coffee():
) )
def test_registry_reproduces_golden_state_keys_for_gas_cooktop_tp2x_ks():
"""TP2X_DA-KS-COOKTOP-000001 (issue #314) -- routes via
for_device_by_resources' DeviceType_/OperationState signature, same as
the original cooktop fixture. /alarms/vs/0 and /kidslock/vs/0 were the
dump's only unbound hrefs; both are common.UNIVERSAL shapes this
registry now picks up individually (common.ALARMS,
common.KIDS_LOCK_VS_FALLBACK)."""
from tests.conftest import _load_device
resources = _load_device("gas_cooktop_tp2x_ks")
golden = json.loads((GOLDEN / "gas_cooktop_tp2x_ks.json").read_text())
state_keys = _new_state_keys("gas_cooktop_tp2x_ks", 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_registry_reproduces_golden_state_keys_for_cooktop(): def test_registry_reproduces_golden_state_keys_for_cooktop():
from tests.conftest import _load_device from tests.conftest import _load_device