Skip to content

chore(release): bump version to 2.0.0b12 - #152

Merged
GreenGrassBlueOcean merged 1 commit into
OpenWebNet-HA:masterfrom
gdluck:chore/release-2.0.0b12
Nov 2, 2026
Merged

GreenGrassBlueOcean merged 1 commit into
OpenWebNet-HA:masterfrom
gdluck:chore/release-2.0.0b12

Conversation

@gdluck

@gdluck gdluck commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Requested by A

What is the problem

OpenWebNet-HA/MyHOME pins OWNd==2.0.0b11 and waits for a release that carries the thirteen PRs merged since v2.0.0-b11 (2026-10-20), so that its pin bump can drop the two pending_decoder_rx dimension 27 frames and regenerate the discovery table for the F461 / MyHomeServer1 profiles without WHO 16. Requested by @GreenGrassBlueOcean on #139.


How is it solved

  1. OWNd/__init__.py: __version__ = "2.0.0b12" (pyproject.toml reads it; tests/test_metadata.py checks PEP 440 and the installed distribution).
  2. CHANGELOG.md: ## [Unreleased] promoted to ## [2.0.0b12] - 2026-11-01, with a fresh empty ## [Unreleased] header above it. The section covers feat(lighting): auto-detect pre-09W29 F414 coarse-only dimmers via speed 240 (0xF0) and emit WHAT 2..10 presets #134, fix(lighting): WHAT 39 is the end of movement of a WHO 1 motion detector #135, fix(connection): refuse WHO 4 dimension 22 in every send path #136, fix(heating): set_central_mode requires a central-unit address #137, fix(message): parse '#'-prefixed dimension 14 setpoint values #138, feat(gateway): WHO 13 dimension 27 model-name decoder #139, feat(heating): OWNHeatingCommand.actuators_status(where) builder for *#4*WHERE*20## #140, fix(profiles): drop WHO 16 from the F461 and MyHomeServer1 profiles #142, feat(lighting): WHO 1001 dimension 11 diagnostic event #145, fix(profiles): alias H/L4684 & HC4684 to H4684Profile and enable WHO 22 on FW >= 6.0.0 #147, docs(changelog): describe send_to_gateway's dimension 22 refusal as it is #148, ci(pytest): set asyncio_default_fixture_loop_scope so pytest-asyncio 1.4.1 does not abort the suite #150 and fix(heating): refuse WHO 4 DIMENSION 22 only without an actuator token in WHERE (oracle thermo-dim22) #151. (The MyHOME#839 companion was folded in on 2026-11-01 and taken out again on the maintainer's review 5738875005: it ships reworked in 2.0.0b13 through feat(message): WHO 1004 thermoregulation diagnostics, WHO 16 tuner steps and station store, WHO 22 seek and step builders (companion of MyHOME#839) #153.)

No other file carries the version string; the only other 2.0.0b11 mention is a historical note in a heating.py comment ("the detector of OWNd 2.0.0b11"), left as is.

Changes callers can notice (already described in the section)

Testing

  • pytest tests on this head with the package reinstalled: 2516 passed (tests/test_metadata.py included); ruff check clean.

🤖 Generated with Claude Code

