Git Product home page Git Product logo

Comments (7)

venturanc avatar venturanc commented on July 20, 2024 1

Sure @jasonacox - I did a writeup about how to use PyPowerwall with SmartThings here:

I need to spend some more time cleaning this up, and perhaps add a web API call to start/stop the solar charging, and maybe tie it all together with pypowerwall with a docker-compose file, and get it published to my own github (after I figure out how to have it read/use the environmental variables to get the tesla login data and input a tesla refresh token, lat/lon and py-powerwall_IP variables - I'll refer to your code on the environmental variables stuff), but here's the quick and dirty functional python script:

"""
 Adapted by Nate Carroll 03/2023 from TesSense w/ SenseLink  -Randy Spencer 2023 Version 9.7
 Python charge monitoring utility for those who have a Tesla Powerwall.
 Uses jasonacox/pypowerwall docker container to get realtime stats from the local gateway for Production 
 and Grid Utilization of electricity to control
 your main Tesla's AC charging amps to charge only with excess production.
 Simply plug in your car, update your info below, and run this in python.
 
 Added: checking of location of Tesla to be sure it's charging at home
 Added: tracks cabin temp and local chargers, vents the car if it gets too hot 
 - removed due to Tesla removal of Vent option from API
"""

username = '[email protected]' # Fill in Tesla login email address/account
lat, lon  = ##.###, -###.###        # Fill in Location where charging will occur (shown at startup)
pypowerwall_IP = '10.x.x.x:8675'	# Fill in IP address and port for pypowerwall docker container
    
RedTxt, BluTxt, NormTxt = '\033[31m', '\033[34m', '\033[m'
RedBG, GrnBG, NormBG = '\033[101m', '\033[102m', '\033[0m'

import datetime, asyncio
import logging, sys#, json
# pip3 install teslapy
import teslapy
#/c Set stdout as logging handler
root_log = logging.getLogger()
root_log.setLevel(logging.WARNING) # WARNING or INFO or DEBUG
handler = logging.StreamHandler(sys.stdout)

def printerror(error, err) :                               # Error message with truncated data
    print(str(err).split("}")[0], "}\n", datetime.datetime.now().strftime( "%a %I:%M %p" ), error)

def printmsg(msg) :                                        # Timestamped message
    print( " ", datetime.datetime.now().strftime( "%a %I:%M %p" ), msg )
    
def PrintUpdate(chargedata, fast) :                        # Display stats at every % change
    print( "\nLevel:",
        chargedata['battery_level'], "%, Limit",
        chargedata['charge_limit_soc'], "%,",
        chargedata['charge_rate'], "MPH",
        chargedata['charger_voltage'], "Volts",
        chargedata['charge_energy_added'], "kWh added,")
    if fast : print("Rate:",
        chargedata['charger_power'], "KWs",
        chargedata['conn_charge_cable'],
        chargedata['fast_charger_type'],
        chargedata['minutes_to_full_charge'], "Minutes remaining\n" )
    else : print(chargedata['charger_actual_current'], "of a possible",
        chargedata['charge_current_request_max'], "Amps,",
        chargedata['time_to_full_charge'], "Hours remaining\n" )

def PrintTemp(car) :
    #Tesla removed vent API in USA due to NTHSA ~01/2023 - causes API error
	#if car.get_vehicle_data()['climate_state']['inside_temp'] > 40 : # 104°F
    #    if not car.get_vehicle_data()['vehicle_state']['fd_window'] : # Not Open
    #        Vent(car, 'vent')
    #else :
    #    if car.get_vehicle_data()['vehicle_state']['fd_window'] :    # Open
    #        Vent(car, 'close')
    print(car.temp_units(car.get_vehicle_data()['climate_state']['inside_temp']), end='')
    if car.get_vehicle_data()['climate_state']['fan_status'] : print(car.get_vehicle_data()['climate_state']['fan_status'], end='')
    if car.get_vehicle_data()['climate_state']['cabin_overheat_protection_actively_cooling'] : print(car.get_vehicle_data()['climate_state']['cabin_overheat_protection_actively_cooling'], end='')

        
def SendCmd(car, cmd, err) :                               # Start or Stop charging
    try :
        car.command(cmd)
    except teslapy.VehicleError as e :
        print(err)
        printmsg(e)

def SetAmps(car, newrate, err) :                           # Increase or decrease charging rate
    try :
        car.command('CHARGING_AMPS', charging_amps = newrate)
    except teslapy.VehicleError as e : printerror("V: "+err, e)
    except teslapy.HTTPError as e: printerror("H: "+err, e)

def SetCharging(car, newrate, msg) :
    print(msg, "charging to", newrate, "amps")
    if newrate == 2 : newrate = 1                          # For API a newrate of 3=3, 2=3, 1=2
    SetAmps(car, newrate, "Failed to change")              #  so to set to 2 newrate must be 1
    if newrate < 5 :                                       # if under 5 amps you need to send it twice:
        SetAmps(car, newrate, "Failed to change 2")

def StartCharging(car) :
    try :                                                  # Collect new data from Tesla
        state = car.get_vehicle_data()['charge_state']['charging_state']
    except teslapy.HTTPError as e:
        printerror("Tesla failed to update, please wait a minute...", e)
        return
    print(GrnBG + "Starting" + NormBG + " charge at 2 Amps")     # Underlined
    if state != "Charging" :
        SendCmd(car, 'START_CHARGE', "Won't start charging")
        SetAmps(car, 1, "Won't start charging 2")
        SetAmps(car, 1, "Won't start charging 3")

def StopCharging(car) :
    print( RedBG + "Stopping" + NormBG + " charge" )            # Underlined
    SendCmd(car, 'STOP_CHARGE', "Failed to stop")

def SuperCharging(chargedata) :                            # Loop while DC Fast Charging
    if chargedata['fast_charger_present']:
        printmsg("DC Fast Charging...")
        PrintUpdate(chargedata, 1)
        return(True)
    
def Wake(car) :
    printmsg("Waking...")
    try : car.sync_wake_up()
    except teslapy.VehicleError as e :
        printerror("Failed to wake", e)
        return(False)
    return(True)

def update_powerwall(): #get site data on solar/grid from local Powerwall gateway via pypowerwall docker container
    instant_stats = requests.get('http://'+pypowerwall_IP+'/aggregates')
    return (instant_stats.json())

def UpdateSense() :  # Update Powerwall and charger voltage
    global power_diff, volts
    try :
        power_diff = update_powerwall()['site']['instant_power']
        volts = int(vehicles[0].get_vehicle_data()['charge_state']['charger_voltage'])
    except :
        printmsg(RedTxt + "Powerwall data timeout or cannot get charger voltage" + NormTxt)
        power_diff = 0
        return(True)
    else :
        #volts = int(car.get_vehicle_data()['charge_state']['charger_voltage'])
        power_diff = int(update_powerwall()['site']['instant_power'])*-1
        
def Vent(car, command) :
    try :  car.command('WINDOW_CONTROL', command = command, lat=lat, lon=lon)
    except teslapy.VehicleError as e : printmsg("Window_Control Failed " + str(e))
    else:  print(RedTxt + "Windows will now", command + NormTxt)
	
async def TesSense() :
    rate = newrate = limit = level = lastime = fullORunplugged = 0
    minrate = 2                                            # Minimum rate you can set the charger to

    retry = teslapy.Retry(total=3, status_forcelist=(500, 502, 503, 504))
    
    
    with teslapy.Tesla(username, retry=retry, timeout=30) as tesla:
        if not tesla.authorized:
            print('Use browser to login. Page Not Found will be shown at success.')
            print('Open this URL: ' + tesla.authorization_url())
            tesla.fetch_token(authorization_response=input('Enter URL after authentication: '))
        vehicles = tesla.vehicle_list()

        print("Starting connection to", vehicles[0].get_vehicle_summary()['display_name'], end='')
        cardata = vehicles[0].get_vehicle_data()
        try:
            print("... [", round(cardata['drive_state']['latitude'], 3), round(cardata['drive_state']['longitude'], 3), "]")
        except: pass
        #print(' last seen ' + vehicles[0].last_seen(), end='') #last seen timestamp in future error
        if vehicles[0]['charge_state']['battery_level']:
            print(' at ' + str(vehicles[0]['charge_state']['battery_level']) + '% SoC\n')
        else: print('\n')

        while (True):                                      # Main loop with night time carve out
            if vehicles[0].get_vehicle_summary()['in_service'] :
                print("Sorry. Currently this car is in for service")
                exit()
        
            if datetime.datetime.now().time().hour < 8 or datetime.datetime.now().time().hour >= 16 :
                printmsg(BluTxt + "Nighttime" + NormTxt +", Sleeping until next hour...")
                if 16 <= datetime.datetime.now().time().hour <= 17 :
                    StopCharging(vehicles[0])
                    printmsg(BluTxt + "4-9pm" + NormTxt +", Stop charging...") #4-9pm peak rate when PowerWall is powering house, so don't charge car and drain powerwall
                await asyncio.sleep(60 * (60 - datetime.datetime.now().time().minute))
                continue

            if UpdateSense() :                             # Collect new data from Energy Monitor
                await asyncio.sleep(20)                    # Error: Return to top of order
                continue

            minwatts = minrate * volts                     # Calc minwatts needed to start charging
                
            if not vehicles[0].available() :               # Car is sleeping
                if power_diff > minwatts and not fullORunplugged :
                    if Wake(vehicles[0]):                  # Initial daytime wake() also, to get status
                        rate = newrate = 0                 # Reset rate as things will have changed
                        continue
                    else:
                        print("Wake error. Sleeping 20 minutes and trying again")
                        await asyncio.sleep(1200)          # Give the API a chance to find the car
                        continue
                else :
                    if fullORunplugged == 1 : print("Full-", end='')
                    elif fullORunplugged == 2 : print("Unplugged-", end='')
                    print("Sleeping, free power is", power_diff, "watts" )
                    if fullORunplugged :
                        printmsg(" Wait twenty minutes...")
                        await asyncio.sleep(1200)
                        continue

            else :                                         # Car is awake
                try :
                    cardata = vehicles[0].get_vehicle_data() # Collect new data from Tesla
                    chargedata = cardata['charge_state']
                except teslapy.HTTPError as e:
                    printerror("Tesla failed to update, please wait a minute...", e)
                    await asyncio.sleep(60)                # Error: Return to top of order
                    continue

                if SuperCharging(chargedata) :             # Display any Supercharging or DCFC data
                    await asyncio.sleep(120)               # Loop while Supercharging back to top
                    continue

                if 'latitude' in cardata['drive_state'] :  # Prevent remote charging issues
                    if round(cardata['drive_state']['latitude'], 3) != lat and \
                       round(cardata['drive_state']['longitude'], 3) != lon :
                        print(round(cardata['drive_state']['latitude'], 3), \
                             round(cardata['drive_state']['longitude'], 3), end='')
                        printmsg(' Away from home. Wait 5 minutes')
                        fullORunplugged = 2                 # If it's not at home, it's not plugged in nor full
                        await asyncio.sleep(300)
                        continue
                else :
                    print(RedTxt + 'Error: No Location' + NormTxt)
                    
                if not chargedata['charging_state'] == "Charging" :   # Not charging, check if need to start

                    if power_diff > minwatts and not fullORunplugged: # Minimum free watts to start charge
                        if chargedata['battery_level'] >= chargedata['charge_limit_soc'] :
                            print("Full Battery, power at", power_diff, "watts" )
                            fullORunplugged = 1
                        elif chargedata['charging_state'] == "Disconnected":
                            print(RedTxt + "Please plug in" + NormTxt + ", power at", power_diff, "watts" )
                            fullORunplugged = 2
                        else :                             # Plugged in and battery is not full so
                            StartCharging(vehicles[0])

                    else :
                        print( "Not Charging, free power is at", power_diff, "watts" )

                else :                                     # Charging, update status
                    if chargedata['battery_level'] < chargedata['charge_limit_soc'] :
                        fullORunplugged = 0                # Mark it as NOT full and AS plugged-in

                    if  level != chargedata['battery_level'] or limit != chargedata['charge_limit_soc'] :
                        level, limit = chargedata['battery_level'], chargedata['charge_limit_soc']
                        PrintUpdate(chargedata, 0)         # Display charging info every % change
                        
                    rate = chargedata['charger_actual_current']
                    newrate = min(rate + int(power_diff/volts), chargedata['charge_current_request_max'])
                                                           
                    print( "Charging at", rate, "amps, with", power_diff, "watts surplus" )

                    if newrate < minrate :                 # Stop charging as there's no free power
                        StopCharging(vehicles[0])
                        newrate = 0
                    elif newrate > rate :                  # Charge faster with any surplus
                        SetCharging(vehicles[0], newrate, "Increasing")
                    elif newrate < rate :                  # Charge slower due to less availablity
                        SetCharging(vehicles[0], newrate, "Slowing")


            if lastime != vehicles[0].get_vehicle_data()['climate_state']['timestamp'] :
                lastime = vehicles[0].get_vehicle_data()['climate_state']['timestamp']
                PrintTemp(vehicles[0])                     # Display cabin temp and fan use
            printmsg(" Wait two minutes...")               # Message after every complete loop
            await asyncio.sleep(120)                       # Could use variable to change frequency of updates, but 2 minutes seems reasonable without hitting Tesla API frequently enough to cause lockout

#run the main program
try:
    await TesSense()
except KeyboardInterrupt:
    print("\n\n Interrupt received, stopping TesSense\n")

from pypowerwall.

venturanc avatar venturanc commented on July 20, 2024 1

I'd be honored to submit a PR to /tools and contribute! Give me just a few days to sort everything out and hopefully will get it in soon.

from pypowerwall.

jasonacox avatar jasonacox commented on July 20, 2024

Hi @venturanc!

How are you running the proxy? Docker or direct via command line? There are some envrionmental settings that change the color settings (see https://github.com/jasonacox/pypowerwall/tree/main/proxy/web):

# default
PW_STYLE=clear

# others
PW_STYLE=black
PW_STYLE=white
PW_STYLE=dakboard
PW_STYLE=grafana

Also, try this: http://x.x.x.x:8675/example.html - it should render an iFrame example like this:

image

from pypowerwall.

venturanc avatar venturanc commented on July 20, 2024

Ahh! Thanks! I'm running the docker version. The example html link does work too. Sorry that I missed that. I'll close this issue that was my user error.

BTW, thanks so much for developing this! I'm using this to get Tesla Powerwall info into Smartthings for home automation (switching off devices if grid outage, reminders to do laundry/dishes when the powerwall is full and excess solar production etc.)

I also have a python script that gets the grid power usage every 2 minutes from pypowerwall during solar production time and if my car is charging, it uses Teslapy to adjust the car charging speed to try and maintain minimal to no grid exports to avoid non-bypassable charges, and it stops charging at 4pm when the 4-9 peak period starts to not drain the powerwall. it was pretty easy to modify the TesSense project and get the data from pypowerwall instead of Sense!

from pypowerwall.

jasonacox avatar jasonacox commented on July 20, 2024

Thanks @venturanc ! I would love to see some of your scripts if you are ever willing to share. Post them here if it is easy or we have a section where cool automation contributions can be added: https://github.com/jasonacox/pypowerwall/tree/main/tools - No pressure. Thanks for the kind words. 🙏

from pypowerwall.

jasonacox avatar jasonacox commented on July 20, 2024

This is awesome! Thanks @venturanc 🙏

Do you mind if I add this to our /tools section (also happy to add a link to your github location once you have it)? Or you could submit a PR youself to get listed as a pypowerwall contributor. 😉

from pypowerwall.

jasonacox avatar jasonacox commented on July 20, 2024

Thanks @venturanc !

from pypowerwall.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.