~/blog/home-energy-2/reconciling-power-meters-home-assistant.md

Three power meters in Home Assistant and only one gets to be the total

// tl;dr

My three brands of power meter in Home Assistant never quite agreed. I made the solar gateway at the mains, plus the battery's output, the only total, used every clamp for breakdown, guarded the subtraction and averaged before comparing. The leftover gap is an error bar, not a hidden load.

A level balance scale with a green site meter on one pan and panel-a, panel-b and a dashed gap box marked plus or minus 150 W on the other, captioned site equals breakdown plus gap

I've got three different brands of power meter feeding Home Assistant. An Enphase Envoy on the solar and grid side, Refoss clamp monitors in two breaker panels, and a Sense monitor. For weeks they disagreed with each other, and every time I built a number on top of them, one of them found a way to make it wrong.

The fix was deciding which one gets to say how much the whole property uses. The rest are breakdown.

Why one total and not three

What I had:

  • The Envoy sees the grid connection, the solar, and the battery. It's the only thing that covers the whole property.
  • One Refoss unit has 18 channels on the critical-load panel, including both mains of that panel.
  • A second Refoss has 6 channels on the main panel. It only covers a few big loads: the AC condenser, the dryer and two more circuits.
  • The Sense started out on a detached sub-feed and later moved to the house mains, with one extra clamp kept on that sub-feed.
  • The pool pump reports its own power through a cloud integration.

Every one of these is accurate about what it clamps. The trouble starts when you add or subtract them. Each one samples at its own pace and drops out on its own schedule.

So I gave the job of "total" to one meter. The Envoy sits at the point where power enters the property, so nothing can hide from it. Everything else gets subtracted from it, and whatever is left over is a sensor I call unaccounted.

The site meter needed fixing first

Picking the Envoy as the total meant I had to trust it. At first I couldn't.

There's a free test for this. The critical panel is a strict subset of the property, so if the site total ever reads lower than that one panel, the site meter is wrong. No modelling needed. Over three days that happened for 6 minutes, and the worst minute had the site at minus 1,408 W while the panel alone was drawing 1,498 W. Over a longer stretch it was 4 hours out of 166.

The bigger problem showed up once the home battery started cycling. The Envoy's consumption figure matched grid plus solar to within a watt or so on average, through battery swings of several kilowatts in both directions. Two real current clamps can't agree that well. In my setup the consumption number is derived, not measured, so it misses whatever the battery is supplying. When the battery discharges, real house load is consumption plus the battery's output.

That "derived" part is my inference from the numbers. I haven't seen it in Enphase's docs.

So my "true site load" is the Envoy's consumption plus the battery's signed power. I checked that hour by hour over 43 hours against the energy counters (solar plus import, minus export, plus what the battery put out, minus what it took in), and it came out within about 1%. Before that fix, every evening reading after the battery kicked in was low.

On a good week the meters do line up. Over one week in late July, before the battery started cycling, the house breakdown summed to 105.8 kWh a day against 106.0 kWh from the site meter minus the sub-feed. Four instruments, 0.2% apart.

Subtraction needs a guard

The unaccounted sensor is just the site total minus every clamped circuit. That's a template sensor, and the first version had the standard trap in it: every input was converted with a default of zero.

When the Envoy dropped offline one evening, its value became 0 instead of unknown. The unaccounted sensor then recorded a steady minus 5,517 W for 46 minutes. It looked like a real reading. There was no meter behind it at all.

The fix is an availability template that refuses to produce a number unless every input has one. I built mine as template helpers in the UI. In YAML it looks about like this, with placeholder names:

template:
  - sensor:
      - name: Unaccounted power
        unit_of_measurement: W
        device_class: power
        state_class: measurement
        availability: >
          {{ has_value('sensor.site_load_w')
             and has_value('sensor.panel_a_mains_w')
             and has_value('sensor.panel_b_circuits_w') }}
        state: >
          {{ states('sensor.site_load_w') | float
             - states('sensor.panel_a_mains_w') | float
             - states('sensor.panel_b_circuits_w') | float }}

Now the sensor shows a gap when a meter drops out, which is the honest answer. The flaky cloud integration on the pool pump means I see those gaps fairly often. My current version guards 14 inputs and leaves the pool unguarded on purpose, because a closed pool reporting nothing should count as zero, not blank the whole chain.

The same trap sits in any template that does math across meters. Mine had a second one in the panel total that adds the two mains together. It hadn't bitten yet, but only because that unit had never dropped out.

Meters don't tick together

This took me longest to see. I measured how often each meter actually reports, over about 69 hours:

Meter Median gap between reports Longest gap
Refoss clamps 16 s 219 s
Envoy 64 s 572 s
Sense 120 s 260 s
Pool pump about 45 min about 61 min

The pool pump is variable speed and reports about every 45 minutes. So any instant subtraction was using a pool figure that was on average 22 minutes old, and wrong by hundreds of watts whenever the pump ramped. That gave me runs of negative unaccounted power, around minus 537 W for up to 10 minutes at a time. No per-minute filter was going to fix that.

Three rules came out of this.

Work in hourly averages for levels. The pool pump holds its speed for much longer than its reporting gap, so the hour washes the staleness out. Per-minute data is still useful for spotting a load by its on and off shape. Just don't read amounts off it.

Time-weight everything. Home Assistant's history stores a row each time a state changes, not on a fixed clock, so a plain average over those rows over-counts busy periods. A naive average once told me a circuit ran 54% of the time. The real figure was 0.2%.

Average before you floor. Later, when I estimated the sub-feed as whatever was left after every clamp, I clipped negatives to zero. The dryer's heating element cycles every minute or two, the Refoss sees each switch within seconds, and the Envoy catches up a minute later. Every cycle swung the estimate by about 5 kW down and then 6 kW up. Clipping kept only the upward half. Replayed over nine and a half days and 71 dryer runs, that over-counted by about 21 kWh, roughly 2 kWh a day. Taking a 10-minute time-weighted mean first and clipping after cut the error over the same stretch to 0.4 kWh.

The same thing happens between the Envoy and the Sense now that both see the house mains. Single readings disagree by up to 1.5 kW when the house swings from importing to exporting, because each polls about once a minute and not in step. On a 10-minute average they agree within a couple hundred watts. I only chart the averaged gap.

When a gap is worth chasing

The leftover gap tempted me more than once. One night the unaccounted sensor sat around 180 W after midnight and I decided there was a standing load I hadn't found.

There wasn't. I recomputed the same window for three nights in a row and corrected each for the clamps that existed on that night. They came out at about plus 181 W, plus 24 W and minus 126 W. A sum of real loads can't be negative, so at least 126 W of that is the meters disagreeing. The average across the three was about 26 W, with roughly 150 W of scatter either side.

That scatter is the error band. If the night-to-night spread brackets zero, there's nothing to find. Now I treat anything under about 250 W of unaccounted as metering noise, and the alert that watches for a new unexplained load trips at 500 W.

The other check I lean on is the Sense against the Envoy. With both on the mains, a 10-minute sample on the first day had them 13 W apart at the grid connection. The Sense's "solar" pair read far below the Envoy's solar. Then I worked out its clamps sit on the Enphase feed, so it sees solar plus the battery, not solar alone. Compared against that, the two agreed within about 22 W on a 20-minute mean.

What I'd do first next time

  • Pick the meter at the service entrance as the only total, and make sure it includes the battery.
  • Find a subset check that's free, like a panel that can never exceed the site, and run it before trusting anything.
  • Guard every template that does math across meters. Unknown should stay unknown.
  • Average over time before you subtract, clip or alert, and time-weight the average.
  • Measure your own error band before hunting a phantom load.

The meters still don't agree minute to minute, and they won't. But I know which one is the total, and I know how big the disagreement is.

home assistant: 2026.7.4 (meter build-out), 2026.10.1 (current)
home assistant docs checked: 2026.11.0
solar gateway: Enphase Envoy with battery, local integration
clamp monitors: Refoss 18-channel and 6-channel, local integration
whole-house monitor: Sense, cloud integration

$ ./ask --help

Stuck on something similar? Ask below and I'll try to help you work through it. It helps to include:

  • --running hardware, OS, versions
  • --tried what you've already tried
  • --error the exact message, word for word

Comments are public, so leave out passwords, keys and public IP addresses.