API Key PowerStream Ecoflow

Hello à tous,

J’ai lu pas mal sur le net qu’il était impossible de récupérer une API key pour le powerstream de chez Ecoflow. En demandant celle pour ma delta 2 max j’ai quand même posé la question pour le powerstream et voici la réponse :

Vous avez déjà réussi à récupérer quelque chose du powerstream ?

Bonne journée

Mathis

Hello, merci pour la découverte de cette API, je cherchais justement à obtenir des infos du PowerStream.

Ancienne API

J’arrive déjà à obtenir pour mes Delta Pro des infos via :

curl https://api.ecoflow.com/iot-service/open/api/device/queryDeviceQuota?sn=...

{"code":"0","message":"Success","data":{"soc":7,"remainTime":672,"wattsOutSum":0,"wattsInSum":0},"eagleEyeTraceId":"ea1a2a1e7f17049059312765100d0007","tid":""}

Mais pour le PowerStream j’obtiens :

{"code":"6012","message":"the device is offline","eagleEyeTraceId":"ea1a2a1e7f17049062169888792d0007","tid":""}

Alors que le PS est bien Online dans l’application mobile.

C’est peut-être lié à la réponse que tu as obtenue : le PowerStream est « spécial » est doit être interrogé via leur nouvelle API.

Nouvelle API

Mais en testant avec les mêmes appKey/secretKey sur l’API proposée par Ecoflow dans la documentation de developer.ecoflow.com, je me prends une erreur :

Endpoint : https://api-e.ecoflow.com/iot-open/sign/device/quota/all?sn=...

{"code":"8521","message":"signature is wrong","tid":"90fe1d3883924f55b8147fcbd54dde0e.304.17049060759698281"}

Pourtant j’ai bien signé le message conformément à leur documentation, d’ailleurs j’arrive bien à reproduire la signature d’exemple.

J’ai obtenu mes clés d’API il y a un an en écrivant au support Ecoflow. Désormais, il faut passer par developer.ecoflow.com, mais il faut attendre une approbation. Je n’ai pas encore reçu l’approbation, c’est peut-être pour ça que la signature n’apparaît pas correcte.

Si j’ai des nouvelles, je te redis.

J’ai re-testé ce matin : la signature était bonne, mais je générais mal le timestamp (attendu en millisecondes, je le générais en secondes).

J’ai désormais bien accès au PowerStream via la nouvelle API (documentation API PowerStream), exemple pour moi ce matin :

