EcoFlow IoT-Platform - offizielle API

Mit ** gekennzeichnete Links auf dieser Seite sind Affiliatelinks.

EcoFlow IoT-Platform - offizielle API
EcoFlow IoT-Platform - offizielle API
  • 06.11.2024
  • Level 2
  • Hardware
  • Batteriespeicher

Vor über 1,5 Jahren habe ich mich mit einer EcoFlow River 2 Pro beschäftigt. Dieses Gerät wollte ich nutzen, um PV-Überschuss zu laden und diesen mit in die Nacht zu nehmen - als Batteriespeicher quasi. Zusätzlich sollte das Gerät als USV dienen. Nachdem EcoFlow gefragt hatte, ob ich das Gerät nicht testen möchte, habe ich genau diesen Plan geschildert. Leider gab es zu diesem Zeitpunkt keine offizielle Schnittstelle, über welche ich das Vorhaben überhaupt realisieren konnte. Mit viel Reverse Engineering der App konnte ich dann per MQTT auf die Cloud-Server zugreifen und die Anfragen der App nachstellen. Aber das war extrem umständlich.

Und genau das möchte die neue IoT-Platform von EcoFlow nun besser machen. In diesem Beitrag möchte ich kurz zeigen, was genau möglich ist und wie die Geräte angesprochen werden können. Da die Dokumentation nicht vollständig ist, kann ich leider nur eine Basis liefern. Du musst dann selbst ausprobieren, was genau geht (und was nicht). Denn EcoFlow hat ja nicht nur Power Stations im Sortiment, sondern auch Wechselrichter, große Batteriespeicher und Smart Home Panels. All diese Geräte soll die IoT-Platform vereinen. Soweit die Theorie. Schauen wir uns das einmal genauer an.

Was wird benötigt?

  • Ein EcoFlow-Gerät
  • Ein Cloud-Konto (mit der App eingerichtet), welchem das EcoFlow-Gerät zugeordnet wird
  • Ein Developer-Konto auf der IoT-Platform (gleiche Zugangsdaten wie die der App!)
  • Ein Access-Key und Secret (kann auf der Platform selbst erstellt werden)

Es ist also kein Kontakt mehr mit dem Hesteller nötig. Damals habe ich die Infos/API-Schlüssel per Mail bekommen.

Video

Hausbau-Kurs

EF ECOFLOW Tragbare Powerstation RIVER 2 Pro, 768 Wh Solargenerator mit LiFeP04, Schnellladung in 70 Minuten, 3x 800 W AC-Steckdosen, Balkonkraft für Notstrom/Camping/Wohnmobile/zu Hause **

EF ECOFLOW Tragbare Powerstation RIVER 2 MAX, 512 Wh Solargenerator mit LiFeP04, Schnellladung in 1 Stunde, bis zu 1000 W Leistung, Balkonkraft für Notstrom/Camping/Wohnmobile/zu Hause Schwarz **

Code-Beispiele

Info: Die Scripts dienen nur als Beispiel für die Kommunikation und sind in einem Produktivsystem so nicht nutzbar (es fehlt eine regelmäßige Ausführung uvm.)

Beispiele (ioBroker)

Die ersten Schritte konnte ich schnell im ioBroker machen. Hier hole ich z.B. alle Geräte und lese die Infos aus und kann schon erste Werte auf meiner River 2 Pro schreiben:

// v0.2
const crypto = require('node:crypto');
const axios = require('axios');

// Hier Zugangsdaten von https://developer-eu.ecoflow.com/us/security eintragen
const accessKey = '';
const secretKey = '';

function flattenKeys(obj, prefix) {
    const getPrefix = (k) => {
        if (!prefix) return k;
        return (Array.isArray(obj)) ? `${prefix}[${k}]` : `${prefix}.${k}`;
    };
    let res = {};

    Object.keys(obj).forEach(k => {
        if (typeof obj[k] === 'object') {
            res = { ...res, ...flattenKeys(obj[k], getPrefix(k)) };
        } else {
            res[getPrefix(k)] = obj[k];
        }
    });

    return res;
}

