Receive charging alerts on your phone ( and smart watch )

hello.
strange because telegram custom code version 1 work fine
but this new one, impossible to make it working…
job vas correct, like my_telegramV2.poll
and custom was my_telegramV2
and inside python code, def poll():
all is correct.
also my token and bot_chat id, I put this from old one
you did update from this post ?
strange it is not working…
thanks

me too
not working
but telegram version 1 work fine
strange

What v1 / v2 code are you referring to ?

the v1 is the first one you post there few months ago
v2 is the new one, with gps, co2 and other feature :slightly_smiling_face:

Ah. So v1 is -

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

and v2 -

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

So the change is using urllib.urlencode() - the intention here is to encode some characters ( such as the british pound sign ) as per api spec.

Maybe it works less well with other character sets ? Not sure.

What text ( exactly ) are you sending ?

Thanks.
I exactely copy - past your post without change. ``` si this one

Only I put my token and chat_bot ID. The same as V1

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 = ‘759619847:AAEei0EZ0z7HfGWfaazvI6SAhb2ny-zx2oU’
bot_chatID = ‘735785677’

“”"
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 90%

  5. Send Telegram message when charging reaches 100%

  6. 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(“Charge arrete. Derniere charge connu “+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("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    70% alert

    #if soc >= 70 and persistance[‘SOC’] < 70:

    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    75% alert

    #if soc >= 75 and persistance[‘SOC’] < 75:

    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    80% alert

    if soc >= 80 and persistance[‘SOC’] < 80:
    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    85% alert

    #if soc >= 85 and persistance[‘SOC’] < 85:

    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    90% alert

    if soc >= 90 and persistance[‘SOC’] < 90:
    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    99.5% alert

    if soc >= 99.5 and persistance[‘SOC’] < 99.5:
    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    100% alert … not work because 100% is impossible

    #if soc >= 100 and persistance[‘SOC’] < 100:

    bot_sendtext("Voiture en charge a "+format(batt_power,’.2f’)+“kW. Etat de la batterie “+format(soc,’.1f’)+”%”)

    each % alert

    #lastsoc = persistance[‘SOC’]
    #for level in xrange(1, 100):
    #if soc >= level and lastsoc < level:

    bot_sendtext("Charging now at a rate of "+format(batt_power,’.2f’)+“kW. State of charge now “+format(soc,’.1f’)+”%”)

    break

    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

log.info ("Sending message with "+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’

disable autopi sleep

def disable_sleep():
args = [‘sleep’]
kwargs = {
‘enable’: False,
}
salt’power.sleep_timer’

I did try the french text from my console with v2 -

peter26@Kona $  my_charge_status.bot_sendtext "Charge arrete. Derniere charge connu 10%"
16e737229d31f230c9d241ab498c8cb8:
  return: null

and got the message on my phone.

I also tried a re-copy and paste from github to my autopi config and still got the message on my phone.

So I’m still not sure why the v2 ( ie version on github ) doesn’t work for you.

Anything useful in the logs ?

Ok.
So i will copy past this from GitHub too and doing test do be sure all is ok.
I will see the log too and tell you.
Thanks

Hi,

I’m trying to get this working with the Ioniq but I’m not sure whats wrong…

Am i translating these wrong? 000_Rapid Charge Port CCS Plug 2101 {j:6}
From what I can tell the rapid charge should be 12:13, as in J
And &2^6 as in :6

These 3 all return 1 when the car is charing at a ‘normal’ plug.

 def get_charging():
        args = ['driving']
        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'])&128)/128

def get_charging_chademo():
        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

def get_charging_normal():
        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

I’ve also had no success with getting the Ignition, that should be:
000_BMS Ignition BMS Ignit. 2101 {ay:2}

def get_carState():
        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

Any tips?

These seem reasonable to my eye.

I would try dumping the whole PID in the different cases. ie in the console -

obd.query test mode=21 pid=01 force=true

I had the same issue upon using the newer version. I watch the logs and discovered that I needed to delete the old persistent data file. Once I did that, it started working.

I do have one remaining issue that I wanted to make time to fix and open a PR for, and that is a bug where if you have multiple charge sessions without using the car between, all is well at the end of the first session, but on the second session you’ll receive a message per minute with incomplete data. I only notice this because I top up with solar power where possible, so don’t always complete a charge in one go.

1 Like

Good catch, thanks. There is my_charge_status.delete for this purpose, I added this to the comment block in the script.

I’ve not seen this myself but I’ll keep an eye out for it.

1 Like

So far I’ve been unable to replicate this -

45

Turns out that the repeated charge thing was just coincidence. I think the actual issue may be to do with establishing location. Haven’t had a chance to dig in further yet.

47

I had delete all of charge_status V1.
Now I use only the last version. V2

I will see if it is working or not.
But we have same car, so it should be ;).

Okay, could be related to the location. If you see this again pls get your logs ( especially any stack traces ).

Its possibly worth catching exception on nearest_charger() -

# get nearest charger
#
def nearest_charger():
    try:
        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']
    except:
        log.info('Unable to get location')

    return 'No local charger found'

i am best than you, lol
look at this ->
i am the best ecologic man in the world 06

I update now for translation in French:
alslo km. but for this I only change “wltp” value to 449 km
maybe it is ok like that
40

Caught this in the log today, so I’ll drop in the exception handling you suggested which should catch it:

Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/salt/utils/schedule.py"
, line 894, in handle_func
  ret['return'] = self.functions[func](*args, **kwargs)
File "/opt/autopi/salt/modules/my_charge_status.py", line 110,
in poll
  bot_sendtext(nearest_charger())
File "/opt/autopi/salt/modules/my_charge_status.py", line 277,
in nearest_charger
  location = get_location()
File "/opt/autopi/salt/modules/my_charge_status.py", line 272,
in get_location
  return __salt__['ec2x.gnss_nmea_gga'](*args, **kwargs)
File "/var/cache/salt/minion/extmods/modules/ec2x.py", line 45
8, in gnss_nmea_gga
  "utc": obj.timestamp.isoformat(),
AttributeError: 'NoneType' object has no attribute 'isoformat'

Working, but a lots of thing strange: :thinking: :rofl:

PNG