TP1X_FAC_TIME_23K reports three previously unbound hrefs: a UV-C sterilization LED and a ventilation-reminder alarm (both plain On/Off toggles), and a second PM1-rated dust filter with no live usage/status fields on this particular dump. The PM1 filter capability gates each entity on its own field's presence rather than a blanket ignore, since the TP1X_DA-AC-CAC-01001_0000 cassette AC (issue #191) reports the same href with full live data -- this also closes two of that device's ten documented coverage gaps (UV LED and the PM1 filter) as a side effect.
84 lines
2.9 KiB
Python
84 lines
2.9 KiB
Python
"""Tests for the TP1X_FAC_TIME_23K air conditioner (issue #270).
|
|
|
|
A 2-in-1 system (x.com.samsung.da.numofsubdevice='2') whose UUID-prefixed
|
|
sibling materializes via the flat fallback (issue #205) -- unrelated to the
|
|
three capability gaps this fixture actually locks in (UV LED, ventilation
|
|
alarm, and the unmodeled PM1 filter), which live entirely on canonical
|
|
hrefs already exercised by the master's own resources.
|
|
"""
|
|
|
|
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.discovery import discover
|
|
from custom_components.localthings.registry.entities import SwitchDesc
|
|
from tests.conftest import _load_device
|
|
|
|
|
|
def _airconditioner():
|
|
resources = _load_device("airconditioner_tp1x_fac_time_23k")
|
|
info = resources["/information/vs/0"]
|
|
reg = for_device_by_model(
|
|
info["x.com.samsung.da.modelNum"],
|
|
info["x.com.samsung.da.description"],
|
|
)
|
|
return reg, resources
|
|
|
|
|
|
def _bound():
|
|
reg, resources = _airconditioner()
|
|
return discover(resources, reg.capabilities, reg.pattern_capabilities), resources
|
|
|
|
|
|
def _state():
|
|
bound, resources = _bound()
|
|
return flatten(bound, resources)
|
|
|
|
|
|
def _desc(key):
|
|
bound, _ = _bound()
|
|
return next(b.desc for b in bound if b.desc.key == key)
|
|
|
|
|
|
def test_model_resolves_to_airconditioner_registry():
|
|
reg, _ = _airconditioner()
|
|
assert reg is not None and reg.name == "airconditioner"
|
|
|
|
|
|
def test_no_unbound_hrefs():
|
|
reg, resources = _airconditioner()
|
|
unbound = []
|
|
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
|
|
assert unbound == []
|
|
|
|
|
|
def test_uv_led_switch_reads_on_and_has_write_contract():
|
|
state = _state()
|
|
assert state["uv_led"] is True
|
|
desc = _desc("uv_led")
|
|
assert isinstance(desc, SwitchDesc)
|
|
path, body = desc.write_fn("Off", {})
|
|
assert path == ["uvled", "vs", "0"]
|
|
assert body == {"x.com.samsung.da.modes": "Off"}
|
|
|
|
|
|
def test_ventilation_alarm_switch_reads_off_and_has_write_contract():
|
|
state = _state()
|
|
assert state["ventilation_alarm"] is False
|
|
desc = _desc("ventilation_alarm")
|
|
assert isinstance(desc, SwitchDesc)
|
|
path, body = desc.write_fn("On", {})
|
|
assert path == ["ventilation", "setting", "vs", "0"]
|
|
assert body == {"alarm": "On"}
|
|
|
|
|
|
def test_pm1_filter_has_no_live_fields_so_no_state_is_reported():
|
|
"""This dump's /filter/airdustPM1filter/vs/0 has only
|
|
filterCapacity/filterCapacityUnit/filterResetType -- no filterUsage or
|
|
filterStatus -- so every AIR_FILTER_PM1 entity's exists_fn gates it out.
|
|
The href still covers (see test_no_unbound_hrefs above) without
|
|
fabricating a reading from nothing (the 'don't guess' rule); contrast
|
|
with test_airconditioner_cac.py, whose dump has live data on the same
|
|
href and does get real air_filter_pm1_* state."""
|
|
state = _state()
|
|
assert not any(k.startswith("air_filter_pm1_") for k in state)
|