One of my objectives with the autopi is to receive alerts on my phone relating to charging.
For example, you stop off for lunch and a rapid charge, if the charge fails or completes, it would be great to get a notification … nothing worse than checking the car after 1 hour to find it failed after 10mins.
Before I start, this is definitely work-in-progess. Fixes, improvements, better ideas more than welcome ! I’ll update this post with changes as needed.
I figured telegram would be a good choice of app to sent alerts to - wide support and easy to integrate to. However, you will need to get a token and chatid - see https://www.mariansauter.de/2018/01/send-telegram-notifications-to-your-mobile-from-python-opensesame/ for some info.
Anyways, the idea is to have a custom script that is periodically run that -
- Checks if the car is charging
- Sends telegram message when charging starts
- Sends telegram message when charging reaches 80%
- Sends telegram message when charging reaches 100%
- Sends telegram message when charging stops
- Disables sleep whilst charging
I created my_charge_status
custom code -
Custom code is -
import logging
import requests
import pickle
import os
log = logging.getLogger(__name__)
# Telegram tokens - see https://www.mariansauter.de/2018/01/send-telegram-notifications-to-your-mobile-from-python-opensesame/
#
bot_token = 'xxx'
bot_chatID = 'xxx'
"""
Poll to see if car is being charged. If so :
1. Disable auto sleep whilst charging
2. Send Telegram message when charging starts
3. Send Telegram message when charging reaches 80%
4. Send Telegram message when charging reaches 100%
5. Send Telegram message when charging stops
"""
def poll():
# enable sleep in case anything goes wrong below
#
enable_sleep()
# load previous status
#
persistance = load()
# check if we are driving or charging
#
driving = get_driving()
if driving == 1 or driving == -1:
if persistance['charging'] == True:
bot_sendtext("Charging stopped. Last known State of charge "+format(persistance['SOC'],'.1f')+"%")
persistance['charging'] = False
save(persistance)
return {"msg": "Not charging"}
# now we are charging
#
disable_sleep()
batt_power = get_charging_power()
soc = get_soc()
# alert if just started to charge
#
if persistance['charging'] == False:
bot_sendtext("Charging started at a rate of "+format(batt_power,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"%")
# 80% alert
#
if soc >= 80 and persistance['SOC'] < 80:
bot_sendtext("Charging now at a rate of "+format(batt_power,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"%")
# 100% alert ... not sure if this can really happen
#
if soc >= 100 and persistance['SOC'] < 100:
bot_sendtext("Charging now at a rate of "+format(batt_power,'.2f')+"kW. State of charge now "+format(soc,'.1f')+"%")
# store status for next time
#
persistance['charging'] = True
persistance['SOC'] = soc
save(persistance)
return {"msg": "Charging at "+format(batt_power,'.2f')+"kW, SOC now "+format(soc,'.1f')+"%"}
# send message to telegram
#
def bot_sendtext(bot_message):
send_text = 'https://api.telegram.org/bot' + bot_token + '/sendMessage?chat_id=' + bot_chatID + '&parse_mode=Markdown&text=' + bot_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")
# check if we are driving. Returns :
# 0 - charging
# 1 - driving
# -1 - can't read data
def get_driving():
try:
args = ['driving']
kwargs = {
'mode': '220',
'pid': '101',
'header': '7E4',
'baudrate': 500000,
'formula': 'bytes_to_int(message.data[53:54])',
'protocol': '6',
'verify': False,
'force': True,
}
return (int(__salt__['obd.query'](*args, **kwargs)['value'])&4)/4
except:
return -1
# get charging power
#
def get_charging_power():
args = ['charging_power']
kwargs = {
'mode': '220',
'pid': '101',
'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])*256+bytes_to_int(message.data[16:17]))/10.0)/1000.0',
'protocol': '6',
'verify': False,
'force': True,
}
return __salt__['obd.query'](*args, **kwargs)['value']*-1.0
# get display state of charge
#
def get_soc():
args = ['soc']
kwargs = {
'mode': '220',
'pid': '105',
'header': '7E4',
'baudrate': 500000,
'formula': 'bytes_to_int(message.data[34:35])/2.0',
'protocol': '6',
'verify': False,
'force': True,
}
return __salt__['obd.query'](*args, **kwargs)['value']
# enable autopi sleep
#
def enable_sleep():
args = ['sleep']
kwargs = {
'enable': True,
}
__salt__['power.sleep_timer'](**kwargs)
# disable autopi sleep
#
def disable_sleep():
args = ['sleep']
kwargs = {
'enable': False,
}
__salt__['power.sleep_timer'](**kwargs)
( hopefully this made in without format errors ! )
You’ll need to setup telegram, get your own bot_token and chatid - see link above.
I’m running this as a scheduled job every 10 mins -
I’m not entirly sure what to set the autopi sleep times to - but given I’m polling every 10mins, I’ve set -
- Inactivity fallback period to 900 seconds ( 15mins )
- Inactivity after sleep period to 900 seconds ( 15 mins )
On my phone I get alerts such as -
( last message should have been 100%, at the time I was using SOC BMS instead of SOC Display )
Since its on the phone, the usual smart watch notifications can be used -
One problem with the Kona Electric is that it takes so long to discharge the battery that testing this takes days Still, big journey today so I can do some more testing tonight.
As I said that the beginning, fixes, improvements, better ideas more than welcome.