Ioniq BEV 28 kWh; staying awake, charging messages, CO2 usage

Custom code that will work with the Ioniq BEV.
Anyone that has ideas on what to add in addition? :slight_smile:

The below keeps the Autopi awake and sends telegrams with charging, CO2 usage, milage etc.

42

Jobs for the below scripts:
39%20AM

Note: Telegram IDs have to be changed to real ones

my_sleep_reset:
Resets the sleep timer to five minutes every one or two minutes (job setting), if the car is running or charging, until it stops charging or running then the sleep timer runs its course and autopi goes to sleep
The initial sleep timer set in the config should be set to 5 minutes as well

import os
import requests

BOT_TOKEN = '8888888888888888888999999999999999999999999888888'
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)

my_co2save:
Initially calcualted the CO2 savings/usage but is now only saving the milage and used for CO2 saving calculation when charging

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

BOT_TOKEN = '88888888888888888888888888888888888888888'
BOT_CHATID = '888888888'

def poll():
    # Store CO2 Savings changed to km, saved and used in formula for CO2 savings in charging messages
    #

    co2persistance = loadco2()
    co2savings = get_co2savings()	
    #milage = get_milage()
    #co2usage = get_co2usage()	
	

    if co2savings != -1:
        co2persistance['charging'] = True
        co2persistance['CO2'] = co2savings
        saveco2(co2persistance)
        #bot_sendtext("sleep disabled")
        #bot_sendtext(power.sleep_timer)
        #return {"msg": "Driving and CO2 savings are: "}
        return {"msg": "Driving and milage is: "+format(co2persistance['CO2'],'.0f')+" km"}
    else:
        #return {"msg": "not driving no CO2 saving, however "}
        return {"msg": "NOT driving no CO2 data saving, milage "+format(co2persistance['CO2'],'.0f')+" km"}
    # store CO2 for next time
    #
    #co2persistance['charging'] = True
    #co2persistance['CO2'] = co2savings
    #saveco2(co2persistance)

    return {"msg": "milage for CO2 calc: "+format(co2persistance['CO2'],'.0f')+" km"}

# Environment stuff -----------------------------------------------------------------------------

# load CO2 persistance
#
def loadco2():
    try:
        co2persistance = pickle.load( open( 'co2fixed.p', 'rb' ) )
    except:
        co2persistance = { 'charging': False, 'CO2': 0 }

    return co2persistance

# save co2persistance
#
def saveco2(co2persistance):
	if co2persistance != -1:
		pickle.dump( co2persistance, open( "co2fixed.p", "wb" ) )

# delete co2persistance
#
def deleteco2():
    os.remove("co2fixed.p")


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

# Odometer
# CO2
def get_co2savings():
    try:
        args = ['co2savings']
        kwargs = {
        'mode': '22',
        'pid': 'B002',
        'header': '7C6',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[10:12])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        #return __salt__['obd.query'](*args, **kwargs)['value']*0.1534/1000
        return __salt__['obd.query'](*args, **kwargs)['value']
    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)

my_status;
Sends Telegram messages charging; at start, during and when finished.
A Swedish mile is 10 km, hence the 10 km in the 1.44 etc.
The CO2 savings is based on Swedish electric power (factor 0.047 CO2 cost per kWh) and the Ioniq power consumption in average (1.44)
ICE car CO2 emmision (factor 2.67 CO2 well to wheel per liter gas)
It probably needs some testing to get the right decimal places if changing the factor, milage size and ICE gas usage

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 = '8888888888888888888888888888888888U'
BOT_CHATID = '8888888888'



