Start a new topic

Off grid solar charging python script

Aloha from  Hawai'i :)  I have been searching this forum to see if anyone has posted this before, and it looks people have but didn't find a solution.


I have an off grid solar setup that I use to charge the 4 EV that our household has.  I use the Open EVSE and a Shelly UNI to modulate the EV charging rate.  I do this thru a python script.  The function of the script is to monitor the voltage of the battery connected to my solar inverter.  When the battery is full, the charge of the EV is started.  After starting the charge the script attempts to maintain a set voltage thru charge rate adjustments.  If the voltage falls below a set threshold then charging is ended until it rises above the start charge setpoint.  


I'm sure there is plenty of room for improvement and maybe an easier way to accomplish this task, however I have hopes this helps those that are looking and maybe starts a discussion for and easier way :)


The Shelly uni is only $12, it can do up to a 30V without any extras, much more with the addition of a voltage divider.  https://www.shelly.com/en-us/products/shop/shelly-uni-us


This is the script I wrote, my coding is not good and I have never had any training whatsoever in code :(


import requests

import traceback

import time

# Setpoint voltage for maintaining charging

setpoint_voltage = XX.X # Define the setpoint voltage

# Voltage at which charging should start

start_voltage = XX.X # Define the start voltage

# Voltage at which charging should stop

stop_voltage = XX.X # Define the stop voltage

# Minimum and maximum charge rates in amps

min_current = XX # Define the minimum charge rate

max_current = XX # Define the maximum charge rate

# Maximum number of retries

max_retries = 10

# Shelly device API URL

shelly_api_url = "http://192.168.1.XXX/status"

shelly_username = "XXXXXXXXX"

shelly_password = "XXXXXXXXXX"

# OpenEVSE API URL

openevse_api_url = "http://openevse-XXXX.local"

openevse_client_id = 1 # Define the OpenEVSE client ID

# Function to get current voltage from Shelly device

def get_voltage():

    retries = 0

    while retries < max_retries:

        try:

            response = requests.get(shelly_api_url, auth=(shelly_username, shelly_password))

            if response.status_code == 200:

                data = response.json()

                adc_voltage = data.get("adcs", [])[0].get("voltage", None)

                if adc_voltage is not None:

                    return adc_voltage

                else:

                    print("ADC voltage data not found in response")

                    return None

            else:

                print("Failed to fetch voltage data from Shelly device. Status code:", response.status_code)

                return None

        except requests.RequestException as e:

            print("Request failed:", e)

            retries += 1

            print("Retrying...")

            time.sleep(5) # Wait before retrying

    print("Max retries reached. Could not fetch voltage data.")

    return None

# Function to control OpenEVSE charging based on ADC voltage

def control_charging():

    charge_current = 0 # Start with no charging

    charging_started = False # Flag to track if charging was initiated

    voltage_below_stop = False # Flag to track if voltage fell below stop voltage after charging started

    manual_start = False # Flag to track manual start

    # Manual start option (runs once at the beginning)

    manual_start_input = input("Do you want to start charging manually? (yes/no): ").strip().lower()

    if manual_start_input == "yes":

        charging_started = True

        manual_start = True

        print("Charging started manually.")

    else:

        manual_start = False

    while True:

        voltage = get_voltage()

        if voltage is not None:

            print("Current voltage:", voltage)

            # Start charging when voltage exceeds start voltage or manually started

            if (voltage >= start_voltage and not charging_started) or manual_start:

                charge_current = min_current

                charging_started = True

                voltage_below_stop = False # Reset the flag

                manual_start = False # Reset manual start flag

                print("Starting charging at minimum rate")

            # Control charge current based on voltage

            if charging_started:

                if voltage > setpoint_voltage:

                    charge_current = min(max_current, charge_current + 1)

                    print("Increasing charge rate by 1 Amps. New charge rate:", charge_current)

                elif voltage < setpoint_voltage:

                    if charge_current > min_current:

                        charge_current = max(min_current, charge_current - 1) # Enforce min_current

                        print("Decreasing charge rate by 1 Amps. New charge rate:", charge_current)

                    elif charge_current == min_current:

                        print("Charge rate is already at minimum.")

            print("Charge current:", charge_current)

            # Stop charging if voltage falls below stop_voltage

            if charging_started and voltage <= stop_voltage:

                charge_current = 0

                voltage_below_stop = True

                charging_started = False # Ensure charging is stopped

                print("Stopping charging due to voltage below stop_voltage")

            # Restart charging if voltage goes back above start_voltage after previously stopping

            if voltage >= start_voltage and voltage_below_stop:

                voltage_below_stop = False # Reset the flag

                print("Voltage back above start_voltage. Waiting for start_voltage to restart charging.")

            # Make/update claim on OpenEVSE with adjusted charge current

            payload = {

                "state": "active" if charge_current > 0 else "disabled",

                "charge_current": charge_current,

                "max_current": max_current,

                "auto_release": True

            }

            retries = 0

            while retries < max_retries:

                try:

                    response = requests.post(f"{openevse_api_url}/override", json=payload)

                    print("Response from OpenEVSE API:", response.text)

                    break

                except requests.RequestException as e:

                    print("Request failed:", e)

                    retries += 1

                    print("Retrying OpenEVSE command...")

                    time.sleep(5) # Wait before retrying

            if retries == max_retries:

                print("Max retries reached. Could not send command to OpenEVSE API.")

        time.sleep(30) # Adjust the polling interval as needed

# Start controlling charging

try:

    control_charging()

except Exception as e:

    print("An unexpected error occurred:", e)

    traceback.print_exc()


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


1 person likes this idea

Your Mac book will work for now :) but a pi would be better long term ;) there are plenty of recourses out there to help setting up the pi. I’m pretty sure python is built into the pi OS? So that saves a step :)

