Monitoring a Victron + Enphase hybrid system with Grafana: what turned out to be useful

A Victron ESS next to an Enphase system is two energy systems sharing one house. Each comes with its own app, and each app is confident about its own half of the truth. To see what is actually going on – where the PV goes, when the batteries cycle, when the grid is touched and at what price – I built a set of Grafana dashboards on my self-hosted stack. This post walks through the panels that turned out to be genuinely useful for monitoring such a hybrid system, and a few traps I fell into on the way.

The dashboard labels are in Luxembourgish; I translate the important ones as we go.

The setup

  • Victron side: 3× MultiPlus-II 48/6500 (three-phase ESS), Ekrano GX, two DIY 16 kWh LiFePO4 packs (EEL V6 Pro with JK BMS, 32 kWh in total), three Victron VM-3P75CT energy meters in the roles grid, loads and PV.
  • Enphase side: 37 IQ8 microinverters on two roofs, AC-coupled on the Victron AC-out, plus two IQ Battery 5P.
  • Data path: Telegraf reads the Ekrano over MQTT into InfluxDB 2 (raw data with limited retention, plus a 1-minute bucket kept indefinitely). openHAB polls the Envoy (per-panel data, Enphase battery) and stores it in its own bucket, together with the day-ahead spot price and the PV forecast. The JK BMS packs are read over an isolated RS485-to-Ethernet converter and published via MQTT, down to individual cell voltages.
  • Frontend: Grafana 13, everything self-hosted, no vendor cloud in the loop (Enphase cloud data is pulled nightly as a backup only).

A word of honesty about the data. Two current clamps are not yet where they will end up: the server circuit currently bypasses the loads meter on one phase, and the Enphase IQ batteries sit outside the PV meter, so Venus counts their charging as household load. Everything that depends on the loads meter – household consumption, autarky – is therefore biased for now, and I left those panels out of this post. What you see below is based on the PV, grid and battery measurements, which are correct.

1. Planning: forecast, spot price, state of charge

The first dashboard is the one I open in the morning. It shows today’s PV forecast against what has been produced so far, the forecast for tomorrow (here: available from 17:55), both batteries’ state of charge, the current spot price and which price source is active. The price feed queries Energy-Charts and falls back to a second source if that fails; the active source is shown so a silent failover does not go unnoticed.

Energy planning dashboard with forecast, SoC and spot price tiles and a 36-hour spot price bar chart
Planning view: forecast vs. production, SoC of both battery systems, and the spot price for the next 36 hours.

Below it, the spot price is plotted against the grid power. The blue areas are export. On the left you see the classic midday surplus at low prices; on the right, the batteries being sold into the evening price peak. The lower chart shows both batteries’ state of charge – note how differently the small Enphase batteries (blue) and the 32 kWh Victron bank (green) cycle.

Spot price versus grid export, and state of charge of Victron and Enphase batteries over 48 hours
Spot price vs. grid power (export negative), and the state of charge of both battery systems.

Since the forecast drives decisions (how much to sell in the evening, how much reserve to keep for a dark day), I track how good it is. The table compares the forecast made the evening before and the one made in the morning with actual production. On sunny days the forecast is decent; on overcast days it is off by a factor of two or more in either direction, which is exactly why the battery strategy must not trust a single forecast.

Forecast versus production for the last 14 days with a table of deltas and factors
Forecast quality: evening and morning forecast vs. actual production, with deltas and ratios per day.

2. PV: per panel and against the sun

Enphase microinverters report per panel, which makes shading and faults visible that a string inverter would hide. The tiles show the last value per panel, grouped by roof (house roof panels 1–13, garage roof 14–37). The Envoy only updates per-panel data about once an hour, so the tiles state how old the last value is.

Tiles showing the current power of each of 37 Enphase panels, grouped by roof
Per-panel power, grouped by roof. At peak, the house roof reaches about 308–318 W per panel, the garage roof about 285–293 W.

The heatmap over time and the per-panel averages are where it gets interesting. The peaks are almost identical across a roof, but the averages are not: panels 14, 15, 20, 21 and 26 on the garage roof average well below their neighbours. Same peak, lower average means partial shading during part of the day, not a defective module.

