-
-
Save i3p9/649424fcc8ebebcbcc24f78f9b90f186 to your computer and use it in GitHub Desktop.
import argparse | |
import teslapy | |
import requests | |
from twilio.rest import Client | |
def parse(): | |
parser = argparse.ArgumentParser(description='Tesla Charge Reminder via Twilio') | |
requredParser =parser.add_argument_group('Required arguments') | |
requredParser.add_argument('-e', '--email', help='Email for authentication.',required=True) | |
requredParser.add_argument('-p', '--password', help='Password for authentication.',required=True) | |
parser.add_argument('-o', '--otp', help='OTP for 2FA (OPTIONAL).', default='') #not yet implemented | |
args = parser.parse_args() | |
email = args.email | |
password = args.password | |
otp = args.otp | |
return email, password, otp | |
def checkStatus(email,password,otp): | |
acceptedCharge = 75 | |
with teslapy.Tesla(email, password) as tesla: # add lambda:'passcode' if mfa is enabled, it's not yet supported | |
tesla.fetch_token() | |
vehicles = tesla.vehicle_list() | |
vehicles[0].sync_wake_up() | |
chargePercentage = vehicles[0].get_vehicle_data()['charge_state']['battery_level'] | |
if (vehicles[0].get_vehicle_data()['charge_state']['charging_state'] != "Charging"): | |
if(chargePercentage > acceptedCharge): | |
txt = "Car not plugged in and has "+str(chargePercentage)+"% Charge! Plug it in" | |
sendTextMessage(txt) | |
else: | |
txt = "Car is plugged in and has "+str(chargePercentage)+"% Charge." | |
sendTextMessage(txt) | |
def sendTextMessage(txt): | |
client = Client("ACxxxxxxxxxxxxxx", "zzzzzzzzzzzzz") #Get API and secret from Twilio | |
yourNumber = "+15552221422" | |
client.messages.create(to=yourNumber, from_="+FromNumber", body=txt) | |
if __name__ == '__main__': | |
email, password,otp = parse() | |
checkStatus(email, password,otp) |
@michaelgreed Thanks a lot for the input! I have fixed the string issue (happened probably because I couldn't test the script) and added args instead of hardcoding email/password. As for error handing, can you let me know the specific errors since I've got no way to test the api so that I can implement them. Cheers!
For the 2FA part, I was thinking adding pyOTP
package in a separate script to grab the otp but not sure if it's a good idea security-wise to expose your OTP secret like that. How are you handling yours?
I've taken the approach of splitting this into two programs now. Program #1 can take the password and OTP as inputs and will authenticate with the servers generating the cache file that can then be used repeatedly (for I think up to 30 days) with the second program without needing to reauthenticate):
Tesla_Authenticate.py:
import teslapy
import getpass
def checkStatus():
email = "[email protected]"
password = getpass.getpass("Enter Password:")
otp = input("Enter OTP:")
with teslapy.Tesla(email, password, lambda: otp, lambda _: 'Device #1') as tesla:
tesla.fetch_token()
vehicles = tesla.vehicle_list()
vehicles[0].sync_wake_up()
if __name__ == '__main__':
checkStatus()
Tesla_Probe.py:
import teslapy
import os
def Probe():
email = "[email protected]"
password = "foobar" # doesn't matter, we want the failure if cache is bad
try:
with teslapy.Tesla(email, password) as tesla:
tesla.fetch_token()
vehicles = tesla.vehicle_list()
vehicles[0].sync_wake_up()
vehicles[1].sync_wake_up()
vd0 = vehicles[0].get_vehicle_data()
vd1 = vehicles[1].get_vehicle_data()
if (vd0['charge_state']['charging_state'] == "Disconnected") or (vd0['charge_state']['charging_state'] == "") or (vd0['charge_state']['charging_state'] == ":null"):
Notify_Me('Eeyore charger not connected (SOC: '+str(vd0['charge_state']['battery_level'])+'%, Range: '+str(vd0['charge_state']['battery_range'])+' miles)')
if (vd1['charge_state']['charging_state'] == "Disconnected") or (vd1['charge_state']['charging_state'] == "") or (vd1['charge_state']['charging_state'] == ":null"):
Notify_Me('Red Barron charger not connected (SOC: '+str(vd1['charge_state']['battery_level'])+'%, Range: '+str(vd1['charge_state']['battery_range'])+' miles)')
except:
Notify_Me('Authentication failure, need to reauthenticate ASAP!')
def Notify_Me(message):
os.system('/usr/local/bin/Prowl 0 "Tesla" "'+message+'" ""')
if __name__ == '__main__':
Probe()
This lets me run the first script, prompts me for my password, and then I can pull the current OTP number from my phone to do the authentication. Using pyotp would be a bad idea IMHO, almost as bad as hardcoding passwords (probably worse in this case if you hardcoded BOTH the password and the OTP secret :)
Oh definitely DO NOT want to hardcore otp secret. So okay if the toket lasts for 30 days then it's fine, I was wondering if you need a token frequently, which would cause manual input, thus not really making it "automation". Thanks for you script, I'll try to incorporate into mine. Thank you for all the pointers as well!
Need to str() chargePercentage access as you cannot concatenate an int to a string. Might also want to handle the exception that can be thrown on authentication failure (in my case, I needed to add OTP handling as well and then handle the exception case when the cached credentials no longer were valid and I needed to reauthenticate...not a great idea hard-coding passwords in scripts). Very handy starting point though, thanks!