SMA EV Charger get data using curl - how to this in Node-RED?

Hi all,

below is a trace ( I do not know if this is the correct name because in principle I have no clue about programming with Linux, Perl, Curl and so on and, in addition, English is not my native language... ) of some curl commands used to login to my EV Charger (type SMA EV Charger) and to read out some data. This trace is copied from my Rasperry Pi 2B shell running with Raspberry Pi OS Bullseye.

The code for these commands is based on devices / modules created for FHEM (fhem.de), a GPL'd perl server for house automation.
One problem for me is, that PERL is an anathema for me!

My goal is to achieve this (connect to the SMA EV Charger and get data) with Node-RED where I am already using the sma-webconnect node to get data from my SMA PV (solar) inverter.

Actually I am only interested in getting a few data (like the value of "Measurement.ChaSess.WhIn", "Measurement.Operation.EVeh.Health", "Measurement.Operation.Health" and "Measurement.Operation.Evt.Msg") and to use it for a chart or a pushover notification on my smartphone.

Can somebody help me to implement this in Node-RED?
Is it in general possible to use a function node with some JavaScript code?
And can somebody help me in writing this code?

Or do I need a HTTP request node?
And can somebody help me to configuring this node?

I wish you all a Happy New Year and I hope for some helpful feedback!

Kind regards
Thomas

CURL COMMAND LOGIN:
curl -i -s -k -X 'POST' -H 'Host: 192.168.178.87' -H 'Connection: close' -H 'Accept: application/json, text/plain, */*' -H 'User-Agent: okhttp/3.10.0' -H 'Sec-Fetch-Site: same-origin' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Dest: empty' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7' -H 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' -H 'Content-Length: 56' -H 'Origin: https://192.168.178.87' -H 'Referer: https://192.168.178.87/webui/login' --data-binary 'grant_type=password&username=<USERNAME>&password=<PASSWORD>' https://192.168.178.87/api/v1/token

CURL RESPONSE TOKEN:
HTTP/1.1 200 OK
Connection: close
Date: Sat, 08 Jan 2022 19:55:36 GMT
Access-Control-Allow-Origin: https://192.168.178.87
Access-Control-Allow-Credentials: true
Cache-Control: no-cache,no-store,must-revalidate
Pragma: no-cache
Cache-Control: no-store
Set-Cookie: JSESSIONID=node0vpbqavqu641o1v3a9s5pe7qbi509.node0;Path=/;Secure;HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json

{"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDE2NzE3MzYsInN1YiI6IldCQWRtaW4iLCJ1aWQiOiJkYThmNzMxNi1kZmUxLTQzNjMtODU2Yi0yZmI5YjMwNzJkNTAiLCJleHAiOjE2NDE2NzUzMzZ9.EOUQkXk9Fw7vhZeFE-2TFxHqoNv5fJZo7CXs7H-be6o","expires_in":3600,"refresh_token":"eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDE2NzE3MzYsInN1YiI6IldCQWRtaW4iLCJ1aWQiOiJkYThmNzMxNi1kZmUxLTQzNjMtODU2Yi0yZmI5YjMwNzJkNTAiLCJleHAiOjE2NDE3NTgxMzYsInR5cGUiOiJyZWZyZXNoIn0.v4OPHTvTHy050-PTVtKhBFWkKahYTtCHOUN2iRcyEKs","token_type":"bearer","uiIdleTime":"60"}

CURL COMMAND LIVE DATA
curl -i -s -k -X 'POST' -H 'Host: 192.168.178.87' -H 'Connection: close' -H 'Accept: application/json, text/plain, */*' -H 'User-Agent: okhttp/3.10.0' -H 'Sec-Fetch-Site: same-origin' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Dest: empty' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7' -H 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' -H 'Content-Length: 30' -H 'Referer: https://192.168.178.87/webui/login' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDE2NzE3MzYsInN1YiI6IldCQWRtaW4iLCJ1aWQiOiJkYThmNzMxNi1kZmUxLTQzNjMtODU2Yi0yZmI5YjMwNzJkNTAiLCJleHAiOjE2NDE2NzUzMzZ9.EOUQkXk9Fw7vhZeFE-2TFxHqoNv5fJZo7CXs7H-be6o' -b 'node03v08nb30tz041fqxrsfye75cv502.node0' --data-binary '[{"componentId":"IGULD:SELF"}]' https://192.168.178.87/api/v1/measurements/live/

CURL RESPONSE LIVE DATA:
HTTP/1.1 200 OK
Connection: close
Date: Sat, 08 Jan 2022 19:58:08 GMT
Cache-Control: no-cache,no-store,must-revalidate
Content-Type: application/json