It sounds like a Pi3 uses less power and is less cost. What its yours?

G

Even the cheapest pi could run this with no drama. :). Whatever you get will be perfect ;)

OK! I think I have all parts ordered or here. I'll VERY likely post more questions when I start in on it.....

G

Howzit going?
Looking to copy/paste your excellent efforts.
What have you all decided to use for running code to monitor the Shelly Uni and control the "Advanced Series 48A Level 2 - SAE J1772" (I assume...)
Thanks

Hey James - does that Shelly support an a/c CT as well? I don't want the charger to overload my inverter when my wife runs the dryer, washer, hot pot, hairdryer all at the same time.
So I'm hoping to throttle the EV charging current when the house total loads (including the EV charger) approach my inverter's limit.

Again - really appreciate you sharing your work - giving confidence to the rest of us.

Stay well, Randy / BWC

(yet) Another question - how does your EV charger respond when (Heaven forbid) your CPU running the python script goes down?
Do you do any 'keep alive' pinging or other health check to leave the desired state when the code isn't running?


Curious - Randy

Aloha :) for that you’re probably better off going the home assistant route for the additional throttling. This script is designed to only use one variable for evse control. You can then do some complicated stuff with home assistant without any code experience. I have a multi evse system that does load balancing and throttling based on inverter load that would be easy to make a blueprint of if your interested.
Aloha, forgot to mention.. a solar assistant tied in with the home assistant can really open up an unlimited amount of opportunities for automation. Then you really wouldn’t need the Shelly devices at all.
In that case it just keeps doing what it’s doing. It’s always good to have a safety net if it’s critical to shut off in certain situations

Mahalo James - what is "home assistant" (and solar assistant)?

@boulderwise Home Assistant is an Open Source Home Automation platform. It supports a ton of hardware and allows automation without programming knowledge. https://www.home-assistant.io/


Solar Assistant is similar, a solar platform that supports a lot of inverters and integrates into Home Assistant. https://solar-assistant.io/


Both are rabbit holes, you should explore...

Login or Signup to post a comment