Skip to content

Commit

Permalink
Updates to support scheduled charging
Browse files Browse the repository at this point in the history
  • Loading branch information
themonomers committed Sep 22, 2024
1 parent 6cb396c commit 3d9a0b6
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 12 deletions.
108 changes: 103 additions & 5 deletions go/vehicle/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,109 @@ func stopChargeVehicle(vin string) map[string]interface{} {
return body
}

// Uses new endpoint to add a schedule for vehicle charging.
// Scheduled Time is in minutes after midnight, e.g. 7:30 AM
// = (7 * 60) + 30 = 450
func AddChargeSchedule(vin string, lat float64, lon float64, sch_time int, id int) map[string]interface{} {
if vin == MX_VIN {
return addChargeSchedule(vin, lat, lon, sch_time, id)
}

var url = BASE_PROXY_URL +
"/vehicles/" +
vin +
"/command/add_charge_schedule"

payload, _ := json.Marshal(map[string]interface{}{
"days_of_week": "All",
"enabled": true,
"start_enabled": true,
"end_enabled": false,
"lat": lat,
"lon": lon,
"start_time": sch_time,
"one_time": false,
"id": id,
})

req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
common.LogError("AddChargeSchedule(): http.NewRequest", err)

req.Header.Set("Content-Type", "application/json")
req.Header.Add("authorization", "Bearer "+ACCESS_TOKEN)
resp, err := getHttpsClient().Do(req)
common.LogError("AddChargeSchedule(): getHttpClient", err)

defer resp.Body.Close()
body := map[string]interface{}{}
json.NewDecoder(resp.Body).Decode(&body)
return body
}

func addChargeSchedule(vin string, lat float64, lon float64, sch_time int, id int) map[string]interface{} {
var url = BASE_OWNER_URL +
"/vehicles/" +
getVehicleId(vin) +
"/command/add_charge_schedule"

payload, _ := json.Marshal(map[string]interface{}{
"days_of_week": "All",
"enabled": true,
"start_enabled": true,
"end_enabled": false,
"lat": lat,
"lon": lon,
"start_time": sch_time,
"one_time": false,
"id": id,
})

req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
common.LogError("addChargeSchedule(): http.NewRequest", err)

req.Header.Set("Content-Type", "application/json")
req.Header.Add("authorization", "Bearer "+ACCESS_TOKEN)
resp, err := http.DefaultClient.Do(req)
common.LogError("addChargeSchedule(): http.DefaultClient.Do", err)

defer resp.Body.Close()
body := map[string]interface{}{}
json.NewDecoder(resp.Body).Decode(&body)
return body
}

// Uses new endpoint to remove a schedule for vehicle charging.
// The Owner API for this function on older model vehicles throws
// an error ("x509: certificate signed by unknown authority") unlike
// other endpoints. This endpoint works for both newer and older model
// cars.
func RemoveChargeSchedule(vin string, id int) map[string]interface{} {
var url = BASE_PROXY_URL +
"/vehicles/" +
vin +
"/command/remove_charge_schedule"

payload, _ := json.Marshal(map[string]interface{}{
"id": id,
})

req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
common.LogError("RemoveChargeSchedule(): http.NewRequest", err)

req.Header.Set("Content-Type", "application/json")
req.Header.Add("authorization", "Bearer "+ACCESS_TOKEN)
resp, err := getHttpsClient().Do(req)
common.LogError("RemoveChargeSchedule(): getHttpClient", err)

defer resp.Body.Close()
body := map[string]interface{}{}
json.NewDecoder(resp.Body).Decode(&body)
return body
}

// Per Tesla (https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-commands#set-scheduled-charging):
// This endpoint is not recommended beginning with firmware version 2024.26.
//
// Sends command and parameter to set a specific vehicle to charge
// at a scheduled time. Scheduled Time is in minutes after midnight,
// e.g. 7:30 AM = (7 * 60) + 30 = 450
Expand Down Expand Up @@ -182,11 +285,6 @@ func setScheduledCharging(vin string, sch_time int) map[string]interface{} {
"enable": true,
"time": sch_time,
})
/*
payload = {
'enable': 'True',
'time': time
}*/