Heatmap of power per panel over two days
Power per panel over two days. Shading shows up as panels that start late or drop early.
Bar gauges of average and peak power per panel
Average (left) vs. peak (right) per panel. The low averages on panels 14, 20, 26 and others point to shading, not to faulty modules.

The array is deliberately over-panelled: 455 W modules on IQ8 microinverters that deliver at most about 330 W AC each, so 37 × 330 W ≈ 12.21 kW is the hard ceiling. Plotting PV power against the brightness measured on the south façade makes the clipping visible: above roughly 80 klx the maximum (orange) flattens at the clipping line. Over-panelling costs a little at noon in summer and gains a lot in the morning, the evening and in winter, which is what a self-consumption system needs.

PV power, sun position and brightness; PV power binned by brightness with a clipping line at 12.21 kW; PV yield per day
Top: PV power with sun azimuth, elevation and brightness on three façades. Bottom left: PV power per brightness bin, with the 12.21 kW clipping line. Bottom right: daily yield.

3. Where does the PV go?

This was the hardest panel to get right, and the most instructive. The question is simple: of every kWh the roof produces, how much is used directly, how much goes into the Victron battery, how much into the Enphase battery, and how much is exported?

My first version used daily totals: direct use = PV − export − battery charging. It produced days with zero direct use on a 70 kWh day, which is physically impossible. The reason: daily totals cannot tell where energy came from. Two flows were counted twice:

  • Evening battery export. When the Victron sells battery energy into the evening price peak, that energy appears in the export counter – but it had already been counted as PV going into the battery at midday.
  • Battery-to-battery transfers. When the Enphase batteries discharge in the morning while the Victron is charging, the same kWh is counted as charging twice.

The fix is to do the bookkeeping minute by minute (in Flux, joining the Victron 1-minute data with the Enphase battery data): in each minute, export counts at most as much as the PV produced in that minute, battery charging counts at most what is left of PV after export, and the remainder is direct use. Battery export at night and transfers between batteries then drop out automatically. The four parts add up to the PV meter’s energy counter within about half a percent.

Stacked daily bars of PV split into direct use, Victron battery, Enphase battery and export, plus the shares over 30 days
Where the PV went over 30 days: 26 % direct use, 34 % Victron battery, 16 % Enphase battery, 24 % export.

A side effect of building this panel: comparing the old and the new numbers quantifies how much energy is moved around rather than produced. Over three weeks, about 50 kWh of the export counter was battery energy sold in the evening, and several dozen kWh went into the Victron from the Enphase batteries or the grid. Every battery-to-battery kWh passes through two conversions and loses 10–15 %. That is an argument for configuring the two systems so they do not feed each other.

4. Grid: peaks and the 3 kW line

My grid tariff has a reference power of 3 kW; drawing more costs a surcharge. The peak panel shows the highest grid import per day, with a dashed line at 3 kW and red bars above it. The histogram counts minutes per 250 W bin, split into green (up to 3 kW) and red (above): 51 of roughly 41,500 minutes in 30 days. Export shows up to the left of zero.

Bar chart of the daily maximum grid import with a dashed line at 3 kW
Highest grid import per day; red bars exceed the 3 kW reference power.
Histogram of grid power in 250 W bins, red above 3 kW
Minutes per grid-power bin over 30 days. Red: above 3 kW.

The grid operator assesses peaks as 15-minute averages, while these panels use 1-minute averages. A red bar is therefore a warning, not necessarily a surcharge: a three-minute spike may well average out below 3 kW over the quarter hour.

The Victron grid meter also measures voltage, frequency, neutral current and the voltage between neutral and protective earth. These are not needed for energy accounting, but they are cheap early-warning indicators: a slowly rising PEN voltage, for instance, can hint at a deteriorating neutral connection long before anything fails.

Voltage per phase and grid frequency over 30 days
Grid quality over 30 days: 226–234 V per phase, 49.97–50.03 Hz.

5. ESS control: what is the system doing, and why?

When the ESS is steered externally (grid setpoint, minimum SoC, peak shaving), it is easy to lose track of what is currently in effect. One row of tiles mirrors the relevant Ekrano settings, next to the spot price and the grid power. If the setpoint was left at an export value by a rule that crashed, it is visible at a glance.

Spot price versus grid power, 36-hour price forecast, and tiles with current ESS settings
Spot price and grid power, plus the current ESS state: control mode, setpoint, minimum SoC, active SoC limit, peak shaving, limits.

A state timeline shows the system state over the day (discharging, external control, etc.), the AC input source and the grid alarm. It is the fastest way to see whether an automation actually did what it was supposed to do at 18:00.

State timeline of the ESS system state, grid source and grid alarm over 24 hours
System state, grid source and grid alarm over 24 hours.

6. Batteries: throughput, cells, health

Daily charge and discharge energy and equivalent full cycles give a feel for how hard the battery is worked: here about 13–14 kWh per day and 12.6 full cycles in 30 days. The daily SoC range shows how deep the cycles go; in the darker second half of October, the Victron no longer reached 100 % every day.

Daily charge and discharge energy and full cycles per day
Charge/discharge per day (415.7 / 403.1 kWh in 30 days) and full cycles per day (32 kWh = 1 cycle).
Daily SoC range and daily maximum cell spread
Daily minimum/maximum SoC and the daily maximum cell spread.

The 24-hour view combines SoC (Victron and Enphase), pack voltage and the charge voltage limit (CVL) on one chart, with power, current, minimum/maximum cell voltage and cell spread below. With LiFePO4, voltage is almost flat between roughly 20 and 90 % SoC and rises steeply only at the top, so the cell spread is mainly meaningful near full charge – which is exactly where it peaks.

SoC and voltage over 24 hours, battery power, voltage and current, min/max cell voltage and cell spread
24-hour battery view: SoC of both systems, pack voltage vs. charge voltage limit, power, min/max cell voltage and cell spread.

For the EEL packs, the JK BMS data go one level deeper. The overview shows SoC, voltage, current, cell spread, alarms and MOSFET temperature per pack; the bars below show each cell’s deviation from the pack mean. ±5 mV at rest is a healthy, well-balanced pack.

JK BMS overview of two packs with cell deviation from the pack mean
JK BMS overview for both packs and the deviation of each cell from the pack mean.
All 16 cell voltages of both packs over six hours
All 16 cells per pack during a charge. The cells stay together until the top, where balancing has to do its job.

Two panels I would recommend to anyone with a JK BMS. First, the sense-wire resistance per cell: it is the same, symmetrical pattern in both packs (highest around cell 9), so it reflects wire length rather than a bad connection – but a single cell suddenly deviating from that pattern would point to a loosening terminal. Second, the pack data table with throughput, cycles, full capacity and state of health.

Sense-wire resistance per cell for both packs and a table of pack data
Sense-wire resistance per cell, and pack data (throughput, cycles, capacity, SoH).

7. Where does the base load go?

Finally, individual consumers measured with Shelly plugs. In my case the servers and the network equipment run at around 500 W around the clock, roughly 12–13 kWh a day. That is the single biggest lever on the night-time battery drain, and it is also why consumption figures that miss the server circuit are badly off.

Power per device from Shelly plugs and energy per device over seven days
Measured devices: servers, network (with and without UPS), server network and ventilation.

Lessons learned

  • Do not trust daily totals for energy flows. As soon as batteries export or charge each other, only a per-minute calculation tells you where the PV went.
  • Never sum percentages. A legend that showed 2013 % “total” self-sufficiency taught me that one. And an unweighted mean of daily ratios lies too: weight by kWh.
  • Self-consumption in percent rewards a smaller PV array. In kWh, it is the number that matters economically. Autarky in percent is the meaningful ratio.
  • Meters have noise. The PV meter showed 6.5 W at night: CT offset and the microinverters’ filter capacitors, not moonlight. A small dead band (below 15 W shows as 0) keeps the live view honest without touching the stored data.
  • Do not draw lines across gaps for regularly sampled data; show the gap. Values from systems that only report on change (openHAB) are step lines that hold their last value.
  • Two y-axes with the same unit mislead. If both are °C or both are W, use one axis.
  • Know your CTs. Before interpreting consumption or autarky, verify what each clamp actually measures. Mine still need work, and the post shows only what is coherent today.

Stack: Victron Venus OS on Ekrano GX → MQTT → Telegraf → InfluxDB 2; Enphase Envoy and JK BMS → openHAB / MQTT → InfluxDB 2; Grafana 13. All self-hosted.

Leave a Reply