{
    "code": "0",
    "message": "Success",
    "data": {
        "20_1.pv2Temp": 200,
        "20_1.invOutputWatts": 0,
        "20_1.pv2WarningCode": 0,
        "20_1.pv2InputVolt": 0,
        "20_1.pv2RelayStatus": 0,
        "20_1.wirelessWarnCode": 0,
        "20_1.batInputVolt": 485,
        "20_1.invErrCode": 0,
        "20_1.dynamicWatts": 0,
        "20_1.batStatue": 5,
        "20_1.pv2OpVolt": 0,
        "20_1.invOnOff": 1,
        "20_1.invOpVolt": 2304,
        "20_1.installCountry": 18002,
        "20_1.llcOpVolt": 26,
        "20_1.feedProtect": 1,
        "20_1.batTemp": 140,
        "20_1.upperLimit": 100,
        "20_1.invInputVolt": 0,
        "20_1.pv1ErrCode": 128,
        "20_1.pv1InputVolt": 0,
        "20_1.invFreq": 500,
        "20_1.heartbeatFrequency": 15,
        "20_1.chgRemainTime": 143999,
        "20_134.task10": {
            "taskIndex": 9,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_1.llcTemp": 190,
        "20_134.task11": {
            "taskIndex": 10,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_1.pv1Temp": 200,
        "20_1.pv2InputCur": 0,
        "20_1.batOpVolt": 540,
        "20_1.permanentWatts": 500,
        "20_1.bpType": 1,
        "20_1.llcWarningCode": 0,
        "20_1.invOutputCur": 10,
        "20_1.pv1InputWatts": 0,
        "20_1.lowerLimit": 0,
        "20_1.invTemp": 0,
        "20_1.llcStatue": 5,
        "20_1.pv1WarnCode": 0,
        "20_1.wirelessErrCode": 0,
        "20_1.batWarningCode": 0,
        "20_1.pv1RelayStatus": 0,
        "20_1.llcErrCode": 0,
        "20_1.invStatue": 1,
        "20_1.batInputWatts": 0,
        "20_1.pv2Statue": 1,
        "20_1.batErrCode": 0,
        "20_1.dsgRemainTime": 143999,
        "20_1.batSoc": 1,
        "20_1.invBrightness": 33,
        "20_134.task7": {
            "taskIndex": 6,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_1.pv2ErrCode": 128,
        "20_1.invWarnCode": 0,
        "20_134.task6": {
            "taskIndex": 5,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_1.invRelayStatus": 0,
        "20_134.task9": {
            "taskIndex": 8,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_134.task8": {
            "taskIndex": 7,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_134.task3": {
            "taskIndex": 2,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_134.task2": {
            "taskIndex": 1,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_134.task5": {
            "taskIndex": 4,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_134.task4": {
            "taskIndex": 3,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_1.llcInputVolt": 540,
        "20_1.updateTime": "2024-01-11 05:02:55",
        "20_134.task1": {
            "taskIndex": 0,
            "type": 0,
            "timeRange": {
                "isConfig": false,
                "timeData": 0,
                "timeMode": 0,
                "startTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "stopTime": {
                    "sec": 0,
                    "week": 0,
                    "min": 0,
                    "hour": 0,
                    "month": 0,
                    "year": 0,
                    "day": 0
                },
                "isEnable": false
            }
        },
        "20_1.supplyPriority": 1,
        "20_1.ratedPower": 8000,
        "20_1.batInputCur": 0,
        "20_1.pv1Statue": 1,
        "20_134.updateTime": "2024-01-11 04:33:04",
        "20_1.pv1OpVolt": 0,
        "20_1.pv1InputCur": 0,
        "20_1.installTown": 0,
        "20_1.pv2InputWatts": 0
    },
    "tid": "de6a4c29db754c7f8446b99fd32cbc5a.301.17049597217118689"
}
1 « J'aime »

Bonjour,
je viens d’acheter le powerstream, et découvre qu’il serait maintenant possible d’ intégrer les infos dans jeedom.
d’après vous, il est maintenant possible de récupérer ces infos dans le plugin Ecoflow existant ?

Hello @c-geek ,
nenes1704 m’a demandé si je pouvais éventuellement intégrer sa Powerstream dans le plugin EcoFlow.
Je ne suis pas un expert dans ce qui est des appels API mais je me penche sur la question…

Je butté pas mal sur leur doc car j’ai l’impression qu’il me manque des infos/connaissances.
J’ai tenté ça avec ma River pro 2 :

curl -X GET \
https://api-e.ecoflow.com/iot-open/sign/device/quota/all?sn=P2EBZ7XE9150XXX \
-H 'Content-Type: application/json;charset=UTF-8' \
-H 'accessKey: 5db8844a5ab74a9c837773eb1c0d4XXX' \
-H 'nonce: 123456' \
-H 'timestamp: 1706977644123' \
-H 'sign: e26cef6ff141feb4fd2fb43a54e688be795378d8694156ff7c643cc074530a91'

Avec pour réponse : signature is wrong.
J’ai pourtant bien généré ma signature avec la chaine de paramètres que j’ai encryptée avec la secretkey puis passée en base64.
Je ne vois pas non plus comment je peux générer le paramètre « nonce » pour lequel j’ai mis 123456 par défaut.
Est-ce que tu utilises l’appKey en tant que accessKey?
Dans le doc Ecoflow, ils parlent de param1 et param2 et je ne sais pas trop comment les utiliser dans ma request initiale.

Aurais-tu encore un sample de tes tests?

Merci
Lionel

Bonjour,

je ne suis pas un expert mais je serais interressé de savoir comment vous avez généré les informations demandé, comme la signature par exemple.
Je viens de recevoir mes accès et de créer un couple accesskey/secretkey. Mais je bloque sur la suite.

Cordialement,

Mathieu

1 « J'aime »

Hello @m_arth ,
en fait j’ai suvi le lien originel de @Mathis90 .
Il y a une page qui détaille un peu plus la méthodo :
https://developer-eu.ecoflow.com/us/document/generalInfo
Après, tout le reste c’est de la manipulation d’un script bash ou directement dans le shell.

Si jamais tu progresses, fais moi signe. Je suis toujours intéressé par ajouter cette feature (ou partager le code avec d’autres bien sûr)

Lionel

Salut, oui j’ai vu, mais rien compris à la partie signature :slight_smile:
Si je trouve un peu de temps je vais voir pour me mettre sur le sujet.

Mathieu

Bonjour, désolé je ne vois vos réponses que maintenant suite à un MP.

Voici le code de signature, je l’ai fait en Rust mais n’importe quel langage de programmation peut le faire :

pub static ACCESS_KEY: &str = "clé d'accès fournie par https://developer-eu.ecoflow.com/us";
pub static SECRET_KEY: &str = "clé secrète fournie par https://developer-eu.ecoflow.com/us";

async fn quota(serial_number: &String) -> anyhow::Result<()> {
    let nonce = 537642;
    let timestamp = time::SystemTime::now().duration_since(time::UNIX_EPOCH)?.as_millis().to_string();
    let signed = format!("sn={}&accessKey={}&nonce={}&timestamp={}", serial_number, ACCESS_KEY, nonce, timestamp);
    let signature = sign(signed.as_str(), SECRET_KEY);
    println!("{}", signature);
    let req = reqwest::Client::builder()
        .build()?
        .get(reqwest::Url::parse(format!("https://api-e.ecoflow.com/iot-open/sign/device/quota/all?sn={}", serial_number).as_str()).unwrap())
        .header("accessKey", ACCESS_KEY)
        .header("timestamp", timestamp)
        .header("nonce", nonce)
        .header("sign", signature);
    println!("{:?}", req);
    let response = req.send().await?;
    let res: String = response
        .text()
        .await?;
    println!("{:?}", res);

    Ok(())
}