Add x.com.st.d.hood to _OIC_TYPE_TO_KEY, and document why oic.d.cooktop is not

Measured on nine Samsung appliances on Korean-market firmware, every one of
which populates /oic/d with a concrete type:

    4x  oic.d.airconditioner   AJ023CN1UBC1 system A/C, "Samsung System A/C"
    3x  oic.d.refrigerator     "[refrigerator] Samsung"
    1x  oic.d.cooktop          TP1X_DA-KS-COOKTOP, "Samsung Cooktop"
    1x  x.com.st.d.hood        AHD-WW-TP1-22-COMMON, "Samsung Hood"

Every one agreed with what for_device_by_model already concluded from the board
token, so this is corroboration rather than a correction.

`x.com.st.d.hood` was the one type with a registry to point at and no row, so
this adds it.

`oic.d.cooktop` is left out on purpose, with a comment saying why: the induction
above reports it, but `cooktop` and `induction_cooktop` are unrelated registries
that happen to share the English word, and the OCF type cannot tell them apart.
Mapping it to either key would misroute the other, and since resolve() consults
this table first it would override a COOKTOP/CT board token that had it right.
Same shape of argument as the existing oic.d.robotcleaner note.

One incidental data point on the docstrings' "only ever helps a minority of
dumps": that may understate it. Nine out of nine here answer /oic/d with a usable
type, across four families. Not enough hardware to generalise from, but enough
that it looks less like a rare bonus than the comments assume.

Verified: the full suite passes (1024 tests, Python 3.13 via
requirements-dev.txt).
This commit is contained in:
Geunwon Mo
2026-08-01 09:02:49 +09:00
parent f36b3016cc
commit 618fc5fa51
2 changed files with 24 additions and 0 deletions
@@ -214,6 +214,16 @@ def _consumer_model_key(description: str) -> Optional[str]:
# device-type vocabulary (used for categories with no `oic.d.*` equivalent),
# same prefix convention as the `x.com.samsung.da.*` resource fields
# elsewhere in this codebase.
#
# `oic.d.cooktop` is deliberately absent, and is the one measured type left out.
# A TP1X_DA-KS-COOKTOP induction reports it, but `cooktop` and
# `induction_cooktop` are two unrelated registries that happen to share the
# English word (see by_type/cooktop.py's docstring: the NA9300K gas family keeps
# burner state in /mode/vs/0's options array, a completely different OCF
# surface). The OCF type does not distinguish them, so mapping it to either key
# would silently misroute the other -- and as the *primary* signal it would
# override a `COOKTOP`/`CT` board token that had it right. Same reasoning as
# `oic.d.robotcleaner` above: no unambiguous key to point at, so no row.
_OIC_TYPE_TO_KEY: dict[str, str] = {
'oic.d.airconditioner': 'airconditioner',
'oic.d.airpurifier': 'air_purifier',
@@ -223,6 +233,7 @@ _OIC_TYPE_TO_KEY: dict[str, str] = {
'oic.d.refrigerator': 'refrigerator',
'oic.d.washer': 'washer',
'x.com.st.d.airqualitysensor': 'air_monitor',
'x.com.st.d.hood': 'range_hood', # AHD-WW-TP1-22-COMMON
'x.com.st.d.stickcleaner': 'vacuum_station',
'x.com.st.d.steamcloset': 'air_dresser',
}
+13
View File
@@ -149,6 +149,7 @@ class TestForDeviceByOicType:
('oic.d.oven', 'oven'),
('oic.d.refrigerator', 'refrigerator'),
('oic.d.washer', 'washer'),
('x.com.st.d.hood', 'range_hood'),
('x.com.st.d.stickcleaner', 'vacuum_station'),
('x.com.st.d.steamcloset', 'air_dresser'),
('x.com.st.d.airqualitysensor', 'air_monitor'),
@@ -177,6 +178,18 @@ class TestForDeviceByOicType:
from custom_components.localthings.registry.by_type import for_device_by_oic_type
assert for_device_by_oic_type(('oic.d.robotcleaner',)) is None
def test_cooktop_is_not_mapped_to_either_cooktop_registry(self):
"""'oic.d.cooktop' cannot tell the two cooktop families apart.
A TP1X_DA-KS-COOKTOP induction reports it, but `cooktop` is the
unrelated NA9300K gas family (burner state in /mode/vs/0's options
array, a different OCF surface -- see by_type/cooktop.py). Mapping the
type to either key would misroute the other, and as the primary signal
it would override a board token that had it right.
"""
from custom_components.localthings.registry.by_type import for_device_by_oic_type
assert for_device_by_oic_type(('oic.d.cooktop',)) is None
def test_empty_returns_none(self):
from custom_components.localthings.registry.by_type import for_device_by_oic_type
assert for_device_by_oic_type(()) is None