Gibt es dazu ein Script oder Modul? Wie hast du das gelöst? Da ich auch eine Delta Pro und Powerstream betreibe wäre ich sehr an einer Steuerung der Einspeiseleitung ins Haus interessiert…
Ich habe bisher nur drei oder vier rudimentäre, unaufgeräumte Scripte die ich mir aus den Infos und Beispielen in der EcoFlow-API Facebook Gruppe abgeleitet habe.
Wollte daraus demnächst mal irgendwann was generischeres machen was zumindest einige Abfragen vereint.
Kann mal die die ich habe zur Verfügung stellen falls du schon spielen willst.
Eines zum Setzen der Einspeiseleistung habe ich noch nicht, aber das kann ich dir sicher nächste Woche aus einem der anderen ableiten. Steht bei mir eh als nächstes auf der Liste
Zum Screenshot:
Leistungen der beiden Solarmodule (DPro und PS), ladestand DPro, Lesitungs schwarze Steckdose (=die direkt an DPro) und Kühlbox (an 12V DPro) ermittle ich aus der Statusabfrage der DeltaPro und PS.
Leistung des Kühlschranks kommt ebenfalls vom PS weil der Kühlschrank an einem EcoFlow Schalt/Mess Plug hängt.
Waschmaschine hängt an einem Homematic Schalt/Mess Adapter.
Momentanleisting Stromzähler lese ich über ein Hichi WLAN Interface aus.
Die weiße Steckdose symbolisiert die restlichen Verbraucher im Haus. Den Wert berechne ich mir aus Lesitung Stromzähler + Abgabe PS - aller anderen 230V Verbraucher im Bild
Mittlerweile schaut das PopUp auch noch etwas besser aus und beinhaltet Tageswerte für Erzeugung und Netzbezug
Tach zusammen!
Habe auch ganz neu einen EcoFlow PowerStream Wechselrichter und setze mich ebenfalls gerade mit der API auseinander…
Wer schon einen access-/secret-key aus dem Developer-Portal hat, kann sich ja mal mit folgendem Codeschnipsel spielen:
<?php
$sn = 'HW123ABC';
$accessKey = 'DeinAccessKey';
$secretKey = 'DeinSecretKey';
$timestamp = (string)intval(microtime(true) * 1000);
$nonce = substr(base64_encode(hash("sha256", $timestamp, True)), 0, -1);
$str = 'accessKey='.$accessKey.'&nonce='.$nonce.'×tamp='.$timestamp;
$sign = hash_hmac('sha256', $str, $secretKey);
//$url = 'https://api-e.ecoflow.com/iot-open/sign/device/list';
$url = 'https://api-e.ecoflow.com/iot-open/sign/device/quota/all?sn='.$sn;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$headers = array(
'Content-Type: application/json',
'accessKey:'.$accessKey,
'nonce:'.$nonce,
'timestamp:'.$timestamp,
'sign:'.$sign,
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$resp = json_decode(curl_exec($curl));
curl_close($curl);
echo $resp->data->{'20_1.invDemandWatts'}.PHP_EOL;
print_r($resp);
?>
Ich habe nun auch meine Freischaltung zum Developer-Portal bekommen. Und mit dem Codeschnipsel von @Strichcode kann ich von meiner DELTA Pro auch Werte lesen. Danke.
Wie funtioniert das nun aber, wenn ich z.B. die Ausgänge schalten möchte oder andere Parameter an der DELTA Pro ändern möchte?
Hat das schon jemand hier umgesetzt?
Danke für einen weiteren „Codeschnipsel“
Gruß
Rainer
Hallo @erpe,
ich habe noch keine Powerstation, deswegen habe ich noch nichts zum schalten…
Die Doku zu Deiner Delta Pro gibt es hier: Ecoflow Developer
So wie ich das verstehe, musst Du eigentlich nur den Curl-Aufruf anpassen und in den passenden PUT-Befehl umbauen. Beispiele findest Du auf der verlinkten Seite, mein Code-Schnipsel ist im Prinzip der GET von dort, für die PHP-Syntax gibt es Online-Übersetzer wie z.B. hier:
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api-e.ecoflow.com/iot-open/sign/device/quota');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"sn\":\"DCABZ***\",\"params\":{\"cmdSet\":32,\"id\":66,\"enabled\":1}}");
$headers = array();
$headers[] = 'Content-Type: application/json;charset=UTF-8';
$headers[] = 'Accesskey: OCHzRuj6NLF7o43***';
$headers[] = 'Timestamp: 1681872798000';
$headers[] = 'Nonce: 238752';
$headers[] = 'Sign: 0f3a1b102cd9a306307b7a9a0f0a9add7b8bfc93cf11***';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
Vielleicht kommst Du damit schon alleine etwas weiter…
Gruß
Strichcode
Ungetestet und ins blaue geschrieben vielleicht so um den X-Boost (id66) zu aktivieren:
<?php
$sn = 'HW123ABC';
$accessKey = 'DeinAccessKey';
$secretKey = 'DeinSecretKey';
$timestamp = (string)intval(microtime(true) * 1000);
$nonce = substr(base64_encode(hash("sha256", $timestamp, True)), 0, -1);
$str = 'accessKey='.$accessKey.'&nonce='.$nonce.'×tamp='.$timestamp;
$sign = hash_hmac('sha256', $str, $secretKey);
$url = 'https://api-e.ecoflow.com/iot-open/sign/device/quota';
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($curl, CURLOPT_POSTFIELDS, "{\"sn\":".$sn.",\"params\":{\"cmdSet\":32,\"id\":66,\"enabled\":1}}");
$headers = array(
'Content-Type: application/json;charset=UTF-8',
'accessKey:'.$accessKey,
'nonce:'.$nonce,
'timestamp:'.$timestamp,
'sign:'.$sign,
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$resp = json_decode(curl_exec($curl));
if (curl_errno($curl)) {
echo 'Error:' . curl_error($curl);
}
curl_close($curl);
print_r($resp);
?>
Hallo,
nun hat es etwas gedauert…
Aber mit viel Lesen und Suchen in der EcoFlow API Dokumentation und im Internet konnte ich nun die Funtionen der API für mein DELTA PRO erfolgreich umsetzen. Das EcoFlow Java-Script war an einigen Stellen hilfreich, da das Senden/Schalten dann doch eine Herausforderung war.
Das „EcoFlow API Main“ Script enthält die Funktionen zum Abfragen und Senden von Parametern und der Device-Liste.
Das EcoFlow API Main Script:
<?php
// this should be replaced your accessKey and secretKey from EcoFlow Open Platform
$accessKey = "Dein accessKey";
$secretKey = "Dein secretKey";
$HOST = "https://api-e.ecoflow.com";
$GET_MQTT_CERTIFICATION_URL = $HOST . "/iot-open/sign/certification";
$DEVICE_LIST_URL = $HOST . "/iot-open/sign/device/list";
$SET_QUOTA_URL = $HOST . "/iot-open/sign/device/quota";
$GET_QUOTA_URL = $HOST . "/iot-open/sign/device/quota";
$GET_ALL_QUOTA_URL = $HOST . "/iot-open/sign/device/quota/all";
function getMQTTCertification() {
global $accessKey, $secretKey, $GET_MQTT_CERTIFICATION_URL;
$jsonObject = [];
$response = getHttpUriRequest("GET", $GET_MQTT_CERTIFICATION_URL, $jsonObject, $accessKey, $secretKey);
echo "response: getMQTTCertification|" . $response;
}
function deviceList() {
global $accessKey, $secretKey, $DEVICE_LIST_URL;
$jsonObject = [];
$response = getHttpUriRequest("GET", $DEVICE_LIST_URL, $jsonObject, $accessKey, $secretKey);
if ($response['code'] === '0') {
$data = $response['data'];
return $data;
}
throw new RuntimeException('Error getting deviceList: ' . $response['message']);
}
function setQuota(string $deviceSN, string $params) {
global $accessKey, $secretKey, $SET_QUOTA_URL;
$json = '{"sn": "'.$deviceSN.'",'.$params.'}';
$jsonObject = json_decode($json, true);
$response = getHttpUriRequest("PUT", $SET_QUOTA_URL, $jsonObject, $accessKey, $secretKey);
if ($response['code'] === '0') {
return $response['message'];
}
throw new RuntimeException('Error setting quota information: ' . $response['message']);
}
function getQuota(string $deviceSN, string $params) {
global $accessKey, $secretKey, $GET_QUOTA_URL;
$json = '{"sn":"'.$deviceSN.'",'.$params.'}';
$jsonObject = json_decode($json, true);
$response = getHttpUriRequest("POST", $GET_QUOTA_URL, $jsonObject, $accessKey, $secretKey);
if ($response['code'] === '0') {
$data = $response['data'];
ksort($data, SORT_STRING);
return $data;
}
throw new RuntimeException('Error getting quota information: ' . $response['message']);
}
function getAllQuota(string $deviceSN) {
global $accessKey, $secretKey, $GET_ALL_QUOTA_URL;
$url = $GET_ALL_QUOTA_URL . "?sn=" . $deviceSN;
$jsonObject = [];
$response = getHttpUriRequest("GET", $url, $jsonObject, $accessKey, $secretKey);
if ($response['code'] === '0') {
$data = $response['data'];
ksort($data, SORT_STRING);
return $data;
}
throw new RuntimeException('Error getting all quota information: ' . $response['message']);
}
function getHttpUriRequest($httpMethod, $url, $req, $accessKey, $secretKey) {
$nonce = createNonce(); //(string) random_int(100000, 999999);
$timestamp = createTimestamp(); //time() * 1000;
$signature = generateSignature($nonce, $timestamp, $req);
$headers = array(
'Content-Type: application/json;charset=UTF-8',
'accessKey:'.$accessKey,
'nonce:'.$nonce,
'timestamp:'.$timestamp,
'sign:'.$signature,
);
if ($httpMethod === 'GET') {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$request = json_decode(curl_exec($curl), true);
curl_close($curl);
} elseif ($httpMethod === 'PUT') {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($req));
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$request = json_decode(curl_exec($curl), true);
curl_close($curl);
} elseif ($httpMethod === 'POST') {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($req));
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$request = json_decode(curl_exec($curl), true);
curl_close($curl);
} else {
throw new Exception("HTTP method not supported");
}
return $request;
throw new RuntimeException('Error getting information: ' . $request['message']);
}
/**
* Generates a signature for the given nonce, timestamp, and data.
*
* The generated signature is used for authentication purposes in the EcoFlow API. It ensures the integrity and
* security of the communication between the client and the API by verifying the authenticity of the request.
*
* The method follows these steps to generate the signature:
* 1. Flattens the input `$data` array into a single-dimensional array using the `flattenData()` helper function.
* 2. Sorts the flattened data array alphabetically by the keys using the `ksort()` function with the `SORT_STRING`
* flag.
* 3. Concatenates the flattened and sorted data array into a string using the `http_build_query()` function.
* 4. Appends the access key, nonce, and timestamp to the concatenated string using the `sprintf()` function.
* 5. Removes any leading ampersand (`&`) from the resulting base string using the `ltrim()` function.
* 6. Encrypts the base string using the HMAC-SHA256 algorithm along with the secret key.
* 7. Converts the resulting byte array into a hexadecimal string using the `bin2hex()` function.
*
* @param string $nonce The nonce value. This is a random string that is used once in each request to prevent
* replay attacks.
* @param string $timestamp The timestamp value. This is the current time in milliseconds.
* @param array{
* sn?: string,
* cmdCode?: string,
* params?: array<string, string|int>
* } $data The data to be included in the signature. This can include the serial number (sn), command code
* (cmdCode), and other parameters (params).
*
* @return string The generated signature. This is a hexadecimal string that is used for authentication in the
* EcoFlow API.
*
* @throws Exception If an error occurs during the generation of the signature.
*/
function generateSignature(string $nonce, string $timestamp, array $data): string {
global $accessKey, $secretKey;
// Flatten, sort, and concatenate the data array.
$flattenedData = flattenData($data);
ksort($flattenedData, SORT_STRING);
// Concatenate accessKey, nonce, and timestamp.
$signatureBase = http_build_query($flattenedData);
$signatureBase = urldecode($signatureBase);
$signatureBase .= sprintf('&accessKey=%s&nonce=%s×tamp=%s', $accessKey, $nonce, $timestamp);
$signatureBase = ltrim($signatureBase, '&');
// Encrypt with HMAC-SHA256 and secretKey.
$signatureBytes = hash_hmac('sha256', $signatureBase, $secretKey, true);
// Convert bytes to hexadecimal string.
return bin2hex($signatureBytes);
}
/**
* Flattens a multidimensional array into a one-dimensional array with dot notation keys.
*
* This method is used to flatten a multidimensional array into a one-dimensional array. The keys of the
* one-dimensional array are generated by concatenating the keys of the multidimensional array with a dot ('.').
* The keys are prefixed with the provided prefix, if any. If the key is 0, it is replaced with an empty string.
*
* If the value of a key-value pair is an array, a recursive call is made to flatten the nested array. The nested
* array is passed as the `$data` parameter, and the newly generated key is passed as the `$prefix` parameter.
*
* After processing all the key-value pairs, the method returns the `$flattened` array, which contains the
* flattened version of the input `$data` array.
*
* @param array<int|string, array<int|string, array<int, string>|int|string>|string>|array<string, int|string> $data
*
* @return array<int|string, int|string> The flattened one-dimensional array with dot notation keys.
*/
function flattenData(array $data, string $prefix = ''): array {
$flattened = [];
foreach ($data as $key => $value) {
if (is_integer($key)) {
$newKey = $prefix === '' ? $key : sprintf('%s%s', $prefix, "[". $key."]");
}
else{
$newKey = $prefix === '' ? $key : sprintf('%s.%s', $prefix, $key);
}
$newKey = is_string($newKey) ? rtrim($newKey, '.') : (string) $newKey;
if (is_array($value)) {
// Recursive call for nested arrays.
$flattened = array_merge($flattened, flattenData($value, $newKey));
continue;
}
// Append to a flattened array.
$flattened[$newKey] = $value;
}
return $flattened;
}
/**
* Generates a random nonce.
*
* This method generates a random nonce for the EcoFlow API. A nonce is a random string that is used once in each
* request to prevent replay attacks. The nonce is generated as a random integer between 100000 and 999999, which is
* then converted to a string.
*
* The purpose of the nonce is to ensure the uniqueness and integrity of each API request by including a one-time,
* randomly generated value. This helps to protect against duplicate or replayed requests.
*
* The method utilises PHP's built-in `random_int()` function to generate a cryptographically secure random integer
* within the specified range. The generated integer is then cast to a string to be included in the API request.
*
* @return string The randomly generated nonce as a string.
*
* @throws RandomException If an error occurs during the generation of the random integer, such as a failure of the
* random number generator or an invalid range.
*/
function createNonce(): string {
return (string) random_int(100000, 999999);
}
/**
* Generates a timestamp in milliseconds.
*
* This method generates a timestamp for use in the EcoFlow API. The timestamp is created by instantiating a
* DateTime object with the current time in the UTC time zone. The DateTime object is then formatted to include
* microseconds using the format string 'U.u'. The formatted time is multiplied by 1000 to convert it to
* milliseconds and rounded to the nearest whole number.
*
* The generated timestamp is used as part of the authentication process when making requests to the EcoFlow API.
* It ensures that the timestamp is based on a standardised time reference and is not affected by local time
* differences, making it suitable for use in a distributed system.
*
* @return string The generated timestamp as a string representation of the current time in milliseconds.
*
* @throws Exception If an error occurs during the creation of the DateTime object or when formatting the time.
* The calling code should handle this exception appropriately.
*/
function createTimestamp(): string {
$dateTime = new DateTime((string) null, new DateTimeZone('UTC'));
$formatted = (int) $dateTime->format('U.u');
return (string) round($formatted * 1000);
}
und hier noch ein Script zum Aufruf und Testen der Funktionen.
<?php
include_once("12345.ips.php"); //hier das EcoFlow API Main Script eintragen
$deviceSN = "DCE*****"; //sn deines Gerätes
$resp = getAllQuota($deviceSN); //liefert ein sortiertes Array mit allen Parametern/Werten
//$resp = setQuota($deviceSN, '"params":{ "cmdSet": 32, "id": 66, "enabled": 0, "xboost": 0 }'); //liefert bei Erfolg "Success"
//$resp = getQuota($deviceSN, '"params":{ "cmdSet": 32, "id": 66, "quotas": ["inv.cfgAcEnabled", "inv.cfgAcXboost"] }'); //liefert Array mit angefragten Parameter(n) und Wert(en)
//$resp = deviceList(); //liefert Array der Geräte mit sn, deviceName, online, productName
print_r($resp);
Die „gut dokumentierten“ Funktionen sind von github, die ich aber an einigen Stellen anpassen musste, damit z.B. das Senden von Befehlen überhaupt funktionierte.
Was hiervon auch mit anderen EcoFlow Geräten funktioniert, kann ich leider nicht sagen - ich habe nur ein DELTA PRO.
Viel Spaß beim Testen.
Gruß
Rainer
Hi @erpe
danke für dein Script. Leider bekomme ich bei mir das Setzen der Werte nicht hin.
In Java habe ich es schon erfolgreich getestet aber in IPS bekomme ich beim setQuota immer den „Error setting quota information: signature is wrong in /mnt/data/symcon/scripts/45814.ips.php:41“
Laut WWW soll ein „apt install php8.2-curl“ helfen. Musstest Du hier noch etwas installieren? Ich habe IPS 7.2 installiert.
Danke schon mal und Grüße
Roy
HI @Fasteagle
da ich nur eine DELTA Pro habe, und es da nicht viel zu Schalten gibt,
hatte ich das Setzen nur an ein paar Werten ausprobiert, die in der API beschrieben sind. Das funktioniert mit meinen Skript auch immer noch.
Welches EcoFlow Gerät hast du den? Die Parameter für das Setzen der Werte ist bei jedem Gerät wohl etwas anders.
Bei mir läuft auch IPS 7.2 (auf Windows 11), ich habe da nichts weiter installiert.
Signature is wrong deutet ja auf ein Problem bei der Anmeldung hin. Da ist aber zwischen Lesen und Schreiben von Werten kein Unterschied - ausser in der HTTP Methode.
Wie sieht dein setQuota aufruf aus? Ist der SetCmdRequest auch JSON?
Gruß
Rainer
Hallo Rainer,
ich habe eine Delta Pro 3 bekomme das Problem mit meinem wie auch mit deinem angepassten Script.
Deins habe ich wie folgt für $json angepasst:
function setQuota(string $deviceSN, string $params) {
global $accessKey, $secretKey, $SET_QUOTA_URL;
// $json = ‚{„sn“: "‘.$deviceSN.‚",‘.$params.‚}‘;
$json= ‚{„sn“:"‘.$deviceSN.‚",„cmdId“:17,„dirDest“:1,„dirSrc“:1,„cmdFunc“:254,„dest“:2,„needAck“:true,‘.$params.‚}}‘;
Die Funktion rufe ich so auf:
$resp = setQuota($deviceSN, ‚„params“:{ „cfgPlugInInfoAcInChgPowMax“: 400‘);
Habe die Parameter des JSON aus der Ecoflow API für die Delta Pro 3 übernommen.
Habe auch schon verschiedenen Parameter probiert. Immer das identische Ergebnis.
Die get Methoden funktionieren alle.
hinter der 400 fehlt die }
hier stimmt das Format auch nicht, da ist eine } zu viel
Nicht schön, aber ob ich die } In der Funktion entferne und dafür im Übergabeparameter hinzufüge kommt doch aufs gleiche Ergebnis:
{„sn“:„MRXXXXXXXXXXXX“,„cmdId“:17,„dirDest“:1,„dirSrc“:1,„cmdFunc“:254,„dest“:2,„needAck“:true,„params“:{ „cfgPlugInInfoAcInChgPowMax“: 400}}
versuche mal
$resp = setQuota($deviceSN, '{"sn":"'.$deviceSN.'","cmdId": 17, "dirDest": 1, "dirSrc": 1, "cmdFunc": 254, "dest": 2, "needAck": true, "params": {"cfgPlugInInfoPvHDcAmpMax": 400}}');
mit meiner setQuota Funktion auszuführen.
Da der JSON String mit in die Anmeldung einfließt, sollte der auch keine Fehler enthalten.
füge bitte den php code hier als vorformatierten Text </> ein. Dann ist der hier besser zu lesen.
Selbe Response:
Fatal error: Uncaught RuntimeException: Error setting quota information: signature is wrong in /mnt/data/symcon/scripts/45814.ips.php:43 Fatal error: Uncaught RuntimeException: Error setting quota information: signature is wrong in /mnt/data/symcon/scripts/45814.ips.php:43
Ja, mein Fehler. $deviceSN ist dann doppelt.
der Aufruf erfolgt mit
$resp = setQuota($deviceSN, '"params": {"cfgPlugInInfoPvHDcAmpMax": 400}');
und in der Funktion ersetze mal die Zeile mit $json = … mit
$json = '{"sn": "'.$deviceSN.'","cmdId": 17, "dirDest": 1, "dirSrc": 1, "cmdFunc": 254, "dest": 2, "needAck": true,'.$params.'}';
ich hatte das schon bei meinem Test berücksichtigt, dass die SN nicht doppelt kommt:
function setQuota(string $deviceSN, string $json) {
global $accessKey, $secretKey, $SET_QUOTA_URL;
$jsonObject = json_decode($json, true);
$response = getHttpUriRequest("PUT", $SET_QUOTA_URL, $jsonObject, $accessKey, $secretKey);
Aufruf:
$resp = setQuota($deviceSN, '{"sn":"'.$deviceSN.'","cmdId": 17, "dirDest": 1, "dirSrc": 1, "cmdFunc": 254, "dest": 2, "needAck": true, "params": {"cfgPlugInInfoPvHDcAmpMax": 400}}');
Wenn ich mir das JSON ausgeben lasse, sieht das ja auch gut aus und entspricht bis auf die SN ja auch genau dem Beispiel auf der Ecoflow API Doku.
Das identische JSON wenn ich es über JAVA Aufrufe funktioniert.
Was halt verwunderlich ist, dass die GET Methoden funktionnieren.