APSystems - Solar API

APsystems Integration with Homey via n8n and OpenAPI
Hi all,

Since November 2025 my TOON stopped receiving solar data from APSystems. There have been many conversations around finding a workaround but nothing worked for me.

Yesterday I sat down and started working on it with a successful result.

My solution uses a combination of a local server running n8n and Homey. I’m not sure if the n8n step can be executed from within Homey itself, perhaps it could, but for me this was the easiest option.
I hope this helps others in the same situation. Here’s how I did it.


Step 1 — Request OpenAPI access

Login to the APSystems app → Settings → OpenAPI Service → Request Service.

Their OpenAPI is free for 1,000 monthly calls. Not quite as wide as I’d like, but the alternative is a paid subscription which I tend to avoid.

This will give you:

  • App ID

  • App Secret

You will also need:

  • SID — found under Account Details → Personal Info

  • EID — found under the Module section in various places in the app


Step 2 — n8n workflow

The workflow consists of these nodes:

Schedule Trigger A series of cron expressions to get as much and as granular solar data as possible while staying within the 1,000 call limit. Example setup that gives 32 calls/day (992/month):

0 8 * * *
0,30 9 * * *
*/15 10-15 * * *
0,30 16 * * *
0,30 17 * * *
0 18 * * *

Make sure to set the timezone to Europe/Amsterdam (or your local timezone) under workflow settings.

Code node (JavaScript) This generates the timestamp, nonce and the string to sign as required by the APSystems signature specification:

function generateHex32() {
  let result = '';
  for (let i = 0; i < 32; i++) {
    result += Math.floor(Math.random() * 16).toString(16);
  }
  return result;
}

const appId = 'YOUR_APP_ID';
const sid = 'YOUR_SID';
const eid = 'YOUR_EID';
const timestamp = Date.now().toString();
const nonce = generateHex32();
const method = 'GET';
const signMethod = 'HmacSHA256';
const requestPathForSignature = eid;
const stringToSign = `${timestamp}/${nonce}/${appId}/${requestPathForSignature}/${method}/${signMethod}`;

return [{
  json: {
    appId,
    timestamp,
    nonce,
    sid,
    eid,
    today: new Date().toISOString().split('T')[0],
    stringToSign
  }
}];

Crypto node

  • Action: HMAC

  • Type: SHA256

  • Value: {{ $json.stringToSign }}

  • Secret: [Your_App_Secret]

  • Encoding: Base64

  • Output field name: signature

HTTP Request node

  • Method: GET

  • URL: https://api.apsystemsema.com:9282/user/api/v2/systems/YOUR_SID/devices/ecu/energy/YOUR_EID

  • Query parameters: energy_level=minutely and date_range={{ $json.today }}

  • Headers:

    • X-CA-AppId: {{ $json.appId }}

    • X-CA-Timestamp: {{ $json.timestamp }}

    • X-CA-Nonce: {{ $json.nonce }}

    • X-CA-Signature-Method: HmacSHA256

    • X-CA-Signature: {{ $json.signature }}

Code node (JavaScript) — extract latest power

const data = $input.first().json.data;
const powerList = data.power;
const timeList = data.time;
const latestPower = powerList[powerList.length - 1];
const latestTime = timeList[timeList.length - 1];

return [{
  json: {
    latestPower,
    latestTime,
    today: data.today
  }
}];

HTTP Request node — push to Homey

  • Method: POST

  • URL: your Homey webhook URL

  • Header: Content-Type: application/json

  • Body:

{
  "latestPower": {{ $json.latestPower }},
  "latestTime": "{{ $json.latestTime }}",
  "today": {{ $json.today }}
}


Step 3 — Homey flow

Step 3 — Homey flow
I used the community Webhooks app to receive the incoming data from n8n.

The flow works in two parts:
Part 1 — Parse the webhook body
After the webhook trigger, a HomeyScript is called with the Body tag as argument. The script parses the JSON and writes the three values to Homey logic variables:

//updateZon
const data = JSON.parse(args[0]);
const vars = await Homey.logic.getVariables();

const updateLogicVar = async (name, value) => {
const v = Object.values(vars).find(varObj => varObj.name === name);
if (v) await Homey.logic.updateVariable({ id: v.id, variable: { value: value } });
};

await updateLogicVar(‘latestPowerW’, parseFloat(data.latestPower));
await updateLogicVar(‘latestUpdateTime’, data.latestTime);
await updateLogicVar(‘totalPowerTodaykWh’, parseFloat(data.today));

return {
power: parseFloat(data.latestPower),
energy: parseFloat(data.today)
};

I created three Homey logic variables:
• latestPowerW (Number) — current solar output in Watts
• latestUpdateTime (Text) — timestamp of the last reading
• totalPowerTodaykWh (Number) — total energy produced today in kWh

Part 2 — The virtual device

I created a virtual device to display the values on Toon.

Two separate flows listen for variable changes:
• When latestPowerW changes → set measure_power on the virtual solar panel device
• When totalPowerTodaykWh changes → set meter_power on the virtual solar panel device

Sunset reset
A third flow triggers 30 minutes before sunset and sets latestPowerW to 0. ensuring the virtual device doesn’t show stale power readings after the sun goes down.


Hope this helps anyone.

My main objective was to continue the existing automations regarding my Tahoma window blinds/shutters. I’s not realtime data but, I can live with the 15 minute interval.

Happy to answer any questions!