Improving Our Solar Battery Savings
By
Ben Tasker /
Developer
Feb 18, 2025
Navigate to:
This blog was originally posted on www.bentasker.co.uk
This project was built using a previous version of InfluxDB. InfluxDB 3.0 moved away from Flux and a built-in task engine. Users can use external tools, like Python-based Quix, to create tasks in InfluxDB 3.0.
Last month, I analyzed the performance of our solar install. I found that, while our solar battery was generating daily savings, it would likely never save enough to offset its purchase cost.
There were a number of factors involved, but one of the bigger ones was the battery’s not always being sufficiently charged, with our average max daily charge level being 72%. The battery gets a full charge on sunny days, but British weather being what it is, there are plenty of days dragging that average down.
As I noted at the time, solar energy isn’t the only source from which the battery can be charged: there’s also the option of charging from the grid (ideally when prices are low).
So, in the month since, that’s exactly what I’ve done.
In this post, I will describe how I’ve configured things, analyze the impact of the change, and talk about some changes that my electricity supplier (Octopus) has recently made. Energy consumption monitoring is a time series use case so I write the metrics that I collect into InfluxDB. It’s designed specifically for time series, which allows me to easily visualize data in real-time as well as look at historic data.
Configuration
It was my intention to build automation that consumes pricing information and dynamically controls when the battery should charge. There’s a wealth of information out there on how to control a Solis Inverter from HomeAssistant.
However, as soon as I tried to set this up, I ran into a fundamental issue: all the setup guides and tools rely on older data sticks.
My inverter (a Solis RHI-3.6K-48ES-EG
) came with a third-generation stick (model S3-WIFI-ST
), which, unlike older counterparts, does not expose Modbus locally. As a result, tools like solismod and pysolarman
cannot work with them.
There is actually a guide to getting things working using a rs485 splitter, but I’ve not tried it yet (in fact, I only found it while double-checking the links above as part of drafting this post).
However, Soliscloud has an interface that allows you to set the registers on your inverter manually. It’s not enabled by default, so access needs to be requested. Once granted, there’s no API access (update: there is now), but it does what it says on the tin.
The navigation interface is quite simplistic:
Solis still seems to be working on improving it, as several things have moved around/changed since I first started playing with the interface.
The default mode is Self-Consumption
(basically, it prioritizes household demand over export revenue). Within that menu there are some additional options:
The
Charge and Discharge
button opens a side pane that allows charge times and rates to be configured.
It’s worth noting here that, to a certain extent, it doesn’t really matter what you enter as a charge rate (as long as it’s not too low): the Battery Management System (BMS) will not allow the battery to charge faster than it’s capable of. Still, it is better to err on the side of caution in case that ever changes.
The changes made here, though, will have absolutely no impact until the schedule is enabled.
On the Self-Use menu is a button labeled Time of Use Switch
. Anything ending in Switch
is essentially a boolean—you turn it on or off. In this case, the switch defines whether or not the Charge/Discharge schedule is observed.
Adjusting time of use
In my previous post, one of the mitigations that I suggested was adjusting settings so that energy would only be drawn from the battery when most valuable (i.e., at peak times). The idea was that any energy remaining after peak would be carried forward into the next day’s peak rather than trickling away overnight.
I did look into doing this, but unfortunately, the inverter’s settings don’t quite work that way:
- If you set the
Discharge Current
field to0
and specify a time range, the battery will not discharge during that time (good!) - However, it will also consider it a discharge period and will, therefore, not charge the battery (boooo!)
So, although you can technically set the battery to only discharge at certain times, it probably won’t have any charge to discharge in the first place.
In fiddling around to get this working, I learned a few things about the Solis Inverter’s scheduling:
- If you set a charge period, the battery will charge at that rate, taking energy from the grid to top-up whatever the panels are delivering.
- If you set a discharge period, the battery will constantly discharge at the specified rate, exporting energy to the grid if there’s insufficient household demand to consume it (this took me by surprise, resulting in me accidentally dumping a battery load to the grid shortly before peak time started).
- There isn’t a way (other than setting a discharge period), within
Self-Use
, to tell the inverter not to charge the battery from the panels. - If you switch to
Grid Feedback Priority
the meaning of the schedule changes. The battery will not be charged from the panels unless there’s a charge period defined.
In effect, Self-Use
mode means the tooling only really allows you to influence how the battery interacts with the grid: When should it charge from it, and when should it discharge to it? This is actually quite logical, given that the mode is supposed to try and ensure that consumption primarily uses locally generated energy, but it can be a little restrictive.
Routine schedule
With no ability to dynamically shift the charging schedule based on price, I needed to choose a time range that would generally see low prices.
The obvious choice for this, of course, was in the small hours of the night, so I set an appropriate schedule:
The next night, the battery charged as it should. Except, of course, that charge was long gone by the time we reached peak (in fact, it was gone before I even got out of bed).
I messed around with settings for a bit but ultimately found that there isn’t a way to configure things so that the charge is maintained until peak (at least, not if we want the panels to top it up).
Helpfully, it’s not just the morning that prices are lower: there are a good few hours in the afternoon (where Solar farms are presumably picking up the slack) with lower pricing, too:
So, I updated the configuration so that we charge the battery from 12 pm to just before peak time starts:
This carries an additional benefit: the battery will have had an entire morning of Solar charging, so we’ll only use the grid for whatever’s necessary to top it up (of course, if import prices drop below export value, we’ll need to re-assess that).
Impact on charge levels
The change didn’t have as significant an impact on the average charge level as I’d been expecting:
from(bucket: "Systemstats/rp_720d")
|> range(start: 2023-08-14T00:00:00Z)
|> filter(fn: (r) => r["_measurement"] == "solar_inverter")
|> filter(fn: (r) => r["_field"] == "batteryPowerPerc")
|> keep(columns: ["_time", "_field", "_value"])
|> aggregateWindow(every: 1d, fn: max, createEmpty: false)
|> mean()

However, on closer inspection, this average is pulled down by a handful of particularly overcast days on which the only charge that the battery got was from the grid.
If we instead look at the proportion of days where the battery was charged to at least 65% (i.e., enough to see us through even a busier peak):
from(bucket: "Systemstats/rp_720d")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "solar_inverter")
|> filter(fn: (r) => r["_field"] == "batteryPowerPerc")
|> keep(columns: ["_time", "_field", "_value"])
|> aggregateWindow(every: 1d, fn: max, createEmpty: false)
|> map(fn: (r) => ({r with
_field: if r._value > 65.0 then "full" else "partial",
_value: 1
}))
|> sum()
The difference is quite pronounced:
Prior to the change, nearly
31%
of days didn’t result in the battery reaching a 65%
charge level (yay, British weather…). Since the change was made, that number has dropped to just 6%
.
The difference is even more pronounced if we use a 45% charge threshold: partial charges account for 20%
before and 1%
after the change.
Impact on savings
The introduction of the afternoon charge schedule led to a fairly significant change in daily savings rates (the change was made at the red line):
(If you’re wondering about those earlier multi-pound savings, they’re explained here and, unfortunately, are not repeatable).
The change had two outcomes:
- Savings, when realized, tended to be higher than before.
- Negative savings have occurred more frequently.
Negative savings occur when the total financial value (i.e., the cost of import from the grid or lost export cost) of the power that we store in the battery is higher than the equivalent import value of the energy when it’s discharged.
In the time covered, one of the primary determining factors seems to be whether or not we use the oven in the evening: If we cook with the air fryer (or a gas hob), we don’t draw enough energy at peak time. The battery’s charge is then released gradually over the course of the night when prices are lower, leading to negative savings.
So, just as in my last analysis, there is a perverse incentive to use less energy-efficient appliances in the evening to help drive the numbers up.
Of course, no one actually benefits from improving those figures in that way: using extra energy unnecessarily is still a net loss. This is an example of the sort of thinking that ultimately leads to trying to burn tires in order to improve the efficiency of burning coal to generate Cryptocurrency.
Utterly crazy.
Anyway, despite those days with negative savings, the average daily saving has increased to £0.152
(from £0.125
). Although that change is only a few pence, it still represents a 21%
increase in the average daily saving.
At that rate, the battery will have paid itself off in… yeah, let’s not go there.
If we can eliminate negative-savings days (don’t you just love a magic wand?), then the average savings becomes 0.29
a day. That roughly halves the break-even time (but still leaves it many times the battery’s rated lifetime).
Getting free electricity
Our energy tariff is Octopus Agile, which sometimes sees prices drop very low (or even become negative). Unfortunately, that’s not really happened since I updated the charge schedule.
However, we’re now enrolled in a new Octopus scheme: Power-Ups.
With power-ups, we occasionally get periods where our electricity is free despite the Agile price not being
0
. The scheme aims to show local network operators that excess renewable energy can be consumed and discourage them from switching off green generation when surpluses are expected.
With the electricity being free, it obviously makes sense to charge the battery from the grid rather than from solar, helping drive up that day’s savings.
In order to account for these in my stats, I created a new field—override_price
—which allows me to override the reported agile unit cost with a simple curl.
curl \
-d 'octopus_pricing,tariff_direction=import,charge_type=usage-charge,payment_method=None,reason=misc/solar#6 override_price=0.0 1692365400000000000' \
"http://192.168.3.96:8086/write?db=Systemstats"`
This sets the cost to 0.0p/kWh
for that 30-minute slot.
The following query allows us to look at when there have been power-ups and how much time they covered:
SELECT
count("override_price") * 30 as "minutes"
FROM "Systemstats"."autogen"."octopus_pricing"
WHERE $timeFilter
GROUP BY time(1d) fill(null)

As you might expect, the impact that power-up periods have had on battery savings has been pretty positive (relevant days are marked with red dots):
A number of factors have played into savings performance so far, but the primary one is striking a balance between solar and grid charging.
The maximum rate at which the battery can charge is limited, so it’s simply not possible to take the battery from empty to full in 2 hours. This means that it’s important to let the battery charge from solar during the morning because otherwise, there likely won’t be enough charge to cover the peak.
Conversely, it also doesn’t make sense to put too much solar energy into the battery because then we won’t be able to fully utilize the free grid power.
To complicate matters, the battery’s maximum charge rate is influenced by things like temperature, so there is an unavoidable element of guesswork in scheduling charges.
I found that the key lies in not relying solely on the Soliscloud schedules and instead switching things around manually:
- Set a schedule to charge from the grid during the power-up (so far, that’s been 1400 - 1600).
- In the morning, let the battery charge from Solar until it’s at about 47% (on some days, we reached that by 1030).
- Toggle off the
Time of Use Switch
and then switch the inverter toFeedback Grid Priority
mode (so that it’ll start exporting rather than charging the battery) - Just before the start of the power-up, switch back to
Self-Use
and toggleTime of Use
back on so that the battery charges from the grid.
This approach, of course, relies on me being available at the right times to switch things around. But, it also leads to much higher export volumes (and therefore revenue).
In effect, each exported
kWh
becomes worth double what it would otherwise have been.
- Rather than storing
1 kWh
, we export it (export revenue makes balance+15p
). - During the power-up period, we replace it using free grid power stored in the battery (balance remains
+15p
). - We later discharge and consume it at the prevailing import price (assuming
30p/kWh
, the balance becomes+45p
).
The power-up allows us to claim the export value, avoiding loss of opportunity cost (which, as I wrote last time, is a major contributor to the low savings rate), and still realize a full kWh’s worth of savings during peak times.
Although that level of gain couldn’t exist without the battery, it isn’t fully accounted for in the battery savings figures—to see the true benefit of the approach, we need to look at the total system savings figure instead:
As a rule, the days with power-ups outperformed their adjacent days. The exception is the 18th of August: not only was the weather crap, but it was also my first spin at configuring the inverter to take advantage of a power-up, and I ballsed it up a bit.
It’s worth noting, too, that the power-ups also delivered non-solar savings that aren’t accounted for in these graphs: we shifted some of our load to the power-up window, and so we were able to run the washing machine, etc., for free.
The result is that our consumption costs were reduced by at least sixty pence a day:
The grid, for its part, gets an additional supply of green energy (our exports) at times of lower supply, without needing to spin down green generation capacity.
Further improvements
A couple of days ago, I made an additional change to the schedule.
Although much lower than the evening peak, there is a pricing peak each morning (coinciding with people getting ready for work, etc.).
At that time in the morning, our panels are active but far from operating at their best, so it seems sensible to take a small overnight charge to help shave the morning peak.
The savings, if any, will likely be a few pence at most, but that’s still a reasonable percentage improvement in the battery’s daily value. It will also likely be higher in winter when our panels start generating later in the day.
The outcome
Battery break-even is still decades away. But given that there is no way to unpurchase the battery, any additional savings that the battery can generate are still welcome, not least because it helps lower our energy bills. If I were being smart, I could probably find a way to invest those savings so that their growth could contribute further to achieving break-even.
Because our day-to-day activities vary, charging the battery (whether from the grid or solar) carries an element of risk: the battery’s ability to unlock savings is quite reliant on whether or not we demand sufficient energy at peak times.
That could potentially be mitigated by limiting battery discharge to certain periods, but it’s not something that’s currently possible with this inverter (or at least, not without significant trade-offs).
Somewhat unsurprisingly, the best way to unlock additional battery savings is to use the battery more. Ensuring the battery has at least enough charge to survive peak usage has helped increase savings by about 20%
.
The introduction of Octopus Power-Ups has also proven beneficial, allowing the system to derive a reasonable amount of additional value. The benefit will, very likely, be less during winter as we won’t be able to export anywhere near the same amount of energy. Conversely though, our batteries will likely be in more need of a grid charge.
My work on this, clearly, isn’t done.
First, I need to see what benefit (if any) we gain from shaving that morning peak.
Once I’ve got meaningful stats on that, the next step is likely going to be adding a rs485 switch into the mix so that I can potentially start automating.
With automation in place, I’ll be more able to:
- Avoid running up negative savings: On days when we routinely don’t use the oven, the automation can disable (or perhaps just reduce) the scheduled grid charge.
- Ensure a sensible charge level on cloudy days: Weather forecasting can be used to define whether we’re likely to need a prolonged grid charge to cope with the peak.
- Automate switching between Self-Use and Grid-Priority, allowing finer control over when the battery charges and discharges.
I don’t believe we’ll ever reach the point where buying the battery won’t have been a financial mistake, but given that we’ve got it, I think there’s still some scope for additional savings—particularly as the rest of the system is over-performing enough to offset the cost of the battery relatively quickly.