Charging the car on sunshine — and teaching the pool to wait its turn
Solar on the roof, an EV in the driveway, and a tariff that charges 40c at 4pm and 8c at midnight. A real build: taking a cheap Tuya charger off its cloud, making it follow live solar surplus amp by amp, and letting the pool have what is left.
Solar on the roof and an EV in the driveway should be an obvious win. In practice most people end up charging the car on grid power at the worst possible time of day, because the charger has no idea the sun is out and the tariff has no idea the car is plugged in.
This is a write-up of a real build: a cheap Tuya EV charger taken off the manufacturer’s cloud, taught to follow live solar surplus amp by amp, and then made to share what is left with a pool pump. It is a good illustration of what “solar-aware” actually requires, and where the sharp edges are.
The money case comes first
The economics are what make this worth engineering at all. A fairly typical South-East Queensland time-of-use plan looks something like this:
- Super off-peak (midnight–6am): about 8c/kWh
- Peak (4pm–8pm weekdays): about 40c/kWh
- Solar feed-in: about 2c/kWh
Two things fall straight out of that. Charging during the peak window costs roughly five times what charging after midnight does. And exporting surplus solar at 2c is close to giving it away — every kilowatt-hour you can self-consume instead is worth far more than what the retailer will pay you for it.
So the rules are simple to state and surprisingly awkward to implement: use the sun if it is there, use cheap overnight power if it is not, and never charge during peak.
Solar-aware is not the same as solar-triggered
This is the part most off-the-shelf setups get wrong, and it is the single most important idea in the build.
A simple approach turns the charger on when export exceeds some threshold and off when it does not. On a clear day that works. On a normal Australian day, with cloud moving across, it produces a charger that switches on and off every thirty seconds. That is hard on the contactor, confusing for the car, and delivers less energy than leaving it alone.
The alternative is to modulate the current instead. Rather than asking “should I stop?” every fifteen seconds, you ask “should I ease off a notch?” Ask a jittery signal the first question and you have built a strobe light. Ask it the second and you have built cruise control.
Step one: get it off the cloud
The charger in this build is a Kolanky 15A portable unit — a Tuya device, which by default only talks to Tuya’s servers. That is a problem for reliability and for control latency, and it is the reason we build on Home Assistant in the first place.
Using LocalTuya, the device’s local encryption key is pulled from the Tuya IoT portal once, and from then on Home Assistant talks to the charger directly over the LAN. No internet round trip, no dependence on a vendor staying in business, and control response in milliseconds rather than seconds.
Then fix the sensors it reports
LocalTuya’s auto-detection got several things wrong on this device, in ways that would quietly poison any automation built on top:
- Voltage reported as 22V instead of 220V — a scaling factor out by ten
- Current reported with the wrong unit entirely (“1 V”)
- A phantom “Set 400A” slider on a device with a 15-amp maximum
A device template with the correct scaling factors sorts this out. It is worth dwelling on because it is the sort of thing that does not announce itself: the automation runs, the dashboard looks plausible, and every decision it makes is based on a number that is wrong by an order of magnitude.
The detail that makes it possible
Most of the value in this build rests on one discovery: datapoint 114 accepts a live current value. Write a number to it over LocalTuya and the charge current changes immediately, anywhere from 6 to 15 amps, without unplugging the car or restarting the session.
That is what turns a dumb on/off appliance into something that can track a moving solar curve. Without it, the best available behaviour is a relay.
One hard limit worth knowing: writing 16 to that datapoint does not raise the current. The device silently ignores the command and holds a low value. Fifteen amps is the practical ceiling, and the automation has to be written to respect it rather than assume the write succeeded.
Two loops, not one
The controller is deliberately split in two, because the two questions it needs to answer move at completely different speeds.
The slow loop — every two minutes
Decides whether to charge at all. It considers the super off-peak window, any manual override, whether daytime solar conditions justify charging, whether the peak window is active (in which case: never), and whether the car is actually plugged in and below its target state of charge.
The fast loop — every fifteen seconds
Decides how many amps, and nothing else. It tracks live grid import and export with hysteresis built in, so a cloud passing overhead changes the rate rather than stopping the session.
Separating them matters. A single loop trying to do both ends up either too slow to follow the sun or too twitchy to leave running. The effect of adding the fast loop is visible in the energy chart further down — the grid-import band on each daylight bar narrows sharply from the moment it went in.