req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
common.LogError("setScheduledCharging(): http.NewRequest", err)
Expand Down
9 changes: 7 additions & 2 deletions go/vehicle/charge.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ func scheduleM3Charging(m3_data map[string]interface{}, mx_data map[string]inter

total_minutes := (start_time.Hour() * 60) + start_time.Minute()

SetScheduledCharging(M3_VIN, total_minutes)
// Remove any previous scheduled charging by this program, temporarily set to
// id=1, until I can figure out how to view the list of charge schedules and
// their corresponding ID's.
RemoveChargeSchedule(M3_VIN, 1)
AddChargeSchedule(M3_VIN, m3_data["response"].(map[string]interface{})["drive_state"].(map[string]interface{})["latitude"].(float64), m3_data["response"].(map[string]interface{})["drive_state"].(map[string]interface{})["longitude"].(float64), total_minutes, 1)
StopChargeVehicle(M3_VIN) // for some reason charging starts sometimes after scheduled charging API is called

// send email notification
Expand Down Expand Up @@ -198,7 +202,8 @@ func scheduleMXCharging(m3_data map[string]interface{}, mx_data map[string]inter

total_minutes := (start_time.Hour() * 60) + start_time.Minute()

SetScheduledCharging(MX_VIN, total_minutes)
RemoveChargeSchedule(MX_VIN, 1)
AddChargeSchedule(MX_VIN, mx_data["response"].(map[string]interface{})["drive_state"].(map[string]interface{})["latitude"].(float64), mx_data["response"].(map[string]interface{})["drive_state"].(map[string]interface{})["longitude"].(float64), total_minutes, 1)
StopChargeVehicle(MX_VIN) // for some reason charging starts sometimes after scheduled charging API is called

// send email notification
Expand Down
11 changes: 8 additions & 3 deletions python/Charger.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from TeslaVehicleAPI import getVehicleData, setScheduledCharging, stopChargeVehicle
from TeslaVehicleAPI import getVehicleData, addChargeSchedule, removeChargeSchedule, stopChargeVehicle
from GoogleAPI import getGoogleSheetService
from Email import sendEmail
from Climate import setM3Precondition, setMXPrecondition
Expand Down Expand Up @@ -57,7 +57,11 @@ def scheduleM3Charging(m3_data, mx_data, m3_target_finish_time, mx_target_finish

total_minutes = (start_time.hour * 60) + start_time.minute

setScheduledCharging(M3_VIN, total_minutes)
# Remove any previous scheduled charging by this program, temporarily set to
# id=1, until I can figure out how to view the list of charge schedules and
# their corresponding ID's.
removeChargeSchedule(M3_VIN, 1)
addChargeSchedule(M3_VIN, m3_data['response']['drive_state']['latitude'], m3_data['response']['drive_state']['longitude'], total_minutes, 1)
stopChargeVehicle(M3_VIN) # for some reason charging starts sometimes after scheduled charging API is called

# send email notification
Expand Down Expand Up @@ -103,7 +107,8 @@ def scheduleMXCharging(m3_data, mx_data, m3_target_finish_time, mx_target_finish

total_minutes = (start_time.hour * 60) + start_time.minute

setScheduledCharging(MX_VIN, total_minutes)
removeChargeSchedule(MX_VIN, 1)
addChargeSchedule(MX_VIN, mx_data['response']['drive_state']['latitude'], mx_data['response']['drive_state']['longitude'], total_minutes, 1)
stopChargeVehicle(MX_VIN) # for some reason charging starts sometimes after scheduled charging API is called

# send email notification
Expand Down
72 changes: 72 additions & 0 deletions python/TeslaVehicleAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,75 @@ def stopChargeVehicle(vin):


##
# Uses new endpoint to add a schedule for vehicle charging.
# Scheduled Time is in minutes, e.g. 7:30 AM =
# (7 * 60) + 30 = 450
#
# author: [email protected]
##
def addChargeSchedule(vin, lat, lon, start_time, id):
try:
if vin == M3_VIN:
return TeslaVehicleCommandProxy.addChargeSchedule(vin, lat, lon, start_time, id)

url = (BASE_OWNER_URL
+ '/vehicles/'
+ getVehicleId(vin)
+ '/command/add_charge_schedule')

payload = {
'days_of_week': 'All',
'enabled': True,
'start_enabled': True,
'end_enabled': False,
'lat': lat,
'lon': lon,
'start_time': start_time,
'one_time': False,
'id': id
}

return requests.post(
url,
json=payload,
headers={'authorization': 'Bearer ' + ACCESS_TOKEN}
)
except Exception as e:
logError('addChargeSchedule(' + vin + '): ' + str(e))


##
# Uses new endpoint to remove a schedule for vehicle charging.
#
# author: [email protected]
##
def removeChargeSchedule(vin, id):
try:
if vin == M3_VIN:
return TeslaVehicleCommandProxy.removeChargeSchedule(vin, id)

url = (BASE_OWNER_URL
+ '/vehicles/'
+ getVehicleId(vin)
+ '/command/remove_charge_schedule')

payload = {
'id': id
}

return requests.post(
url,
json=payload,
headers={'authorization': 'Bearer ' + ACCESS_TOKEN}
)
except Exception as e:
logError('removeChargeSchedule(' + vin + '): ' + str(e))


##
# Per Tesla (https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-commands#set-scheduled-charging):
# This endpoint is not recommended beginning with firmware version 2024.26.
#
# Sends command and parameter to set a specific vehicle to charge
# at a scheduled time. Scheduled Time is in minutes, e.g. 7:30 AM =
# (7 * 60) + 30 = 450
Expand Down Expand Up @@ -131,6 +200,9 @@ def setScheduledCharging(vin, time):


##
# Per Tesla (https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-commands#set-scheduled-departure):
# This endpoint is not recommended beginning with firmware version 2024.26.
#
# Sends command and parameters to set a specific vehicle to charge and/or
# precondition by a departure time. Departure Time and Off-Peak Charge End
# Time are in minutes, e.g. 7:30 AM = (7 * 60) + 30 = 450
Expand Down
72 changes: 70 additions & 2 deletions python/TeslaVehicleCommandProxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def getVehicleData(vin):
+ 'vehicle_state;'
+ 'gui_settings;'
+ 'vehicle_config;'
+ 'closures_state;'
+ 'drive_state'))
+ 'drive_state;'
+ 'charge_schedule_data'))

