Skip to content

Instantly share code, notes, and snippets.

@durable-developer
Last active January 30, 2026 11:18
Show Gist options
  • Select an option

  • Save durable-developer/e6611b3acc4f8889326c6c5e72fbf1de to your computer and use it in GitHub Desktop.

Select an option

Save durable-developer/e6611b3acc4f8889326c6c5e72fbf1de to your computer and use it in GitHub Desktop.
Fetch SolarMAN PV data

Example of how to fetch data from Solarman PV OPEN API v1.1.6

To avoid unnecessarily struggling to connect Node-RED to the Solarman PV OPEN API (globalapi.solarmanpv.com).

Compatibility

Developed and tested on Node-RED 4.

Prerequisites

  • app ID and app secret, request by sending an email to [email protected]
  • login details, register an account with Solarman Smart (home.solarmanpv.com)
  • the password has to be SHA256 encrypted first (you can use an online convertor, crypto-js or a Node-RED package), Solarman recommends this online tool

Set-up

  • fill in app ID, app secret, email and password (SHA256 encrypted) in the set flow data node

Request steps

  1. request (and store) the access token with account token
  2. request (and store) the station ID with station list
  3. request (and store) the device ID with station device
  4. request historical data with device historical or current data with device currentData

Trouble shooting

[
{
"id": "753058b1a55fbfa2",
"type": "tab",
"label": "Solarman PV OPEN API (v1.1.6)",
"disabled": false,
"info": "",
"env": []
},
{
"id": "97e2b57ad550d351",
"type": "catch",
"z": "753058b1a55fbfa2",
"name": "catch (error)",
"scope": null,
"uncaught": false,
"x": 90,
"y": 40,
"wires": [
[
"929402fd6c835c5f"
]
]
},
{
"id": "929402fd6c835c5f",
"type": "debug",
"z": "753058b1a55fbfa2",
"name": "error",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 290,
"y": 40,
"wires": []
},
{
"id": "e3e50394532dd5fc",
"type": "comment",
"z": "753058b1a55fbfa2",
"name": "read.me",
"info": "# Example of how to fetch data from Solarman PV OPEN API v1.1.6\nTo avoid unnecessarily struggling to connect Node-RED to the Solarman PV OPEN API (globalapi.solarmanpv.com). \n\n## Prerequisites\n - app ID and app secret, request by sending an email to [email protected]\n - login details, register an account with Solarman Smart (home.solarmanpv.com)\n - the password has to be SHA256 encrypted first (you can use an online convertor, crypto-js or a Node-RED package)\n\n## Set-up\n - fill in app ID, app secret, email and password (SHA256 encrypted) in the _set flow data_ node\n\n## Request steps\n - request (and store) the access token with _account token_\n - request (and store) the station ID with _station list_\n - request (and store) the device ID with _station device_\n - request historical data with _device historical_",
"x": 900,
"y": 40,
"wires": []
},
{
"id": "1e4a47fa3de24e98",
"type": "http request",
"z": "753058b1a55fbfa2",
"name": "account token",
"method": "use",
"ret": "obj",
"paytoqs": "body",
"url": "https://{{location}}/account/{{version}}/token?appId={{appId}}",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 640,
"y": 180,
"wires": [
[
"c3a4c40f5255993e"
]
]
},
{
"id": "f9e283500afe1c56",
"type": "inject",
"z": "753058b1a55fbfa2",
"name": "set",
"props": [],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 70,
"y": 100,
"wires": [
[
"bb9204f882f692fe"
]
]
},
{
"id": "bb9204f882f692fe",
"type": "change",
"z": "753058b1a55fbfa2",
"name": "set flow data",
"rules": [
{
"t": "set",
"p": "headers.contentType",
"pt": "flow",
"to": "application/json",
"tot": "str"
},
{
"t": "set",
"p": "location",
"pt": "flow",
"to": "globalapi.solarmanpv.com",
"tot": "str"
},
{
"t": "set",
"p": "version",
"pt": "flow",
"to": "v1.0",
"tot": "str"
},
{
"t": "set",
"p": "appId",
"pt": "flow",
"to": "APPID",
"tot": "str"
},
{
"t": "set",
"p": "appSecret",
"pt": "flow",
"to": "APPSECRET",
"tot": "str"
},
{
"t": "set",
"p": "email",
"pt": "flow",
"to": "EMAIL",
"tot": "str"
},
{
"t": "set",
"p": "password",
"pt": "flow",
"to": "PASSWORD_SHA256",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 310,
"y": 100,
"wires": [
[]
]
},
{
"id": "87ca417fa31eca6c",
"type": "inject",
"z": "753058b1a55fbfa2",
"name": "set request data",
"props": [
{
"p": "method",
"v": "POST",
"vt": "str"
},
{
"p": "headers['content-type']",
"v": "headers.contentType",
"vt": "flow"
},
{
"p": "location",
"v": "location",
"vt": "flow"
},
{
"p": "version",
"v": "version",
"vt": "flow"
},
{
"p": "appId",
"v": "appId",
"vt": "flow"
},
{
"p": "payload.appSecret",
"v": "appSecret",
"vt": "flow"
},
{
"p": "payload.email",
"v": "email",
"vt": "flow"
},
{
"p": "payload.password",
"v": "password",
"vt": "flow"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 100,
"y": 180,
"wires": [
[
"1e4a47fa3de24e98"
]
]
},
{
"id": "c3a4c40f5255993e",
"type": "change",
"z": "753058b1a55fbfa2",
"name": "store tokens in flow data",
"rules": [
{
"t": "set",
"p": "accessToken",
"pt": "flow",
"to": "payload.access_token",
"tot": "msg"
},
{
"t": "set",
"p": "tokenType",
"pt": "flow",
"to": "payload.token_type",
"tot": "str"
},
{
"t": "set",
"p": "refreshToken",
"pt": "flow",
"to": "payload.refresh_token",
"tot": "msg"
},
{
"t": "set",
"p": "authorization",
"pt": "flow",
"to": "payload.token_type & ' ' & payload.access_token",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 850,
"y": 180,
"wires": [
[]
]
},
{
"id": "a9e64147829d42c7",
"type": "http request",
"z": "753058b1a55fbfa2",
"name": "station list",
"method": "use",
"ret": "obj",
"paytoqs": "body",
"url": "https://{{location}}/station/{{version}}/list",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 220,
"wires": [
[
"32980a623324c86b"
]
]
},
{
"id": "f086f7bea987a9c0",
"type": "inject",
"z": "753058b1a55fbfa2",
"name": "set request data",
"props": [
{
"p": "method",
"v": "POST",
"vt": "str"
},
{
"p": "headers['content-type']",
"v": "headers.contentType",
"vt": "flow"
},
{
"p": "headers.Authorization",
"v": "authorization",
"vt": "flow"
},
{
"p": "location",
"v": "location",
"vt": "flow"
},
{
"p": "version",
"v": "version",
"vt": "flow"
},
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "{}",
"payloadType": "json",
"x": 100,
"y": 220,
"wires": [
[
"a9e64147829d42c7"
]
]
},
{
"id": "32980a623324c86b",
"type": "change",
"z": "753058b1a55fbfa2",
"name": "store station in flow data",
"rules": [
{
"t": "set",
"p": "station",
"pt": "flow",
"to": "payload.stationList[0]",
"tot": "msg"
},
{
"t": "set",
"p": "stationId",
"pt": "flow",
"to": "payload.stationList[0].id",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 850,
"y": 220,
"wires": [
[]
]
},
{
"id": "6f380cbadc4d7196",
"type": "inject",
"z": "753058b1a55fbfa2",
"name": "set request data",
"props": [
{
"p": "method",
"v": "POST",
"vt": "str"
},
{
"p": "headers['content-type']",
"v": "headers.contentType",
"vt": "flow"
},
{
"p": "headers.Authorization",
"v": "authorization",
"vt": "flow"
},
{
"p": "location",
"v": "location",
"vt": "flow"
},
{
"p": "version",
"v": "version",
"vt": "flow"
},
{
"p": "payload.stationId",
"v": "stationId",
"vt": "flow"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 100,
"y": 260,
"wires": [
[
"2bce0978628ccb5c"
]
]
},
{
"id": "2bce0978628ccb5c",
"type": "http request",
"z": "753058b1a55fbfa2",
"name": "station device",
"method": "POST",
"ret": "obj",
"paytoqs": "body",
"url": "https://{{location}}/station/{{version}}/device",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 640,
"y": 260,
"wires": [
[
"1386834c067c1a87"
]
]
},
{
"id": "1386834c067c1a87",
"type": "change",
"z": "753058b1a55fbfa2",
"name": "set devices in flow data",
"rules": [
{
"t": "set",
"p": "devices",
"pt": "flow",
"to": "payload.deviceListItems",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 850,
"y": 260,
"wires": [
[]
]
},
{
"id": "133119fc2aef77ea",
"type": "function",
"z": "753058b1a55fbfa2",
"name": "time type 1",
"func": "const now = new Date();\nmsg.payload.endTime = now.toISOString().slice(0, 10);\nmsg.payload.startTime = msg.payload.endTime;\nmsg.payload.timeType = 1\n\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 350,
"y": 300,
"wires": [
[
"234d749502406d34"
]
]
},
{
"id": "234d749502406d34",
"type": "http request",
"z": "753058b1a55fbfa2",
"name": "device historical",
"method": "use",
"ret": "obj",
"paytoqs": "body",
"url": "https://{{location}}/device/{{version}}/historical",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 640,
"y": 300,
"wires": [
[]
]
},
{
"id": "90b0f3605d7f5e5a",
"type": "inject",
"z": "753058b1a55fbfa2",
"name": "set request data",
"props": [
{
"p": "method",
"v": "POST",
"vt": "str"
},
{
"p": "headers['content-type']",
"v": "headers.contentType",
"vt": "flow"
},
{
"p": "headers.Authorization",
"v": "authorization",
"vt": "flow"
},
{
"p": "location",
"v": "location",
"vt": "flow"
},
{
"p": "version",
"v": "version",
"vt": "flow"
},
{
"p": "payload.deviceId",
"v": "devices[1].deviceId",
"vt": "flow"
},
{
"p": "payload.deviceSn",
"v": "devices[1].deviceSn",
"vt": "flow"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 100,
"y": 300,
"wires": [
[
"bbd4c797c1cedbe5",
"133119fc2aef77ea",
"a0294e5a38b7b1b4",
"9acfcfcc077ad513",
"bffc0fd094ec04b5"
]
]
},
{
"id": "a0294e5a38b7b1b4",
"type": "function",
"z": "753058b1a55fbfa2",
"name": "time type 2",
"func": "msg.payload.endTime = '2024-01-31';\nmsg.payload.startTime = '2024-01-01';\nmsg.payload.timeType = 2\n\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 350,
"y": 340,
"wires": [
[
"234d749502406d34"
]
]
},
{
"id": "bbd4c797c1cedbe5",
"type": "function",
"z": "753058b1a55fbfa2",
"name": "time type 3",
"func": "msg.payload.endTime = '2024-01';\nmsg.payload.startTime = '2023-02';\nmsg.payload.timeType = 3\n\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 350,
"y": 380,
"wires": [
[
"234d749502406d34"
]
]
},
{
"id": "9acfcfcc077ad513",
"type": "function",
"z": "753058b1a55fbfa2",
"name": "time type 4",
"func": "msg.payload.endTime = '2024';\nmsg.payload.startTime = '2000';\nmsg.payload.timeType = 4\n\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 350,
"y": 420,
"wires": [
[
"234d749502406d34"
]
]
},
{
"id": "bffc0fd094ec04b5",
"type": "http request",
"z": "753058b1a55fbfa2",
"name": "device currentData",
"method": "use",
"ret": "obj",
"paytoqs": "body",
"url": "https://{{location}}/device/{{version}}/currentData",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 650,
"y": 340,
"wires": [
[]
]
}
]
@durable-developer
Copy link
Author

durable-developer commented Jun 13, 2025

Hello, can you help me on how to retrieve the currentData?

@golfredom I just updated the flow with a new node: device currentData. This retrieves the real-time data as described in the SolarmanOpenAPI documentation. Hope it works for you!

@golfredom
Copy link

Thank you very much!! thanks for you hard work!!!

@killamilla0815
Copy link

killamilla0815 commented Jul 11, 2025

Hello @durable-developer

I copied the flow into my Node-Red Docker on Pi application and entered the required details.
1st thing to notice is about the PW: this must not be entered in clear text but SHA256 hashed, then the API communcation worked for me.

Now I am struggling about another limitation: I've added some add. debug nodes to the flow in order to check the structure of the API output data, but the debug window is automatically wiped after around 10-15s which is far to less to go through all the data.

Google tells me that the auto wiping is probably due to limited cache memory on the instance that runs Node-Red, is there any chance to preserve the debug output ?

I was also trying to utilize the "write file" node, but this didnt work for, no file was generated.

Thanks for your support.

@durable-developer
Copy link
Author

durable-developer commented Jul 12, 2025

@killamilla0815

Thank you for using the flow and contributing!

I copied the flow into my Node-Red Docker on Pi application and entered the required details. 1st thing to notice is about the PW: this must not be entered in clear text but SHA256 hashed, then the API communcation worked for me.

Indeed the password should be SHA256 encrypted. I am not sure how it was working for me before. I added this requirement to the instructions. Great feedback!

Now I am struggling about another limitation: I've added some add. debug nodes to the flow in order to check the structure of the API output data, but the debug window is automatically wiped after around 10-15s which is far to less to go through all the data.

This sounds like a Node-RED issue/limitation. I am not running Node-RED within a docker and debugging has never been a problem for me. Not sure what the solution would be. It might also limit Node-RED nodes.
But I have several functions to format the SolarMAN response into chart data. I can share some to help you. What data specifically are you trying to get from which response?
Also, the SolarMAN API documentation of course has an example response for each request, maybe that can help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment