I've had solar and batteries at home for quite some time now, and despite my experience with them being really awesome, there were a few little things that were bugging me. Using systems from various different suppliers doesn't always provide the perfect integration, so I hacked together my own!
No, not that kind of hacking!
I have done that kind of hacking over the years, and many times I've blogged about it right here, from cars to cctv cameras and routers to hotel websites, but this blog post isn't about that type of hacking, it's about what I consider to be the more traditional definition of the term. This is the type of hacking where you get something to perform a function that it wasn't intended to perform, where you take something and make it better than it was by tinkering with it. Think more 'life hack' than 'computer hack', and you're heading along the right lines here.

The problem
I have a few different system at home that all are related to power consumption, and whilst they are all 'smart', they don't fully integrate. Here's the overview:
- ☀️Solar Panels: We have 14 Perlight solar panels managed by Enphase that make up the 4.2kWp array on our roof, and they produce energy when the sun shines. This part is pretty straightforward!
- 🔋Tesla Powerwalls: We have 3x Tesla Powerwall 2 in our garage that were purchased to help us load-shift our energy usage. Electricity is very expensive in the UK and moving from peak usage which is 05:30 to 23:30 at £0.27/kWh, to off-peak usage, which is 23:30 - 05:30 at £0.07/kWh, is a significant cost saving.
- 💡Smart Tariff: My wife and I both drive electric cars and our electricity provider, Octopus Energy, has a Smart Charging tariff. If we plug in one of our cars, and cheap electricity is available, they will activate the charger and allow us to use the off-peak rate, even at peak times.
All of this is great, but it does present a few issues:
1) The main issue is that the Powerwalls will always fill up to 100% on the off-peak rate overnight, aiming to maximise how much power they can supply us during the peak-rate, load shifting the maximum possible amount and saving the most money. That's great, but it doesn't factor in that we have a 4.2kWp solar array on our roof. Once the sun rises and we start generating solar power, the batteries are still full, or almost full, and we quickly begin exporting energy to the grid. Our export tariffs in the UK are terrible and our supplier only pays us a measley £0.04/kWh for exported energy, which is a complete waste! I want a way to tell the Powerwalls to only charge to, let's say, 80% on grid power, and leave headroom for solar generation during the day. No such feature exists.
2) The next issue is that the Powerwalls know about our cheap off-peak tariff overnight from 23:30 to 05:30, but not the flexibility of our Smart Charging tariff during peak times. That means that if we plug one of our cars in during the day to top them up, the Powerwalls think we're on peak rates and will charge the car from the batteries and not the grid. I'd like the Powerwalls to be able to respond to our current energy price, rather than a fixed time schedule. No such feature exists.
3) The final issue is similar to the above, but about our Powerwall batteries rather than the car batteries. If we have a window of cheap electricity from our electricity supplier, then the Powerwalls should top themselves up when rates are cheap. Not only does this feature not exist, but it also needs to factor in point #1 about not overfilling the batteries and leaving room for solar generation. Argh!
Home Assistant and Teslemetry to the rescue!
I've talked about Home Assistant a lot and we use it extensively throughout our house. From lights to appliances, monitoring, alarm systems and more, Home Assistant has a finger in almost every pie! The Tesla Powerwalls integrate nicely with Home Assistant and provide all of their information which I've formulated into a nice dashboard.

I can keep a track of the State of Charge (SoC), power coming in and out of the batteries, power coming in and out of the grid, and much more!
Of course, we also have an integration for our solar array too, and the Enphase integration allows us to see exactly what's going on with our solar array, and all of the statistics you'd ever want.

Finally, our energy supplier has a pretty cool API, and there's an integration for that in Home Assistant too! We can see a whole bunch of different data about our gas and electricity supply, and, crucially, the current rate we're being charged for electricity!

So, I now have all of the information that I could possibly need in one place, but there was one final piece of the puzzle to solve. The Tesla Powerwall integration in Home Assistant does not allow for control over the Tesla Gateway or Tesla Powerwall devices themselves, it simply retrieves information. No problem, though, because after some quick Googling, I found the Tesla Fleet API.
Fleet API is a data and command service providing access to Tesla vehicles and energy devices. Developers can interact with their own devices, or devices for which they have been granted access by a customer.
The Fleet API would allow me to have the level of control over my Powerwalls that I needed, and there are a bunch of API clients out there that I could get up and running and build something from. Rather than getting knee deep in building something when my time is a little tight right now, I thought I'd take a look at what solutions exist already, and I quickly found Teslemetry.
Using Teslemetry
The Teslemetry subscription is super cheap, at £2.26/mo including taxes, and it came with a free trial period, and a Home Assistant integration, so it seemed like a winner. At this price it's really not worth spending any time on writing my own code, and I can support the service if it provides a benefit. Getting it set up was a breeze, logging in to my Tesla account and granting it access to only the Powerwalls and not the car, and then hooking that up to the Home Assistant integration.

As you can see, this is no longer just information coming from the Powerwalls, I now have the ability to control settings on the Powerwalls too! This is what's going to help me create the automations that I need to ensure the Powerwalls behave how I want them to behave.
Capping the battery power when grid charging!
The first automation I wanted to create was to limit how high the batteries will charge when charging from the grid. The primary benefit here would be to limit the SoC on the overnight charge, so that once the solar starts generating, there would be room in the battery to store than, rather than export it to the grid. There may also be another benefit here in preventing the batteries from hitting 100% SoC and then sitting at 100% SoC for a prolonged period of time, which can be bad for their health over the long term.

This automation is set to run every 30 minutes, at the top and bottom of the hour, as that's the time window that my electricity rate can change. Here's the YAML for the automation:
alias: Powerwall - Disable Grid Charging
description: ""
triggers:
- trigger: time_pattern
minutes: /30
conditions:
- condition: state
entity_id: switch.shireburn_allow_charging_from_grid
state: "on"
- condition: or
conditions:
- condition: numeric_state
entity_id: >-
sensor.octopus_energy_electricity_1_snip_3_current_rate
above: 0.11
- condition: numeric_state
entity_id: sensor.shireburn_charge
above: 75
actions:
- action: switch.turn_off
metadata: {}
data: {}
target:
entity_id: switch.shireburn_allow_charging_from_grid
mode: single
So, every 30 minutes, I'm checking to see if the cost of electricity is high, or the battery is above 75% SoC, and if so, we're disabling grid charging. This means the battery will only charge when electricity is cheap and the SoC is below the target of 75%. In these summer months where we are producing plenty of solar power, I can keep the target SoC on grid charging at 75%, leaving plenty of room to store solar generation, and in the winter months, I will likely set the target SoC back to 100% as we will have little solar generation and will need all of the battery backup we can get.
The automation to enable grid charging is then of course very similar, checking for energy cost and SoC target, and then enabling grid charging when required.

And here's the YAML for that one too:
alias: Powerwall - Enable Grid Charging
description: ""
triggers:
- trigger: time_pattern
minutes: /30
conditions:
- condition: state
entity_id: switch.shireburn_allow_charging_from_grid
state: "off"
- condition: numeric_state
entity_id: sensor.shireburn_charge
below: 75
- condition: or
conditions:
- condition: time
after: "23:30:00"
before: "05:30:00"
weekday:
- mon
- tue
- wed
- thu
- fri
- sat
- sun
- condition: numeric_state
entity_id: >-
sensor.octopus_energy_electricity_1_snip_3_current_rate
below: 0.11
enabled: true
actions:
- action: switch.turn_on
metadata: {}
data: {}
target:
entity_id: switch.shireburn_allow_charging_from_grid
mode: single
This has worked out really well and is doing exactly what we need it to do. If we look at the SoC on the batteries over the last 30 days, you can see when I enabled this automation as they're no longer hitting 100% SoC on the overnight charge, and you can also see a few days before the change when solar managed to charge the batteries back up to 100% and then we ended up exporting to the grid.

Apart from some testing on the two nights where I let them charge back to 100% SoC, you can see that the automation is now keeping the batteries at a much better maximum SoC.
Responding to dynamic pricing
When we had the Powerwalls and the solar array installed, we didn't have a dynamic pricing tariff. Our plan was for the solar production and battery power to carry us from 05:30 all the way through to 23:30, completely avoiding the peak costs, and load shifting our entire usage into the off-peak window. That works perfectly well and we don't need to worry about responding to the dynamic pricing from a cost standpoint.
The other thing that's changed for us is that we now have two electric cars using the EV charger at home. Both of our cars are programmed to only charge during the off-peak window of 23:30 to 05:30, and this hasn't caused any problems for us so far either. We don't do enough driving that we've ever worried about both of us needing to charge at the same time, nor during peak time. The other consideration is that the cars will charge from the Powerwall batteries if they think the grid power is expensive, and ideally we want the cars to draw power from the grid, not the batteries.
All of that said, it'd still be nice to have the ability to respond to the dynamic pricing and absorb some power from the grid when it's cheap, and charge the cars on grid power rather than battery power if we ever wanted to. The question is, how do I do that? This isn't something the Powerwall is designed to do either.
Utilising Powerwall settings to achieve our goal
Looking at the settings that are surfaced through Teslemetry, there isn't a huge amount of options to go at, so it was easy enough to explore them and see if any of them could be used to do what I need.

I'm already leveraging the 'Allow charging from grid' setting as we've seen above, and the 'Allow Export' and 'Storm Watch' aren't going to be useful here. That leaves the 'Operation Mode' and 'Backup Reserve' options to explore.

The Operation Mode options can be summarised as:
- Autonomous - Uses the time settings to load-shift as I've covered so far and does that fully automatically.
- Backup - Saves 100% of the power for backups during grid outage, which isn't really useful to us.
- Self Consumption - The focus is on charging from solar and using 100% of that power to run the house.
This means that Autonomous mode is the correct setting for us as it achieves what we set out to achieve with the battery, leaving us the Backup Reserve option to play with, which is the one that sounds like it can do what we need.
The purpose of Backup Reserve is to set the minimum SoC that the battery will drop down to during operation, always saving the amount specified for grid outages to keep you online. As you can see from my screenshots, we've always had this set to 0% as we want to use the full capacity of the batteries to load shift, and we aren't too concerned about having reserves for if the grid goes down.
What I found during testing was that if you use the Backup Reserve and Grid Charging options together, you can create some specific behaviour that results in exactly what we need. Here's a table that gives an overview of the scenarios I'm looking at, with the main one highlighted in bold:
Battery SoC % | Backup Reserve SoC % | Grid Charging | Battery Charging | Power Draw |
---|---|---|---|---|
50% | 0% | Disabled | No | From Battery |
50% | 75% | Disabled | No | From Grid |
50% | 0% | Enabled | No | From Battery |
50% | 75% | Enabled | Yes | From Grid |
80% | 0% | Disabled | No | From Battery |
80% | 75% | Disabled | No | From Battery |
80% | 0% | Enabled | No | From Battery |
80% | 75% | Enabled | No | From Battery |
By controlling the Backup Reserve %, we can control whether the battery is discharging or not. If the Backup Reserve % is lower than the SoC %, the battery will discharge, and if the Backup Reserve % is higher than the SoC %, the battery will not discharge.
Alongside that, we can also control if the battery is actively charging from the grid, rather than just allowing our house to draw its power from the grid, using the Grid Charging control. We can now use these combinations of settings to get the Powerwalls to do exactly what we want! In summary:
The Powerwalls will always charge to 75% when electricity is cheap, meaning we always have spare capacity to absorb solar production, and we can use the car charger any time we like.
Here are the YAML configurations for the automations, of which there are now 4, and I've converted them to use variables for the cost and SoC thresholds so they're not hardcoded in the automations:
alias: Powerwall - Disable Grid Charging
description: ""
triggers:
- trigger: time_pattern
minutes: /30
conditions:
- condition: state
entity_id: switch.shireburn_allow_charging_from_grid
state: "on"
- condition: or
conditions:
- condition: numeric_state
entity_id: >-
sensor.octopus_energy_electricity_1_snip_3_current_rate
above: input_number.electricity_cost_threshold
- condition: numeric_state
entity_id: sensor.shireburn_charge
above: input_number.powerwall_soc_threshold
actions:
- action: switch.turn_off
metadata: {}
data: {}
target:
entity_id: switch.shireburn_allow_charging_from_grid
mode: single
alias: Powerwall - Enable Grid Charging
description: ""
triggers:
- trigger: time_pattern
minutes: /30
conditions:
- condition: state
entity_id: switch.shireburn_allow_charging_from_grid
state: "off"
- condition: numeric_state
entity_id: sensor.octopus_energy_electricity_1_snip_3_current_rate
below: input_number.electricity_cost_threshold
enabled: true
- condition: numeric_state
entity_id: sensor.shireburn_charge
below: input_number.powerwall_soc_threshold
actions:
- action: switch.turn_on
metadata: {}
data: {}
target:
entity_id: switch.shireburn_allow_charging_from_grid
mode: single
alias: Powerwall - Set Backup Reserve To 0%
description: ""
triggers:
- trigger: time_pattern
minutes: /30
conditions:
- condition: numeric_state
entity_id: sensor.octopus_energy_electricity_1_snip_3_current_rate
above: input_number.electricity_cost_threshold
- condition: template
value_template: "{{ states('number.shireburn_backup_reserve') | int != 0 }}"
actions:
- action: number.set_value
metadata: {}
data:
value: "0"
target:
entity_id: number.shireburn_backup_reserve
mode: single
alias: Powerwall - Set Backup Reserve To 75%
description: ""
triggers:
- trigger: time_pattern
minutes: /30
conditions:
- condition: numeric_state
entity_id: sensor.octopus_energy_electricity_1_snip_3_current_rate
below: input_number.electricity_cost_threshold
- condition: template
value_template: >-
{{states('number.shireburn_backup_reserve') | int !=
states('input_number.powerwall_soc_threshold') | int}}
actions:
- action: number.set_value
metadata: {}
data:
value: "75"
target:
entity_id: number.shireburn_backup_reserve
mode: single
We've had these running for a few days now and, as far as I can see, they seem to be behaving exactly how I want them to behave! That said, if you're a Powerwall expert, Home Assistant expert, or have just spotted something that I can do be doing better, drop by the comments below and let me know!