EV car power management

I have tested your code and settings on my IONIQ electric MY18 @AndersO, but i only got messages about AutoPi is going to sleep


No charging messages att all

@el93mon
Hej Mats,
I’m afk today til Sunday, with only my phone.
The script only enables our disables sleep. I have another script that send messages about charging. I can post that when I get back.
You should get messages with sleep enables as well though from this one. If the # is removed.

Not work for me at all.
I am novice and understand that i do something wrong but i don´t now what?
I test custom code direct in the prompt and get answer : ‘’‘import’’ is not available.’

Hej Anders!

I thought something was missing, nice if you can share the code later. :slight_smile:

/Mats

Hej Mats.

Here is the code for the charging.
I just added the GOM range, but I cannot get the air temp, so I am using battery temp, I think it works ok, but not when the battery is warm from driving, the range is to long. I will try to get the air temp a bit more but if i cannot I will change that to a fixed value when the temp is above something like 22 degrees (then keep 22 when warmer), to be decided.

import logging
import requests
import pickle
import os
from enum import Enum

log = logging.getLogger(__name__)

# Telegram tokens - see https://www.mariansauter.de/2018/01/send-telegram-notifications-to-your-mobile-from-python-opensesame/
#
BOT_TOKEN = '888888888888888888xxxxxxxxxxxxxxxxxxx888888888888888'
BOT_CHATID = '888888888'



def checkChargeStatus():

    # load previous status
    #
    persistance = load()

    # check if we are driving or charging
    #
    charging = get_charging()
    if charging == 0 or charging == -1:
        if persistance['charging'] == True:
            bot_sendtext("Charging stopped. Last known State of charge "+format(persistance['SOC'],'.1f')+"% BMS")
            #bot_sendtext("Charging stopped. Last known State of charge "+format(persistance['SOC'],'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display")
            persistance['charging'] = False
            save(persistance)
        return {"msg": "Not charging"} # Does nothing ?


    chargingPower = get_charging_power()
    soc = get_soc()
    soc_display = get_soc_display()
    temp = get_temp()

    # alert if just started to charge
    #
    if persistance['charging'] == False:
        bot_sendtext("Charging started at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")


    # 40% alert
    #
    if soc >= 40 and persistance['SOC'] < 40:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

    # 50% alert
    #
    if soc >= 50 and persistance['SOC'] < 50:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

    # 60% alert
    #
    if soc >= 60 and persistance['SOC'] < 60:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

    # 70% alert
    #
    if soc >= 70 and persistance['SOC'] < 70:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

    # 80% alert
    #
    if soc >= 80 and persistance['SOC'] < 80:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

   # 90% alert
    #
    if soc >= 90 and persistance['SOC'] < 90:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

    # 100% alert ... not sure if this can really happen
    #
    if soc >= 94 and persistance['SOC'] < 94:
        bot_sendtext("Charging now at a rate of "+format(2*chargingPower,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"% BMS - "+format(soc_display,'.1f')+ "% Display GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km")

    # store status for next time
    #
    persistance['charging'] = True
    persistance['SOC'] = soc
    save(persistance)

    #return {"msg": "Charging at "+format(chargingPower,'.2f')+"kW, SOC now "+format(soc,'.1f')+"% "+format(temp,'.1f')+" temp"}
    #return {"msg": "Charging at "+format(chargingPower,'.2f')+"kW, SOC now "+format(soc,'.1f')+"% "+format(0.01*soc*2800/(16.08813 + (temp*(-0.13873))),'.1f')+" km"}
    return {"msg": "Charging at "+format(chargingPower,'.2f')+"kW, SOC (BMS) now "+format(soc,'.1f')+"% GOM now "+format(0.01*soc_display*2800/(16.08813 + (temp*(-0.13873))),'.0f')+"km"}

# send message to telegram
#
def bot_sendtext(message):
    send_text = 'https://api.telegram.org/bot' + BOT_TOKEN + '/sendMessage?chat_id=' + BOT_CHATID + '&parse_mode=Markdown&text=' + message
    requests.get(send_text)

# load persistance
#
def load():
    try:
        persistance = pickle.load( open( 'charge_status.p', 'rb' ) )
    except:
        persistance = { 'charging': False, 'SOC': 0 }

    return persistance

# save persistance
#
def save(persistance):
    pickle.dump( persistance, open( "charge_status.p", "wb" ) )

# delete persistance
#
def delete():
    os.remove("charge_status.p")



# OBD Queries -----------------------------------------------------------------------------

# get Battery Temprature
#
def get_temp():
    args = ['temp']
    kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'twos_comp(bytes_to_int(message.data[17:18]),8)',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
    return __salt__['obd.query'](*args, **kwargs)['value']

def get_charging_power():
        args = ['charging_power']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        #'formula': '(twos_comp(bytes_to_int(message.data[13:14])*256+bytes_to_int(message.data[14:15]),16)/10.0)*((bytes_to_int(message.data[15:16])+bytes_to_int(message.data[16:17]))/10.0)/1000.0',
        'formula': '(twos_comp(bytes_to_int(message.data[12:13])*256+bytes_to_int(message.data[13:14]),16)/10.0)*((bytes_to_int(message.data[14:15])+bytes_to_int(message.data[15:16]))/10.0)/100.0',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return __salt__['obd.query'](*args, **kwargs)['value']*-1.0

# get BMS state of charge
#
def get_soc():
    args = ['soc']
    kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[6:7])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
    return __salt__['obd.query'](*args, **kwargs)['value']/2.0

# get display state of charge
#
def get_soc_display():
    try:
        args = ['soc']
        kwargs = {
            'mode': '21',
            'pid': '05',
            'header': '7E4',
            'baudrate': 500000,
            'formula': 'bytes_to_int(message.data[33:34])',
            'protocol': '6',
            'verify': False,
            'force': True,
            }
        return __salt__['obd.query'](*args, **kwargs)['value']/2.0
    except:
        return -1

