Skip to content

refactor(coordinator): ♻️ declare entity_active gates on the descriptions - #817

Merged
rhammen merged 2 commits into
mainfrom
refactor/declarative-entity-active-gates
Sep 25, 2026
Merged

rhammen merged 2 commits into
mainfrom
refactor/declarative-entity-active-gates

Conversation

@rhammen

@rhammen rhammen commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

🔍 What this fixes

Stacked on #816: its base is feat/815-twin-heat-meters, so the diff shows only the refactor. Once #816 merges, GitHub retargets this PR to main.

#816 added entity_active_key: a description can declare "this entity only exists if register X says so", and entity_active_formula checks that register instead of the entity's own. Three older gates still lived in coordinator.entity_active as per-register special cases:

  • Smart Grid (P1030): the three offset numbers only exist while Smart Grid is on (Expose Smart Grid heating/DHW temperature offsets as number entities #765).
  • Mixing-circuit type (P0042/P0130/P0780): the three cooling target numbers only exist on a circuit that can cool.
  • Room thermostat (P0033): the two room-thermostat sensors and the room-temperature impact factor only exist with a thermostat fitted.

Every new niche feature added another branch there, and the gate sat far from the entity it governs. This PR moves all three onto their descriptions. Only the gates that cannot be written as a single register comparison stay in the coordinator: firmware, device, and solar (a heuristic over several registers).

✨ Changes

  • coordinator.py

    • The MK, room-thermostat and P1030 branches are removed from entity_active, along with _room_thermostat_present() and the P1030 branch of _special_visibility. The coordinator shrinks by about 40 lines.
    • New formula operator in a,b,c. Each item is compared exactly as == would compare it, so a raw code and its decoded name can share one list.
    • When entity_active_key is set, the entity's own register must also be returned. This was Smart Grid-only before (fix(coordinator): 🐛 entity_active creates entities for registers the controller does not expose #738); every declared gate now gets it.
  • Declarations in number_entities_predefined.py / sensor_entities_predefined.py:

    Entities Gate
    Smart Grid offsets (3) entity_active_key=P1030, "!= off"
    MK cooling targets (3) entity_active_key=P0042/P0130/P0780, "in 3,4,cooling,heating_cooling"
    Room thermostat sensors (2) + impact factor entity_active_key=P0033, "!= 0"

    The MK gate lists the names as well as the codes, so it keeps working if the type register later gets a selection datatype (When cooling is active status flips between cooling and no_request #773). For Smart Grid, P1030 is always decoded by SmartGridMode, so off reads "off". The smart_grid_enabled helper is unchanged.

  • Behaviour: the room thermostat entities are no longer created on a controller that does not report parameter 33, and the mixing-circuit cooling targets are no longer created when their own register is missing (such an entity used to stay on unknown). No unit in the diagnostics corpus is affected.

🧪 Tests

  • The old special-case tests are replaced with tests that run the real predefined descriptions (TestDeclaredActiveGates), so dropping a declaration now fails a test. There are also new tests for the in operator.
  • New guards in test_predefined_entities.py:
    • no LuxParameter may be used as a visibility gate; existence belongs in entity_active_key
    • a declared gate must not raise when its register decodes to a name
    • every declared formula must parse; a malformed one falls through to True
    • a numeric threshold must sit on a register that decodes to a number. Otherwise "none" != 0 compares as strings and is True, and the entities return on every install. Verified to fail when P0033 is given a selection datatype.
    • entity_active_key must not sit on a description without a register of its own

Full run: 1404 passed, 1 skipped, coverage 100%. ruff check, ruff format --check, basedpyright (0 errors) and codespell are clean.

🤖 Generated with Claude Code

rhammen and others added 2 commits September 25, 2026 20:44
…ions

- move the Smart Grid (P1030), mixing-circuit cooling (P0042/P0130/P0780)
  and room thermostat (P0033) gates out of coordinator.entity_active into
  entity_active_key + entity_active_formula on their descriptions
- add an `in a,b,c` formula operator, each item compared as `==`, so a raw
  code and its decoded name can share one list
- a declared gate also requires the entity's own register to be returned
  (#738), generalising the check that was Smart Grid only
- drop _room_thermostat_present and the P1030 branch of _special_visibility;
  entity_active keeps only firmware, device and solar gates
- behaviour: room thermostat entities are no longer created when P0033 is
  absent; mixing-circuit cooling targets now also pass the device check and
  need their own register
- tests exercise the real predefined descriptions; guards forbid parameters
  as visibility gates and check declared gates survive decoded names

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- fail when a numeric gate threshold sits on a register that decodes to a
  name: "none" != 0 compares as strings and is True, so the entities would
  return on every install (P0033 is the likely candidate)
- fail when a declared formula does not parse, which falls through to True
- fail when entity_active_key sits on a description without a register of
  its own, which could then never exist
- update comments that still called the room-thermostat entities
  V0122-gated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from feat/815-twin-heat-meters to main September 25, 2026 19:46
@rhammen
rhammen merged commit 51e6714 into main Sep 25, 2026
7 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.

1 participant