OWNd/__init__.py: __version__ = "2.0.0b12". CHANGELOG.md: [Unreleased]
promoted to [2.0.0b12] - 2026-11-01 (every PR merged since v2.0.0-b11:
OpenWebNet-HA#134, OpenWebNet-HA#135, OpenWebNet-HA#136, OpenWebNet-HA#137, OpenWebNet-HA#138, OpenWebNet-HA#139, OpenWebNet-HA#140, OpenWebNet-HA#142, OpenWebNet-HA#145, OpenWebNet-HA#147, OpenWebNet-HA#148, OpenWebNet-HA#150,
OpenWebNet-HA#151) with a fresh empty [Unreleased] header.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@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

Copy link
Copy Markdown
Contributor

Hi @gdluck — quick request alongside this release bump!

We just opened OpenWebNet-HA/MyHOME#839 following @andrea-mag's two new H/L4684 (FW 6.0.11) bus captures on OpenWebNet-HA/MyHOME#466 (comment), which gave us:

  1. Our first physical WHO 1004 thermoregulation diagnostic frames (*#1004*66*11*...##, *#1004*#66*22*...##, *#1004*#0*11*...##) alongside a physical H/L4684 WHO 1001 lighting diagnostic frame (*#1001*91*11*111111111111111111111111##); and
  2. Our first physical dual-stack WHO 16 + WHO 22 F500 FM radio tuner session (105.30 MHz, Station 5, RDS "MY HOME " -> "DIM*SOFT").

Since @fedem95 is currently unavailable, I cannot approve a PR that I open myself on OWNd — whereas I can review and approve a companion PR if you author it. Would you be willing to write this companion OWNd PR?

(Note: MyHOME#839 already works cleanly on OWNd==2.0.0b11 / 2.0.0b12 using raw frame fallbacks where builders don't exist yet, so this does not need to block merging #152 — whether you prefer to ship 2.0.0b12 as-is and open a follow-up PR for 2.0.0b13, or fold it in first, is completely up to you. I'm ready to review and approve #152 and the companion PR right away.)

Below is the exact, self-contained specification mapped directly to OWNd/message/ so nothing is left ambiguous:


1. WHO 1004 Thermoregulation Autodiagnostics (OWNd/message/heating.py + OWNd/message/__init__.py)

Mirroring your OWNLightingDiagnosticEvent (OWNd/message/lighting.py:967-1042) and OWNGatewayDiagnosticEvent / OWNGatewayDiagnosticCommand (OWNd/message/gateway.py:643-709), add OWNHeatingDiagnosticEvent(OWNEvent) and OWNHeatingDiagnosticCommand(OWNCommand) in OWNd/message/heating.py, register both via:

register_event_parser(1004, OWNHeatingDiagnosticEvent)
register_command_parser(1004, OWNHeatingDiagnosticCommand)

and re-export both in OWNd/message/__init__.py.

Evidence & Wire Shapes

  • Encyclopedia KB: kb_who_04_thermoregulation_diagnostics (WHO 1004, OPEN.db / libdiag.so s2o_selfDiagnosticsA).
  • Physical H/L4684 (FW 6.0.11) + 3550 central unit + F430/4 zone 66 trace (tests/fixtures/traces/issue_466/myhome_trace_HL4684_all_2026-11-01T11-30-19.json in MyHOME#839):
    • *#1004*66*11*111111111111111111111111## — Probe / zone 66, DIMENSION 11, 24-bit mask ('1' = healthy, '0' = active fault).
    • *#1004*#66*22*1111111111111111## — Central-unit zone 66 actuators (WHERE = "#66"), DIMENSION 22, 16-bit mask.
    • *#1004*#0*11*111111111111111111111111## — 3550 central unit (WHERE = "#0"), DIMENSION 11, 24-bit mask.
  • Safety / Parser Notes:
    • Do not add 1004 to OWNMessage.interface in OWNd/message/base.py:333 ([0, 1, 2, 14, 15, 1001]) unless guarded against #0 / #Z, because WHO 1004 uses #0 (#0#C) for central units and #1..#99 for zone actuators. (base.py:105-110 already parses self._where as "#0", "#66", "66" cleanly.)
    • is_who4_dimension_22 (OWNd/message/heating.py:93-110) anchors on \*#\s*\+?0*4\s*\*, so *#1004*#66*22## is already not blocked by the WHO 4 Dimension 22 crash guard — worth adding an explicit regression assertion assert not is_who4_dimension_22("*#1004*#66*22##") and assert not is_who4_dimension_22("*#1004*#66*22*1111111111111111##").

OWNHeatingDiagnosticEvent(OWNEvent) Scope & Properties

Three WHERE addressing scopes per kb_who_04_thermoregulation_diagnostics:

  1. Probe / standalone zone (WHERE = "1".."99") -> scope == "probe", zone_number == int(where):
    • DIMENSION 7: slave_count: int | None (0..8 from single integer dimension_value[0], else None).
    • DIMENSION 8: 24-bit '0'/'1' slave-probe diagnostic mask.
    • DIMENSION 11: 24-bit '0'/'1' master-probe diagnostic mask (*#1004*66*11*111111111111111111111111##).
    • DIMENSION 13: local offset failure (raw preserved in dimension_value).
  2. Central unit (WHERE = "#0" or #0#C matching _WHO4_CENTRAL_WHERE_REGEX) -> scope == "central", zone_number is None:
    • DIMENSION 11: 24-bit '0'/'1' central unit diagnostic mask (*#1004*#0*11*111111111111111111111111##).
    • DIMENSION 20: zone failure summary (raw in dimension_value).
    • DIMENSION 21: active fault-zone discovery (faulty_zone: int | None — 0 when no faulty zones, 1..99 when reporting a faulty zone).
  3. Zone actuators (WHERE = "#1".."#99", optionally #Z#C) -> scope == "zone_actuators" (or "zone"), zone_number == int(where[1:]):
    • DIMENSION 22: 16-bit '0'/'1' actuator diagnostic mask (*#1004*#66*22*1111111111111111##).
    • DIMENSION 23: per-actuator 1..9 diagnostic status (raw in dimension_value).

Properties (matching OWNLightingDiagnosticEvent fail-closed semantics):

  • scope: str | None ("probe" | "central" | "zone_actuators" | None)
  • zone_number: int | None (1..99 for probe / zone actuators, None for #0 or unrecognized WHERE)
  • diagnostic_mask: str | None — exact '0'/'1' string only when len(dimension_value) == 1, set(value) <= {"0", "1"}, and len(value) == 24 for dimension in (8, 11) or len(value) == 16 for dimension == 22; otherwise None (fail closed on wrong length, trailing *val, or non-binary chars).
  • cleared_bits: tuple[int, ...] | None — 1-based positions from the left where bit == "0" (() when all '1', None when diagnostic_mask is None).
  • has_fault: bool | None — bool(self._cleared_bits) when self._cleared_bits is not None, else None.
  • slave_count: int | None — int 0..8 on scope == "probe" and dimension == 7, else None.
  • faulty_zone: int | None — int 0..99 on scope == "central" and dimension == 21, else None.

OWNHeatingDiagnosticCommand(OWNCommand) Builders

  • request_probe_diagnostic(cls, zone: int | str) -> OWNHeatingDiagnosticCommand -> *#1004*{z}*11## (validate 1 <= z <= 99 integer, rejecting #0/#Z).
  • request_probe_slave_count(cls, zone: int | str) -> OWNHeatingDiagnosticCommand -> *#1004*{z}*7## (1 <= z <= 99).
  • request_probe_slave_diagnostic(cls, zone: int | str) -> OWNHeatingDiagnosticCommand -> *#1004*{z}*8## (1 <= z <= 99).
  • request_central_diagnostic(cls, where: str = "#0") -> OWNHeatingDiagnosticCommand -> *#1004*{target}*11## (validate with existing _validate_central_where in heating.py:126).
  • request_faulty_zones(cls, where: str = "#0") -> OWNHeatingDiagnosticCommand -> *#1004*{target}*21## (validate with _validate_central_where).
  • request_zone_actuator_diagnostic(cls, zone: int | str) -> OWNHeatingDiagnosticCommand -> *#1004*#{z}*22## (accept 1..99 or "#1".."#99", normalize to #{z} with 1 <= z <= 99).

2. WHO 16 FM Tuner Frequency Step & Station Preset Store (OWNd/message/sound.py)

Firmware Oracle & Encyclopedia Evidence

  • Encyclopedia KB: kb_who_16_what_frequency_step (WHAT 4001..4015 / 4101..4115), kb_who_16_dim_station (DIMENSION 7 & DIMENSION 10 station read/store on F500 / F500N).
  • Firmware Oracle (bt_difson):
    • *16*4001*101## (step_frequency_up, 1..15 steps -> 4001..4015) -> ACK (*#*1##) on MH200N (040209), F454 (020055), H4684 (020054), emitting SCS 0xD2 subcommand 0x04 <step>.
    • *16*4101*101## (step_frequency_down, 1..15 steps -> 4101..4115) -> ACK (*#*1##) on MH200N, F454, H4684, emitting SCS 0xD2 subcommand 0x14 <step>.
    • *#16*101*#10*5## (store_station, preset 1..15) -> ACK (*#*1##) on MH200N (040209), F454 (020055), H4684 (020054), emitting SCS EC 01 09 05 ($0494018FA0\r).

Additions in OWNd/message/sound.py

  1. OWNSoundCommand (alongside seek_up / seek_down / select_track around sound.py:384-426, using step: int = 1 to match next_track / previous_track at sound.py:356-381):
    • step_frequency_up(cls, where: str | int, step: int = 1) -> OWNSoundCommand:
      • Validate _require_non_empty_where(where) and 1 <= int(step) <= 15 (else ValueError), emit *16*{4000 + s}*{target}##.
    • step_frequency_down(cls, where: str | int, step: int = 1) -> OWNSoundCommand:
      • Validate _require_non_empty_where(where) and 1 <= int(step) <= 15 (else ValueError), emit *16*{4100 + s}*{target}##.
    • store_station(cls, where: str | int, station: int) -> OWNSoundCommand:
      • Validate _require_non_empty_where(where) and 1 <= int(station) <= 15 (else ValueError, matching the 1..15 extended F500N preset range documented in select_track), emit *#16*{target}*#10*{st}##.
  2. OWNSoundEvent (sound.py:79-126):
    • Decode 4001 <= self._state <= 4015 -> frequency_step_forward: int | None (self._state - 4000) and 4101 <= self._state <= 4115 -> frequency_step_backward: int | None (self._state - 4100), matching track_step_forward / track_step_backward (6001..6015 / 6101..6115 at sound.py:85-94).
    • At sound.py:116, extend elif self._dimension == 7 and self._dimension_value: to elif self._dimension in (7, 10) and self._dimension_value: so WHO 16 DIMENSION 10 station reports populate track / station identically to DIMENSION 7.

3. WHO 22 Tuner Seek/Step Builders & Dimension 11 Cleared-Preset Flag (OWNd/message/sound.py)

Firmware Oracle & Physical Trace Evidence

  • Firmware Oracle (bt_multi): On both MH200N (040209) and MyHomeServer1 (021602), *22*5*2#1## (seek_up), *22*6*2#1## (seek_down), *22*7*2#1## (step_frequency_up), and *22*8*2#1## (step_frequency_down) all return *#*1## ACK and put EC 01 05/06/01/02 01 on the SCS bus. (store_preset *22*33#<P>*<where>## is already implemented at sound.py:1077!)
  • Physical H/L4684 (FW 6.0.11) F500 trace (tests/fixtures/traces/issue_466/myhome_trace_HL4684_all_2026-11-01T11-48-15.json in MyHOME#839):
    • When manually tuned off-preset or before selecting a station, the F500 emits *#22*5#2#1*11*1*10530*0## (preset == 0), and when Station 5 is selected at the same frequency, it emits *#22*5#2#1*11*1*10530*5## (preset == 5).
    • Right now in OWNSoundDiffusionEvent (sound.py:594-595), values[2] == "0" leaves self._preset = None, which makes an explicit "no preset (0)" Dimension 11 report indistinguishable from frames that don't carry a preset field at all (such as Dimension 5 *#22*2#1*5*1*10530##).

Additions in OWNd/message/sound.py

  1. OWNSoundDiffusionCommand (alongside next_station / previous_station at sound.py:1195-1207, using where: str | int = "6" for consistency):
    • seek_up(cls, where: str | int = "6") -> OWNSoundDiffusionCommand -> *22*5*{target}##
    • seek_down(cls, where: str | int = "6") -> OWNSoundDiffusionCommand -> *22*6*{target}##
    • step_frequency_up(cls, where: str | int = "6") -> OWNSoundDiffusionCommand -> *22*7*{target}##
    • step_frequency_down(cls, where: str | int = "6") -> OWNSoundDiffusionCommand -> *22*8*{target}##
  2. OWNSoundDiffusionEvent (sound.py:506, 551-596):
    • Add is_preset_cleared: bool (default False), set to True when self._dimension == 11, self._frequency_khz is not None, and values[2] == "0" (self._preset remains None for backwards compatibility, while consumers like MyHOME can check event.is_preset_cleared directly instead of inspecting dimension_value[2]).
    • Decode self._what in (5, 6, 7, 8) into boolean properties is_seek_up (what == 5), is_seek_down (what == 6), is_step_frequency_up (what == 7), is_step_frequency_down (what == 8) + human-readable log lines.

Let me know what you think — happy to review & approve as soon as you're ready!

@gdluck

gdluck commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Happy to. I will author the companion as a separate PR on top of master (WHO 1004 OWNHeatingDiagnosticEvent / OWNHeatingDiagnosticCommand, WHO 16 step_frequency_up / step_frequency_down / store_station plus the 4001..4015 / 4101..4115 and dimension 10 decode, WHO 22 seek_up / seek_down / step_frequency_up / step_frequency_down plus is_preset_cleared and the WHAT 5..8 flags), with the MyHOME#839 traces and the oracle rows as evidence, so #152 can ship 2.0.0b12 as is and the companion goes into the next version; if you would rather fold it into b12, say so and I rebase it onto this branch instead.

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor

Awesome, thank you @gdluck!

Let's fold it into 2.0.0b12 — otherwise we'd immediately need to cut another 2.0.0b13 release right after anyway.

Whichever workflow is cleanest for you works for me:

  • Either open the companion PR against master first (I'll review and approve it right away so it merges cleanly into master), and then rebase #152 on top of it so 2.0.0b12 includes everything (and I'll approve #152 right after);
  • Or fold/rebase it directly into this #152 branch (chore/release-2.0.0b12) with the updated CHANGELOG.md so a single review & approval ships 2.0.0b12 in one go.

Standing by to review and approve as soon as it's up!

@gdluck

gdluck commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

The companion is up as a draft: #153 (WHO 1004 OWNHeatingDiagnosticEvent / OWNHeatingDiagnosticCommand, WHO 16 step_frequency_up / step_frequency_down / store_station + WHAT 40xx / 41xx and dimension 10 decode, WHO 22 seek_up / seek_down / step_frequency_up / step_frequency_down + is_preset_cleared and the WHAT 5..8 flags), on master so #152 can ship as is. Three things worth your eye, all in the body: (1) *16*4001*101##, *16*4101*101##, *22*7*2#1##, *22*8*2#1## and the WHO 1004 frames are not in the oracle index I can see, and the firmware versions you cite (MH200N 040209, F454 020055, MyHomeServer1 021602) are not among its twelve gateways, so those builders rest on your statement; (2) the *#16*101*#10*5## bus telegram in the index is $0493018FD4 ($0494018FA0 is *22*5*2#1##); (3) the off-preset *#22*5#2#1*11*1*10530*0## is not in the T11-48-15 trace, only the *5## form, so it is a spec-derived test input. Say if you want it folded into b12 instead.

@gdluck

gdluck commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Folded in, option two: the three feature commits of #153 are cherry-picked unchanged onto this branch (d4ff9e4, f99b890, 3da84b6) and 1a86d28 moves their CHANGELOG bullets under [2.0.0b12], so one review ships everything. On this head: 2676 passed with the package reinstalled, verify_coverage.py 6,919 / 6,919, sync_documentation.py --check, ruff and mypy clean. #153 stays open only as the reference for the evidence and the three oracle caveats (the WHO 16 / WHO 22 step frames and WHO 1004 are not in the oracle index I can see; the *#16*101*#10*5## telegram in the index is $0493018FD4; the off-preset dimension 11 report is a spec-derived test input); close it with this one or tell me to.

@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.

Hi @gdluck — thank you for putting #153 together so quickly and folding it in here, and especially for flagging the three oracle/trace discrepancies in your comment (*16*4001*101## / *22*7*2#1## absent from the oracle index, $0493018FD4 vs $0494018FA0, and *#22*5#2#1*11*1*10530*0## absent from the T11-48-15 trace).

Your instincts on all three were spot-on: I ran a full binary disassembly + openwebnet-mcp / own-firmware-oracle audit against libdiag.so, bt_difson, and bt_multi, and my earlier specification comment (#152 (comment)) contained several protocol errors that carried straight into d4ff9e47..1a86d28a (4001..4115 instead of 5001..5115, WHO 16 DIM #10 6..15 NACKed by bt_difson + unreachable DIM 10 event decode, WHO 22 WHAT 7/8 unimplemented in bt_multi instead of 5#STEP/6#STEP, WHO 1004 R22P0 absent in libdiag.so + misdecoded DIM 7/21/23 + unique_id collisions, and non-existent kb_who_* slugs). That is 100% on me for giving you an unverified spec.

Action for #152 (2.0.0b12): Reset back to 76b9550c to ship right away

Could you please reset chore/release-2.0.0b12 back to your clean version-bump commit 76b9550c4c2f19bf766efe2a7a7a8f74a935fed7 and force-push?

git checkout chore/release-2.0.0b12
git reset --hard 76b9550c4c2f19bf766efe2a7a7a8f74a935fed7
git push --force-with-lease
  • (Note: I didn't push the reset to your branch myself because master's ruleset enforces require_last_push_approval, which would prevent me from approving #152 afterward.)
  • 76b9550c (OWNd/__init__.py + CHANGELOG.md only) is completely clean, and OpenWebNet-HA/MyHOME currently pins OWNd==2.0.0b11 in manifest.json without calling any #153 symbols (with the matching WHO 16 store_station / DIM 10 cleanup opened in OpenWebNet-HA/MyHOME#844), so as soon as #152 is back on 76b9550c I will approve and merge 2.0.0b12 immediately.
  • Let's then rework the companion changes cleanly on #153 for 2.0.0b13 — I have posted the full primary-source audit and corrected, firmware-verified specification directly on OpenWebNet-HA/OWNd#153 (review).

@gdluck
gdluck force-pushed the chore/release-2.0.0b12 branch from 1a86d28 to 76b9550 Compare November 2, 2026 15:31
@gdluck

gdluck commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Reset: the branch is back at 76b9550 (OWNd/__init__.py + CHANGELOG.md only), force-pushed with lease, body restored. The companion is being reworked on #153 to your corrected specification for 2.0.0b13.

@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.

Thanks @gdluck — verified head is back on \76b9550c4c2f19bf766efe2a7a7a8f74a935fed7\ (\CHANGELOG.md\ + \OWNd/init.py\ only) and all 8 CI / MyHOME checks are green. Approving and merging \2.0.0b12.

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit 360e4f4 into OpenWebNet-HA:master Nov 2, 2026
15 of 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