refactor(registry): drop oneUiVersion from device-type detection
oneUiVersion looks like the signal you'd want -- the device naming its own
type, '7.0 Dishwasher' -- and it was the first thing detection consulted. It
never earned the position:
- Only 7 of 49 fixtures report it at all.
- All 7 resolve to the same registry from their modelNum board token alone.
- No device-support issue has ever been fixed by adding a mapping for it.
Every one went through modelNum. The alias keys it needed in
_REGISTRY_BY_KEY ('airpurifier', 'air_conditioner', 'hood') were
speculative when the registries were first written and never used since.
So it bought a key-normalizing helper (_type_key), a lookup with a suffix
fallback (for_device), three alias keys, and a second config-flow step whose
only reason to exist was phrasing a sentence about oneUiVersion -- for a
signal that has never once been decisive.
Remove it from detection. It stays in diagnostics, where it's genuinely
useful: it names the firmware generation ('7.0 Air conditioner' is Tizen
Lite), which matters when triaging an issue.
Detection order was also duplicated in four places -- the coordinator, the
config flow's probe, the golden-regression harness, and the skill -- which
is how the harness and the shipped order drift apart. Collapse it into
by_type.resolve(resources), and call that everywhere.
The two "appliance type not recognized" config steps become one. They
differed only in whether they blamed a missing oneUiVersion, which is not a
distinction a user can act on, and never was.
Verified by the full suite (795 passing), including every golden regression
-- so entity output is byte-identical for all 49 device fixtures.
TestOneUiVersionIsNotConsulted locks in the premise rather than just the
outcome: for every dump that reports a oneUiVersion, the model strings alone
must still reach a registry. If a future device breaks that, the test says
so instead of the device silently losing half its entities.
Also note in requirements-dev.txt that Python 3.13 resolves the pinned
harness floor -- 3.12 and older resolve nothing and fail the whole install.
This commit is contained in:
@@ -5,8 +5,8 @@ description: >-
|
||||
/device/0 diagnostics dump. Use when a device-support issue lands, a device
|
||||
raises the "incomplete capability coverage" repair, a diagnostics JSON needs
|
||||
triaging, or you're mapping OCF resources to HA entities. Covers reading dumps,
|
||||
routing an unrecognized board family to a registry (oneUiVersion, modelNum
|
||||
board tokens, resource signatures),
|
||||
routing an unrecognized board family to a registry (modelNum board tokens,
|
||||
resource signatures),
|
||||
OCF-standard vs vendor hrefs, the diagnostic/config/normal entity taxonomy,
|
||||
preferring dynamic (device-reported) select options over hardcoded lists,
|
||||
ensuring every href is bound or ignored, and locking it in with a fixture +
|
||||
@@ -49,15 +49,7 @@ discovery = importlib.import_module('custom_components.localthings.registry.disc
|
||||
adapter = importlib.import_module('custom_components.localthings.registry.adapter')
|
||||
|
||||
resources = json.load(open('dump.json'))['data']['resources']
|
||||
info = resources.get('/information/vs/0', {})
|
||||
one_ui = resources.get('/otninformation/vs/0', {}).get('swVersionInfo', {}).get('oneUiVersion', '')
|
||||
# Same three-stage order the coordinator uses -- see §3.
|
||||
reg = (
|
||||
(by_type.for_device(one_ui) if one_ui else None)
|
||||
or by_type.for_device_by_model(info.get('x.com.samsung.da.modelNum', ''),
|
||||
info.get('x.com.samsung.da.description', ''))
|
||||
or by_type.for_device_by_resources(resources)
|
||||
)
|
||||
reg = by_type.resolve(resources) # the same entry point the coordinator uses
|
||||
unbound = []
|
||||
bound = discovery.discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
|
||||
state = adapter.flatten(bound, resources) # {entity_key: value}
|
||||
@@ -71,21 +63,30 @@ regenerate a golden.
|
||||
|
||||
## 3. Route the device to a registry — add a row, never a branch
|
||||
|
||||
If `for_device*` returns `None`, the device falls back to common capabilities
|
||||
and loses roughly **half** its entities (measured across the fixture corpus:
|
||||
843 of 1510 bound entities survive). So routing is the first thing to fix, and
|
||||
`registry/by_type/__init__.py` is deliberately kept boring:
|
||||
If detection returns `None`, the device falls back to common capabilities and
|
||||
loses roughly **half** its entities (measured across the fixture corpus: 843 of
|
||||
1510 bound entities survive). So routing is the first thing to fix, and
|
||||
`registry/by_type/__init__.py` is deliberately kept boring.
|
||||
|
||||
1. **`for_device(one_ui_version)`** — `/otninformation/vs/0`'s
|
||||
`swVersionInfo.oneUiVersion`, e.g. `'7.0 Dishwasher'`. The device naming
|
||||
its own type, so it's tried first — but only a minority of hardware
|
||||
reports it, so never assume it exists.
|
||||
2. **`for_device_by_model(model_num, description)`** — the workhorse. Both
|
||||
`resolve(resources)` is the only entry point — the coordinator, the config
|
||||
flow's probe and the golden-regression harness all call it, so the order can't
|
||||
drift between what ships and what the tests assert. Two stages:
|
||||
|
||||
1. **`for_device_by_model(model_num, description)`** — the primary path. Both
|
||||
fields come from `/information/vs/0`. Board-family tokens are matched
|
||||
against `modelNum` first, then `description`, then the fuzzy two-letter
|
||||
consumer-model prefix.
|
||||
3. **`for_device_by_resources(resources)`** — last resort for boards that
|
||||
report no `/information/vs/0` at all. Needs a *distinctive* signature.
|
||||
2. **`for_device_by_resources(resources)`** — for boards that report no
|
||||
`/information/vs/0` at all. Needs a *distinctive* signature.
|
||||
|
||||
**`oneUiVersion` is not consulted.** It looks like the obvious signal — the
|
||||
device naming its own type, `'7.0 Dishwasher'` — and it used to be stage one.
|
||||
But only a minority of hardware reports it, every device that does is already
|
||||
typed by its modelNum board token (`TestOneUiVersionIsNotConsulted` checks that
|
||||
against the whole corpus), and no device-support issue was ever fixed by adding
|
||||
a mapping for it. Don't reintroduce it as a detection stage; it stays in
|
||||
diagnostics as a firmware-generation marker (`'7.0 Air conditioner'` means
|
||||
Tizen Lite), which is useful when triaging.
|
||||
|
||||
### Adding a board family
|
||||
|
||||
|
||||
Reference in New Issue
Block a user