[{"channelId":"Measurement.ChaSess.WhIn","componentId":"IGULD:SELF","values":[{"time":"2021-12-31T11:49:35.404Z","value":0}]},{"channelId":"Measurement.Chrg.ModSw","componentId":"IGULD:SELF","values":[{"time":"2021-12-30T12:02:15.161Z","value":4950}]},{"channelId":"Measurement.GridMs.A.phsA","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:08.460Z","value":0}]},{"channelId":"Measurement.GridMs.A.phsB","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:08.461Z","value":0}]},{"channelId":"Measurement.GridMs.A.phsC","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:08.461Z","value":0}]},{"channelId":"Measurement.GridMs.Hz","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":50}]},{"channelId":"Measurement.GridMs.PhV.phsA","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.368Z","value":230.839}]},{"channelId":"Measurement.GridMs.PhV.phsB","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":231.695}]},{"channelId":"Measurement.GridMs.PhV.phsC","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":231.074}]},{"channelId":"Measurement.GridMs.TotPF","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.370Z","value":0}]},{"channelId":"Measurement.GridMs.TotVA","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":0}]},{"channelId":"Measurement.GridMs.TotVAr","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":0}]},{"channelId":"Measurement.InOut.GI1","componentId":"IGULD:SELF","values":[{"time":"2021-12-22T08:52:34.801Z","value":0}]},{"channelId":"Measurement.Metering.GridMs.TotWIn","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":0}]},{"channelId":"Measurement.Metering.GridMs.TotWIn.ChaSta","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.369Z","value":0}]},{"channelId":"Measurement.Metering.GridMs.TotWhIn","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.316Z","value":505470}]},{"channelId":"Measurement.Metering.GridMs.TotWhIn.ChaSta","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:04.316Z","value":505470}]},{"channelId":"Measurement.Operation.EVeh.ChaStt","componentId":"IGULD:SELF","values":[{"time":"2022-01-08T19:58:08.381Z","value":200111}]},{"channelId":"Measurement.Operation.EVeh.Health","componentId":"IGULD:SELF","values":[{"time":"2021-12-22T08:53:06.007Z","value":307}]},{"channelId":"Measurement.Operation.Evt.Msg","componentId":"IGULD:SELF","values":[{"time":"2021-12-31T11:49:35.466Z","value":302}]},{"channelId":"Measurement.Operation.Health","componentId":"IGULD:SELF","values":[{"time":"2021-12-31T11:49:35.466Z","value":307}]},{"channelId":"Measurement.Operation.WMaxLimSrc","componentId":"IGULD:SELF","values":[{"time":"2021-12-22T08:52:16.233Z","value":2608}]},{"channelId":"Measurement.Wl.ConnStt","componentId":"IGULD:SELF","values":[{"time":"2021-12-22T08:52:45.955Z","value":303}]},{"channelId":"Measurement.Wl.SigPwr","componentId":"IGULD:SELF","values":[{"time":"2021-12-22T08:52:45.956Z","value":0}]},{"channelId":"Setpoint.PlantControl.Inverter.FstStop","componentId":"IGULD:SELF","values":[{"time":"2021-12-28T22:23:30.583Z","value":1467}]}]

FLOW... (use CTRL+I) to import this

