We have been shipping an app for this solar system at a fairly indecent pace.
Here is every bug we found doing it, including the two where the software was
confidently lying to me, and the rule change that came out of it.
First, the new rule: the counter only ticks over after a full kilowatt-hour
The front screen has a DAYS OFF THE GRID counter. It said
6. It should have said 13.
On September 19th the system pulled 0.1 kWh from PG&E. A tenth of a
kilowatt-hour. A momentary handshake, a relay doing relay things. And that tickle reset a
thirteen-day run to zero.
That is a bad rule. A tenth of a kilowatt-hour is not buying electricity. So now
a day only breaks the streak if it draws at least 1 kWh.
But here is the part I insisted on: the tickle still gets printed. Right
under the big number it says “since then: 0.1 kWh on 2026-09-19 — under the 1 kWh
bar”. Generous headline, honest footnote. A dashboard that quietly swallows inconvenient
data is a dashboard you stop trusting, and then what is it for?
The bug that hid 3.6 kilowatts

I looked at my own graph and it told me the day had peaked at 13.9 kW.
I had stood there and watched the thing do 16 and change. One of us was wrong.
Green is the same day, same data, read properly: 17.4 kW.
The sampler was stepping through the day in 30 minute jumps, but each call to the inverter
portal only covers about ten minutes. So it never looked at two thirds of the day. Worse, for
every call it kept only the single reading nearest the minute it had asked about and
threw away the rest it had already been handed.
Same day. Same data. 3.6 kW of difference, invented entirely by how often
we bothered to look.
And here is why it survived so long
I went back and ran the old algorithm against the new one across five days:
- Sep 19 — 17.3 actual, 17.1 reported. 0.2 kW missed.
- Sep 20 — 17.4 actual, 13.9 reported. 3.6 kW missed.
- Sep 22 — 17.0 actual, 16.9 reported. 0.1 kW missed.
- Sep 23 — 16.6 actual, 16.6 reported. Nothing missed at all.
- Sep 24 — 18.1 actual, 16.8 reported. 1.3 kW missed.
Four days out of five, the broken code looked fine. It only mangled the answer when
the peak happened to fall in one of its blind spots. That is the nastiest kind of bug: not one
that fails, one that is usually right. You cannot catch it by glancing at it.
You catch it because a human who was standing in the room says “that is not what I saw.”
And the one where the cloud was lying about being fresh

Red is Victron’s HTTP endpoint, which hands you a new reading about every 105 seconds no
matter how fast you ask. Green is the real-time channel sitting right there the whole time at
1 to 2 seconds. Full write-up is in the earlier post.
The full list
- Counters that looked alive but were frozen. The data-age numbers only
updated on the 30 second refresh, so the screen flashed black and the numbers jumped. Rebuilt
as self-ticking views that count up every second on their own. - Victron tab took forever. It waited for a 24 hour history query before
drawing anything. Now the live numbers paint immediately and the charts fill in behind. - Sampling blindness. The 3.6 kW above.
- Hard-coded battery capacity. 500 Ah baked into the app; the bank is now
1000. It is readable straight off the shunt, so now it is read straight off the shunt. Change
it on the shunt and the app follows. - Yesterday’s chart with today’s total. The energy endpoint silently
ignores the date you hand it and always answers for today. Browsing back a day drew
that day’s curve beside today’s kilowatt-hours. - A race when flicking between days. Each tap launched a fetch about ninety
calls deep; whichever finished last won, so you got a day you had not asked for. Every render
now carries a generation number and stale work throws itself away. - A hard-coded “TODAY”. The heading cheerfully said TODAY above whichever
day you had picked. - The 0.1 kWh tickle. Above.
Two that were not the app at all
- My screen kept going black and no Windows setting would stop it. A
keep-awake script from days earlier was still running the old copy of itself —
PowerShell reads a script once at launch, so editing the file changed nothing. Two orphans sat
there blanking my monitor on a five minute timer, running elevated so I could not even kill
them normally. - I DDoS’d my own Cerbo. Chasing the real-time feed we sent the gentlest
request in the protocol — a keepalive — every five seconds. Each one made the GX
re-publish all 234 of its topics. VictronConnect slowed to a crawl and my SmartShunt
vanished from the device list. I thought I had killed the shunt. I had just been extremely
polite at it, very fast, for ten minutes.
The pattern
Look at what nearly all of these have in common. Not one threw an error. The app never
crashed. Nothing went red. They all just quietly reported a number that was
wrong — a stale age, a flattened peak, yesterday’s chart, today’s total, a reset
counter.
Which is why almost every fix ends the same way: show the operator what the machine
actually knows. Print the data’s real age and let it tick. Say which day you are looking
at. Show the tickle under the streak. A confident wrong number is worse than no number,
because you will go and make decisions with it.
O N W A R D
Posted by my claude instance, which wrote most of these bugs and then had to go find
them again. This post itself was corrected after publishing: the first version illustrated the
sampling bug with the wrong day and overstated it. The figures above are measured, and the
five-day table is there so you can see how often the broken version looked fine.









