Receive charging alerts on your phone ( and smart watch )

Right makes sense :slight_smile:

Ugg ā€¦ I would remove the little database with my_charge_status.delete and see if that helps.

1 Like

Still not go to sleep autopi
Itā€™s fully tested plord ?
image

Iā€™m not sure what you mean. My autopi goes to sleep and sends alerts when charging ( or when I left the car unlocked by mistake ).

I doubt it :slight_smile:

Iā€™m really waiting for the long awaited update before spending more time on this ā€¦ hopefully then all the sleep nonsense can be handled better.

Mmm my not go to sleep ā€¦

You can post your energy settings ? Something not work

I just use the defaults.

In order to debug sleeps I setup a trigger to send me emails -

25

Personally Iā€™m just waiting for the long awaited update before investing more time.

2 Likes
    It's impossible for me, I've tried lot of ways but, at the moment, I can't save the custom code with my token and id of telegram, something is wrong but I don't know what.

My custom code is this:

import logging
import requests
import pickle
import os
import json
import urllib
from math import sin, cos, sqrt, atan2, radians

log = logging.getLogger(__name__)

# Telegram tokens - see https://www.mariansauter.de/2018/01/send-telegram-notifications-to-your-mobile-from-python-opensesame/
#
bot_token = ā€˜942007221:AAGnGgGQcy4fU-glBYubShKf7T8MLKWjhzgā€™
bot_chatID = ā€™5921092ā€™

# Constants
#
# average ICE g/km
ice_emissions = 120.1 

# Average UK g/kWh
# electric_emissions = 225.0 

# Octopus g/kWh
electric_emissions = 0.0 

# max range
#
wltp = 279

# FIX THIS - add in SOH to calcs

# list of non-stanard chargers
#
chargers = [ 
        {'latitude':0, 'longitude':0, 'msg':'At home, '+'5p/KWh overnight'}
     ]

"""
Poll to see if car is being charged.  If so :

1. Disable auto sleep whilst charging
2. Send Telegram message when charging starts
    - Estimated miles per kWh ( based on miles travelled and delta Accumulative Charge Power )
    - Estimaed CO2 saved on fuel since last charged ( based on ICE emissions and electricity supplier )
    - Estimated total CO2 saved since purchase ( based on total milage and ICE emissions )
    - Details of nearest charger ( ie probabally what is being used to charge car )
    - Estimated range ( based on WLTP )
3. Send Telegram message when charging reaches each 10%
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:
            if persistance['soc'] >= 99:
                persistance['soc'] = 100
            bot_sendtext('Charging *stopped*. Last known State of charge *'+format(persistance['soc'],'.1f')+'%* ('+format(wltp*persistance['soc']/100, '.1f')+' miles) charged '+format(persistance['cec']-persistance['start_cec'],'.1f')+'kWh')
            persistance['charging'] = False
            save(persistance)
        return {'msg': 'Not charging'}

    batt_power = get_charging_power()

    # avoid fake charging
    #
    if batt_power <= 0:
        return {'msg': 'Not charging - power less than zero'}

    # now we are charging
    #
    disable_sleep()
    soc = get_soc()
    cec = get_cec()

    # alert if just started to charge
    #
    if persistance['charging'] == False:
        last_charge_odo = persistance['odo']
        last_charge_soc = persistance['soc']
        odo = get_odometer()
        persistance['odo'] = odo
        persistance['start_cec'] = cec
        if last_charge_soc != soc:
            mperkwh = (odo-last_charge_odo)/(last_charge_soc*64.0/100.0-soc*64.0/100.0)
        else:
            mperkwh = 0.0
        co2saved = (ice_emissions*(odo-last_charge_odo)*1.609) - electric_emissions*(last_charge_soc*64.0/100.0-soc*64.0/100.0)
        bot_sendtext('Estmated *'+format(mperkwh,'.2f')+'mi/kWh* since last charge')
        bot_sendtext('*'+format(co2saved/1000,'.2f')+'Kg* CO2 saved since last charge')
        bot_sendtext('*'+format(odo*ice_emissions/1000000,'.2f')+'tonnes* CO2 saved in total')  
        bot_sendtext(nearest_charger()) 
        bot_sendtext('Charging *started* at a rate of '+format(batt_power,'.2f')+'kW. State of charge now *'+format(soc,'.1f')+'%* ('+format(wltp*soc/100, '.1f')+' miles)')


    # each 10% alaert
    #
    for level in xrange(0, 100, 10):
        if soc >= level and persistance['soc'] < level:
            bot_sendtext('Charging *now* at a rate of '+format(batt_power,'.2f')+'kW. State of charge now *'+format(soc,'.1f')+'%* ('+format(wltp*soc/100, '.1f')+' miles)')
            break

    # store status for next time
    #
    persistance['charging'] = True
    persistance['soc'] = soc
    persistance['cec'] = cec
    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?' + urllib.urlencode({'chat_id': bot_chatID, 'parse_mode': 'Markdown', 'text': unicode(bot_message).encode('utf-8')})
	requests.get(send_text)
    
# load persistance
#
def load():
    try:
        p = pickle.load( open( 'charge_status.p', 'rb' ) )
    except:
        p = { 'charging': False, 'soc': 0.0, 'odo': 0, 'cec': 0.0, 'start_cec': 0.0 }

    return p

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

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

# dump persistance
#
def dump():
    return load()

# 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,
        }
        # note - sums are done outside of the forumla due to autopi failing
        # with 0
        #
        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']

# get odometer
#
def get_odometer():
    args = ['odometer']
    kwargs = {
        'mode': '22',
        'pid': 'B002',
        'header': '7C6',
        'baudrate': 500000,
        'formula': 'bytes_to_int(message.data[11:12])*16777216+bytes_to_int(message.data[12:13])*65536+bytes_to_int(message.data[13:14])*256+bytes_to_int(message.data[14:15])',
        'protocol': '6',
        'verify': False,
        'force': True,
    }
    return __salt__['obd.query'](*args, **kwargs)['value']

# get Accumulative Charge Power
#
def get_cec():
    args = ['odometer']
    kwargs = {
        'mode': '220',
        'pid': '101',
        'header': '7E4',
        'baudrate': 500000,
        'formula': '(bytes_to_int(message.data[41:42])*16777216+bytes_to_int(message.data[42:43])*65536+bytes_to_int(message.data[43:44])*256+bytes_to_int(message.data[44:45]))/10.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)

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

# get nearest charger
#
def nearest_charger():
    location = get_location()

    for charger in chargers:
        lat1 = radians(charger['latitude'])
        lon1 = radians(charger['longitude'])
        lat2 = radians(location['latitude'])
        lon2 = radians(location['longitude'])
        dlon = lon2 - lon1
        dlat = lat2 - lat1
        a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
        c = 2 * atan2(sqrt(a), sqrt(1 - a))
        dist = 6373.0 * c

        if dist < 0.02:
            log.info('found local charger '+charger['msg'])
            return charger['msg']

    log.info('https://api.openchargemap.io/v3/poi/?output=json&distance=0.1&maxresults=1&latitude='+str(location['latitude'])+'&longitude='+str(location['longitude']))
    result = requests.get('https://api.openchargemap.io/v3/poi/?output=json&distance=0.1&maxresults=1&latitude='+str(location['latitude'])+'&longitude='+str(location['longitude']))
    for i in result.json():
        return i['OperatorInfo']['Title']+', '+i['AddressInfo']['Title']+', '+i['UsageCost']

    return 'No local charger found'

can you edit the post and contain the text as pre-formatted ?

eg like this

1 Like

Iā€™ve edited my post, is it well now?

I noticed bot_token and bot_chatId are contained within a backtick rather than a single quote.

Also have you given the script a name ? eg my_testcode or something ?

1 Like

Iā€™ve copied the raw ā€œ my_charge_status.pyā€, in this line ā€œ log = logging.getLogger(name)ā€ Do I have to change my ā€œnameā€ to my_charge_status, or what name do I give it?

Thanks in advance

Here is a picture - changes I made circled. Note that the create button is available.

26

1 Like

The ā€œcreateā€ button doesnā€™t respond, Iā€™ve tried everything, I think everything is ok, but, I donĀ“t find out the problem

2019-08-22_17-34-54

Doesnā€™t look like you fixed the back tick on bot_token. Also just check you donā€™t already have cistom code of that name.

1 Like

Iā€™ve tried everything, the create button is green but doesnā€™t works, I will be patientā€¦

That happened to me. The only solution I found was using other name for the scriptā€¦

1 Like

@plord I want to share my problem with this script which is now solved :slight_smile:

I modified the code and it was not working because I translated English words in french (I am french ^^) with accent then ā€œcharging atā€ became ā€œCharge Ć ā€ and ā€™ Ć  ā€™ is bad for the script then after replacing ā€œĆ ā€ with a without accent, it works !!!

But I copy the script at the start of this topic, could anyone display the last version of the script for me to have the last update ? :smiley:

Et en franƧais si possible vu que je vois des copies Ʃcran en franƧais :yum:

And to have a CRLF character, just add %0A in the text

$ my_charge_status.test

ā€˜my_charge_statusā€™ virtual returned False: Non-ASCII character ā€˜\xc3ā€™ in
file /opt/autopi/salt/modules/my_charge_status.py on line 37, but no encoding
declared; see http://python.org/dev/peps/pep-0263/ for details
(my_charge_status.py, line 37)$ my_charge_status.test