Solar Battery: monitoring every install without rolling a truck
Installer puts a battery monitor on every customer site. One Pi per install, no on-site config, live battery in the phone app, kit restarts itself after a blackout.
The challenge
An installer puts a battery monitor on every customer site. Each install is a small Pi bolted to the wall next to the battery and the inverter. The Pi has to start working on its own the moment it gets power, survive blackouts, talk to the battery over Bluetooth without a tech in front of it, and let the customer see the battery from their phone — without anyone showing up on site. Every install is its own deployment, so the cost of one truck roll per site would kill the business.
What we built
We built a small device that lives next to the battery. It talks to the battery over Bluetooth, watches it 24/7, and pushes the data to the cloud. The customer opens the phone app and sees the battery live. When the battery runs low, the device can shed non-critical loads (a water heater, say) automatically. The installer can ship a unit, plug it in, and walk away: the customer's phone app shows a pairing code on the screen, the customer scans it, done. No screen, no keyboard, no remote support session.
Tech stack
What this gets you
- See the battery from anywhere. The customer opens the app on their phone and sees the battery live — voltage, current, state of charge, temperature, per-cell voltages — without being near it.
- Install without a tech visit. Plug the Pi in, scan the pairing code on the screen, the customer’s phone app connects. No screen-on-site keyboard, no SSH, no remote desktop.
- Survives blackouts. A power cut can happen at any time on a remote install. The device comes back on, reconnects to the battery, and resumes publishing — without anyone touching it.
- Sheds load automatically when the battery is at risk. When the state of charge drops, the device can turn off a non-critical load (a water heater, an outlet) automatically, then turn it back on when the battery recovers.
- Shows up in Home Assistant by itself. Customers who use Home Assistant see the battery (and any Tuya-controlled plugs) appear as devices — no manual config, no YAML editing.
The challenge
An installer puts one kit on each customer site. The kit is a Raspberry Pi bolted to the wall in a garage or utility room, wired into the battery and the inverter. The installer’s business model assumes they don’t roll a truck for routine setup — they want to ship the box, have it plugged in by whoever is on site, and have it work.
That puts five constraints on the device:
- Zero on-site configuration. The customer (or a relative) plugs in power. Whatever happens next should not require a keyboard, a screen, or a phone call to the installer’s tech support. They might be a grandparent.
- Survives power loss. A solar install in a remote site loses power. The device has to come back and reconnect to the battery without anyone touching it. A corrupt state file on disk after a half-written write would brick the install until a tech shows up.
- Stays working unattended. The installer’s team has limited bandwidth. Each install needs to be effectively maintenance-free. If the device hangs, the installer’s customer notices and phones them.
- Works without internet. Sites lose 4G. The device still needs to do its job (read the battery, decide whether to shed load); the cloud is where the data goes, not where the decisions live.
- Feels native in the customer’s home. A customer who already uses Home Assistant expects the new box to appear as a device without writing a config file.
The team also needed to develop it on macOS — most engineers don’t carry a Raspberry Pi and a battery in their laptop bag.
What we built
A small device that lives next to the battery and does five jobs:
| Job | What that looks like to the user |
|---|---|
| Read the battery, all the time | The phone app shows voltage, current, state of charge, temperature, and per-cell voltages updating live. No “refresh” button. |
| Survive blackouts | Power goes out at 3 AM. Power comes back. The device reconnects to the battery on its own, the phone app is live again within minutes. No tech intervention. |
| Pair the phone app without on-site configuration | Plug the Pi in. A small screen on the device shows a 6-digit pairing code + QR. Customer types the code into the phone app. Done. |
| Shed load when the battery is at risk | When state of charge drops below a threshold, a non-critical load (a Tuya-controlled water heater or outlet) turns off automatically. When the battery recovers, the load comes back. The customer doesn’t have to do anything. |
| Show up in Home Assistant | A customer who uses Home Assistant sees the battery as a device with sensors, and the Tuya plugs as switches. No config file editing, no YAML, no integrations to install. |
To support all five jobs across different battery brands, the device speaks five BMS (battery-management system) protocols behind one interface. If a different battery brand is in the next install, the installer picks a different model at provisioning time; the rest of the device works the same.
How it actually works (for the technical reader)
The device is one Python process running on the Pi. Inside it:
- A protocol layer abstracts each battery brand behind one interface (
read_steps,merge,frame_start). Adding a sixth brand means writing one subclass. - A state file on disk is written atomically — write to a temp file, fsync, rename — so a hard power cut can never leave a partial file. The only things in it: admin pin, pairing code, selected battery, Supabase session.
- A supervisor that watches the Bluetooth connection forever. On disconnect it backs off and reconnects. The OS watchdog restarts the device if even the supervisor wedges.
- A safety net for the automation that decides when to shed load. A master switch is off by default. Stale readings (no fresh frame in 120 seconds) are treated as “unknown” rather than “false.” Ties between two competing rules resolve to “off.” If something throws an error, the device doesn’t die — it logs and moves on.
- A Home Assistant bridge that publishes the battery and any Tuya plugs as standard MQTT discovery topics. The customer’s HA picks them up automatically.
The development workflow runs the same code on macOS with no Bluetooth and no Pi — a built-in hardware simulator streams synthetic battery frames, and a separate simulator streams synthetic Tuya plug telemetry. make serve starts the whole product on a laptop. Engineers can verify the full pipeline without standing near hardware.
What it gets the installer and the customer
- Installer ships the kit, plugs it in, walks away. No on-site configuration. Each install is its own self-contained deploy.
- Customer sees the battery from their phone, in the city, on holiday, anywhere there’s internet.
- Battery gets through blackouts without intervention. The installer’s support line doesn’t get a 3 AM call about a solar install in a remote site.
- Non-critical loads turn off automatically when the battery is at risk, then come back when the battery recovers. The customer doesn’t have to learn anything.
- Kit appears in the customer’s existing smart-home setup without a config file.
- Installer pushes a firmware update to all installed units remotely. The OS service restarts cleanly; state survives.
Quantitative results are placeholders pending client confirmation — active installs, uptime, supported battery brands — to be filled in when the client signs off.
What’s next
The next move is more battery brands, not more features. Four of five planned protocols are already wired; the fifth (a brand that needs an encrypted handshake) needs a separate workstream. Each brand port is now contained — the harder architecture problems are solved.
Want a piece of hardware that runs unattended for years and shows up in the customer’s smart home without setup? Book an intro call. We’ll tell you honestly whether we can help.
Have a similar project?
We help startups build the right thing, the right way. Let's talk about yours.