async function apiRequest(method, url, data) {
    const sha256 = (str, key) => crypto.createHmac('sha256', key).update(str).digest('hex');

    const nonce = String(100000 + Math.floor(Math.random() * 100000));
    const timestamp = String(Date.now());

    // Generate data string (sorted by keys)
    let dataStr = '';
    if (data) {
        const flatData = flattenKeys(data);
        const flatDataKeys = Object.keys(flatData);
        flatDataKeys.sort();

        dataStr = flatDataKeys.map(k => `${k}=${flatData[k]}`).join('&') + '&';
    }

    const uri = `${dataStr}accessKey=${accessKey}&nonce=${nonce}&timestamp=${timestamp}`;
    const sign = sha256(uri, secretKey);

    const apiResponse = await axios(
        {
            method,
            baseURL: 'https://api.ecoflow.com',
            url,
            data,
            headers: {
                'Content-Type': 'application/json;charset=UTF-8',
                accessKey,
                nonce,
                timestamp,
                sign,
            },
        }
    );

    console.info(`Received ${apiResponse.status} from ${method} to ${url} (${uri}): ${JSON.stringify(apiResponse.data)}`);

    if (apiResponse.status === 200 && apiResponse.data.code == 0) {
        return apiResponse.data;
    } else if (apiResponse.data.code) {
        throw new Error(`${apiResponse.data.code}: ${apiResponse.data.message}`);
    }
}

async function apiGetRequest(url) {
    return apiRequest('get', url);
}

async function apiPostRequest(url, data) {
    return apiRequest('post', url, data);
}

async function apiPutRequest(url, data) {
    return apiRequest('put', url, data);
}

async function getDeviceQuota(sn, quotas) {
    return apiPostRequest('/iot-open/sign/device/quota', {
        sn: sn,
        params: {
            quotas
        }
    });
}

apiGetRequest('/iot-open/sign/device/list').then(listResponse => {
    for (const device of listResponse.data) {
        console.log(`Found device ${device.productName} with serial number ${device.sn} (online: ${device.online})`);

        /*
        apiGetRequest(`/iot-open/sign/device/quota/all?sn=${device.sn}`).then(reponse => {
            console.info(reponse.data);
        });
        */

        apiPutRequest('/iot-open/sign/device/quota', {
            id: 123456789,
            version: '1.0',
            sn: device.sn,
            moduleType: 5,
            operateType: 'acOutCfg',
            params: {
                enabled: 1,
                xboost: 0,
                out_voltage: 230,
                out_freq: 50
            }
        }).then(() => {
            // Ein wenig warten, bis die Werte gesetzt wurden
            setTimeout(() => {
                getDeviceQuota(
                    device.sn,
                    [
                        'mppt.cfgAcEnabled',
                        'mppt.cfgAcXboost',
                        'mppt.cfgAcOutVol',
                        'mppt.cfgAcOutFreq'
                    ]
                ).then(quotaResponse => {
                    console.log(quotaResponse.data);
                });
            }, 5000);
        });
    }
});

MQTT-Verbindung

Unter /iot-open/sign/certification kann man mit seinem key und secret Zugangsdaten für den MQTT-Server anfordern. Beispiel-Response:

{
  "code": "0",
  "message": "Success",
  "data": {
    "certificateAccount": "open-XXXXXXX",
    "certificatePassword": "XXXXXX",
    "url": "mqtt-e.ecoflow.com",
    "port": "8883",
    "protocol": "mqtts"
  },
  "eagleEyeTraceId": "XXXXXX",
  "tid": ""
}

Mit diesen Daten kann man sich dann verbinden (MQTT über TLS). Interessante Topics sind:

  • /open/${certificateAccount}/${sn}/status
  • /open/${certificateAccount}/${sn}/quota

Auch hier können Werte gesetzt und gelesen werden.

  • /open/${certificateAccount}/${sn}/set
  • /open/${certificateAccount}/${sn}/set_reply
  • /open/${certificateAccount}/${sn/get
  • /open/${certificateAccount}/${sn}/get_reply

Fazit

Insgesamt eine gute Entwicklung, dass EcoFlow sich öffnet. Die API ist leider an manchen Stellen nicht gut dokumentiert, wodurch man nicht so richtig versteht, was man warum setzen muss (gerade in den Requests zum setzen von neuen Werten). Es gibt einige Beispiele in der Dokumentation, aber vollständig ist das nicht.


Transparenz-Hinweis (Level 2)

Für diesen Beitrag wurden mir Produkte kostenfrei zur Verfügung gestellt! Es wurden keinerlei Bedingungen, Richtlinien oder Vorgaben bezüglich der Inhalte, welche ich in meiner Bewertung äußern darf, auferlegt.

Darüber hinaus habe ich keine zusätzliche Vergütung erhalten.

Du willst mehr?

Smart-Home-Trainings von A-Z

Steig' noch tiefer in die Themen ein und meistere Deine Projekte! Über 15.000 Teilnehmer konnten sich schon von der Qualität der Online-Kurse überzeugen.