urllib3.disable_warnings(urllib3.exceptions.SubjectAltNameWarning)

Expand Down Expand Up @@ -101,8 +101,76 @@ def stopChargeVehicle(vin):
except Exception as e:
logError('stopChargeVehicle(' + vin + '): ' + str(e))

##
# Uses new endpoint to add a schedule for vehicle charging.
# Scheduled Time is in minutes, e.g. 7:30 AM =
# (7 * 60) + 30 = 450
#
# author: [email protected]
##
def addChargeSchedule(vin, lat, lon, start_time, id):
try:
url = (BASE_PROXY_URL
+ '/vehicles/'
+ vin
+ '/command/add_charge_schedule')

payload = {
'days_of_week': 'All',
'enabled': True,
'start_enabled': True,
'end_enabled': False,
'lat': lat,
'lon': lon,
'start_time': start_time,
'one_time': False,
'id': id
}

urllib3.disable_warnings(urllib3.exceptions.SubjectAltNameWarning)

return requests.post(
url,
json=payload,
headers={'authorization': 'Bearer ' + ACCESS_TOKEN},
verify=CERT
)
except Exception as e:
logError('addChargeSchedule(' + vin + '): ' + str(e))


##
# Uses new endpoint to remove a schedule for vehicle charging.
#
# author: [email protected]
##
def removeChargeSchedule(vin, id):
try:
url = (BASE_PROXY_URL
+ '/vehicles/'
+ vin
+ '/command/remove_charge_schedule')

payload = {
'id': id
}

urllib3.disable_warnings(urllib3.exceptions.SubjectAltNameWarning)

return requests.post(
url,
json=payload,
headers={'authorization': 'Bearer ' + ACCESS_TOKEN},
verify=CERT
)
except Exception as e:
logError('removeChargeSchedule(' + vin + '): ' + str(e))


##
# Per Tesla (https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-commands#set-scheduled-charging):
# This endpoint is not recommended beginning with firmware version 2024.26.
#
# Sends command and parameter to set a specific vehicle to charge
# at a scheduled time. Scheduled Time is in minutes, e.g. 7:30 AM =
# (7 * 60) + 30 = 450
Expand Down

0 comments on commit 3d9a0b6

Please sign in to comment.