Skip to content

fix(config_flow,legacy_yaml): drop an inert profile argument, say what moved, no unreachable MAC fallback (#804 / #798 / #801 / #808 follow-up) - #821

Merged
GreenGrassBlueOcean merged 4 commits into
OpenWebNet-HA:v2-phase1-architecturefrom
gdluck:fix/zone-where-bound-inert-profile-arg-and-log
Oct 26, 2026
Merged

GreenGrassBlueOcean merged 4 commits into
OpenWebNet-HA:v2-phase1-architecturefrom
gdluck:fix/zone-where-bound-inert-profile-arg-and-log

Conversation

@gdluck

@gdluck gdluck commented Oct 26, 2026 •

Copy link
Copy Markdown

Requested by A

Before: the manual config flow passed gateway.model_number (always None there) as the firmware argument of get_gateway_profile; legacy_yaml logged cover: 2-1 -> 2-1 when only a scope cover's members were canonicalised; _same_mac carried an except Exception for a dr.format_mac that never raises, covered only by monkeypatching it; discovery_whos_for was documented as a mirror of the runtime gate although the two fail in opposite directions at the edges.

After: the flow resolves the corrected model through _profile_for_model(model) (the helper #818 added); the log names the members that moved; the MAC helper compares the two format_mac results and its docstring says why no fallback is needed; the "Mirrors" docstring states the one edge where the script and the gate differ.

What is the problem

Five small follow-ups on merged PRs of mine (#792, #804, #798, #801, #808), found by an adversarial re-read of our PRs after @GreenGrassBlueOcean's #140 review on OWNd. None changes behaviour on a real plant; each is a validator, an inert argument, a log line or a sentence that did not match the code. @GreenGrassBlueOcean a confirmation (or a "drop this one") per item would be welcome.

  1. feat(climate): request actuator status (dimension 20) on add and after reconnect, expose actuators, derive hvac_action #792 _ZONE_WHERE (gateway.py). How it was found: the docstring said OWNd Needs to add ClimateEntityFeature to custom_components.myhome.climate.MyHOMEClimate #140's builder "accepts and rejects the same addresses"; diffing the two regexes instead of trusting the sentence showed OWNd (0099a228) fullmatches #?[0-9]{1,2}(?:#[0-9])?(?:#4#[0-9]{2})? and documents 105, 2#10, 3#4#1 as ValueError, while MyHOME had #?[0-9]+(?:#[0-9]+)? with match + $: 999, 100, 105, 2#10 and "2\n" passed (probe of 23 forms, 5 disagree), so _zone_request_frame("105", 20) built a request for a push-only secondary probe the builder refuses.
  2. feat(config_flow): identify a manually added gateway after authentication #804 inert argument (config_flow_discovery.py). How it was found: checking the call against OWNd's signature on both installed versions, get_gateway_profile(model_name, firmware_version=None); the flow passed gateway.model_number, which async_step_custom_manual sets to None, so the argument did nothing and read as if a model number were a firmware, and the path bypassed _profile_for_model that fix(gateway,climate): ingest H/L4684 FW 6.0.11 trace and fix H/L4684 & WHO 4 bugs (#466) #818 made the runtime use for the same correction.
  3. fix(validate): canonicalise 4-digit area-0 point addresses (0004 → 04) (#147) #798 log line (legacy_yaml.py). How it was found: the rewritten.append(f"{plat}: {d_id} -> {new_id}") was unconditional; a call with a scope cover whose key was already canonical and whose members held 0004 returned ['cover: 2-1 -> 2-1'], a move that moved nothing, and the members change was not reported.
  4. fix(legacy_yaml): warn when myhome.yaml has no section under this gateway's MAC (#106) #801 unreachable fallback (legacy_yaml.py). How it was found: _same_mac had an except Exception whose docstring said it keeps a malformed key at WARNING "instead of raising into the generic Failed to parse error"; HA's dr.format_mac never raises (seven malformed inputs come back unchanged), and a malformed section key never reaches _log_mac_mismatch because validate.config_schema (MacAddress()) rejects it first with exactly that ERROR; the two tests passed only by monkeypatching format_mac to raise.
  5. chore(profiles): derive the discovery rows from the OWNd profile (prep for OWNd #142) #808 "Mirrors" (scripts/update_gateway_profiles.py). How it was found: comparing discovery_whos_for with _profile_supports_who at the edges: the runtime gate fails open (True when supports_who is missing or raises), the script fails closed ([]) and propagates a raise, and the PR's own test pins discovery_whos_for(object()) == [].

What is the solution

  1. Zone validator (dropped from this branch at the maintainer's request: it had a .partition bug on 2#4#4#1 and lags OWNd#148; the .fullmatch catch for "2\n" and the OWNd#148 bounds go into fix(climate): gate unrouted WHO 4 #4#II and actuator status polls #816 instead; the branch is the four commits of items 2-5 rebased on f4a5a90): one or two zone digits, one actuator digit, the two-digit #4#NN suffix, fullmatch; the docstring states the one remaining difference (a one-digit interface 3#4#1 is rewritten to 3#4#01 here, refused there). Invalid list gains 100, 105, 999, 2#10, #100, "2\n"; valid list gains 99, #99, 99#9#4#99.
  2. Config flow (e8003c0): gateway.profile = _profile_for_model(model) with a comment that the firmware is unknown before the first connection; the get_gateway_profile import goes.
  3. legacy_yaml log (a110d8c): a moved key is reported as before; a members-only change reads cover: 2-1 members ['0004', '12'] -> ['04', '12'].
  4. _same_mac (d5736af): compares dr.format_mac(a) == dr.format_mac(b) directly; the two monkeypatch tests are replaced by one on real spellings (hyphens, dots, junk against itself, None) and one showing a not-a-mac section key is one schema ERROR with no WARNING and no mismatch check (dropped rather than reworded because the branch could not be covered without the monkeypatch).
  5. Docstrings (01fc7ce): "the same rule for a real OWNd profile; an object without supports_who yields no rows here, the runtime assumes everything".

State Diagram

flowchart LR
    W["zone address"] --> V{"fullmatch\n#?[0-9]{1,2}(?:#[0-9])?(?:#4#[0-9]{2})?"}
    V -- "2, #0, 2#1, 3#4#01, 99#9#4#99" --> F["*#4*WHERE*20##"]
    V -- "105, 999, 2#10, 2\\n" --> R["ValueError (as OWNd#140)"]
Loading

Hardware Verification

  • N/A: no frame a real plant sends changes. The 2026-10-26 live reads on the F461 (zone 2) and the MyHomeServer1 (zones 35, 43, 47, 60) all use one- or two-digit zones, which the bounded validator keeps.

How is it tested

  • tests/test_climate_actuator_status.py (invalid: 100, 105, 999, 2#10, #100, "2\n"; valid: 99, #99, 99#9#4#99), tests/test_canonical_point_where.py::test_members_only_rewrite_is_logged_as_such, tests/test_legacy_yaml.py (real MAC spellings; not-a-mac key is one schema ERROR, no WARNING), tests/test_gateway_profiles.py (docstring).
  • pytest tests -n 4 --cov=custom_components/myhome: 5000 passed, 1 skipped, TOTAL 16807 0 100.0%; ruff check . clean; scripts/typing_ratchet.py mypy --strict: 0 error(s); scripts/verify_ha_standards.py SUCCESS; scripts/sync_documentation.py --check SUCCESS; scripts/update_gateway_profiles.py --check OK: Table is in sync; scripts/manage_translations.py check no drift.
  • Kimi (independent diff review): no legitimate WHO 4 form newly refused; it caught that match + $ admits "2\n" (taken: fullmatch); _profile_for_model(model) equals get_gateway_profile(model, None) for a resolving canonical name; dropping the try/except changes no result.

Where are the traces

  • N/A (validation, config flow and logging; no protocol change).

What does the Firmware Oracle / Encyclopedia say

  1. OpenWebNet Encyclopedia (WHO = 4): zones are 1..99 (#1..#99 via the central unit), actuators Z#1..Z#9 (Z#0 all), probes 101..899 have no actuators.
  2. Firmware Oracle: thermo-zone-requests (own-firmware-oracle unable to see Automation (cover) events #60 / Failed to load configuration flow: 500 Internal Server Error Server got itself in trouble #63 / Fixed HVACAction modes to correct HA core naming #65): a three-digit probe address answers no dimension-20 request on any gateway, which is why the OWNd builder refuses it and MyHOME now agrees.

🤖 Generated with Claude Code

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gdluck Thanks for splitting these out cleanly — here is the per-item confirmation:

  • Item 2 (config_flow_discovery.py — _profile_for_model(model), #804 follow-up): Confirmed. gateway.model_number was always None on the manual flow and holds model number rather than firmware; _profile_for_model(model) also applies _MODEL_SPELLINGS (HL4684 → H4684).
  • Item 3 (legacy_yaml.py — canonicalise_imported_platforms, #798 follow-up): Confirmed. Properly reports cover: 2-1 members ['0004', '12'] -> ['04', '12'] instead of a no-op cover: 2-1 -> 2-1 move.
  • Item 4 (legacy_yaml.py — _same_mac, #801 follow-up): Confirmed. dr.format_mac never raises in HA Core, and _load_live_yaml injects gw_val[CONF_MAC] = str(gw_key) before validate.config_schema (MacAddress()), so non-MAC section keys are rejected at schema validation before _log_mac_mismatch can run.
  • Item 5 (scripts/update_gateway_profiles.py — discovery_whos_for docstring, #808 follow-up): Confirmed. Accurately documents the script's fail-closed ([]) edge on objects lacking supports_who vs the runtime gate's fail-open (True).

  • Item 1 (gateway.py — _ZONE_WHERE & _validate_zone_where, #792 follow-up): Suggest dropping this commit from #821 (or fixing the two points below):
    1. .partition(BUS_ROUTING) bug on actuator #4 behind a 1-digit interface: _validate_zone_where (custom_components/myhome/gateway.py:314) uses (where or "").partition(BUS_ROUTING) ("#4#"). For "2#4#4#1" (zone 2, actuator 4, interface 1), .partition("#4#") splits on the first "#4#" (zone="2", interface="4#1"), fails len(interface) == 1, and raises ValueError: Invalid zone address: '2#4#4#1' — whereas "2#3#4#1" rewrites to "2#3#4#01" and "2#4#4#01" passes. It needs .rpartition(BUS_ROUTING).
    2. Parity with OpenWebNet-HA/OWNd#148 & direct conflict with #816: OWNd#148 tightens OWNd's _WHO4_WHERE_REGEX to reject #0#0, #0#5..#0#9, #01, and #00, whereas _ZONE_WHERE here still admits them while claiming exact parity with OWNd. Meanwhile, #816 (fix/who4-interface-routing-and-actuator-guards) already modifies these exact lines in gateway.py and tests/test_climate_actuator_status.py (including .rpartition(BUS_ROUTING) and bounded digits), and own-firmware-oracle#65 is now merged so #816 is only waiting on OpenWebNet-Encyclopedia#52.

Recommendation: If you drop commit 73255aa (Item 1) from #821, we can merge Items 2–5 immediately without conflict, and I will fold your .fullmatch() catch ("2\n") + OWNd#148 bounds directly into #816 when rebasing it. (Alternatively, if you prefer keeping Item 1 here, switch .partition → .rpartition, add "2#4#4#1" → "2#4#4#01" to the test, and align _ZONE_WHERE with OWNd#148, and I'll rebase #816 onto it.)

gdluck and others added 4 commits October 26, 2026 20:38
…ile_for_model (OpenWebNet-HA#804 follow-up)

_async_apply_gateway_identity called get_gateway_profile(model,
gateway.model_number): OWNd's signature is (model_name, firmware_version),
and model_number is always None on the manual path, so the second argument
was inert and read as if a model number were a firmware. The runtime resolves
the same correction through _profile_for_model (OpenWebNet-HA#818), which also maps the
SSDP spellings; the flow now uses it, without a firmware, which is not known
before the first connection.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…re canonicalised (OpenWebNet-HA#798 follow-up)

canonicalise_imported_platforms appended "plat: old -> new" for every
rewritten device, also when the key was unchanged and only the `members`
list of a scope cover held a 4-digit area-0 address: the INFO line then read
"1 point address(es) moved to the canonical spelling (cover: 2-1 -> 2-1)",
a move that moved nothing, and dropped what did change. The line now reads
"cover: 2-1 members ['0004', '12'] -> ['04', '12']" for that case; a moved
key is reported as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…penWebNet-HA#801 follow-up)

The `except Exception` fallback of _same_mac was unreachable and its
docstring described a path that does not exist: HA's dr.format_mac never
raises (a value that is no MAC comes back unchanged), and a malformed
section key never reaches _log_mac_mismatch because validate.config_schema
(`Optional(CONF_MAC): MacAddress()`) rejects it first with the generic
"Failed to parse myhome.yaml ...: Invalid MAC address" ERROR, the very outcome
the docstring claimed the fallback avoided. The two tests passed only by
monkeypatching format_mac to raise.

The helper now compares the two format_mac results, the docstring says why
no fallback is needed, and the two monkeypatch tests are replaced by one on
real spellings (hyphens, dots, junk against itself) and one showing that a
'not-a-mac' section key is one schema ERROR with no WARNING and no mismatch
check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s mirror at the edges (OpenWebNet-HA#808 follow-up)

The docstring said the script "mirrors MyHOMEGatewayHandler._profile_supports_who";
the runtime gate fails open (every WHO when supports_who is missing or raises),
the script fails closed ([] when missing) and propagates when supports_who
raises, and test_discovery_whos_follow_the_runtime_gate pins the divergent
discovery_whos_for(object()) == []. Real OWNd profiles agree. The docstring
and the test's say so.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gdluck pushed a commit to gdluck/HomeAssistant-MyHOME that referenced this pull request Oct 26, 2026
…grammar (OpenWebNet-HA#792 follow-up)"

This reverts commit 26c4e63f3b0c3c2bd1dc26dd1b0f1b0b2c7e0ba1 at the
maintainer's request on OpenWebNet-HA#821: the validator change had a `.partition`
bug on an actuator `michnovka#4` behind a one-digit interface (`2#4#4#1`), it
lags OWNd#148's bounds, and OpenWebNet-HA#816 already rewrites the same lines (with
`.rpartition` and bounded digits); the `.fullmatch` catch for "2\n" and
the OWNd#148 bounds will be folded into OpenWebNet-HA#816 instead. A revert rather
than a history rewrite so the branch keeps its pushed commits; the tree
equals the branch without that commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gdluck

gdluck commented Oct 26, 2026

Copy link
Copy Markdown
Author

Thanks for the per-item confirmation. Item 1 is out: 1cbfa26 reverts 26c4e63 (full sha 26c4e633b66fbf06a85d8a8d08e2b9fd17e8a7e7), so the branch tree is exactly items 2-5 (verified equal to a rebase without that commit; 4992 passed, 100 % coverage, ruff clean on that tree). I used a revert instead of rewriting the pushed history; if you prefer a clean four-commit branch for the merge, say so and I will force-push the rebased version.

Agreed on both points for the validator: .partition(BUS_ROUTING) splits 2#4#4#1 at the wrong #4# (.rpartition is right), and the bounds should follow OWNd#148. Folding the .fullmatch catch for "2\n" and the OWNd#148 bounds into #816 as you propose is the better place; I will not touch those lines elsewhere.

@gdluck
gdluck force-pushed the fix/zone-where-bound-inert-profile-arg-and-log branch from 1cbfa26 to ccda545 Compare October 26, 2026 18:45
@gdluck

gdluck commented Oct 26, 2026

Copy link
Copy Markdown
Author

Force-pushed the clean version: the branch is now the four commits of items 2-5 (c38de00, c34e44f, 4f011ef, ccda545) on f4a5a90, no revert pair; same tree as before (4992 passed, 100 % coverage, ruff clean).

@GreenGrassBlueOcean GreenGrassBlueOcean changed the title fix(climate,config_flow,legacy_yaml): bound the zone validator, drop an inert profile argument, say what moved, no unreachable MAC fallback (#792 / #804 / #798 / #801 / #808 follow-up) fix(config_flow,legacy_yaml): drop an inert profile argument, say what moved, no unreachable MAC fallback (#804 / #798 / #801 / #808 follow-up) Oct 26, 2026

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Clean 4-commit branch (c38de00, c34e44f, 4f011ef, ccda545) with Item 1 dropped and gateway.py untouched; items 2–5 (_profile_for_model in config_flow_discovery.py, members-only canonicalisation log and direct dr.format_mac comparison in legacy_yaml.py, and the discovery_whos_for edge docstring) all check out. Note for #816: per OWNd#148 (4f82518), #140's _WHO4_WHERE_REGEX stays as-is (#0#C is 1..99, not 1..4), so #816 will fold in .fullmatch and .rpartition(BUS_ROUTING) against #140's grammar. CI 16/16 green.

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit 7c11a8e into OpenWebNet-HA:v2-phase1-architecture Oct 26, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants