Node-Red and AutoPi API

#1

Hi everyone,

as I promised in another thread, I’m posting the code for using Node-Red to pull data from AutoPi API.

First part is getting your token (that expires every few hours), device_id and unit_id.
This you can get with:

[{“id”:“207111fc.39d516”,“type”:“http request”,“z”:“de854356.27206”,“name”:“login”,“method”:“POST”,“ret”:“obj”,“url”:“https://api.autopi.io/auth/login/",“tls”:"",“x”:310,“y”:760,“wires”:[[“377eeb2a.982804”]]},{“id”:“be7c6baa.bdb1f8”,“type”:“inject”,“z”:“de854356.27206”,“name”:"send login data”,“topic”:"",“payload”:"{“email”:“your@email.domain”,“password”:“YourVeryStrongPassword”}",“payloadType”:“json”,“repeat”:“14400”,“crontab”:"",“once”:false,“onceDelay”:0.1,“x”:130,“y”:760,“wires”:[[“207111fc.39d516”]]},{“id”:“377eeb2a.982804”,“type”:“function”,“z”:“de854356.27206”,“name”:“saveLoginInfo”,“func”:“var token = { payload: msg.payload.token };\nflow.set(“currentToken”, token.payload);\nvar deviceid = { payload: msg.payload.user.devices[0].id };\nflow.set(“device_id”, deviceid.payload);\nvar unitid = { payload: msg.payload.user.devices[0].unit_id };\nflow.set(“unit_id”, unitid.payload);\nreturn [token, deviceid, unitid];”,“outputs”:3,“noerr”:0,“x”:460,“y”:760,“wires”:[[“c615b916.6045”],[“162f9b50.f8c3a5”],[“d15b94d.563ad68”]],“outputLabels”:[“token”,“device_id”,“unit_id”]},{“id”:“c615b916.6045”,“type”:“debug”,“z”:“de854356.27206”,“name”:“token”,“active”:false,“tosidebar”:true,“console”:false,“tostatus”:false,“complete”:“true”,“targetType”:“full”,“x”:650,“y”:700,“wires”:[]},{“id”:“162f9b50.f8c3a5”,“type”:“debug”,“z”:“de854356.27206”,“name”:“device_id”,“active”:false,“tosidebar”:true,“console”:false,“tostatus”:false,“complete”:“true”,“targetType”:“full”,“x”:660,“y”:740,“wires”:[]},{“id”:“d15b94d.563ad68”,“type”:“debug”,“z”:“de854356.27206”,“name”:“unit_id”,“active”:false,“tosidebar”:true,“console”:false,“tostatus”:false,“complete”:“true”,“targetType”:“full”,“x”:650,“y”:780,“wires”:[]}]

The main part is this:

[{“id”:“b46446ff.a25058”,“type”:“moment”,“z”:“de854356.27206”,“name”:"-1h",“topic”:"",“input”:"",“inputType”:“date”,“inTz”:“Europe/Ljubljana”,“adjAmount”:“1”,“adjType”:“hours”,“adjDir”:“subtract”,“format”:“ISO8601”,“locale”:“C”,“output”:“from_utc”,“outputType”:“msg”,“outTz”:“Europe/Ljubljana”,“x”:610,“y”:860,“wires”:[[“24d252b1.194726”]]},{“id”:“bc886b32.2beae8”,“type”:“inject”,“z”:“de854356.27206”,“name”:“batt_power+current+volts”,“topic”:"",“payload”:“soc_display!batt_power!batt_current!batt_volts”,“payloadType”:“str”,“repeat”:“5”,“crontab”:"",“once”:false,“onceDelay”:0.1,“x”:160,“y”:880,“wires”:[[“72f326f4.de27a”]]},{“id”:“88629479.a89ca”,“type”:“www-request”,“z”:“de854356.27206”,“name”:“get data”,“method”:“GET”,“ret”:“obj”,“url”:"",“follow-redirects”:true,“tls”:"",“x”:360,“y”:980,“wires”:[[“d9540f9e.84663”]]},{“id”:“24d252b1.194726”,“type”:“function”,“z”:“de854356.27206”,“name”:“storage > read > list (get PID)”,“func”:“msg.headers = {};\nmsg.headers[‘Authorization’] = 'bearer '+flow.get(“currentToken”);\nmsg.device_id = flow.get(“device_id”);\n//msg.url = “https://api.autopi.io/logbook/storage/read/?from_utc="+msg.from_utc+"&"+“field=obd.batt_power.value”+"&"+“field_type=json”+"&"+“device_id=”+flow.get(“device_id”)+"&"+“interval=1s”+"&"+"end_utc=now”;\nmsg.url = “https://api.autopi.io/logbook/storage/read/?from_utc="+msg.from_utc+"&"+“field=obd.”+msg.pid_id+".value"+"&"+“field_type=json”+"&"+“device_id=”+flow.get(“device_id”)+"&"+“interval=1s”+"&"+“end_utc=now”;\nreturn msg;”,“outputs”:1,“noerr”:0,“x”:550,“y”:920,“wires”:[[“88629479.a89ca”]]},{“id”:“d9540f9e.84663”,“type”:“function”,“z”:“de854356.27206”,“name”:“dataProcessor”,“func”:”// poglej če ima array vsaj 1 podatek, če ne, vrni null\nvar outraw = {}\nvar textout = {}\nif (msg.payload.length >= 1){\n // battery power v kW (V*A)\n if (msg.pid_id == “batt_power”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null; //{ payload: “Trenutna moč: " + msg.payload[msg.payload.length-1].value.toFixed(2) + " kW” };\n }\n // battery tok v A\n else if (msg.pid_id == “batt_current”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // battery tok v V\n else if (msg.pid_id == “batt_volts”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // SOC display\n else if (msg.pid_id == “soc_display”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = { payload: “Stanje akumulatorja: " + msg.payload[msg.payload.length-1].value.toFixed(2) + " %” };\n }\n // battery SOH\n else if (msg.pid_id == “soh”){\n outraw.payload = Number(msg.payload[msg.payload.length-1].value.toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 1\n else if (msg.pid_id == “batt_temp1”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 2\n else if (msg.pid_id == “batt_temp2”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 3\n else if (msg.pid_id == “batt_temp3”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 4\n else if (msg.pid_id == “batt_temp4”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 5\n else if (msg.pid_id == “batt_temp5”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 6\n else if (msg.pid_id == “batt_temp6”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 7\n else if (msg.pid_id == “batt_temp7”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 8\n else if (msg.pid_id == “batt_temp8”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 9\n else if (msg.pid_id == “batt_temp9”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 10\n else if (msg.pid_id == “batt_temp10”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 22\n else if (msg.pid_id == “batt_temp11”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n // temperatura sklopa 23\n else if (msg.pid_id == “batt_temp12”){\n outraw.payload = Number((msg.payload[msg.payload.length-1].value).toFixed(2));\n outraw.topic = “stat/autopi/”+msg.pid_id;\n textout = null;\n }\n else {\n outraw = null;\n textout = null;\n }\n}\nelse {\n outraw = null;\n textout = null;\n}\nreturn [outraw, textout]",“outputs”:2,“noerr”:0,“x”:560,“y”:980,“wires”:[[“56aad096.3322c8”,“720b7c4d.3962b4”],[“4182ad79.fe7c4c”,“865643df.82c22”]],“outputLabels”:[“outRaw”,“TelegramOut”]},{“id”:“720b7c4d.3962b4”,“type”:“mqtt out”,“z”:“de854356.27206”,“name”:“AutoPi out”,“topic”:"",“qos”:“0”,“retain”:“false”,“broker”:“9c9e2d3f.25de88”,“x”:950,“y”:960,“wires”:[]},{“id”:“56aad096.3322c8”,“type”:“debug”,“z”:“de854356.27206”,“name”:"",“active”:false,“tosidebar”:true,“console”:false,“tostatus”:false,“complete”:“true”,“targetType”:“full”,“x”:770,“y”:920,“wires”:[]},{“id”:“bc11f57a.a03c5”,“type”:“change”,“z”:“de854356.27206”,“name”:“move to pid_id”,“rules”:[{“t”:“move”,“p”:“payload”,“pt”:“msg”,“to”:“pid_id”,“tot”:“msg”}],“action”:"",“property”:"",“from”:"",“to”:"",“reg”:false,“x”:420,“y”:860,“wires”:[[“b46446ff.a25058”]]},{“id”:“316b2d84.64e75a”,“type”:“telegrambot-notify”,“z”:“de854356.27206”,“name”:“IoniqBot”,“bot”:“6776ad8c.1cf8e4”,“chatId”:“chatID”,“message”:"",“parseMode”:"",“x”:940,“y”:1020,“wires”:[]},{“id”:“4182ad79.fe7c4c”,“type”:“rbe”,“z”:“de854356.27206”,“name”:"",“func”:“rbei”,“gap”:"",“start”:"",“inout”:“out”,“property”:“payload”,“x”:770,“y”:1020,“wires”:[[“316b2d84.64e75a”]]},{“id”:“8ff29e2.7c3876”,“type”:“inject”,“z”:“de854356.27206”,“name”:"",“topic”:"",“payload”:“soh”,“payloadType”:“str”,“repeat”:“3600”,“crontab”:"",“once”:false,“onceDelay”:0.1,“x”:90,“y”:960,“wires”:[[“bc11f57a.a03c5”]]},{“id”:“865643df.82c22”,“type”:“debug”,“z”:“de854356.27206”,“name”:"",“active”:false,“tosidebar”:true,“console”:false,“tostatus”:false,“complete”:“true”,“targetType”:“full”,“x”:770,“y”:1060,“wires”:[]},{“id”:“12770b91.f25e04”,“type”:“inject”,“z”:“de854356.27206”,“name”:“batt_temp1-12”,“topic”:"",“payload”:“batt_temp1!batt_temp2!batt_temp3!batt_temp4!batt_temp5!batt_temp6!batt_temp7!batt_temp8!batt_temp9!batt_temp10!batt_temp11!batt_temp12”,“payloadType”:“str”,“repeat”:“30”,“crontab”:"",“once”:false,“onceDelay”:0.1,“x”:120,“y”:920,“wires”:[[“72f326f4.de27a”]]},{“id”:“72f326f4.de27a”,“type”:“split”,“z”:“de854356.27206”,“name”:"",“splt”:"!",“spltType”:“str”,“arraySplt”:1,“arraySpltType”:“len”,“stream”:false,“addname”:"",“x”:290,“y”:920,“wires”:[[“bc11f57a.a03c5”]]},{“id”:“9c9e2d3f.25de88”,“type”:“mqtt-broker”,“z”:"",“name”:“HASS”,“broker”:“localhost”,“port”:“1883”,“clientid”:“node-red”,“usetls”:false,“compatmode”:true,“keepalive”:“60”,“cleansession”:true,“birthTopic”:"",“birthQos”:“0”,“birthPayload”:"",“closeTopic”:"",“closeQos”:“0”,“closePayload”:"",“willTopic”:"",“willQos”:“0”,“willPayload”:""},{“id”:“6776ad8c.1cf8e4”,“type”:“telegrambot-config”,“z”:"",“botname”:“IoniqBot”,“usernames”:"",“chatIds”:"",“pollInterval”:“300”}]

I hope I didn’t reveal too much personal info in here, I was in a bit of a hurry :smiley:
If there is anything else you’d like to know, just ask.
And sorry about comments, they’re in Slovenian language. It’s not so hard to understand I hope (the code is mostly using variables named in English).

One more thing, battery power, battery current and battery voltage are requested every 5 seconds.
Battery temperatures of modules 1 through 12 are requested every 30 seconds, state of health is requested every hour.

4 Likes
#2

Hi Nejc,
Seem like a really nice flow, unfortunately I get “not Json format” error when pasted into the import-> clipboard in Node-red. I suspect that paste/copy from this forum corrupt/change some characters.
Is it possible that you can add the code to https://pastebin.com/ ?

Thank you heaps in advance

#3

Hi Andreas,

umm I forgot to mention, you have to edit the 3 nodes: “send login data” you have to put in your own username/e-mail and password + you have to configure your MQTT and Telegram nodes!

I can post code for both parts - tomorrow morning, when I’m at work :slight_smile:

#4

Hi,

Yes, i figured that, but still stuck with pasting the code, trying to clean it up, it has new line chars “\n” and ” instead of " and some other hidden chars that make error when pasting, but I am getting there :slight_smile:
Thank you very much for sharing and helping.

1 Like
#5

https://pastebin.com/NVCw68UU

Sorry for the late reply :frowning:
I had an emergency in the morning and I wasn’t in the office as previously intended :frowning:

1 Like
#6

Awsome, that worked like a charm to import. :slight_smile: Thank you. Have a nice weekend

2 Likes
#7

I got it all working now and have a decent setup, need more polish, but it gives me the information in Home Assistant that I need. Thank you Nejc_Koncan for this great code. Screenshot attached. Didn’t find as good picture of an e-Niro as this one on the Soul ev, but that has to do for now. :slight_smile:
image

4 Likes
#8

I just cleaned the code quite a bit, added stuff and I want to add location / last known position before I post the updated version.
Any ideas how to get this data? The old API site had some useful info but the new Swagger, I can’t get anything useful from there :frowning:

I tried getting ‘track.pos.loc’ but get back ‘Server Error (500)’.
‘track.pos.alt’ returns the altitude,
‘track.pos.nsat’ returns number of available sattelites,
‘track.pos.cog’ retuns something but I have no idea what it is (direction in degrees?),
‘track.pos.sog’ also returns some data, no idea what is means.

I tried using Google cache and Internet Archive Wayback Machine but couldn’t find the old API.

#9

Hi,
I did play around with that my self yesterday, but with no luck, at least you did get some data.
From marine GPS (which i most likely the same here) the abbreviations means

cog = Course Over Ground
sog = Speed over ground
zda = Date & Time
GLL = Geographic position, latitude / longitude

So given that, GLL might be worth trying.

1 Like
#10

Hey,

I tried ‘zda’ and ‘gll’, I don’t get any data back at all.
sog gives values up to about 10 max from what I’ve seen in Postman.

#11

In pure shell script, I’ve been using -

username="my user name"
password="my password"
deviceid="my device id"

token=$(curl --silent --header "Content-Type: application/json" --request POST --data "{ \"email\": \"${username}\", \"password\": \"${password}\" }" https://api.autopi.io/auth/login/ | jq --raw-output '.token')

curl --silent --header "Content-Type: application/json" --header "Authorization: bearer ${token}" "https://api.autopi.io/logbook/raw/?start_utc=2019-05-05T08:00&device_id=${deviceid}&data_type=track.pos" | jq '.'

( jq is a tool just to format the output, usually needs to be installed )

The above is using logbook/raw with data_type=track.pos. Results look like -

{
  "count": 7932,
  "results": [
    {
      "data": {
        "utc": "08:50:42",
        "sog": 10.1,
        "loc": {
          "lat": 51.42841,
          "lon": -0.84751
        },
        "nsat": 7,
        "cog": 92.36,
        "alt": 45
      },
      "ts": "2019-05-16T08:50:43.217669Z"
    },
    {
      "data": {
        "utc": "08:50:35",
        "sog": 4.8,
        "loc": {
          "lat": 51.42838,
          "lon": -0.84779
        },
        "nsat": 9,
        "cog": 27.07,
        "alt": 45
      },
      "ts": "2019-05-16T08:50:36.422919Z"
    },

Looks to me that sog is km/h BTW.

You can also use -

curl --silent --header "Content-Type: application/json" --header "Authorization: bearer ${token}" "https://api.autopi.io/logbook/storage/read/?device_id=${deviceid}&field_type=geo_point&field=track.pos.loc&from_utc=2019-05-05T08:00&interval=10s" | jq '.'

For the non-raw data.

1 Like
#12

Ahh that’s what I’ve been missing:
https://api.autopi.io/logbook/raw/?start_utc=2019-05-05T08:00&device_id=${deviceid}&data_type=track.pos"

Either I’m really stupid or I am, the new api.autopi.io is currently pretty darn useless :frowning:
No offence guys but would it be possible to get access to the old one for some time? :angel:

#13

Hi Nejc

The API itself should not have changed (much), just the interactive API documentation that has been changed.

But it is possible that we have accidentally broken something.
What issues are you seeing?

Best regards
/Malte