12 KiB
HA Energy/Power Analysis Methodology
How to diagnose unexpected power consumption (e.g. nighttime grid draw) using HA history data. Based on a session diagnosing ~200W nighttime grid import caused by a heat pump water heating cycle.
Sensor Inventory (This Homelab)
Grid / Net Power
| Entity | Name | Notes |
|---|---|---|
sensor.evcc_grid_power |
Leistung Netz | EVCC aggregate grid power (W). Positive = import. |
sensor.netzfluss_saldo |
Tibber Pulse Netzfluss Saldo | Tibber Pulse grid balance (W). Negative = export. Higher resolution than EVCC. |
sensor.stromverbrauch_haus_netto |
Stromverbrauch Haus netto | Net house power consumption (W) |
Home / Load
| Entity | Name | Notes |
|---|---|---|
sensor.evcc_home_power |
Leistung Heim | Total home consumption (W) |
sensor.stromverbrauch_rest |
Stromverbrauch "Rest" | Unknown/rest consumption |
PV
| Entity | Name | Notes |
|---|---|---|
sensor.evcc_pv_power |
Leistung PV | Aggregate PV power (W) |
sensor.hm_1500_power |
HM-1500 Power | Hoymiles HM-1500 via OpenDTU/MQTT |
sensor.schuppen_string_1_power |
Schuppen 1 Power | Hoymiles Schuppen 1 |
sensor.schuppen_2_power |
Schuppen 2 Power | Hoymilles Schuppen 2 |
Batteries
| Entity | Name | Notes |
|---|---|---|
sensor.evcc_battery_power |
Leistung Batterie | EVCC aggregate battery (W). Positive = discharging. |
sensor.evcc_battery_soc |
Batterie Ladezustand | EVCC aggregate SOC (%) |
sensor.sn_3007105790_battery_power_discharge_total |
SMA Battery Discharge | SMA SunnyBoyStorage discharge (W) |
sensor.sn_3007105790_battery_power_charge_total |
SMA Battery Charge | SMA SunnyBoyStorage charge (W) |
sensor.sn_3007105790_battery_soc_total |
SMA Battery SOC | SMA SunnyBoyStorage SOC (%) |
sensor.marstek_venus_modbus_batterieleistung |
Marstek Batterieleistung | Marstek Venus (W). Negative = discharging. |
sensor.marstek_venus_modbus_ac_leistung |
Marstek AC-Leistung | Marstek AC output (W) |
sensor.marstek_venus_modbus_batterie_ladezustand |
Marstek SoC | Marstek Venus SOC (%) |
sensor.marstek_target_power |
Marstek Target Power | EVCC/setpoint target for Marstek (W) |
sensor.sma_target_power |
SMA Target Power | EVCC/setpoint target for SMA (W) |
Large Consumers (Individual)
| Entity | Name | Notes |
|---|---|---|
sensor.shellypro3em_08f9e0e98b9c_total_active_power |
Wärmepumpe Total | Heat pump total active power (W). Major consumer — up to 5.4kW. |
sensor.strommessung_sauna_total_active_power |
Sauna Total | Sauna total active power (W) |
sensor.cu401b_s_verdichterleistung |
CU401B Verdichterleistung | Heat pump compressor power (kW) |
sensor.cu401b_s_verdichtermodulation |
CU401B Verdichtermodulation | Heat pump compressor modulation (%) |
sensor.cu401b_s_kompressorphase |
CU401B Kompressorphase | Compressor phase: off/preparing/heating/pause |
sensor.cu401b_s_ww_speichertemperatur |
WW-Speichertemperatur | Hot water tank temp (°C). Target: 46°C, Hysteresis: 4K |
sensor.ev_charger_schon_leistung_ladestation |
EV Charger Leistung | SMA EV Charger station power (W) |
sensor.evcc_wallbox_charge_power |
Wallbox Ladeleistung | EVCC wallbox charge power (kW) |
sensor.trockner_device_power |
Trockner Power | Dryer power (W) |
sensor.samsung_qn90aa_65_tv_power |
Samsung TV Power | TV power (W) |
EVCC Config / Tariff
| Entity | Name | Notes |
|---|---|---|
number.evcc_residual_power |
Restleistung | EVCC residual power target (W). Default 100W. |
sensor.evcc_tariff_grid |
Kosten Netz | Current grid price (€/kWh) |
binary_sensor.evcc_battery_grid_charge_active |
Hausbatterie Netzladen | Whether battery is charging from grid |
Heat Pump Config (ViCare / CU401B S)
| Entity | Name | Notes |
|---|---|---|
climate.vicare_heating |
CU401B S Heizung | Climate entity. Presets: comfort/eco/home/sleep |
number.cu401b_s_ww_temperatur |
WW-Temperatur | WW target temp (°C). Currently 46. |
number.cu401b_s_ww_hystereseschalter_ein |
WW-Hysterese ein | WW hysteresis ON (K). Currently 4. |
number.cu401b_s_ww_hystereseschalter_aus |
WW-Hysterese aus | WW hysteresis OFF (K). Currently 4. |
automation.komfort_heizung |
Komfort Heizung | Automation controlling heating comfort mode |
Analysis Workflow
-
Confirm the anomaly: Query
sensor.evcc_grid_powerhistory for the suspected time window. Summarize in 30-min buckets to see the pattern. Important: Ask the user for approximate time — they may point to a different window than the first obvious spike. In one session, the first analysis found a 02:47 heat pump spike, but the user clarified the issue was at 04:30+, revealing a second smaller spike that was initially overlooked. -
Decompose the power equation: At any moment:
grid = home - pv - battery_discharge + battery_charge. Query all four simultaneously to see which component is causing the imbalance. -
Check battery SOC trajectory: If batteries drain to low SOC during the night, they can't cover the remaining demand → grid import kicks in. Plot SOC over time for both batteries.
-
Identify the triggering consumer: Query individual large-consumer sensors (heat pump, sauna, EV charger, dryer) for the same time window. Look for spikes or sustained elevated consumption.
-
Correlate with heat pump phases: The
cu401b_s_kompressorphaseandcu401b_s_verdichtermodulationentities show exactly when the compressor starts, what phase it's in, and how hard it's working. Theww_speichertemperaturshows whether it's a hot-water heating cycle (temp rising toward target). -
Check EVCC control targets:
marstek_target_powerandsma_target_powershow what EVCC is commanding the batteries to do. If these are 0, EVCC isn't actively discharging the batteries — they may be in passive mode. -
Summarize findings: Present a timeline table showing the key events and a root-cause explanation, followed by prevention options.
Multiple Spikes Per Night (2026-07-09 Session)
In one session, two distinct grid import spikes were found in a single night:
| Time | Grid Peak | Home Peak | Heat Pump | Cause |
|---|---|---|---|---|
| 02:47 | 4,808 W | 6,151 W | 5,440 W | Heat pump WW heating (compressor 47-50% modulation, WW temp 38→47°C) |
| 04:30 | 3,570 W | 4,920 W | 91 W (standby) | Unknown — heat pump was idle. Possibly a second WW cycle or another consumer not covered by monitored sensors. |
Lesson: Don't assume the first spike is the only one. Query the full 02:00–07:00 window and look for ALL grid import periods >100W. The 04:30 spike showed home power at 4,920W but the heat pump was only drawing 91W — the 4,800W delta came from an unmonitored consumer. Future analysis should query ALL available power sensors (including sensor.stromverbrauch_haus_netto which aggregates differently from evcc_home_power) and consider unmonitored circuits.
Prevention Strategies for Nighttime Grid Import
Heat Pump WW Timing
The most impactful fix: shift hot water preparation to daytime PV hours.
- ViCare schedule: Set WW heating to only occur between 10:00–15:00 (main PV production window).
- Increase hysteresis: Change
ww_hystereseschalter_einfrom 4K to 6K → starts later, fewer cycles. - Lower WW target: Reduce
ww_temperaturfrom 46°C to 43°C → less energy per cycle.
Battery SOC Management
Ensure batteries start the night at 100%:
- EVCC priority SOC: Ensure
evcc_priority_socis set appropriately so batteries reserve enough for overnight. - Force charge before sunset: If PV forecast predicts low production, trigger battery charging from PV surplus in late afternoon.
- Marstek discharge limit:
number.marstek_venus_modbus_entladeleistung_einstellencontrols max discharge rate. Lowering it extends battery runtime but may not fully cover peaks.
Combination Approach (Recommended)
Both WW timing AND battery management together provide the most robust solution.
EVCC-Side Battery Discharge Diagnosis
When the question shifts from "what consumed the power?" to "why didn't the battery cover the gap?", the EVCC api/state endpoint provides the answer. This is a different diagnostic axis from the consumption-side analysis above.
Key Fields in curl -s http://10.0.30.10:7070/api/state
| Field | Path | Meaning |
|---|---|---|
| Controllable | battery.devices[].controllable |
false = EVCC reads SoC/power but cannot command discharge |
| Discharge control | batteryDischargeControl |
false = passive monitoring, no active discharge management |
| Residual power | residualPower |
Watts EVCC tolerates from grid before reacting (default 100) |
| Battery mode | batteryMode |
unknown = no active mode set |
| Per-battery power | battery.devices[].power |
Current output (W) — compare to home power |
| Per-battery SOC | battery.devices[].soc |
High SOC + low output = battery is limited, not empty |
Diagnosis Flow
- Check
controllable— iffalse, EVCC cannot increase discharge. The battery's own internal limit is the bottleneck. - Check
residualPower— if >0, EVCC deliberately allows that much grid import as baseline. - Compare battery output to home power — if battery output << home power and SOC is high, the battery's internal discharge limit (not EVCC) is the constraint.
- Verify by checking spike behavior — if the battery briefly delivered much more during a spike (e.g. Marstek hit 1,437W during heat pump cycle but settled to ~650W steady-state), that confirms an internal limiter, not a capacity issue.
Case Study: Marstek Venus Not Covering 200W Gap (2026-07-10)
Symptom: ~200W continuous grid import 03:00–04:00 despite Marstek SoC at 73%.
Initial hypothesis (INCOMPLETE): EVCC controllable: false + residualPower: 100 + Marstek internal discharge limit ≈ 650W.
Actual root cause: The HA automation "Marstek Kaskaden-Steuerung (Modbus)" (automation.marstek_modbus_steuerung) controls the Marstek, NOT EVCC. EVCC sees controllable: false because it's only a passive observer. The automation has a feedback-loop bug in its echter_bedarf formula:
# BUG: uses setpoint (number.*_einstellen) instead of measured (sensor.*)
echter_bedarf: "{{ power_home - production + marstek_current_discharge - marstek_current_power }}"
# marstek_current_discharge = states('number.marstek_venus_modbus_entladeleistung_einstellen') ← SETPOINT
This causes the discharge setpoint to spiral: 0 → 870 → 1740 → 2500 (capped) within 3 cycles (6 min). The Marstek's anti_feed mode then throttles actual output to ~650W (preventing grid export from the oversized command). The ~200W gap is the difference between what the automation commands (2500W) and what the Marstek actually delivers after anti_feed throttling (~650W), versus what the house needs (~870W).
Evidence: During the 02:43–02:57 heat pump spike, Marstek delivered up to 1,437W (proving capacity exists), but settled back to ~650W afterward — consistent with the automation ramping the setpoint up and anti_feed throttling back.
Fix: Replace setpoint variables with measured AC power in the automation:
# FIXED:
marstek_actual_ac: "{{ states('sensor.marstek_venus_modbus_ac_leistung') | float(0) }}"
echter_bedarf: "{{ power_home - production - marstek_actual_ac }}"
Result: echter_bedarf = 870 - 0 - 650 = 220W → new setpoint = 650 + 220 = 870W. Stable convergence.
See references/marstek-cascade-automation.md for the full automation YAML and analysis.
Query Templates
See references/ha-browser-console-access.md for the browser-console technique used to execute these queries when no local API token is available.
Summary query (all sensors, 30-min buckets)
// Adapt entity list and time range as needed
const entities = 'sensor.evcc_grid_power,sensor.evcc_home_power,sensor.evcc_battery_power,sensor.evcc_pv_power';
const url = '/api/history/period/2026-07-09T03:00:00?filter_entity_id=' + entities +
'&end_time=2026-07-09T06:00:00&minimal_response';
Individual consumer spike detection
// Filter history records where power > threshold
const spikes = hist.filter(h => parseFloat(h.state) > 100).map(h => ({
t: h.last_changed.substring(11, 16),
v: parseFloat(h.state)
}));