Conversation
Design artifacts for the full HACS integration redesign (dimplex_wpm): - DESIGN.md: architecture, entity model + medium device tree, measurement source matrix (measured vs estimated per quantity), capabilities, native estimation engine, write-safety gating, device profiles, roadmap. - spec/REGISTERS.md: canonical, adversarially-verified register map (single source of truth) — version-aware status/lock/fault addresses (L/M target), holding-only (FC03) rule, digit-group energy formula, real power vs estimation, SG Ready, settings, enums. - spec/dimplex_modbus_spec_raw.md: verbatim dump of the official Dimplex NWPM Modbus TCP wiki (32 pages) used as the authoritative source. - existing integration/: reference YAML (working estimator "sauce") + dashboards to be ported. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuild the component around a single canonical register table instead of hand-written entities. No writes yet (read-only foundation). Core: - registers.py: canonical RegisterSpec/EnergyGroup table (HA-free, unit-tested) with version-dependent addresses (status/lock/fault/sensor-error per H/J/L/M), holding-only (FC03) reads + coils, scale/sign decode, digit-group energy formula, read-plan clustering (≤100 regs/chunk), module + capability gating. - profiles.py: DeviceProfile (LAK9 calibration LUT/COP for M1 + generic_wpm), per-model default modules/capabilities. - coordinator.py: rewritten to read the active register set, decode, resolve enum text (version-aware), combine energy groups, derive fault/lock flags. - entity.py: shared mixin (device_info on the medium device tree, unique_id, value-presence availability). - sensor.py / binary_sensor.py: entities generated from coordinator.specs + energy groups + controller_info; coils + derived problem flags. - const.py: medium device-tree module names, operating-mode map, enum resolver, profile/module/capability config keys + resolvers; default firmware L/M. - config_flow.py: two-step flow (connection+profile → modules+metering with device-probe auto-hint); validation now reads a holding register (FC03). - modbus_client.py: add read_coils. - manifest.json: correct docs/issue URLs, codeowner @nb3rt, integration_type hub. - strings/en translations for the new flow. Tests: tests/test_registers.py (pure-python, 6 cases) — decode, energy digit groups, version addresses, capability/module filtering, read-plan clustering. Also adds spec/DASHBOARD_DESIGN.md (background design output, for M3). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the estimation "sauce" natively (used when a meter is absent; see the measurement-source matrix). All formulas are pure/unit-tested. - estimation.py: Hz→W LUT (status-aware: DHW/defrost factors), EN14511 2D-COP interpolation (fixes the YAML round() bracketing bug), compressor/heater/total power, thermal compressor/heater/defrost-loss/loop, house/installation split via alpha(dΔT/dt), hydraulic flow estimate. - coordinator.py: computes derived power/COP/heat/flow each cycle, ΔT + its time derivative, and measured-vs-estimated selection (electrical_power_best / heat_output_best, cop_measured). Live tunables dict seeded from the profile. - sensor.py: Analytics device gains computed sensors (each tagged source= measured|estimated) and trapezoidal kWh integration sensors (RestoreSensor): electrical energy, heat energy, heat-to-house, heat-to-installation. Gated by capability / estimation_possible. - number.py (new platform): live calibration entities (k_dhw, k_defrost, k_defrost_loss, heater/pump power, alpha base/sensitivity/deadband) → coordinator.tunables, RestoreNumber-persisted, NOT behind the write gate. - __init__.py: pass the profile object + flow-sensor entity; add NUMBER platform. - const.py: drop dead register-strategy / EMS-BMS-lock stubs (superseded by capabilities). Tests: tests/test_estimation.py (6 cases) — LUT, status-aware power, COP grid+bilinear+clamp, thermal/defrost, alpha deadband/clamp, flow clamp. Total 12/12 pure-python tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- .github/workflows/ci.yml: hassfest, hacs/action (category integration, ignore brands until submitted), ruff check, pytest. - manifest.json: sort keys to hassfest order (domain, name, then alphabetical); pin version 0.2.0. - hacs.json: keep only valid keys (name, country, homeassistant, render_readme); drop non-schema domains/integration_type/filename/zip_release. - pyproject.toml: ruff (E/F/W/I/UP, isort force-sort-within-sections) + pytest config. Applied ruff import-sort autofix across the package. Locally green: ruff clean, 12/12 pytest, manifest hassfest-sorted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read-only by default; all writes go to the heat pump only when the "enable control" option is on, are range-clamped, and use documented encodings. - registers.py: WriteSpec table + pure encode/decode (to_raw/from_raw), incl. enum-coded registers (5036 curve offset K=raw-19; cool-setpoint 15+0.5·raw). Writable set: DHW setpoint/min/max, HC1 room/fixed-flow/curve-end/curve-offset, pool setpoint (module-gated), operating-mode select. build_read_plan gains extra_holding so current values are read back. - coordinator.py: enable_control flag → active_write_registers, their addresses added to the read plan, decoded into values (number=display, select=raw code). - number.py: DimplexWritableNumber (writes holding register, range-clamped, ModbusException→HomeAssistantError). Calibration numbers unchanged/ungated. - select.py: DimplexWriteSelect (operating mode) alongside the existing SG Ready select; both gated. - __init__.py: pass enable_control from options. Tests: +4 (write encode/decode/clamp, offset19, cool-setpoint, module gate); 16/16 pure-python tests pass. NOTE: exact register scaling for some setpoints is unverified without hardware; gate is OFF by default and writes are range-validated. Climate entities (HC1/DHW) are the remaining M2 sub-item. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial audit (4 agents) findings addressed: Critical/blocking: - modbus_client.close(): pymodbus 3.x close() is SYNC — drop the await (was a guaranteed TypeError on every unload/reload), wrap defensively. - device.py: remove host/IP from the root device name (it seeds entity_ids → was baking the host into every controller entity_id, non-portable). - __init__: default to the 'lak9' profile when none is set (was silently falling back to generic → empty Analytics). Major/minor: - select.py: SG Ready select uses _attr_name (had translation_key with no translation → unnamed/unstable id); guard current_option against unmapped codes; writable select availability based on last_update_success. - number.py: writable-number availability based on last_update_success (operable even if read-back register is absent). - config_flow.py: OptionsFlow no longer assigns self.config_entry (HA 2024.11+ deprecation); async_get_options_flow() takes no arg. - sensor.py: suppress estimated heat_energy_kwh when a heat meter exists (Energy Dashboard double-count); (meter)/(est.) name suffixes; energy-group display precision; add flow_rate_smoothed (EMA) sensor. - coordinator.py: compute EMA-smoothed flow. - registers.py: inverter_frequency device_class=frequency; HC1 room setpoint step=1 (avoid silent 0.5 truncation; scaling "verify on device"); gate pv_surplus behind electric-meter cap; energy groups named "(meter)"; add HC2/3 cooling-setpoint WriteSpec (uses the previously-dead cool_setpoint encoder). Deferred (tracked): climate (HC1/DHW) platform; full dashboard entity-id reconcile (done in M3); on-device verification of setpoint scaling. ruff clean, 16/16 pytest, manifest hassfest-sorted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Authored the front-end against the REAL (derived) entity-id scheme, per the
pre-M3 audit reconcile.
- dashboards/dimplex_wpm.yaml: combined 5-view dashboard (Overview, Heat &
Energy, History, Control, Diagnostics/Calibration). Built-in cards only (no
HACS frontend dependency); module/meter cards self-hide via per-card
visibility; no climate cards (platform deferred).
- spec/ENTITY_IDS.md: authoritative entity_id list + the device-name→prefix
derivation rule (has_entity_name + _attr_name, no translation_key).
- Naming for clean/predictable slugs: Analytics device renamed from
"Energy & analytics" → "Analytics" (prefix analytics_); ΔT/α computed-sensor
names switched to ASCII ("Temperature difference", "House heat fraction").
- README rewritten for the redesigned integration (profiles, capabilities,
measurement-source matrix, dashboards, write gate, limitations).
- spec/DASHBOARD_DESIGN.md: status note (v1 authored, slugs reconciled).
ruff clean, 16/16 pytest, dashboard + ci YAML validated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Brand assets shipped inside the integration (HA 2026.02+ supports in-package brand images, no separate home-assistant/brands PR needed): icon.png (256) + icon@2x.png (512) — square Dimplex mark, transparent; logo.png + logo@2x.png — full wordmark, transparent. Generated from the official Dimplex logo (recolored to brand red, alpha from source). - translations/pl.json + de.json: full config + options flow (mirrors strings.json key structure; hassfest-compatible). Entity-name localization (translation_key) intentionally deferred — entities use _attr_name so entity_ids (and the shipped dashboards) stay stable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per developers.home-assistant.io/blog/2026/02/24/brands-proxy-api and the integration file-structure docs, in-package brand images must live in custom_components/<domain>/brand/ (local images override the brands CDN via the new /api/brands proxy; no manifest change needed). Moved icon/icon@2x/logo/ logo@2x there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- climate.py: HC1 and DHW thermostats behind the control gate. Single HEAT mode; current temperature from a read value (room/return for HC1, DHW temp for DHW), target temperature reuses the writable setpoint WriteSpec (encoding, range and read-back shared with the number platform). Platform.CLIMATE added. - dashboards: Control view gains thermostat cards (visibility-gated); Heat & Energy view gains optional apexcharts-card section (dual-axis power/COP + stacked thermal) with the built-in history-graph kept as fallback. - docs: ENTITY_IDS + README updated (climate now shipped). ruff clean, 16/16 pytest, dashboard YAML valid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entities now use translation_key instead of _attr_name, so names localize while
entity_ids stay English-derived and language-independent.
- entity.py: _apply_common sets _attr_translation_key = key. SG Ready select
uses translation_key too.
- strings.json + translations/en.json: full entity{} section (129 names);
English strings equal the former _attr_name, so entity_ids are unchanged...
- ...except the 4 integration energy sensors whose names were dynamic
("(est.)"/"(meter)"): fixed to stable names → ids drop the _est suffix
(analytics_electrical_energy / heat_energy / heat_energy_to_house /
heat_energy_to_installation). Dashboard + ENTITY_IDS updated accordingly.
- translations/pl.json + de.json: 129 localized entity names each (full
coverage; key structure matches strings.json — hassfest-compatible).
- ENTITY_IDS.md: header note updated (translation_key → English-derived ids).
ruff clean, 16/16 pytest, all translation files key-match strings.json.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Toward Silver/Gold: - PARALLEL_UPDATES on every platform (0 for read, 1 to serialize writes). - diagnostics.py: config-entry diagnostics (config + coordinator data, host redacted) — Gold "diagnostics" rule. - quality_scale.yaml: honest done/todo/exempt across Bronze→Platinum. - README: Removal section (Bronze docs-removal) + quality-scale note. Bronze/Silver largely met; documented gaps: HA-runtime test coverage, entry.runtime_data, and Gold polish (icon/exception translations, reconfigure). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
From the adversarial pre-release review (no blockers; these are the real ones): - modbus_client.write_register: map ConnectionError/OSError → ModbusException so control writes to an offline device surface a friendly HomeAssistantError instead of an unhandled traceback. - Fix entity_id collision: raw register sensor 5170 renamed "Electrical power" → "Electrical power (meter)" (id analytics_electrical_power_meter) so it no longer clashes with the canonical "Electrical power" (electrical_power_best). - controller_info: replace the always-zero "consecutive_failures" attribute with "last_update_success". - Naming/UX: drop the redundant device word from ventilation/passive-cooling/ solar entity names (was producing doubled display names + ids like ventilation_ventilation_*); regenerated en/strings, patched pl/de (incl. stripping the German "Lüftung " prefix). ENTITY_IDS.md updated. - select.py: read software_version from the coordinator (consistency). ruff clean, 16/16 pytest, en/pl/de key-parity with strings.json, dashboard valid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nb3rt
marked this pull request as ready for review
June 21, 2026 21:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Complete redesign of the Dimplex WPM (Modbus TCP) integration. Replaces the
v0.1.0 skeleton with a register-table-driven architecture.
What's included
(H/J/L/M) status/lock/fault/sensor-error addresses; medium device tree;
capabilities + device profiles (LAK9 + generic); two-step config flow with a
device-probe auto-hint.
comp/heater/defrost/loop, house/installation split, hydraulic flow; native kWh
(RestoreSensor) for the Energy Dashboard. Measured-vs-estimated selection with
a
sourceattribute. Live calibrationnumberentities.operating-mode selects, HC1/DHW climate thermostats; range-validated.
in-package brand images; en/pl/de localization; diagnostics; quality_scale.yaml;
CI (hassfest + HACS + ruff + pytest, all green).
Notes
hardware before relying on writes (gate is off by default; writes validated).
Design:
DESIGN.md· register map:spec/REGISTERS.md· entity ids:spec/ENTITY_IDS.md.🤖 Generated with Claude Code