Charging on solar surplus,
at the hour the grid is congested
A Fronius Symo GEN24 Plus, a Tesla Wall Connector Gen 3, a Model 3 and a Raspberry Pi 4, tied together with evcc so the car soaks up the array's midday peak on site — and holds the voltage at the feed-in point below the level at which the installation starts protecting itself.
- Array
- 7 kWthree-phase
- Inverter
- 6.0 kW ACSymo GEN24 Plus
- Controller
- Raspberry Pi 44–7 W, wired
- Control path
- evcc + BLEno internet needed
- History
- InfluxDB 2.9Grafana 13.1
The electrical installation — and the reason everything above it works.
Thirty years in business, seventy-plus specialists, ANRE-authorised (ANRE is Romania's national energy regulator; only electricians it licenses may work on a grid connection), a Fronius partner, and more than a thousand completed installations — and it shows in the part nobody photographs. Software can only ever be as good as the installation underneath it.
If you are from Arad or Timiș and you are thinking about photovoltaics or an EV charger — electrica-arad.ro. Ask for the full job. They are worth it.
The engine. Every control decision in this build — when to start, how many amps to pull, when a cloud has lasted long enough to stop — is evcc's. It speaks Modbus to the Fronius, BLE to the car and HTTP to the charger, it runs entirely on the local network with nothing off-site, and it supports hundreds of devices because a genuinely active community keeps adding them. It is the rare open-source project that is not merely published but alive.
→ Sponsor evcc
build, tuning & the calls that mattered
Anthropic · claude-opus-5, claude-fable-5
Section 01Dashboard in action
The result first. Three screenshots of the live board, taken on 27 August 2026 — the Pi’s health at the top, power in the middle, charging at the bottom. The rest of the page explains how to build it.
Section 02The reasoning
On a clear day the whole street exports at once. Voltage at the feed-in point climbs, and above a certain level the installation starts defending itself — by throwing away production, at precisely the hour the array makes the most of it.
Why any of this exists
One household’s reasons, in the order they actually arrived. Everything after this section is the argument they led to, and everything after that is the build.
I had the panels first. Three years ago I chose Fronius over Huawei and the other Chinese brands deliberately, and it was not a price decision: I was not willing to hand the controls of my own generation to a state that behaves like an adversary rather than a partner. An Austrian company, European engineering, inside the European Union. It cost more. I would choose it again.
Then I had the overvoltage problem — arriving at my own feed-in point rather than in a regulator’s consultation document.
And I have a hunch that 2026 slowly turns into another 1979. So I did the arithmetic instead of worrying about it. A new Tesla Model 3, charged on as much of my own surplus as I can get into it, costs less to run over its life than a ten-year-old 2.0-litre diesel — not because the car is cheap, but because the fuel is already on the roof and it is already paid for.
So I bought the car, installed evcc, and everything else followed from that one step. Configuring evcc made it plain that I could not make an informed decision about any of it without knowing the numbers, so the InfluxDB integration went in next. Then the Grafana integration, and the dashboard began. And once there were software packages running on a Pi in my own house, I had to have monitoring, backup, restore and update mechanisms.
That chain — panels, overvoltage, a feeling, a car, then everything the car dragged in behind it — is the entire project.
The prosumer build-out
Romania asked households to put solar on their roofs, and in under three years a third of a million of them did. The wires underneath were not rebuilt to carry it.
The state's invitation was explicit and it worked. Registered prosumers — households and small businesses that both draw from the grid and export to it — went from roughly a hundred thousand at the end of 2023 to 332,684 at the end of April 2026, and the rate has not slowed: more than 10,600 new connections in that single month, the fastest-growing prosumer population in Europe. Almost a third of them now have batteries as well.
| Date | Prosumers | Capacity | Basis |
|---|---|---|---|
| December 2023 | ~100,000 | 1,400 MW | approximate |
| October 2024 | ~160,000 | 2,000 MW | approximate |
| December 2025 | 294,000 | 3,400 MW | register |
| April 2026 | 332,684 | 3,789 MW | register, 30 April |
3,789 MW is the equivalent of more than five Cernavodă reactors, and it was assembled without a single tender, by families buying panels a roof at a time.
The subsidy, and its withdrawal
Casa Verde Fotovoltaice was the state's own programme, and it is the reason a hundred thousand of these roofs exist.
Run by the Environment Fund Administration since 2019, the scheme offered households a non-refundable grant of up to 20,000 lei — roughly €4,000 — toward a complete rooftop system. With the grant, an installation paid for itself in under two years rather than five or six. That is the whole mechanism, and on paper it is a good one.
The execution was less good. In its early years the AFM held installations up for two full years and definitively rejected almost 9,000 applicants, most of them for reasons that were the agency's own. Later sessions ran on a fixed budget, a limited number of places and a window that closed within hours, on a platform that fell over while it was open. Applicants kept coming anyway. Something over 100,000 households got their panels through the scheme; several times that number stopped waiting and paid in full themselves.
Then the programme was dropped from the AFM's 2026 budget, its €286 million redirected, with roughly €76 million left behind for battery storage. The invitation was withdrawn. The installed base it created did not go anywhere.
Overvoltage, and who is asked to absorb it
The regulator's own account of the problem points at the network. The remedy on the table points at the rooftops.
When a street exports at once, voltage at each feed-in point rises, and above a threshold every inverter on it begins to curtail and then disconnect. ANRE does not dispute where that comes from: the overvoltage that shuts inverters down is attributed to ageing and undersized networks, badly set transformers, and a grid that was never adapted to two-way flow. The consequence, however, lands on the prosumer, who is disconnected — in some places several times a day — and warned against touching the inverter's settings.
The remedy most often proposed is not a better and smarter grid. It is a legal right for distributors to reach into privately owned inverters and switch them off remotely, as a grid-balancing tool, alongside the idea of capping household connections at 5 kW. Equipment bought, installed and paid for by the household, with the off-switch held elsewhere.
253 V is the number the second half of this page is built around. It is the level at which the inverter curtails and stops, and every design decision exists to keep the feed-in point below it without anyone being disconnected. The argument and the build are about the same four volts.
The Spanish blackout
When the Iberian peninsula went dark, the first explanation offered nearly everywhere was solar. The investigations found otherwise: renewables played no role in causing the blackout. The failures identified were a voltage miscalculation by the grid operator and conventional plants that did not perform the voltage-control duty they were there to perform. Solar generation was part of how supply was restored.
Blame the panels, investigate, find the fault in the wires.
The Dutch response
The Netherlands reached this wall first, with a worse version of the same problem, and answered it by expanding the network rather than by reaching into anyone's equipment.
A third of Dutch homes have panels — the European record — on a grid designed around a handful of large gas plants. The response was a National Grid Congestion Action Plan: change the law so that grid-expansion permits are granted faster, secure land for network extensions in advance, and declare those extensions of important social interest so that licensing takes a year and a half less. Behind it sits network investment heading toward €8 bn a year.
The principle is the part worth importing. The network was adapted to what the customers had already installed, rather than the other way round.
Truth be told
One concession first, because it is a real one. Under the settlement rules as they stand, some of the cost of all this genuinely does land on neighbours who have no panels. That is a fair objection and it deserves a fair answer rather than a dismissal. Nobody should be asked to subsidise someone else's electricity.
What we truly need is a Romanian equivalent of the Dutch action plan: a published programme of network reinforcement with dates attached and consequences for distributors that miss them. That does not require anyone's inverter to be switched off from a control room.
ANRE figures via Economica.net and InvesTenergy; Greenpeace Romania on the AFM programme; Digi24 on the 2026 budget; Economedia and National.ro on disconnections and inverter settings; Energy Magazine on the 5 kW proposal; BBC News on the Iberian blackout; S&P Global and AOL/Reuters on the Dutch congestion plan; Puterea on Law 160/2026.
What follows is one house's answer to the same problem, arrived at from the other end. It does not wait for the network to be rebuilt and it does not ask a neighbour to pay for anything: it moves the largest load in the house to the hour the surplus exists, and holds the voltage at the feed-in point below the level at which the installation starts protecting itself.
| Device | Threshold or rating | Consequence |
|---|---|---|
| Fronius Symo GEN24 Plus inverter over-voltage response | 253 V | The inverter curtails and stops. The threshold is a technician-level setting — the customer role cannot see or change it. |
| DigiTOP V-PRO-3F63A three-phase voltage relay in the board | 257 V over-voltage cut-off | Physically disconnects the house to protect the equipment behind it. The only device in the installation that can open the circuit, and the only one of the three whose threshold is set on the device itself. |
| ETITEC V T2 surge arresters on the incoming busbars, reference only | stamped 255/20 | Does not act at these levels: 255 V is Uc, the highest voltage it can sit at continuously, and 20 kA its nominal discharge current — neither is a trip point. |
Before this build, the feed-in point regularly passed 253 V around 13:00. Every one of those excursions was production lost for good. The DigiTOP was installed together with the Wall Connector and was first set at 253 V, then at 255 V. The relay opened at least ten times a day and took the house with it. 257 V is the current setting, because DigiTOP is too sensitive.
The lever
Voltage at the feed-in point rises with exported power. Anything consumed on site never reaches the street, so local consumption at midday pulls the voltage back down. A battery would be the other way to absorb it and this is the next step, hopefully financed by Casa Verde 2026. The car is on the driveway through the working day, and needs to be charged anyway. Why not for free?
Absorb the surplus exactly when the grid is congested. The car now takes the whole surplus and a little from the grid on top of it, and the feed-in point stays below 250 V instead of crossing 253 V. Nothing trips, and nothing is curtailed. This is not tuned for the cheapest kilowatt-hour — it is tuned for the highest local consumption during the hours the grid is weakest.
What the site has done so far
These are lifetime registers, read from the two Fronius meters.
| Register | Source | Value |
|---|---|---|
| Lifetime PV yield | Solar meter — the inverter itself | 16,264.9 kWh |
| Lifetime export | Grid meter — energy (reverse) | 12,924.3 kWh |
| Lifetime import | Grid meter — energy | 3,396.5 kWh |
| Export ÷ import | derived | 3.81× |
Subtracting export from yield puts self-consumption at roughly 3,340 kWh — about 20 % of everything the array has produced.
Section 03Architecture
Four devices, one local network, nothing external in the command path.
In short: the inverter measures, the Pi decides, the car obeys. The charger just delivers power and reports what it sees; the car is what actually adjusts how much it draws, one amp at a time, over Bluetooth. Everything runs inside the house — no cloud account is in the loop, and the internet can be down while the car charges.
Why the fine control happens on the car
A Gen 3 Wall Connector cannot be told what current to deliver. It reports — power, per-phase current and voltage, contactor state — but the interface is read-only; the only current it modulates by itself is the share it negotiates inside a power-sharing group. All of the modulation has to be commanded on the vehicle, which is why the Bluetooth bridge is the centrepiece of the build rather than a convenience.
Commanding the car also gets underneath the 6 A floor IEC 61851 puts on the pilot signal. At three phases and roughly 720 W per amp, 6 A means 4.3 kW of surplus before charging could begin at all; the car accepts 1 A, which drops the threshold to about 720 W. This belongs to vehicle control in general rather than to Bluetooth: the fleet API reaches the same currents.
Bluetooth or the fleet API
Both routes command the car. They differ in what they cost, what they depend on, and who else is in the path.
| Bluetooth LE this build | Fleet API | |
|---|---|---|
| Runs on | The Pi, over BLE, 5–15 m from the car | Tesla's servers, over the internet |
| Needs | A paired key on the Pi | Developer account, app approval, OAuth client, access and refresh tokens |
| Costs | Nothing | $10 monthly credit, then metered |
| Works when the car is away | No | Yes |
| Third party in the path | None | Tesla, plus a command proxy |
The car is on the driveway, the Pi is a few metres away, and the one thing the fleet API offers — control from anywhere — is worth nothing here. Against that it wants an account, tokens, a public command proxy, an internet connection in the command path, and a meter that ticks every time the controller changes its mind. The controller changes its mind all day.
There is a second cost worth naming. A Tesla Wall Connector cannot control itself, so an internet-based setup needs a public command proxy to sign commands on the way through — another moving part, and a third party holding credentials to the car. The Bluetooth bridge is that proxy, running on the Pi, reachable by nobody outside the house.
| Component | Responsibility |
|---|---|
| Fronius Symo GEN24 Plus | Produces. Also the solar meter — evcc reads the inverter's own SunSpec unit for PV power. |
| Fronius Smart Meter | Measures the grid exchange at the feed-in point. SunSpec unit 200, read through the inverter. This is the number Solar mode steers on. |
| Tesla Wall Connector Gen 3 | Switches the contactor and meters the charge. Serves as both the charger and the loadpoint's energy meter. |
| Raspberry Pi 4 | Runs everything: evcc, the BLE proxy, InfluxDB, Grafana and the custom scripts. |
| Tesla Model 3 | Accepts the commanded current. The only device in the chain that can go below 6 A. |
Section 04The hardware
Three small purchases carry the whole control stack: a Raspberry Pi, a memory card for its system, and an ordinary USB disk for the measurement history. Everything else in this section is advice on where to put them.
The full list
- A Raspberry Pi 4 B, its original USB-C power supply, and a microSD card of 16 GB or more
- A USB disk for the history — any size, and an old spinning drive is fine.
- A laptop with Raspberry Pi Imager installed, and WinSCP or any SCP client.
- A Fronius GEN24 inverter on the same network, with a Smart Meter at the feed-in point read through it
- A Tesla Wall Connector Gen 3 and the QR-code card that shipped with it
- The car's NFC key card — the physical card, not the phone key
- The inverter's Customer password, or owner access to Fronius Solar.web to reset it
- A router with 2.4 GHz Wi-Fi and access to its admin page for DHCP reservations
- An Ethernet cable from the Pi to the router — the Pi runs wired
- A Wi-Fi range extender
The Pi has to sit within Bluetooth range of the car — realistically 5–15 m, through no more than one wall.
A microSD card is a device with a finite number of writes. Good for OS, but not for a database.
So InfluxDB's two volumes go on a USB disk. An old 2.5” or 3.5” drive you already own is the right answer here — the database is a few gigabytes a year and it is written in a steady trickle, so neither capacity nor speed is the constraint.
Everything else stays on the card: Docker images, Grafana's own volume, evcc's database.
Put it in a black port, not a blue one. SuperSpeed USB radiates broadband noise straight across 2.4 GHz — Intel documented it in 2012 and every USB 3 enclosure since has done it to some degree. On most machines that is just a curiosity. The USB 2 ports give 480 Mbit/s, which is hundreds of times more than needed, so the trade costs you nothing at all. Since Pi is close to the router, because it is wired, so the Bluetooth can own the Pi's antenna, avoiding 2.4 GHz noise becomes a necessity.
Measured, on the disk this build uses. A 500 GB USB drive in a Gembird 2.5” USB 3.0 enclosure — the kind that has been in a drawer for a decade. At the rate the two loggers write, capacity is not the number that will ever run out on this disk.
Section 05The inverter
Two interfaces to switch on: Modbus for evcc, the Solar API for the voltage readings that feed the dashboard. Both are a web form in the inverter, reached from a browser on the home network, so this is done before the Pi is built rather than after — and the installer, when it runs, finds them already answering.
The inverter has two switches to flip in its own web page — one lets the Pi read the meters, the other exposes the voltages.
If you do not have the Customer password
No installer is needed. The registered system owner in Solar.web can reset it:
- Tap the optical sensor on the front of the inverter once — the right-hand LED flashes blue and the WLAN access point opens
- Open the inverter's web interface in a browser, from the home network, at its usual address
- Click Forgot password? for the Customer role — a PIN is generated
- Go to Fronius Solar.web → Settings → Components → Actions on the inverter → Reset Customer Password
- Enter the PIN, take the recovery key, set the new password
The function only appears to the account registered as owner of the system in Solar.web. The Technician password — the role that holds the 253 V over-voltage setting — can be reset solely through the installer or Fronius support.
Modbus TCP — the data source for evcc
In the inverter's interface: Communication → Modbus.
| Setting | Value |
|---|---|
| Slave as Modbus TCP | enabled |
| Port | 502 |
| SunSpec model type | int + SF |
| Meter address | 200 |
Save. The inverter may briefly report a stopped state — it is restarting its communication side and comes back on its own within a minute or two.
The inverter is always unit 1; the Smart Meter is unit 200. Both are reached at the same IP and port. That distinction is what lets one device serve as both the solar meter and the grid meter — and getting it right is the difference between a measured PV figure and a derived one.
Solar API — the voltage source for the logger
Communication → Solar API → enabled. It coexists with Modbus without issue.
Section 06The Wall Connector
All it has to do is join the network; everything else is read locally.
The charger only needs to join the IoT 2.4GHz Wi-Fi, from the Tesla app, standing next to it. After that it is never configured again — it just reports what it sees.
From the Tesla app: profile → Add product → Wall Connector, then scan the QR code from the quickstart card or the label on the side of the unit. The app connects over Bluetooth, so stand near the charger, then join it to the Wi-Fi. Once connected, find it in the router's client list and reserve whatever address it picked up. The installer never asks for it and never records it — the Wall Connector belongs to evcc, and you enter this address once in evcc's browser configuration. Reserve it first so that what you type there stays true.
http://CHARGER_IP/api/1/vitals
"contactor_closed":false,"vehicle_connected":false — exactly right with nothing plugged in.The whole response is worth reading once, because it is the only view of the charger you get without a car attached, and three of its numbers are easy to misread later.
{"contactor_closed":false,"vehicle_connected":false,"session_s":0,
"grid_v":225.2,"grid_hz":49.936,"vehicle_current_a":0.3,
"currentA_a":0.3,"currentB_a":0.3,"currentC_a":0.3,"currentN_a":0.5,
"voltageA_v":0.0,"voltageB_v":2.8,"voltageC_v":3.0,"relay_coil_v":12.0,
"pcba_temp_c":37.0,"handle_temp_c":44.4,"mcu_temp_c":46.8,
"uptime_s":587301,"input_thermopile_uv":-329,"prox_v":0.0,
"pilot_high_v":12.0,"pilot_low_v":12.0,"session_energy_wh":702.500,
"config_status":5,"evse_state":1,"current_alerts":[]}
Section 07The distribution board
An outdoor enclosure on the exterior wall, directly above the Wall Connector. Four rows, with the instruments in the bottom compartment.
The electrician's cabinet, photographed here so you know what is what.
| Label | Meaning | Device |
|---|---|---|
| REȚEA | Grid, incoming | Busbars L1/L2/L3/N; ETITEC V T2 surge arresters, stamped 255/20 |
| CASĂ | House | Noark, 4-pole; three single-pole Legrand breakers in the top row |
| INVERTOR | Inverter | ETI, 4-pole |
| AUTO | Car / Wall Connector | Noark Ex9B C32, 415 V~ |
| GENERAL | Main | Noark Ex9B C40, 415 V~ |
| — | Instruments | Fronius Smart Meter (SunSpec unit 200) · DigiTOP V-PRO-3F63A True RMS |
Section 08Network and addressing
Everything lives on one home network. Three devices get fixed addresses on the router, the Pi runs on a cable, and the two outdoor devices stay on 2.4 GHz Wi-Fi.
| Element | Address | Port | Role |
|---|---|---|---|
| Fronius Symo GEN24 | INVERTER_IP | 502 · 80 | Modbus TCP (SunSpec) and Solar API |
| Fronius Smart Meter | via the inverter | SunSpec unit 200 | Grid meter at the feed-in point |
| Tesla Wall Connector | CHARGER_IP | 80 | /api/1/vitals |
| Raspberry Pi | PI_IP | — | Host for everything below |
| evcc UI & API | PI_IP | 7070 | Control, configuration and statistics |
| Tesla BLE proxy | PI_IP | 8080 | Key generation, pairing, commands to the car |
| InfluxDB | PI_IP | 8086 | Time-series store, org home, buckets evcc and system |
| Grafana | PI_IP | 3000 | Dashboards |
| Router admin | ROUTER_IP | 8443 | Network settings |
The Pi runs wired, with its Wi-Fi radio off
On a Raspberry Pi 4, Wi-Fi and Bluetooth share one antenna. Network traffic on the Wi-Fi radio destabilises the Bluetooth link to the car, which is the one link in this system that carries commands rather than readings. So the Pi never joins Wi-Fi at all, it is wired before it is first powered on, it takes its address from a DHCP reservation, and its Wi-Fi radio is switched off for good by the installer. One wired path, one radio doing one job. The installer does this itself, and refuses to do it while eth0 is still down.
The Wall Connector and the inverter stay on Wi-Fi, on the router's IoT SSID — both refuse 5 GHz networks, so it has to be the 2.4 GHz band.
Putting the two outdoor devices on an IoT network is worth doing for tidiness, but it is not isolation. Everything still shares one flat subnet, and it has to: the Pi is wired, the inverter and the Wall Connector are wireless, and every reading in this build crosses between them — Modbus TCP and the Solar API to the inverter, HTTP to the charger. A separate SSID with no VLAN behind it is a naming convention rather than a boundary — worth knowing rather than assuming otherwise.
Section 09Pi OS install
Put the operating system on the card with Raspberry Pi's own tool, plug in network and power, and log in once from the laptop.
| Setting | Value |
|---|---|
| Device | Raspberry Pi 4 |
| Operating system | Raspberry Pi OS Lite (64-bit), under “Raspberry Pi OS (other)” |
| Storage | The SD card — then enter the customisation wizard |
| Hostname | evcc |
| Location | Your city. It sets the timezone every Grafana timestamp depends on — every point in InfluxDB is UTC, and this is what turns it back into the hour you actually charged |
| User account | Username and password, written down — the installer asks for that username |
| Wi-Fi | Leave it blank. The Pi is wired, and the installer blocks this radio anyway — filling it in here only creates a credential to be revoked later and an antenna the Bluetooth link has to share |
| SSH | Enabled, with password authentication — enough on a trusted LAN; keys are the stricter option |
| Raspberry Pi Connect | Not needed — everything happens on the local network |
Start from a genuinely blank card. A card carrying an old partition table is the most common reason a fresh image refuses to boot. Open a Command Prompt as administrator on the laptop — this step assumes Windows.
clean is not reversible and it does not ask. Point it at the wrong number and it wipes that drive — the laptop's own disk is in the same list. Disk numbers change between machines and between reboots, so identify the card by its size, every time, and never by a number remembered from last time.
diskpart list disk // identify the card by its size, not its number select disk 1 // substitute the number you just read clean exit
DiskPart succeeded in cleaning the disk. Windows will then offer to format the card — dismiss it. Raspberry Pi Imager writes its own filesystem next.1 in the recipe above, which is exactly why the number is read from the list and never typed from memory. Then clean, one line of confirmation, and the card is bare metal for the Imager.Then Raspberry Pi Imager, with the customisation wizard:
evcc — the name the router's client list will show.us.Section 10The software install
Everything the Pi runs arrives as one release from the public repository, and one script installs it. Two lines at the terminal, one run, and — on a fresh machine — one reboot.
Log in from the laptop — ssh USERNAME@PI_IP, the account created in the Imager wizard, at the address the router reserves for the Pi — and run:
curl -fsSLO https://github.com/mihai-gafiteanu/solar-surplus-releases/releases/latest/download/get.py sudo python3 get.py
The script proves the release before the system is touched: it resolves the newest tag, downloads the package and its sha256, verifies the hash, checks that the package's stated version agrees with the tag, unpacks it into a scratch directory and runs the release's own selftest inside it — a release that fails its own checks is refused whole. Only then is the package installed, at /usr/lib/solar-surplus, and the installer run in the same foreground to build the machine: system packages, Docker, evcc, the containers, the tokens, the board, both loggers, the nightly maintenance.
The installer asks eight questions, once — the account name, the Pi's and the inverter's addresses, the disk's mount point, and the four that name the network share the nightly backup pushes to, the password typed hidden. The answers are saved to /etc/solar-surplus/install.conf, so a second run asks nothing. Six secrets are generated and none is printed to the screen: they land in one KeePass import file in the home directory, mode 600. Copy it to the machine KeePass runs on, import it, then delete both copies.
InfluxDB keeps every reading for ever — a write load a microSD card is not built for — so the history lives on a USB disk. A disk already labelled influxdb, from a previous life of this system, is adopted with not one byte written to it. A blank USB disk is offered, and preparing it is the one destructive act in this build, so it happens only after you type the device path in full. No disk, and the run stops rather than fall back to the card: the card would work, and it would wear out.
A fresh install ends by announcing its reboot a minute ahead — sudo shutdown -c cancels it — and everything just built comes back on its own, which is the proof. The installer can be run again at any time: every step checks its own work first, and it never overwrites a file you changed.
| What | Where it happens |
|---|---|
| Switch on Modbus TCP and the Solar API | The inverter's web interface |
| Join the Wall Connector to Wi-Fi | The Tesla app, next to the charger |
| Generate the BLE key and pair the car | You, in the car, with the key card |
It checks the first and the third and prints whichever is still outstanding. Taken in order, the inverter is already switched on by now, so a first run leaves only the car.
Section 11evcc configuration
The installer builds the stack; evcc still has to be told what hardware it talks to. That happens once, in the browser, at http://PI_IP:7070. Fifteen screens, in the order they appear.
The first visit asks for a password, then offers a wizard.
The charger first.
Then the car. The Pi runs a Bluetooth proxy with its own page, at http://PI_IP:8080. The key is generated there and sent to the car once.
Last, the two meters. Both answer at the inverter’s address; the unit ID tells them apart.
Everything entered here lands in one file, evcc.db, and the nightly backup carries it.
Section 12The nightly backup
The Pi backs itself up at half past ten every evening, before it updates anything. Five archives, each one proved before it is trusted.
| Name | What it carries | Why it matters |
|---|---|---|
answers | /etc/solar-surplus — the eight answers and every generated secret | A few kilobytes that are the install's identity. Restored first, they turn a reinstall into a continuation |
key | The car-pairing key | The one thing that cannot be regenerated — a new key means the key-card ceremony in the car, again |
evcc-db | evcc.db — the configured devices | The loadpoint, the charger, the car |
influxdb | The history, and every file carrying a token it honours | Every reading since the build went live |
grafana | The Grafana data volume | The board itself ships with every release; the volume carries what was changed on it |
Each archive is named <name>-backup-YYYY-MM-DD.tar.gz and written twice over. First to the history disk itself, under /mnt/influxdb/backups — a tier that survives a card failure and rides the disk into the next install. Then to the Samba share named at install time, pushed over smbclient — deliberately not a mount, so a sleeping NAS can never hang a boot — and every push is proved: the copy is fetched back off the share and its sha256 compared before the run believes it. Rotation keeps the newest fourteen of each small archive and the newest four of the history, on both tiers alike.
Nothing needs to be run — the evening window does this on its own. A backup on demand is one command:
sudo systemctl start maintenance-backup.service
Section 13Restore
The dead card is the rehearsed case, not the disaster. A reinstall is the same install run over the archives — one run, one stop, one reboot.
- While the old Pi still answers — run one last backup, and copy the
answersandkeyarchives to the laptop as well: until the new card boots, the history disk is the identity's only carrier. - Fresh card — image it exactly as before, boot, and run the same two install lines. The run brings the OS current first; that is the long wait.
- The stop. A machine with no identity halts before the first question and lists the newest archives it found — read straight off the history disk, plus anything copied to
/tmp. One Enter takes the identity back: the answers land, and nothing is asked. - The rest is the run's own work. The key, the devices and the board go back once the stacks they unpack into exist. A history archive is refused — the surviving disk is newer than any copy of it, and the refusal is the correct outcome. The run ends in the announced reboot.
- When it comes back — the board's history reaches back before the reinstall, and the car accepts the restored key with no ceremony, no card, no driver's seat: restoring the key is the pairing. Proved on a real reinstall, 15 August 2026.
Its data is newer than any archive, so the restore refuses the history and says so. If an install ever offers to erase the history disk instead, stop it — that means the answers did not come back, and the run is minting a new identity rather than continuing yours.
The same script restores by hand, for surgery on a live machine — one archive back, or all of them. When the disk died too, --fetch first pulls the newest set back off the share:
sudo python3 /usr/lib/solar-surplus/files/maintenance-backup.py --fetch /tmp sudo python3 /usr/lib/solar-surplus/files/maintenance-backup.py --restore key
Section 14Maintenance and updates
One maintenance window a day, at 22:30 — past the year's latest sunset, so it never competes with surplus charging. Backup, then updates, then a reboot that rehearses the cold start. The whole story is told before you sleep.
- Backup. The five archives of the previous sections, pushed and proved.
- Updates. Six components in order: the stack itself first, so the new release's rules govern the evening — then evcc, Grafana, InfluxDB, the BLE proxy, and Raspberry Pi OS last, the one that can ask for a reboot.
- Reboot. Guarded — never under a running window, never mid-install — and deferred with a note when the guard says no. The daily reboot is a feature: the cold-boot path is rehearsed every day, so the day it matters is never its first.
- On boot, the report. The Pi waits for its four services to answer, writes down what came up and how many seconds it took, then takes the day's first version reading.
The policy: everything up to a major version is taken by the window itself. Only a major waits — for one command from the owner, which raises the pin and updates in the same run — and the raise lands in a file no release ever rewrites, so the word, once given, survives every later install. Between windows, a versions table repaints itself every quarter of an hour: what is installed, what is published, which of the two is ahead. Nothing here waits for the clock, either: the whole window, or any single component, starts on demand with one systemctl start.
A bad release is not repaired in place — it is retreated from. Any published release installs by tag, over the same proving ladder as the newest, and the last three verified packages are cached on the Pi itself, for the day GitHub is out of reach:
sudo python3 get.py --tag vX.Y
And when something feels wrong before anything is known, the release checks itself — no unit started, no network touched, nothing written:
python3 /usr/lib/solar-surplus/selftest.py
482 passed, 0 failed — the count grows with the releases; the zero is the number that matters.Section 15The board
Everything the build measures lands on one screen. It answers three questions: is the car charging, is the Pi healthy, and did last night’s chores run. Here is the whole board, piece by piece, as it looked live on 24 August 2026.
The board comes with the install and lives at http://PI_IP:3000. The left half is the Pi. The right rail is history. The bottom half is the car and the sun.