Skip to content

arch(protocol): move remaining OpenWebNet string/dimension edge-case decoding to OWNd and remove production shims/MagicMock checks #736

Closed

Description

@GreenGrassBlueOcean

Architectural Assessment & Severity


Problem Statement

While custom_components/myhome/where_grammar.py (enforced by tests/test_where_usage_guard.py) successfully centralized WHO=4 WHERE grammar (1..99 zone, #0 central unit, Z#N actuator, 0#N pump, PZZ probe), several other OpenWebNet protocol edge cases and string-parsing workarounds have accumulated inside MyHOME instead of being strictly modeled in OWNd:

  1. WHO=1 APL / Area-0 Addressing & Timer Frame String Construction in const.py:
    • is_apl_address(), area_of_where(), and normalize_where() (custom_components/myhome/const.py:244-321) implement OpenWebNet 2-digit vs. 4-digit APL and Area 00 / 100 string parsing directly in Home Assistant.
    • build_timed_turn_on_command() (const.py:519-555) formats raw *1*{what}*{where}## and *#1*{where}*#2*{h}*{m}*{s}## frame strings as fallbacks.
  2. WHO=1 Group Dimension Private-Attribute Parsing in light_group.py:
    • MyHOMELightGroup.handle_event() (custom_components/myhome/light_group.py:180-196) reaches into getattr(msg, "_dimension_value", []) and manually decodes Dimension 1 (raw - 100 for 100 <= raw <= 200), Dimension 14 (int(1000000 / int(vals[0]))), and Dimension 12 (h <= 360 sentinel check for *12*511*127*255), duplicating OWNLightingEvent parsing.
  3. WHO=4 Sign-Magnitude Temperature Decoding in const.py:
    • who4_raw_to_celsius() (custom_components/myhome/const.py:214-222) decodes 4-digit SXXX temperature strings (1xxx negative, 0xxx positive tenths of °C) in MyHOME when OWNd leaves raw dimension values undecoded.
  4. WHO=16 Stepped Volume (WHAT=1001..1015 / 1101..1115) String Splitting in media_player_power.py:
    • apply_volume_delta_from_what() (custom_components/myhome/media_player_power.py:57-92) splits what.split("#", 1)[0] and checks 1001 <= what_code <= 1015 / 1101 <= what_code <= 1115 to compute step / 31.0 because OWNSoundDiffusionEvent (OWNd/message/sound.py) only maps discrete WHAT codes in _SOUND_WHAT_TABLE.
  5. Obsolete OWNd Shims & MagicMock Checks in Production Code:
    • try ... except ImportError shims in climate.py:49-55 (MESSAGE_TYPE_SEASON, merged in OWNd#97) and sensor.py:39-48 (MESSAGE_TYPE_AUTO_UPDATE_INTERVAL, merged in OWNd#102), plus MESSAGE_TYPE_ZONE_STATE = "zone_state" in climate.py:90 (OWNd#60).
    • _StatusRequestLogFilter in gateway.py:128-146 mutating OWNd log records in-place for OWNd < 2.0.0b8.
    • 5 production checks for MagicMock (gateway_events.py:285, 327, gateway_resync.py:124, sensor.py:849, services.py:77).

Proposed Solution (PR Scope)

  1. Extend Typed Protocol Models in OWNd:
    • Export MESSAGE_TYPE_ZONE_STATE (WHO=4 Dimension 7) and ensure all WHO=4 temperature dimensions decode sign-magnitude SXXX values in OWNHeatingEvent.
    • Decode WHO=16 stepped volume WHAT=1001..1015 (+1..+15) and WHAT=1101..1115 (-1..-15) as a typed volume_step_delta: int | None property on OWNSoundDiffusionEvent.
    • Expose typed brightness, color_temp_kelvin, and hs_color properties on WHO=1 group dimension commands/events (*#1*#G*#D*...##) in OWNd/message/lighting.py.
    • Move is_apl_address, area_of_where, and normalize_where into OWNd's addressing module (and re-export in MyHOME alongside where_grammar.py).
  2. Purge Protocol Hacks, Shims & MagicMock Checks in MyHOME:
    • Replace raw _dimension_value parsing in light_group.py, what.split("#") volume math in media_player_power.py, and who4_raw_to_celsius in const.py with typed OWNd properties.
    • Delete the obsolete ImportError shims in climate.py and sensor.py and _StatusRequestLogFilter in gateway.py.
    • Remove all 5 MagicMock checks from custom_components/myhome/ and fix test mocks to supply typed attributes.
    • Add an AST/regex guard test in tests/test_where_usage_guard.py forbidding MagicMock references and private _dimension_value accesses inside custom_components/myhome/.

Verification Plan

  • Run pytest tests/test_where_usage_guard.py (extended with protocol boundary and no-MagicMock guards).
  • Run full test suite against the updated OWNd package (pytest tests -q --cov=custom_components/myhome) and ruff check.

Activity

  1. added 2 commits that reference this issue on Oct 16, 2026
  2. GreenGrassBlueOcean commented on Oct 21, 2026

    @GreenGrassBlueOcean
    ContributorAuthor

    Resolved in PR #737 (5d9218d1):

    • Moved OpenWebNet protocol edge-case decoding to OWNd.
    • Removed obsolete ImportError shims and all production MagicMock checks across custom_components/myhome/.
    • Added AST/regex protocol-boundary and MagicMock guards in tests/test_where_usage_guard.py.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions