Skip to content

feat(config): wire live phase-aware grid-charge safety entities (issue #831, Part 1) - #836

Open
woopstar wants to merge 1 commit into
mainfrom
feat/831-phase-aware-charging-foundation
Open

feat(config): wire live phase-aware grid-charge safety entities (issue #831, Part 1)#836
woopstar wants to merge 1 commit into
mainfrom
feat/831-phase-aware-charging-foundation

Conversation

@woopstar

Copy link
Copy Markdown
Owner

Summary

Part 1 of a 3-PR plan to build a live per-phase Huawei grid-charge safety limiter (issue #831 — port of the safety-relevant half of Ambilights/hsem-ambilights#35, Huawei-only, no PowMr).

Why: the MILP plans grid-charging against a forecast, but between solve time and the hardware write, another appliance on the same phase can spike load. Without a live check immediately before the write, HSEM can command Huawei to charge at a rate that pushes a phase over the main fuse rating. The fork author hit this on real hardware; this PR starts building the same protection for this repo's Huawei-only control path.

This PR is foundation-only — no behavior change. The feature is off by default (hsem_phase_aware_charging_enabled = False) until Part 2 wires the actual limiter logic into the applier.

What's in this PR

  • New config toggle hsem_phase_aware_charging_enabled (power step), default False.
  • New entity picker hsem_huawei_solar_batteries_grid_charge_maximum_power (huawei_solar step) — the future limiter's write target. Maps to number.batteries_grid_charge_maximum_power, which already exists per docs/huawei_entities.md but was unwired.
  • New entity pickers hsem_huawei_solar_power_meter_phase_{a,b,c}_active_power (power step) — live per-phase telemetry inputs. Map to sensor.power_meter_phase_{a,b,c}_active_power, also already documented but unwired.
  • SensorConfig / LiveState fields for all four entities, read by config_reader.py / state_collector.py.
  • LiveState.grid_phase_power_w: tuple[float | None, float | None, float | None] — fails closed to None per phase when a reading is unavailable/invalid, so the future limiter (Part 2) can't guess a missing phase.
  • Diagnostics: new fields exposed via extra_state_attributes on the working-mode sensor (phase_aware_charging_enabled, grid_phase_power_w, huawei_batteries_grid_charge_max_power_w, plus entity-ID attributes under extended_attributes).
  • Docs: docs/huawei_entities.md (marks both entities as used), docs/config-flow-reference.md, docs/sensors-reference.md.
  • Translations: labels + descriptions added to both config and options steps in translations/en.json.

Files changed

  • custom_components/hsem/const.py
  • custom_components/hsem/custom_sensors/config_reader.py
  • custom_components/hsem/custom_sensors/state_collector.py
  • custom_components/hsem/custom_sensors/working_mode_sensor.py
  • custom_components/hsem/flows/huawei_solar.py
  • custom_components/hsem/flows/power.py
  • custom_components/hsem/models/live_state.py
  • custom_components/hsem/models/sensor_config.py
  • custom_components/hsem/translations/en.json
  • docs/config-flow-reference.md, docs/huawei_entities.md, docs/sensors-reference.md
  • tests/sensors/test_state_collector.py, tests/test_config_validation.py

Delivery plan (tracked in issue #831)

  • Part 1 (this PR): foundation wiring — entities configurable, off by default, no behavior change.
  • Part 2: live phase limiter core — utils/phase_power.py gets compute_phase_charge_limits(); new custom_sensors/phase_charge_limiter.py computes a safe grid-charge cap from live phase telemetry; wired into applier.py so Huawei's grid-charge cap is actually written per-cycle when the toggle is on.
  • Part 3: feedback-free floor + 45s fail-closed transition safety (the original feat(applier): live per-phase Huawei grid-charge safety limiter (Huawei-only port of Ambilights/hsem-ambilights#35 Stage 2) #831 ask) — prevents oscillation from a delayed battery-power echo during a verified downward cap change, and fail-closes to 0 W if telemetry doesn't settle within 45 seconds.

Tests

  • tests/test_config_validation.py: schema round-trip for both new power step fields and the new huawei_solar step field; validation of the phase entity pickers (entity-not-found case); defaults-to-disabled case.
  • tests/sensors/test_state_collector.py: build_sensor_config reads/defaults for phase_aware_charging_enabled, huawei_solar_batteries_grid_charge_maximum_power, and the three phase entities (including the vol.UNDEFINEDNone normalization case, matching the existing _optional_entity() pattern used for other optional Huawei entities).

Test results

  • ./scripts/quality.sh lint — pass (ruff format + check clean)
  • ./scripts/quality.sh typing — pass (0 mypy errors)
  • ./scripts/quality.sh quality — pass (0 pyright/vulture errors)
  • ./scripts/quality.sh test — 2855 passed (full suite, including the new tests)

Known limitations

This PR only wires configuration and live-state plumbing. Enabling the toggle today has no runtime effect — Part 2 is required before the limiter actually computes and writes anything. This is intentional: it lets the config surface, translations, and data model land and be reviewed independently of the (larger, more safety-critical) limiter logic.

Part of #831.

…#831, Part 1)

Foundation-only PR for the live per-phase Huawei grid-charge safety
limiter. No behavior change — the feature is off by default
(hsem_phase_aware_charging_enabled = False) until Part 2 wires the
actual limiter logic into the applier.

Adds:
- hsem_phase_aware_charging_enabled config toggle (power step)
- hsem_huawei_solar_batteries_grid_charge_maximum_power entity picker
  (huawei_solar step) — the write target for the future limiter
- hsem_huawei_solar_power_meter_phase_{a,b,c}_active_power entity
  pickers (power step) — live per-phase telemetry inputs
- SensorConfig / LiveState fields and config_reader/state_collector
  wiring for all four entities
- LiveState.grid_phase_power_w tuple, fails closed to None per phase
  when unavailable
- extra_state_attributes exposure for diagnostics
- docs/huawei_entities.md, docs/config-flow-reference.md,
  docs/sensors-reference.md updates
- translations/en.json labels/descriptions in both config and options
  steps

Both required Huawei entities (number.batteries_grid_charge_maximum_power,
sensor.power_meter_phase_{a,b,c}_active_power) already exist and are
documented in docs/huawei_entities.md; this PR only wires them into
HSEM's config flow and live-state pipeline.

Part of a 3-PR delivery plan tracked in issue #831:
- Part 1 (this PR): foundation wiring
- Part 2: live phase limiter core (utils/phase_power.py +
  custom_sensors/phase_charge_limiter.py, wired into applier.py)
- Part 3: feedback-free floor + 45s fail-closed transition safety

Tests: config flow schema round-trip (power + huawei_solar steps),
validation of the new optional entity fields, config_reader field
reads/defaults. All 2855 existing + new tests pass; lint/typing/quality
gates clean.
@github-actions github-actions Bot added the enhancement New feature or request label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant