Yes – I’ve found PowerWall to be a problem when using Demand Shaper for EV charging.
Instead of charging the EV at cheap night rates, PowerWall discharges to the EV. The solution is to set the Powerall Reserve % to the current % when the EV starts charging starts and then set the Reserve % back to zero when EV charging finishes. This also means the house demand is supplied from the grid during EV charging rather than from PowerWall.
And given it’s the middle of the night, the process needs to be automated.
Per this link, with the latest firmware, accessing PowerWall on the home network to make these changes is no longer possible:
It is necessary to use the API associated with the Tesla Mobile App.
My automate-the-whole-process is still a WIP but these elements are now working:
PYTHON script to get an access token (valid for 45 days):
"""
PURPOSE ...
Get a new Access Token and save it to a file
Access Tokens expire after 45 days
So run this script monthly via crontab -e
Other scripts can read the current Access Token (acc_tok) from the file by including the following lines ...
f = open("/home/pi/ev-charging-scripts/access_token.txt", "r")
acc_tok = f.read()
"""
import requests, json, datetime
from requests.structures import CaseInsensitiveDict
url = "https://owner-api.teslamotors.com/oauth/token"
headers = CaseInsensitiveDict()
headers["Content-Type"] = "application/json"
data = '{"grant_type": "password", "client_id": "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384", "client_secret": "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3", "email": "xxxxxxxxxxx", "password": "yyyyyyy"}'
response = requests.post(url, headers=headers, data=data)
if response.status_code in [200]:
token_dict = json.loads(response.text)
for x, y in token_dict.items():
print(x, y)
else:
print(response.status_code)
x = token_dict["created_at"] + token_dict["expires_in"]
date = datetime.datetime.fromtimestamp(x)
print("Valid UNTIL: " + date.strftime('%Y-%m-%d %H:%M:%S'))
# Over-write the access token in a file (access_token.txt) in the SAME directory as this script
f = open("/home/pi/ev-charging-scripts/access_token.txt", "w")
f.write(token_dict["access_token"])
f.close()
Get the energy_site_id which needs to be done just once & noted:
curl --header "Authorization: Bearer xxxxxxxxxxxxxx" -X GET https://owner-api.teslamotors.com/api/1/products
xxxxxxxxxxxxxx = the Access Token ... do: cat /home/pi/ev-charging-scripts/access_token.txt
This provides a wealth of data including:
"energy_site_id": xxxxxxxxxxxxx,
Make a note of it
PYTHON script to set the Reserve % to the current level:
"""
PURPOSE ...
Set the PowerWall Reserve charge to its CURRENT charge %
"""
import requests, json
from requests.structures import CaseInsensitiveDict
# Enter energy_site identifier ...
en_site = "xxxxxxxxxxx" # the energy_site_id
# Get latest Access Token from file
f = open("/home/pi/ev-charging-scripts/access_token.txt", "r")
acc_tok = f.read()
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = "Bearer "+acc_tok
headers["Content-Type"] = "application/json"
# Get CURRENT charge %
url = "https://owner-api.teslamotors.com/api/1/energy_sites/"+en_site+"/live_st$
reply = requests.get(url, headers=headers)
jsonstr = reply.text.rstrip()
reply = json.loads(jsonstr)
curr_percent = reply['response']['percentage_charged']
print(curr_percent)
# Set the PowerWall Reserve charge to its CURRENT charge %
url = "https://owner-api.teslamotors.com/api/1/energy_sites/"+en_site+"/backup"
# data = {"backup_reserve_percent": curr_percent}
resp = requests.post(url, headers=headers, data=json.dumps(data))
print(resp.status_code)
PYTHON script to set the Reserve % back to zero:
"""
PURPOSE ...
To set the PowerWall Reserve charge back to zero - its normal condition
"""
import requests, json
from requests.structures import CaseInsensitiveDict
# Enter energy_site identifier ...
en_site = "xxxxxxxxxxx"
url = "https://owner-api.teslamotors.com/api/1/energy_sites/"+en_site+"/backup"
# Get latest Access Token from file
f = open("/home/pi/ev-charging-scripts/access_token.txt", "r")
acc_tok = f.read()
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = "Bearer "+acc_tok
headers["Content-Type"] = "application/json"
data = {"backup_reserve_percent": 0}
resp = requests.post(url, headers=headers, data=json.dumps(data))
print(resp.status_code)
Note: All the scripts are in the /home/pi/ev-charging-scripts directory
My next step is to incorporate all this into an overall script that can be run as a cronjob sometime after midnight.
I have a single phase OpenEVSE charger and that can be set up using RAPI over MQTT with one exception – the EV charge to level. In my view, it’s smart not to charge the EV to 100% but better to leave ‘space’ for next day’s free solar charge (hopefully). I have an outstanding forum query on this to @ TrystanLea and @glynhudson:
@ramcq – I’m in the market for a 3 phase EV charger and note that you have a Zappi. Does the Zappi have an accessible API?
Hoping some of this may be of help.