What went wrong
Four problems in this build were worth more than the rest of it combined, because they are the ones that would have quietly cost money for months.
1. A timezone mismatch charged straight through peak
Comparing a naive datetime against a timezone-aware one threw an error mid-evaluation, and the controller happily charged through the 40c window. The rule that came out of it: never do now() - as_datetime(some_state) in Home Assistant. Use as_timestamp() differences instead. This one is worth checking in any existing setup — it fails silently and expensively.
2. The car goes to sleep
An idle EV will ignore a charger asking it to start. The vehicle needs a nudge from its own side to wake and accept the session. Any design that assumes the charger is in charge will eventually sit there doing nothing at 2am.
3. Passing cloud built a strobe light
Covered above — the fix was amp ramping rather than binary switching.
4. The 16-amp ceiling
The device ignores anything above 15A rather than clamping to its maximum, which is a meaningfully different failure mode. Ask for 16 and you do not get 15; you get whatever low value it decides to hold.
Then teach the pool to wait its turn
Surplus solar that the car does not take is worth 2c exported. A pool filter that has to run anyway is a far better home for it.
The pool runs on a daily runtime target rather than a fixed schedule, and it yields to the car:
- Daytime: the filter runs only when the EV is fully charged, or already pulling its maximum 15A with export still left over
- Super off-peak (midnight–6am): any shortfall in the daily runtime is made up at 8c
- Reset at 6am, not midnight: so the “pool day” runs 6am to 6am

That last point looks like a detail and is not. Resetting the counter at 6am means the overnight make-up window sits inside a single pool day, which removes an entire class of cross-midnight arithmetic bugs. It is the kind of decision that costs nothing at design time and saves a fortnight of confusion later.
A necessary note on safety. This build modifies how a piece of EV charging equipment behaves, and EV charging draws sustained high current in a way ordinary appliances do not. In Australia, fixed wiring, dedicated circuits and the installation of EVSE are work for a licensed electrician, and equipment must comply with the relevant standards. Adjusting charge current in software does not change what the circuit, the cabling or the outlet is rated to carry — and the automation must never be the only thing preventing an overload. Get the electrical side signed off properly. We will happily do the automation on top of a compliant installation; we will not do it instead of one.
What it is actually worth
The day above is a fair example. The system generated 23.15 kWh of solar and the house consumed 40.7 kWh. Of the solar produced, 81% was self-consumed rather than exported, and the household ran at 46% self-sufficiency.
The number that makes the whole argument, though, is the export line. 4.47 kWh went back to the grid and earned 22 cents. The same 4.47 kWh, consumed at home instead of bought back during peak, would have been worth closer to $1.80. That gap — roughly twenty to one — is why the effort goes into self-consumption rather than into chasing a better feed-in tariff.
Grid import for the day cost A$6.36 on a household running a car and a pool. Not zero, and it was never going to be. But the expensive hours were avoided, the cheap ones were used deliberately, and it required no additional hardware beyond what was already on the property.

Where it goes next
The obvious refinement is solar forecast data. Right now the overnight decision is made without knowing what tomorrow looks like, so the system will occasionally buy 8c grid power at 3am on the night before a brilliantly sunny day. Feeding a forecast into the slow loop closes that gap.
The full configuration
The complete Home Assistant packages for both the EV controller and the pool runtime manager, along with the LocalTuya device template for the charger, are published in the original write-up on Angry Dad Automates — the community side of what we do. They are there to be copied.
If you would rather not spend a weekend on timezone arithmetic and undocumented Tuya datapoints, that is what we are for.