Makes the integration fully self-contained for HACS distribution.
Users installing via HACS get everything in one directory; no external
samsung_appliance package needed.
All component imports updated to relative (.ocf.*); all test imports
updated to custom_components.localthings.ocf.*. ocf_root_ca.pem moves
with the package and resolves correctly via Path(__file__).parent.
Rename tests/fixtures/dumps/{IP}.json to tests/fixtures/{name}_device.json.
Scrub PII: serialNum, otnDUID, modelNum, diagEndPoint replaced with
TEST-* placeholders. conftest: _load_device(name) as primary API;
_load_resources(ip) kept as legacy shim for existing test call sites.
Remove key_fn from Capability — pattern cap keys are now always derived
automatically: strip digits from href segments, join with '_', append
'_' + desc.key. /door/cooler/0 + 'open' -> door_cooler_open.
DOOR_GENERIC entity key: 'open' (not 'door_open'). Remove _door_key helper.
Fridge registry: DOOR_FRIDGE/DOOR_FREEZER replaced by DOOR_GENERIC pattern cap.
Golden baseline: door_fridge_open -> door_cooler_open.
Move device dumps to tests/fixtures/dumps/ (tracked) so tests run in CI
without local-tools/ dependencies.
- for_device(): suffix fallback for descriptive names
("french_door_refrigerator" → refrigerator registry)
- Capability.href_prefix: replaces rt_filter for pattern caps;
DOOR_GENERIC now uses href_prefix='/door/' (batch reps have no rt)
- discover(): apply href_prefix gate before rt_filter in pattern loop
- Remove dead bound_hrefs variable from discover()
- Remove vestigial default_observe_port from RuntimeDescriptor
conftest: _resources_from_dump() prefers /device/0 batch reps; falls
back to flat resources dict. Fixes _parse_device0_batch to include
stub reps (rep={href:...}) as {} so capabilities still bind on
resources with no current data (e.g. /alarms/vs/0 when no alarm active).
test_golden_regression: _new_state_and_uids now uses by_type.for_device
to select per-device-type capabilities + pattern caps.
Replace PollScheduler + OBSERVE with:
- _parse_device0_batch() extracts {href: rep} from /device/0 list response
- _discover_and_publish() uses by_type.for_device(oneUiVersion) for
device-specific capability registry; falls back to global CAPABILITIES
- _poll_loop thread: batch GET /device/0 at active_interval_s or
idle_interval_s; applies each rep to the state cache
- All OBSERVE machinery removed (subscribe, notifications, fetchback)
Remove observe_paths and poll_tiers from RuntimeDescriptor; add
active_interval_s (default 5s) and idle_interval_s (default 30s).
Delete _make_observe_paths() and _make_poll_tiers() helpers.
Bridge rewire in Task 6 will consume the new fields.
Registry changes from dict[str,Capability] to dict[str,list[Capability]].
discover() gains pattern_caps parameter (fallback for unmatched hrefs)
and gates each candidate on rt_filter and match_fn before binding.
Duplicate hrefs in registry allowed only if all caps set a discriminator.
OVEN_SETPOINT (/temperatures/vs/0) and OVEN_MODE (/mode/vs/0) collide
with fridge hrefs. Remove from ALL; only OVEN_CAVITY (/oven/vs/0) is
oven-unique. Per-device-type registries (v2 plan) will restore them in
the oven-only registry.
Also updates golden baselines to include legitimate new entities added
since original capture (cycle_active, firmware_update, power_switch for
dishwasher; cabinet_light_switch, ice1 for refrigerator).
- Make href Optional[str] = None for pattern capabilities
- Remove observe field (v2 architecture observes all hrefs)
- Add rt_filter, match_fn, key_fn fields to Capability
- Add entities default value tuple[()] to Capability
- Add key_override field to BoundEntity
- Update _key() to use key_override when present
- Update _make_observe_paths() to observe all unique hrefs (OBSERVE as accelerator)
- Guard _build() to skip pattern capabilities (href is None)
- Add test_bound_entity_key_override_takes_precedence test
- Update test_capability_defaults to remove observe assertion
All 61 tests passing (60 existing + 1 new).
C1: SwitchDesc MQTT discovery now emits payload_on/off='On'/'Off',
state_on/off='On'/'Off', and a Jinja2 boolean-to-string value_template
so HA can correctly interpret and command switch entities.
C2: POWER write_fn corrected — path is ['power','vs','0'] segment list
(not a single slash-prefixed string), and payload comparison is
p == 'On' (not truthiness) so 'Off' can actually be sent.
I2: Golden regression oracle strengthened to exact equality for both
state_keys and discovery_unique_ids; prior subset-only check allowed
over-production to go undetected. Two golden tests now fail revealing
stale fixtures (dishwasher: +3 keys; refrigerator: +9 keys) — fixtures
intentionally NOT updated, human review required.
Generates regression oracle from DISHWASHER and REFRIGERATOR descriptors:
- tests/fixtures/golden/dishwasher.json: 21 state keys, 23 discovery entities
- tests/fixtures/golden/refrigerator.json: 26 state keys, 24 discovery entities
- tests/test_golden_regression.py: parametrized test that will turn green as
Tasks 9-12 port capabilities into the new registry
OCF rt is a schema category shared by many unrelated resources.
The stable unique resource address is the href. Update Capability,
discovery, common capabilities, tests, and plan accordingly.
Implement discovery module for converting OCF resource metadata into bound
entities, with support for multi-instance device resources and logging of
unknown capabilities.