def checkChargeStatus():

    # load previous status
    #
    persistance = load()
    
    co2persistance = loadco2()

    # 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). Tons of CO2 saved compared to ICE car: "+format(co2persistance['CO2']*0.1801/1000,'.3f')+" tons."+" CO2 usage (Sweden): "+format(co2persistance['CO2']*0.006768/1000,'.3f')+" tons Total Milage: "+format(co2persistance['CO2'],'.0f')+" km. Cost ICE car gas at 16 SEK/l and 0.7 l/10km: "+format(co2persistance['CO2']*16*0.7/10,'.0f')+" SEK")
            persistance['charging'] = False
            save(persistance)
        return {"msg": "Not charging. Charging stopped. Last known State of charge "+format(persistance['SOC'],'.1f')+"% (BMS). Tons of CO2 saved compared to ICE car: "+format(co2persistance['CO2']*0.1801/1000,'.3f')+" tons"}
    chargingPower = get_charging_power()
    soc = get_soc()
    soc_display = get_soc_display()
    temp = get_temp()
    temp_h = get_temp_h()    
    air_temp = get_air_temp()


    # if air temp is more or equal to 25 then set to 25
    #
    #if air_temp >= 25:
        #temp = 25

    # alert if just started to charge
    #
    if persistance['charging'] == False:
        bot_sendtext("Charging started at a rate of "+format(2*chargingPower,'.2f')+" kW. SOC BMS = "+format(soc,'.1f')+"%, SOC Display = "+format(soc_display,'.1f')+ "%, GOM "+format(0.01*soc_display*2800/(16.08813 + (air_temp*(-0.13873)))/0.89,'.0f')+"km, batt. temp. = "+format(temp,'.0f')+ " - "+format(temp_h,'.0f')+  " Degrees, air temp. = "+format(air_temp,'.0f')+" Degrees")
        # )/0.92 testing bigger range for GOM formula shows less at 15-20 Centigrade

    # Alerts at 40, 50, 60, 70, 80, 90 and 94% SOC BMS
    #
    if (soc >= 40 and persistance['SOC'] < 40) or (soc >= 50 and persistance['SOC'] < 50) or (soc >= 60 and persistance['SOC'] < 60) or (soc >= 70 and persistance['SOC'] < 70) or (soc >= 80 and persistance['SOC'] < 80) or (soc >= 90 and persistance['SOC'] < 90) or (soc >= 94 and persistance['SOC'] < 94):
        bot_sendtext("Charging at a rate of "+format(2*chargingPower,'.2f')+" kW. SOC BMS = "+format(soc,'.1f')+"%, SOC Display = "+format(soc_display,'.1f')+ "%, GOM = "+format(0.01*soc_display*2800/(16.08813 + (air_temp*(-0.13873)))/0.89,'.0f')+" km, batt. temp. = "+format(temp,'.0f')+ " - "+format(temp_h,'.0f')+  " Degrees, air temp. = "+format(air_temp,'.0f')+" Degrees")
        # )/0.89 testing bigger range

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

    return {"msg": "Charging at a rate of "+format(2*chargingPower,'.2f') + " kW. SOC BMS = "+format(soc,'.1f')+"%, SOC Display = "+format(soc_display,'.1f')+ "%, GOM = "+format(0.01*soc_display*2800/(16.08813 + (air_temp*(-0.13873)))/0.89,'.0f')+" km, batt. temp. = "+format(temp,'.0f')+ " - "+format(temp_h,'.0f')+  " Degrees, air temp. = "+format(air_temp,'.0f')+" Degrees. Saved CO2 compared to ICE car: "+format(co2persistance['CO2']*0.1801/1000,'.3f')+" tons."+" CO2 usage (Sweden): "+format(co2persistance['CO2']*0.006768/1000,'.3f')+" tons Total Milage: "+format(co2persistance['CO2'],'.0f')+" km. Cost ICE car gas at 16 SEK/l and 0.7 l/10km: "+format(co2persistance['CO2']*16*0.7/10,'.0f')+" SEK"}
    # )/0.92 testing bigger range

# 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")


# Environment stuff -----------------------------------------------------------------------------

# load CO2 persistance
#
def loadco2():
    try:
        co2persistance = pickle.load( open( 'co2fixed.p', 'rb' ) )
    except:
        co2persistance = { 'charging': False, 'CO2': 0 }

    return co2persistance



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

# get Battery Temprature low
#
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']

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


# get Air Temperature, used to calcualte GOM range
#
def get_air_temp():
    args = ['air_temp']
    kwargs = {
        'mode': '21',
        'pid': '80',
        'header': '7E6',
        'baudrate': 500000,
        'formula': '(bytes_to_int(message.data[14:15])-80)/2',
        '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[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

# Charging CCS
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

# 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[12:13])',
        'protocol': '6',
        'verify': False,
        'force': True,
        }
        return (int(__salt__['obd.query'](*args, **kwargs)['value'])&32)/32
1 Like

Hi @AndersO

This is really awesome. I got the sleep resetter up and running and am now getting messages on telegram :slight_smile:
Now trying to get all the above up and running.

I have not been succesfull in getting any data into widgets on the dashboard yet. Have you been succesfull with that? (PID > Logger > Widget)
I tested the PID’s for SOH and SOC in the terminal and they work fine, buy no data on the dashboard widgets.

Hi @Mikael_Madsen
yes for me they work fine, I have SOH, SOC display and BMS, charging power, battery upper and lower temperature, 12V battery voltage and RPI temperature plus the map.
However I had to redo the loggers after upgrading the software.
I just made the loggers and then the widgets. The widgets appear in the list values as both PID and ODB, I picked ODB (I think for all) and set the timer for them to 15 minutes and it works fine.

@AndersO could you share some screen shots?

I noticed that SOC BMS was in the list as both PID and ODB.
The PID is a float and does not show the “preview” line chart, the OBD is a long and does show the “preview” line chart. At one point I saw some data for the ODB SOC BMS, but it disappeared again.

I do not know why the SOC BMS shows up on the list as OBD and SOC Display and SOH does not, they only appear as PID. Not sure if there is anything that I did wrong in the PID’s or the Loggers, which is why screen shots of PID, Logger and Widget would be awesome.

Thx.

@Mikael_Madsen
Hope this helps:

25%20PM 28%20PM%20002 28%20PM%20001 28%20PM 27%20PM%20002 27%20PM%20001 27%20PM 26%20PM%20002 26%20PM%20001 26%20PM 25%20PM%20002 25%20PM%20001

@AndersO awesome :slight_smile:
I can see that you have created your own can bus, could you share a screen shot of that one?
That might be the issue…

Thx. again :slight_smile:

@Mikael_Madsen
Hi, I dont think I did, i think it was automatically generated for me somehow when I made my profile.
But in any case here is the screenshot:

53%20PM

Hi @AndersO

I deleted all Loggers, synced, and added them again.
Also deleted and recrated after crating the loggers again.
Then the odb showed up in the list and it all works now :slight_smile:

Thx a lot!

Hello,

I am not a computer specialist and I purchased an AutoPi device to get information on my Kona (I also have a Renault Zoe and Renault provides some connected services through a n app). I don’t get any information through Autopi but I don’t necessarily understand all the steps to configure the device. I see you talk about PID, Jobs, loggers …many notions I don’t understand. Could someone give me a simple process (all steps and “objects” to create) to be able to get information on my dashboard ? For the moment, my device is no use for me as nothing appears on my dashboard except position.Screenshot

@JM2915
Hi, did you add loggers for the widgets?
Without loggers they will not retrieve any data from the car.

Hello,

What I did: Copy the script in “Advanced / Custom code”
Then, Created a Logger PID in “Car Explorer / loggers”
Finally created a Widget based on the created PID

@JM2915
Hi,
Not sure what you mean, “from custom code”. The PID that you select in the loggers are from your library, not from any custom code.
The easiest is just to copy from the community library (filtering on the right car) into my library (“Add to my library” from the library PID). It is also possible to add your own.
Just some screenshot samples (SOC Display:)

16 PM

16 PM 001

17 PM

17 PM 001

1 Like

Hi
What power setting in the dongle you use now?
Regard Jörgen

@jorgeli
Hi,
At the moment there is a new version not sure uf that has any effect and needs some changes to work properly. I cannot update my dongle for some reason, support is looking at it.
These are my settings now:
For supress sleep (cannot be seen in the screenshot)
^vehicle/(obd|battery|battery)/(interface_connected|charging|charging_slow)
12.6 and 12.50, see screenshots.
33 PM 34 PM 001 34 PM image

Thankyou very much for a lovely work :slight_smile:
I will try to update again .
But i have the same problem with my Autopi in the car that the modem not working that it should.
Autopi is online but not send or get data.
Sometime it can be fo many days that is not work.
This has been since the first time i get the dongle , but they have never solve the problem.

1 Like

@jorgeli
Hi,

From what i have underatood the internal antenna is not that great.
I had it below my left foot, but then I tried it with the dongle in my knee :slight_smile: , just in front of the steering wheel and in the door, no real difference.

Now I have ordered an antenna and cable, I should get it this week, I think that will do the trick.

It never fails to send Telegram messages, so my guess is that it is the LTE connection that is failing and it is working only on 2 or 3G so when there is more data to send it will not work properly unless it has a 4G connection.

1 Like

Hi Anders
Yes i think it bad antenna to.
But in my case when it works it works fine. But some days it not work at all at the same route i do daily.
And that include all data , telegram messages to.
And it can be for days sometime

1 Like

@jorgeli
FYI I reverted back to the script to keep it awake for the moment, the power setting caused trouble when updating the dongle, it couldn’t do the install. Let’s see if there is anything new in the update that can be used to controll power.

1 Like

After reflashing my device and update to the new software , anything is a mess .
Ita was not good before either and have a very long mail conversation to support , but no solution.
How is it working for you now after update?
Regard Jörgen