Here’s the script that polls the Tesla EV …
# PURPOSE: To poll a Tesla EV periodically to discover whether it is online and if so - what is it's battery % charge, battery range & odometer reading
# ACKNOWLEDGEMENTS: This script is built around a core: carson 1.0.2 https://pypi.org/project/carson/ by Michael Loyd who in turn acknowledges Tim Dorr and dozens of contributors to his tesla-api project
# INITIAL REQUIREMENTS ...
# Do sudo pip3 install carson # NOTE sudo
# Do sudo pip3 install aiohttp # NOTE sudo
# The script will create an INPUT node to receive the data
# Copy this script file to /home/pi and make it executable with: chmod +x /home/pi/tesla_JB+carson.py # using the correct script name
# Run the script with: /usr/bin/python3 /home/pi/tesla_JB+carson.py # using the correct script name
# All being well, data will appear in the emoncms webpage Inputs refreshing every 30 secs
# Create FEEDS using Log to Feed and add UNITS (pencil drop-down) to each
# IMPORTANT NOTE ...
# Data from this script is input to emoncms via an http API. This bypasses emonhub ...
# The EmonHubEmoncmsHTTPinterfacer is not required ...
# IMPORTANT NOTE ...
# FEB 2021 - Tesla changed the process for authentications - script changes have been made - labelled FEB 2021
# FINALLY ONCE THE SCRIPT RUNS OK: Create the tesla-EV.service
# A SERVICE (tesla-EV.service) should be set up as described at the end/below
# MOST IMPORTANT NOTE ...
# If emon is updating but EV asleep ... This may happen when the script is first run ... Wake up the EV by using the Mobile App
# REF: https://pypi.org/project/carson/
# ===============================================================================================
import asyncio, json, datetime, time, requests, aiohttp
from time import sleep
from carson import Session
# Start of User inputs section ===============
# This section commented in FEB 2021 ... Tesla changed the process for authentication
# Tesla EV data
ev_name = 'XXXXXXXXXXXXXX'
# Location of current access token ...
token_file = "/home/pi/tesla-scripts/access_token.txt"
# Emoncms server configuration - Node 15
emoncms_apikey = "XXXXXXXXXXXXXXXXXXXXX" # For Node 15 - adjust as appropriate
emoncms_server = "http://127.0.0.1"
node = "Tesla EV" # Name of the Input(tag) that has ALREADY BEEN CREATED to receive the INPUT data
read_interval = 30 # Polling interval in secs
# Get latest Access Token from file ... added FEB 2021
f = open(token_file, "r")
acc_tok = f.read()
# Name each of the data inputs associated with the newly created emoncms Input(tag)
di1 = "ONLINE is 1 and ASLEEP is 0"
di2 = "Battery Charge percent"
di3 = "Battery Range miles"
di4 = "Odometer miles"
# End of User inputs section ===============
async def main():
# Initialise ...
prior_data2 = 1
prior_data3 = 1
prior_data4 = 1
starttime = time.time()
# Request Tesla EV data at user specified read_interval
while True:
# async with Session(email, password) as session: # JB change FEB 2021
async with Session(access_token=acc_tok) as session:
car = await session.vehicles(ev_name)
state_string = str(car.state)
print(state_string) # ######
# Is the EV ONLINE?
if state_string == "online":
state = 1
else:
state = 0
# print(state) # ########
# If ONLINE - update values but if NOT then use prior values
if state is 1:
json_response = await car.vehicle_data()
jsonstr = json.dumps(json_response, default=str)
data = json.loads(jsonstr)
# print(data['response']['charge_state']['battery_level']) # #####
# print(data['response']['charge_state']['battery_range']) # ####
# print(data['response']['vehicle_state']['odometer']) # ####
data2 = data['response']['charge_state']['battery_level']
prior_data2 = data2
data3 = data['response']['charge_state']['battery_range']
prior_data3 = data3
data4 = data['response']['vehicle_state']['odometer']
prior_data4 = data4
# Send data to emoncms (Note - prior values = current values if ONLINE)
data1 = state
dev_data = {di1: data1, di2: prior_data2, di3: prior_data3, di4: prior_data4}
data = {
'node': node,
'data': json.dumps (dev_data),
'apikey': emoncms_apikey
}
response = requests.post(emoncms_server+"/input/post", data=data)
# Sleep until time to next poll the Tesla EV
time.sleep(read_interval - ((time.time() - starttime) % read_interval))
asyncio.run(main())
# FINALLY ONCE THE SCRIPT RUNS OK: Create the tesla-EV.service as follows:
# Do: CTRL-C to stop the script then - Do: sudo nano /etc/systemd/system/tesla-EV.service and copy & paste in the following (using the correct script name) ...
# NOTE User=root ... this is a Carson requirement
"""
[Unit]
Description=Tesla EV Charge Status
After=network.target
After=mosquitto.service
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/bin/python3 /home/pi/tesla_JB+carson.py
[Install]
WantedBy=multi-user.target
"""
# Then save & exit and to ensure the tesla-EV.service runs on boot up - Do: sudo systemctl start tesla-EV.service AND THEN sudo systemctl enable tesla-EV.service
# AS A VERY LAST CHECK - Do: sudo reboot then SSH in again and check the service is active with: systemctl status tesla-EV.service
# Finally close the SSH terminal. The script/service will continue to run surviving any future reboots
Here’s the Input Processing for the EV soc …
Here’s the Input Processing for the openevse soc that was created …
Here’s the Demand Shaper settings that are used …
Finally here’s the script that is run every evening at 11pm to set up Demand Shaper …
# PURPOSE ...
# To be run as a cronjob prior to midnight
# Regardless of whether the EV is connected - to switch openEVSE from DISABLED state to WAITING state
# And to set up the Demand Shaper parameters
# This includes adjusting the parameters which control the Demand Shaper SMART schedule timing
# And this is achieved WITHOUT needing to access the Demand Shaper webpage - hence can be incorporated into a cron run script
# Facility available from emon ver 10.2.7 and later
# Ref: https://community.openenergymonitor.org/t/emoncms-demand-shaper-module/9097/170
# NOTE - Select Battery Charge Level (input)in the 'spanner dropdown' section of the Demand Shaper webpage (Tesla EV)
# The option (set-device-settings api) is used when current_soc is input manually
# ---------------------------------------------------------------
import requests, json, os, time
# User info ...
# SEASON VARIABLE PARAMETER ... target_soc
# Dependent on the Season and the need to leave 'space' in the EV battery for next days ANTICIPATED charge from excess solar gen
# Max is 0.9 - recognises the limit set to conserve EV battery life
# WINTER season = 0.9 There's close to zero excess solar gen because PowerWall captures what there is
# The primary objective is to MINIMISE EXPORT - less than full EV charge maybe a 'price to pay' - subject to a precautionary minimum limit
# Reliable predictions of solar gen are NOT feasible
# ALSO - House & ASHP loads are variable
# ALSO - PowerWall takes first call on any solar gen in excess of House & ASHP loads but only until it is fully charged
# The primary objective is to MINIMISE EXPORT - less than full EV charge maybe a 'price to pay' - subject to a precautionary minimum limit
# The above does assume that the EV is connected during the day in the MID and Pool seasons
# Select the value of the parameter target_soc from one of the following ...
# Then insert in line 101 below ...
# 0.9 for Winter season = Nov > Feb
# 0.7 for MID season = mid Sep > Oct and Mar > Apr
# 0.5 for Pool season = May > mid Sep
# emoncms server configuration ...
emoncms_apikey = "XXXXXXXXXXXXXXXXXXXX" # For Node 15 - use appropriate key
emoncms_server = "http://127.0.0.1"
# PART 1 ###############################################
# Set up openEVSE ...
# Switch Divert Mode (> Normal Fast then > ECO mode) in order to change openEVSE from DISABLED to the WAITING State - THIS MAY NOT BE CORRECT !!!!!
# Note - openEVSE will have been set to DISABLED by another script earlier in the evening ...
os.system("mosquitto_pub -h localhost -r -u emonpi -P emonpimqtt2016 -t emon/openevse/divertmode/set -m 1") # Note: -r to retain message
time.sleep(10)
os.system("mosquitto_pub -h localhost -r -u emonpi -P emonpimqtt2016 -t emon/openevse/divertmode/set -m 2") # Note: -r to retain message
# PART 2 ###############################################
# Get the Demand Shaper key MODES ...
# Demand Shaper MODES are ...
# divert_mode = 1 = ECO mode ON
# divert_mode = 2 = ECO mode OFF
# Other modes: 'smart' 'off' 'on' 'timer'
params = (
('device', 'openevse'),
('apikey', emoncms_apikey)
)
reply = requests.get(emoncms_server+"/demandshaper/get", params=params)
jsonstr = reply.text.rstrip()
ds_data = json.loads(jsonstr)
print(reply.status_code) # Debugging only
# Set the Demand Shaper key MODES ...
# divert_mode = 1 = ECO mode ON
# divert_mode = 2 = ECO mode OFF
# Other modes are: 'smart' 'off' 'on' 'timer'
ds_data['schedule']['settings']['ctrlmode'] = 'smart'
ds_data['schedule']['settings']['on_completion'] = 'smart'
ds_data['schedule']['settings']['divert_mode'] = 1 # ECO mode ON
# Now save the changed key MODES ...
reply = requests.post(emoncms_server+"/demandshaper/save", params=params, data={'schedule':json.dumps(ds_data['schedule'])})
print(reply.status_code)
# PART 3 ###############################################
# Adjust the parameters which control the Demand Shaper SMART schedule timing
params = (
('apikey', emoncms_apikey),
('device', 'openevse'),
# Insert target_soc value in the following line ...
('settings', '{"target_soc":0.5, "battery_capacity":75, "charge_rate":6.9}'), # XXXX Adjust values as appropriate ...
)
reply = requests.get(emoncms_server+"/demandshaper/set-device-settings", params=params)
print(reply.text) # Debugging only
print(reply.status_code)
# FOR VALIDATION ONLY - print the complete & final Demand Shaper json ...
params = (
('device', 'openevse'),
('apikey', emoncms_apikey)
)
reply = requests.get(emoncms_server+"/demandshaper/get", params=params)
jsonstr = reply.text.rstrip()
ds_data = json.loads(jsonstr)
print (ds_data)
print(reply.status_code)
Formatted to remove large bold font. For future reference, when posting code or output, please put 3 ‘backticks’ (normally found at the top left of the keyboard) on a line of their own before the code, and 3 more backticks also on a line of their own after the code:
```
code
```
If it is something like php you can add a language identifier after the first 3 backticks: ```php
or even ```text
if you don’t want any language markup applied.
Moderator (RW)