Reference design · Automation

NEM Battery Arbitrage With Hard Limits

  • AEMO NEMweb
  • Amber API
  • Open-Meteo
  • Python
  • Home Assistant
  • Sigenergy Modbus
  • Claude Opus

The problem

A battery on the five-minute wholesale market earns from timing, and the obvious way to automate timing is to let a model decide. A model that picks the charge window will sometimes invent a number, and the battery, its warranty and the site's bill pay for it.

What I would build

An agent reads AEMO dispatch and five-minute predispatch prices from NEMweb, the retailer's price feed where the customer has one, a weather-derived solar forecast and the battery's state of charge, and a plain optimiser builds a rolling charge and discharge schedule; the model writes only the explanation. Writes reach the inverter through Home Assistant on the Sigenergy local Modbus integration, behind limits no model output can move: a state-of-charge floor and ceiling, a write rate limit, a kill switch checked before anything else, dry-run by default and an audit line for every attempt. To the published project the design adds a cell-temperature and state-of-health guard, warranty-cycle accounting and a staged go-live, and the owner or installer approves each step out of dry-run.

The logic flow

Click any node for what it does, what it hands on, and where a person is required.

Loading diagram...

What it returns

This is a reference design on a hackathon project that runs one house and has made no live writes, so nothing here is measured on a fleet. The author's seven-day backtest used perfect foresight and matched plain self-consumption to the cent, because export prices sat at or below zero that week. What the design offers each site is a record: every write, every refused trade and every warranty cycle spent.

Why it fits

An installer used to sell a commercial site owner the knowledge of when a battery should charge and when it should export: the tariff, the retailer's 'smart' mode, the setting buried three screens deep in the inverter app. The owner's own assistant can now open the AEMO price file and explain the morning's spread in a paragraph. Bolting a language model onto the old routine, and letting it choose the charge window, hands a battery the owner paid for in full to a component that will sometimes invent a number. The installer's name is on the warranty claim when it does.

So this design keeps the model away from the schedule. A plain optimiser ranks charge and discharge intervals from the NEMweb dispatch and five-minute predispatch prices, the retailer's feed where the customer has one, a weather-derived solar forecast and the measured state of charge; the model writes the two sentences that say why. Every write reaches the inverter through a local Home Assistant controller, behind limits that no output can move: a floor of 10% and a ceiling of 95%, ten writes an hour, a kill switch, dry-run by default and an audit line for each attempt. To the published project I would add a guard on cell temperature and state of health, a count of cycles against the warranty, and a staged go-live in which the owner or the installer, never the agent, approves each step out of dry-run.

A five-year strategy built on this, offered as a sketch rather than a plan, would sell the limits and not the arbitrage. Any customer can now find the spread; the harder thing to show, site by site, is every write their battery received, every trade it declined because the export price sat below zero, and how many warranty cycles each dollar cost. An installer that keeps that record for a fleet, and lets the customer's own tools read it, becomes the one a cautious assistant recommends. The price file is public. The audit log is not.

See it run

Want one of these?

Every part of it is open source and self-hostable. What I bring is the build, the checks around the project's gaps, and the judgement about where your gates should sit.