## LOCATION
#
def get_location():
    args = []
    kwargs = {}
    return __salt__['ec2x.gnss_nmea_gga'](*args, **kwargs)

# Retuns exception
def get_carState():
  #  try:
        args = ['driving']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[53:54])',  # Ignition
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&4)/4

def get_charging():
    try:
        args = ['driving']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[11:12])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&128)/128
    except:
        return -1

# Weird response, always seem to be true
def get_charging_chademo():
#  try:
        args = ['CCS Plug']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[12:13])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&64)/64

# Weird response, always seem to be true
def get_charging_normal():
#  try:
        args = ['J1772 Plug']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[12:13])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&32)/32


23%20PM

@jorgeli

If the code is installed you should be able to run it from the command prompt, not the complete code (I think that is what you tried to do) just the command to execute it (same as in the job), see screenshot:

At the moment i am charging and the return is “sleep disabled”

Hope this helps.

Thank you Anders, i will try it out!

Thanks for you patience and help for me :slight_smile: -)
Yes it works when write in prompt. It says sleep disabled
But the dongle are going to sleep anyway from power settings time.
So the script not working for me :frowning:

Now it works! Many thanks @AndersO

@jorgeli
Hi,
The script makes it stay awake when charging and when drivieng.
In all other cases sleep should be enabled and the dongle should go to sleep after 5 minutes (with the sleep timer set to 300).
Does your dongle sleep when the car is on, when charging or both?.

The dongle is awake Only the 5 min when i drive or charge and then go to sleepmode.
So just follow the dongle power management.

@jorgeli, weird.
How often is the job running that executes the script?
I’ve set it to 6 minutes + run on start, the script sets the sleep to 5 minutes (300 sec) and the setting for the dongle is set to 5 minutes as well.
Does the job have the command properly written?
My guess would be that there is something wrong with the job.

If all is ok I think you need to ask support in that case, mine never goes to sleep while driving or charging. When turned off it does.

Hi Anders
Thanks again for feedback :slight_smile:
Yes something i must have done wrong.
My config is like this:

@jorgeli
Hi, i think you are missing a space, for the Cron schedule I have a space after the 6 minutes: ‘*/6 * * * *’
Try that and see what happens

Thank you very much I thing it work now :slight_smile:

@jorgeli
Good to hear.
FYI there are spaces between all the stars, not sure you are aware of the and not sure it support it without stars.
the star are (* * * * *):

  • minute per hour
  • hour of the day
  • day of the month
  • month of the year
  • day of the week

I’ve followed all your tutorial but I only received: car unlocked.
Do you know why? Thanks

@ricard_ferre_jornet
Hi, I am not sure i understand, car unlocked would be no return or message from what I posted.
The only things i have posted is to keep the autopi awake and to report via telegram on charging, both only hor Ioniq Electric.

New code for staying awake for the Ioniq BEV that works better and is smaller, it has to run at shorter intervals than the sleep timer.
I have it running every 2 minutes and the sleep timer is 300 sec (5 min).
The old code had to run at start and then at a longer interval than the sleep timer.
The new one can/should run several times within the sleep timer period.
What it does it just resets the sleep timer everytime it runs if charging or driving.
I think that the disable_sleep() can also be removed, but not 100% sure:

import os
import requests

BOT_TOKEN = '8888888888888888xxxxxxxxxxxx8888888888'
BOT_CHATID = '88888888888'

def poll():
    charging_ccs = get_charging_ccs()
    charging_normal = get_charging_normal()
    soc = get_soc()
    #if charging_ccs == 1 or (charging_normal != 1 and soc != -1):
    if charging_ccs == 1 or charging_normal == 1 or soc != -1:
        disable_sleep()
        enable_sleep()
        #bot_sendtext("sleep timer reset")
        return {"msg": "sleep timer reset"}
    #else:
        #bot_sendtext("sleep timer running")
        #return {"msg": "sleep timer running"}

# enable autopi sleep
def enable_sleep():
    args = ['sleep']
    kwargs = {
        'enable': True,
        'period': 300,
        'reason': 'charge status',
    }
    __salt__['power.sleep_timer'](**kwargs)

# disable autopi sleep
def disable_sleep():
    args = ['sleep']
    kwargs = {
        'enable': False,
    }
    __salt__['power.sleep_timer'](**kwargs)

def get_charging():
    try:
        args = ['charging']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[11:12])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&128)/128
    except:
        return -1

# get display state of charge
#
def get_soc():
    try:
        args = ['soc']
        kwargs = {
            'mode': '21',
            'pid': '05',
            'header': '7E4',
            'baudrate': 500000,
            'formula': 'bytes_to_int(message.data[33:34])',
            'protocol': '6',
            'verify': False,
            'force': True,
            }
        return __salt__['obd.query'](*args, **kwargs)['value']/2.0
    except:
        return -1

# Get Chrging CCS
def get_charging_ccs():
    try:
        args = ['CCS Plug']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[11:12])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&64)/64
    except:
        return -1

# Get charging 230V
def get_charging_normal():
    try:
        args = ['J1772 Plug']
        kwargs = {
        'mode': '21',
        'pid': '01',
        'header': '7E4',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[11:12])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&32)/32
    except:
        return -1

def bot_sendtext(message):
    send_text = 'https://api.telegram.org/bot' + BOT_TOKEN + '/sendMessage?chat_id=' + BOT_CHATID + '&parse_mode=Markdown&text=' + message
    requests.get(send_text)
2 Likes

I’ve copyed your new code and I’ve put my bot token and bot chadid but I can’t save the code when I put the create button in my autopi.