[{"id":"7a01d3ed98699202","type":"inject","z":"a55f8f0f93e84992","name":"Click me","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":400,"y":640,"wires":[["7a01d3ed98699203"]]},{"id":"7a01d3ed98699203","type":"function","z":"a55f8f0f93e84992","name":"","func":"//original curl:  \n//\n//curl -i -s -k -X 'POST' -H 'Host: 192.168.178.87' -H 'Connection: close' -H 'Accept: application/json, text/plain, */*' -H 'User-Agent: okhttp/3.10.0' -H 'Sec-Fetch-Site: same-origin' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Dest: empty' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7' -H 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' -H 'Content-Length: 56' -H 'Origin: https://192.168.178.87' -H 'Referer: https://192.168.178.87/webui/login' --data-binary 'grant_type=password&username=<USERNAME>&password=<PASSWORD>' https://192.168.178.87/api/v1/token\n\nconst user = \"user\";\nconst pass = \"pass\";\n\nmsg.method = \"post\";\nmsg.url = \"https://192.168.178.87/api/v1/token\";\nmsg.payload = `grant_type=password&username=${user}&password=${pass}`;\nmsg.headers = {\n    \"Host\": \"192.168.178.87\",\n    \"Connection\": \"close\",\n    \"Accept\": \"application/json, text/plain, */*\",\n    \"User-Agent\": \"okhttp/3.10.0\",\n    \"Sec-Fetch-Site\": \"same-origin\",\n    \"Sec-Fetch-Mode\": \"cors\",\n    \"Sec-Fetch-Dest\": \"empty\",\n    \"Accept-Encoding\": \"gzip, deflate\",\n    \"Accept-Language\": \"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7\",\n    \"Content-Type\": \"application/x-www-form-urlencoded;charset=UTF-8\",\n    \"Content-Length\": \"56\",\n    \"Origin\": \"https://192.168.178.87\",\n    \"Referer\": \"https://192.168.178.87/webui/login\"\n};\nmsg.cookies = null;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":580,"y":640,"wires":[["7a01d3ed98699204"]]},{"id":"7a01d3ed98699204","type":"http request","z":"a55f8f0f93e84992","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"credentials":{"user":"","password":""},"x":760,"y":640,"wires":[["5cdd4962fb5278c6"]]},{"id":"7a01d3ed98699205","type":"debug","z":"a55f8f0f93e84992","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1230,"y":640,"wires":[]},{"id":"86c76882daddd902","type":"inject","z":"a55f8f0f93e84992","name":"Click me","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":400,"y":760,"wires":[["86c76882daddd903"]]},{"id":"86c76882daddd903","type":"function","z":"a55f8f0f93e84992","name":"","func":"//original curl:  \n//\n//curl -i -s -k -X 'POST' -H 'Host: 192.168.178.87' -H 'Connection: close' -H 'Accept: application/json, text/plain, */*' -H 'User-Agent: okhttp/3.10.0' -H 'Sec-Fetch-Site: same-origin' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Dest: empty' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7' -H 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' -H 'Content-Length: 30' -H 'Referer: https://192.168.178.87/webui/login' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDE2NzE3MzYsInN1YiI6IldCQWRtaW4iLCJ1aWQiOiJkYThmNzMxNi1kZmUxLTQzNjMtODU2Yi0yZmI5YjMwNzJkNTAiLCJleHAiOjE2NDE2NzUzMzZ9.EOUQkXk9Fw7vhZeFE-2TFxHqoNv5fJZo7CXs7H-be6o' -b 'node03v08nb30tz041fqxrsfye75cv502.node0' --data-binary '[{\"componentId\":\"IGULD:SELF\"}]' https://192.168.178.87/api/v1/measurements/live/\nconst token = flow.get(\"token\");;\nmsg.method = \"post\";\nmsg.url = \"https://192.168.178.87/api/v1/measurements/live/\";\nmsg.payload = `[{\"componentId\":\"IGULD:SELF\"}]`;\nmsg.headers = {\n    \"Host\": \"192.168.178.87\",\n    \"Connection\": \"close\",\n    \"Accept\": \"application/json, text/plain, */*\",\n    \"User-Agent\": \"okhttp/3.10.0\",\n    \"Sec-Fetch-Site\": \"same-origin\",\n    \"Sec-Fetch-Mode\": \"cors\",\n    \"Sec-Fetch-Dest\": \"empty\",\n    \"Accept-Encoding\": \"gzip, deflate\",\n    \"Accept-Language\": \"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7\",\n    \"Content-Type\": \"application/x-www-form-urlencoded;charset=UTF-8\",\n    \"Content-Length\": \"30\",\n    \"Referer\": \"https://192.168.178.87/webui/login\",\n    \"Authorization\": `Bearer ${token}`\n};\nmsg.cookies = {};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":580,"y":760,"wires":[["86c76882daddd904"]]},{"id":"86c76882daddd904","type":"http request","z":"a55f8f0f93e84992","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":760,"y":760,"wires":[["86c76882daddd905"]]},{"id":"86c76882daddd905","type":"debug","z":"a55f8f0f93e84992","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":940,"y":760,"wires":[]},{"id":"4a16b9dc548f9f9f","type":"comment","z":"a55f8f0f93e84992","name":"Login / get token","info":"","x":400,"y":600,"wires":[]},{"id":"5cdd4962fb5278c6","type":"change","z":"a55f8f0f93e84992","name":"store payload.access_token","rules":[{"t":"set","p":"token","pt":"flow","to":"payload.access_token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":640,"wires":[["7a01d3ed98699205"]]},{"id":"df379a4c2f4ef2f1","type":"comment","z":"a55f8f0f93e84992","name":"Use token (get data)","info":"","x":410,"y":720,"wires":[]}]

Open the first function node and set user and pass as required.

DISCLAIMER - untested (since I dont have your device nor have i read any manuals - but it should give you the gist of how to achieve it)

1 Like

Stephen, absolutely FANTASTIC! :ok_hand:
It works, see screenshot.

I am so happy with this, thank you very very much!!!

The only thing I had to add to the function nodes was this:

msg.rejectUnauthorized = false; // without this line,
                                // error UNABLE_TO_VERIFY_LEAF_SIGNATURE is
                                // signaled by the following http request node

But be shure, a lot of new questions will arise soon...
:thinking:

Glad it worked.

Note. The data in the debug output looks like JSON. You might want to change the 2nd http request node to parse the response JSON into a js object. That way you can access values directly...

There’s a great page in the docs that will explain how to use the debug panel to find the right path to any data item.

Pay particular attention to the part about the buttons that appear under your mouse pointer when you over hover a debug message property in the sidebar.

BX00Cy7yHi

https://nodered.org/docs/user-guide/messages

1 Like