Solar and battery control that follows the tariff, not the vendor's app

Homes with solar, batteries, heat pumps and smart tariffs were running on the manufacturers' fixed schedules. We moved control onto Home Assistant, locally, and taught it to follow price bands and the seasons.

Locality
East Midlands, UK
Disciplines
Energy and automation, Systems integration
Stack
  • Home Assistant
  • Modbus
  • Proxmox
  • Matter / Thread
  • Cloudflare Tunnel

At a glance

  • 3

    residential installs

  • 127

    days reconciled against the utility meter

  • 14

    months of meter history backfilled on one site

  • 21

    Matter devices carried across a host rebuild

The challenge

Vendor apps give you a fixed charge window and not much else. Smart tariffs don't work like that: the price changes by band, dispatch windows appear at short notice, and what makes sense in January is wrong by June.

There was a trust problem as well. If the energy dashboard doesn't agree with the meter, nobody believes the automation that's driving off it.

Architecture

Energy automation architectureArchitecture diagram with 8 components. Smart tariff to Home Assistant (dispatch, off-peak). Utility meter data to Home Assistant (reconciliation). Season + master switch to Home Assistant. Home Assistant to Decision sensors. Home Assistant to Inverter + battery (mode + reserve). Home Assistant to Heat pump + hot water. Home Assistant to EV charger.dispatch, off-peakreconciliationmode + reserveSmart tariffDispatch + price bandsUtility meter dataAuthoritativeSeason + master switchHome AssistantLocal controlDecision sensorsWhy it's doing thatInverter + batteryModbusHeat pump + hot waterEV charger

Swipe to explore the diagram

Tariff signals and the season drive the decisions, the utility's own data keeps the sensors honest, and the reasoning is always visible.

What we built

Control

  • Battery control over Modbus with three seasons, state-of-charge latches, dispatch rules and a reserve clamp
  • A master switch and visible decision sensors, so anyone can see why the battery is doing what it's doing
  • Winter arbitrage between export and the night rate, and a hot-water catch-up that ran as a dry run first
  • A glitch filter that strips state-of-charge spikes before they can drive a decision

Data you can trust

  • Meter-grade sensors reconciled against the utility's data, within 0.45% over 127 days on one install
  • History backfilled so each dashboard is one continuous series, including 14 months of import and gas history on one site
  • Phantom solar generation found and removed from the history

Running it

  • Three hardware shapes: virtualised on Proxmox with USB radio passthrough, a dedicated appliance for customer sites, and a reference install we pattern from
  • Remote access through a Cloudflare tunnel, with LAN SSH opened per session only
  • Fixes contributed back upstream to the open-source integrations we depend on

Engineering decisions

Local control beats cloud schedules

Home Assistant owns the inverter and battery schedule directly, and the vendor's time-of-use profiles were retired.

Write down what never gets touched

Certain inverter modes, the export limit and the rate plan are off-limits to automation. Some settings belong to the installer and the network operator.

A shadow day before every cutover

A new controller runs read-only against live data for a day before it takes over, with a numbered checklist and a rollback copy kept for a week. Two writers on one inverter is never allowed.

The box is the source of truth

Config syncs from the live system into git, never the other way, so a stale file in a repo can't overwrite something that works.

Outcome

Batteries charge and discharge on season and tariff under local control across three installs, and the dashboards agree with the utility. One install moved from bare metal to a VM and then to a second host without missing a nightly battery switch.