Open sebjosb opened 1 year ago
I would be highly interested in this feature as well. Thank you for the great work!
Ich hätte auch großes Interesse an dieser Funktion. Vielen Dank für die tolle Arbeit!
Me too
Hey Folks,
Great thread here where someone is using IOBroker to perform this function, seems to be very active, there is also a script (regularly updated) with the details, and some additional users / testers, someone also mentions connecting IOBroker to Home Assistant so I think that that is a workaround for now: https://forum.iobroker.net/topic/66743/ecoflow-connector-script-zur-dynamischen-leistungsanpassung/
(Use Translate in Chrome to read the thread as it's in German)
I'm going to investigate connecting IOBroker to Home Assistant later today to see if I can get it working, if I manage to get it configured I'll post a short guide (or at least link some guides)
UPDATE Have a plan now: Install IOBroker using Docker https://hub.docker.com/r/iobroker/iobroker Configure the script here: https://forum.iobroker.net/topic/66743/ecoflow-connector-script-zur-dynamischen-leistungsanpassung/ Connect IOBroker to Ecoflow Powerstream Connect IOBroker to Home Assistant using this: https://github.com/ioBroker/ioBroker.hass May need to configure some fields, or maybe it just all works, will begin after lunch! Configure HA to adjust other load depending on the current delta pro charge rate, and the solar generation going into the Powerstream (some guess work required here, not concerned with 100% accurate, 90% will be sufficient) UPDATE
I have a Powerstream and a Delta Pro (and house solar as well) where I charge the battery from the surplus house solar and am mostly looking to feed-in the Powerstream solar directly into the house since you cannot charge from Powerstream and direct AC in the Delta Pro at the same time.
My rough plan is to use IOBroker to change the other-loads value of the Powerstream to match the current generation value of the solar panels, by adjusting the value until a max of up-to 10 watts is coming from the battery
I have already setup home assistant to auto-change the charging of the Delta Pro AC with the surplus energy (measured from the Myenergie Zappi EV charger HA Integration) (It's pretty buggy / flaky, but mostly works ;-) )
Tagging @mattwells as well to get his thoughts?
If you can make it work by all means.
It should be fairly easy to implement it now with the code that is already in this project, I haven't had the time to look into it I'm afraid.
@foxthefox I noticed your name in this thread, would you be able to translate this post by FelixCrafter83 please? I feel that it might be really useful but Google translate isn't doing a particularly good job. Something about types 5 and 6 being hours and minutes?
Beim Smartplug ist bei einer proto Nachricht die dritte Stelle kein string, wie auf der erwähnten Decoder Seite angezeigt wird. Es ist ein Array von uint32 mit 24 Einträgen. Jede Stelle ist eine Stunde Watt type 5 ist watth und watt type 6 ist die Zeit, wo er an war in dieser Stunde in Minuten. Also mann muss bei dieser Nachricht nur die Einträge von dem Array bei watt type 5 addieren. Dann erhält mann die watth von dem Tag. Sehr wahrscheinlich ist das beim powerstream auch so.
Hi @mattwells,
Maybe DeepL has done a marginally better job:
"With the smart plug, the third digit of a proto message is not a string, as shown on the decoder page mentioned. It is an array of uint32 with 24 entries. Each digit is an hour watt type 5 is watth and watt type 6 is the time it was on in that hour in minutes. So all you have to do with this message is add the entries from the array at watt type 5. Then you get the watth of the day. It is very likely that this is also the case with the Powerstream."
@foxthefox I noticed your name in this thread, would you be able to translate this post by FelixCrafter83 please? Sure I can, but translation from ddhyland is already perfect.
@mattwells @tolwi - it seems that this function modifies the AC input value:
// Einstellen der Einspeiseleistung function setAC(asn, Value) { if (!ConfigData.Regulation) { //return } let updatedMusterSetAC = musterSetAC; if (Value <= -1) { delete updatedMusterSetAC.item.meta; delete updatedMusterSetAC.item.ValByte; } else { updatedMusterSetAC.header.pdata.value = Value updatedMusterSetAC.header.dataLen = getVarintByteSize(Value) } updatedMusterSetAC.header.seq = Date.now() updatedMusterSetAC.header.deviceSn = asn //log(JSON.stringify(updatedMusterSetAC)) setState(ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + asn + '_thing_property_set.setAC', Value.toString(), true) SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + asn + '/thing/property/set'); }
where masterSetAc is
const musterSetAC = { header: { pdata: { value: 1300, }, src: 32, dest: 53, dSrc: 1, dDest: 1, checkType: 3, cmdFunc: 20, cmdId: 129, dataLen: 3, needAck: 1, seq: 1651831507, version: 19, payloadVer: 1, from: 'ios', deviceSn: 'ABCxxxxxxx123' } };
function SendProto(protomsg, topic) { //return const root = protobuf.parse(protoSource2).root; const PowerMessage = root.lookupType("setMessage"); const message = PowerMessage.create(JSON.parse(protomsg)); const messageBuffer = PowerMessage.encode(message).finish(); //log("Modifizierter Hex-String:" + Buffer.from(messageBuffer).toString("hex")); //log("topic:" + topic); client.publish(topic, messageBuffer, { qos: 1 }, function (error) { if (error) { console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error); } else { if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.'); // } }); } si update the value / the sn and send it to the mqtt address 👯
Thanks!! How could the Script be integrated in Homeassitant, because this is Java Script?
The script is for ioBroker...the idea is to adapt the script to python...if they dit it in javascript is it possible to do it in python :)
I think the above function should be translated into python here
def numbers(self, client: EcoflowMQTTClient) -> list[BaseNumberEntity]:
return [
# These will likely be some form of serialised data rather than JSON will look into it later
# MinBatteryLevelEntity(client, "lowerLimit", "Min Disharge Level", 50, 100,
# lambda value: {"moduleType": 0, "operateType": "TCP",
# "params": {"id": 00, "lowerLimit": value}}),
# MaxBatteryLevelEntity(client, "upperLimit", "Max Charge Level", 0, 30,
# lambda value: {"moduleType": 0, "operateType": "TCP",
# "params": {"id": 00, "upperLimit": value}}),
]
in powesrtream.py
protosource is this constant ...lool...
const protoSource2 =
syntax = "proto3";
message Message {
repeated Header header = 1 ;
bytes payload = 2;
}
message Header {
bytes pdata = 1 [proto3_optional = false];
int32 src = 2 [proto3_optional = true];
int32 dest = 3 [proto3_optional = true];
int32 d_src = 4 [proto3_optional = true];
int32 d_dest = 5 [proto3_optional = true];
int32 enc_type = 6 [proto3_optional = true];
int32 check_type = 7 [proto3_optional = true];
int32 cmd_func = 8 [proto3_optional = true];
int32 cmd_id = 9 [proto3_optional = true];
int32 data_len = 10 [proto3_optional = true];
int32 need_ack = 11 [proto3_optional = true];
int32 is_ack = 12 [proto3_optional = true];
int32 seq = 14 [proto3_optional = true];
int32 product_id = 15 [proto3_optional = true];
int32 version = 16 [proto3_optional = true];
int32 payload_ver = 17 [proto3_optional = true];
int32 time_snap = 18 [proto3_optional = true];
int32 is_rw_cmd = 19 [proto3_optional = true];
int32 is_queue = 20 [proto3_optional = true];
int32 ack_type = 21 [proto3_optional = true];
string code = 22 [proto3_optional = true];
string from = 23 [proto3_optional = true];
string module_sn = 24 [proto3_optional = true];
string device_sn = 25 [proto3_optional = true];
}
message InverterHeartbeat {
optional uint32 inv_err_code = 1;
optional uint32 inv_warn_code = 3;
optional uint32 pv1_err_code = 2;
optional uint32 pv1_warn_code = 4;
optional uint32 pv2_err_code = 5;
optional uint32 pv2_warning_code = 6;
optional uint32 bat_err_code = 7;
optional uint32 bat_warning_code = 8;
optional uint32 llc_err_code = 9;
optional uint32 llc_warning_code = 10;
optional uint32 pv1_statue = 11;
optional uint32 pv2_statue = 12;
optional uint32 bat_statue = 13;
optional uint32 llc_statue = 14;
optional uint32 inv_statue = 15;
optional int32 pv1_input_volt = 16;
optional int32 pv1_op_volt = 17;
optional int32 pv1_input_cur = 18;
optional int32 pv1_input_watts = 19;
optional int32 pv1_temp = 20;
optional int32 pv2_input_volt = 21;
optional int32 pv2_op_volt = 22;
optional int32 pv2_input_cur = 23;
optional int32 pv2_input_watts = 24;
optional int32 pv2_temp = 25;
optional int32 bat_input_volt = 26;
optional int32 bat_op_volt = 27;
optional int32 bat_input_cur = 28;
optional int32 bat_input_watts = 29;
optional int32 bat_temp = 30;
optional uint32 bat_soc = 31;
optional int32 llc_input_volt = 32;
optional int32 llc_op_volt = 33;
optional int32 llc_temp = 34;
optional int32 inv_input_volt = 35;
optional int32 inv_op_volt = 36;
optional int32 inv_output_cur = 37;
optional int32 inv_output_watts = 38;
optional int32 inv_temp = 39;
optional int32 inv_freq = 40;
optional int32 inv_dc_cur = 41;
optional int32 bp_type = 42;
optional int32 inv_relay_status = 43;
optional int32 pv1_relay_status = 44;
optional int32 pv2_relay_status = 45;
optional uint32 install_country = 46;
optional uint32 install_town = 47;
optional uint32 permanent_watts = 48;
optional uint32 dynamic_watts = 49;
optional uint32 supply_priority = 50;
optional uint32 lower_limit = 51;
optional uint32 upper_limit = 52;
optional uint32 inv_on_off = 53;
optional uint32 wireless_err_code = 54;
optional uint32 wireless_warn_code = 55;
optional uint32 inv_brightness = 56;
optional uint32 heartbeat_frequency = 57;
optional uint32 rated_power = 58;
}
message InverterHeartbeat2 {
int32 X_Unknown_1 = 1;
int32 X_Unknown_2 = 2;
int32 X_Unknown_3 = 3;
int32 X_Unknown_4 = 4;
int32 X_Unknown_5 = 5;
int32 X_Unknown_6 = 6;
int32 X_Unknown_7 = 7;
int32 X_Unknown_8 = 8;
int32 X_Unknown_9 = 9;
int32 X_Unknown_10 = 10;
int32 X_Unknown_11 = 11;
int32 X_Unknown_12 = 12;
int32 X_Unknown_13 = 13;
int32 X_Unknown_14 = 14;
int32 X_Unknown_15 = 15;
int32 X_Unknown_16 = 16;
int32 X_Unknown_17 = 17;
int32 X_Unknown_18 = 18;
int32 X_Unknown_19 = 19;
int32 X_Unknown_20 = 20;
int32 X_Unknown_21 = 21;
int32 X_Unknown_22 = 22;
int32 X_Unknown_23 = 23;
int32 X_Unknown_24 = 24;
int32 X_Unknown_25 = 25;
int32 X_Unknown_26 = 26;
int32 X_Unknown_27 = 27;
int32 X_Unknown_28 = 28;
int32 X_Unknown_29 = 29;
int32 X_Unknown_30 = 30;
int32 X_Unknown_31 = 31;
int32 X_Unknown_32 = 32;
int32 X_Unknown_33 = 33;
int32 X_Unknown_34 = 34;
int32 X_Unknown_35 = 35;
int32 X_Unknown_36 = 36;
int32 X_Unknown_37 = 37;
int32 X_Unknown_38 = 38;
int32 X_Unknown_39 = 39;
int32 X_Unknown_40 = 40;
int32 X_Unknown_41 = 41;
int32 X_Unknown_42 = 42;
int32 X_Unknown_43 = 43;
int32 X_Unknown_44 = 44;
int32 X_Unknown_45 = 45;
int32 X_Unknown_46 = 46;
int32 X_Unknown_47 = 47;
int32 X_Unknown_48 = 48;
int32 X_Unknown_49 = 49;
int32 X_Unknown_50 = 50;
int32 X_Unknown_51 = 51;
int32 X_Unknown_52 = 52;
}
message setMessage {
setHeader header = 1;
}
message setHeader {
setValue pdata = 1 [proto3_optional = true];
int32 src = 2 [proto3_optional = true];
int32 dest = 3 [proto3_optional = true];
int32 d_src = 4 [proto3_optional = true];
int32 d_dest = 5 [proto3_optional = true];
int32 enc_type = 6 [proto3_optional = true];
int32 check_type = 7 [proto3_optional = true];
int32 cmd_func = 8 [proto3_optional = true];
int32 cmd_id = 9 [proto3_optional = true];
int32 data_len = 10 [proto3_optional = true];
int32 need_ack = 11 [proto3_optional = true];
int32 is_ack = 12 [proto3_optional = true];
int32 seq = 14 [proto3_optional = true];
int32 product_id = 15 [proto3_optional = true];
int32 version = 16 [proto3_optional = true];
int32 payload_ver = 17 [proto3_optional = true];
int32 time_snap = 18 [proto3_optional = true];
int32 is_rw_cmd = 19 [proto3_optional = true];
int32 is_queue = 20 [proto3_optional = true];
int32 ack_type = 21 [proto3_optional = true];
string code = 22 [proto3_optional = true];
string from = 23 [proto3_optional = true];
string module_sn = 24 [proto3_optional = true];
string device_sn = 25 [proto3_optional = true];
}
message setValue {
optional int32 value = 1;
}
message permanent_watts_pack {
optional int32 permanent_watts = 1;
}
message supply_priority_pack {
optional int32 supply_priority = 1;
}
message bat_lower_pack {
optional int32 lower_limit = 1;
}
message bat_upper_pack {
optional int32 upper_limit = 1;
}
message PowerItem {
optional uint32 timestamp = 1;
optional sint32 timezone = 2;
optional uint32 inv_to_grid_power = 3;
optional uint32 inv_to_plug_power = 4;
optional int32 battery_power = 5;
optional uint32 pv1_output_power = 6;
optional uint32 pv2_output_power = 7;
}
message PowerPack {
optional uint32 sys_seq = 1;
repeated PowerItem sys_power_stream = 2;
//repeated plug_heartbeat_pack sys_power_stream = 2;
}
message PowerAckPack {
optional uint32 sys_seq = 1;
}
message node_massage {
optional string sn = 1;
optional bytes mac = 2;
}
message mesh_child_node_info {
optional uint32 topology_type = 1;
optional uint32 mesh_protocol = 2;
optional uint32 max_sub_device_num = 3;
optional bytes parent_mac_id = 4;
optional bytes mesh_id = 5;
repeated node_massage sub_device_list = 6;
}
message EnergyItem {
optional uint32 timestamp = 1;
optional uint32 watth_type = 2;
repeated uint32 watth = 3;
}
message EnergyTotalReport {
optional uint32 watth_seq = 1;
optional EnergyItem watth_item = 2;
}
message BatchEnergyTotalReport {
optional uint32 watth_seq = 1;
repeated EnergyItem watth_item = 2;
}
message EnergyTotalReportAck {
optional uint32 result = 1;
optional uint32 watth_seq = 2;
optional uint32 watth_type = 3;
}
message EventRecordItem {
optional uint32 timestamp = 1;
optional uint32 sys_ms = 2;
optional uint32 event_no = 3;
repeated float event_detail = 4;
}
message EventRecordReport {
optional uint32 event_ver = 1;
optional uint32 event_seq = 2;
repeated EventRecordItem event_item = 3;
}
message EventInfoReportAck {
optional uint32 result = 1;
optional uint32 event_seq = 2;
optional uint32 event_item_num = 3;
}
message ProductNameSet {
optional string name = 1;
}
message ProductNameSetAck {
optional uint32 result = 1;
}
message ProductNameGet {}
message ProductNameGetAck {
optional string name = 3;
}
message RTCTimeGet {}
message RTCTimeGetAck {
optional uint32 timestamp = 1;
optional int32 timezone = 2;
}
message RTCTimeSet {
optional uint32 timestamp = 1;
optional int32 timezone = 2 [(nanopb).default = 0];
}
message RTCTimeSetAck {
optional uint32 result = 1;
}
message country_town_message {
optional uint32 country = 1;
optional uint32 town = 2;
}
message time_task_config {
optional uint32 task_index = 1;
optional time_range_strategy time_range = 2;
optional uint32 type = 3;
}
message time_task_delet {
optional uint32 task_index = 1;
}
message time_task_config_post {
optional time_task_config task1 = 1;
optional time_task_config task2 = 2;
optional time_task_config task3 = 3;
optional time_task_config task4 = 4;
optional time_task_config task5 = 5;
optional time_task_config task6 = 6;
optional time_task_config task7 = 7;
optional time_task_config task8 = 8;
optional time_task_config task9 = 9;
optional time_task_config task10 = 10;
optional time_task_config task11 = 11;
}
message time_task_config_ack {
optional uint32 task_info = 1;
}
message rtc_data {
optional int32 week = 1 [(nanopb).default = 0];
optional int32 sec = 2 [(nanopb).default = 0];
optional int32 min = 3 [(nanopb).default = 0];
optional int32 hour = 4 [(nanopb).default = 0];
optional int32 day = 5 [(nanopb).default = 0];
optional int32 month = 6 [(nanopb).default = 0];
optional int32 year = 7 [(nanopb).default = 0];
}
message time_range_strategy {
optional bool is_config = 1;
optional bool is_enable = 2;
optional int32 time_mode = 3;
optional int32 time_data = 4;
optional rtc_data start_time = 5;
optional rtc_data stop_time = 6;
}
message plug_ack_message {
optional uint32 ack = 1;
}
message plug_heartbeat_pack {
optional uint32 err_code = 1 [(nanopb).default = 0];
optional uint32 warn_code = 2 [(nanopb).default = 0];
optional uint32 country = 3 [(nanopb).default = 0];
optional uint32 town = 4 [(nanopb).default = 0];
optional int32 max_cur = 5 [(nanopb).default = 0];
optional int32 temp = 6 [(nanopb).default = 0];
optional int32 freq = 7 [(nanopb).default = 0];
optional int32 current = 8 [(nanopb).default = 0];
optional int32 volt = 9 [(nanopb).default = 0];
optional int32 watts = 10 [(nanopb).default = 0];
optional bool switch = 11 [(nanopb).default = false];
optional int32 brightness = 12 [(nanopb).default = 0];
optional int32 max_watts = 13 [(nanopb).default = 0];
optional int32 heartbeat_frequency = 14 [(nanopb).default = 0];
optional int32 mesh_enable = 15 [(nanopb).default = 0];
}
message plug_switch_message {
optional uint32 plug_switch = 1;
}
message brightness_pack {
optional int32 brightness = 1 [(nanopb).default = 0];
}
message max_cur_pack {
optional int32 max_cur = 1 [(nanopb).default = 0];
}
message max_watts_pack {
optional int32 max_watts = 1 [(nanopb).default = 0];
}
message mesh_ctrl_pack {
optional uint32 mesh_enable = 1 [(nanopb).default = 0];
}
message ret_pack {
optional bool ret_sta = 1 [(nanopb).default = false];
}
enum CmdFunction {
Unknown = 0;
PermanentWattsPack = 129;
SupplyPriorityPack = 130;
}
;
This could work. Did you try it out?
I succded in extracting only the part that actually can modify the output value. I have tested it as a node.js application and it is working....any idea how can I interface it with HA ?
HEre it is the code...
`var https = require('https'); const mqtt = require("mqtt"); var protobuf = require("protobufjs");
function httpsRequest(options, data) { return new Promise((resolve, reject) => { const req = https.request(options, res => { let data = ''; res.on('data', chunk => { data += chunk; }); res.on('end', () => { resolve(data); }); });
req.on('error', error => {
console.log("error https ",error);
reject(error);
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
/*
** YOUR DATA HERE ****
****/
var ConfigData = {
email: "xxxxxxxxx", // Die App-Zugangsdaten von ecoFlow
passwort: "yyyyyyy",
seriennummern: [
//############# Diesen Abschnitt für jedes einzelne Gerät anlegen ################
{
seriennummer: "HW51ZEH4SF5R2556", // Die Seriennummer des Gerätes
name: "PowerStream", // beliebiger Namen
subscribe: true, // "true": Alle Daten für dieses Gerät werden angefragt. "false": Es werden keine Statusdaten abgefragt
typ: "PS", // Welches Gerät ist es: Powerstrem:"PS"; DeltaMax:"DM"; DeltaMax2: "DM2"; SmartPlug: "SM"; Andere: "NA"
// Parameter an hier nur für PowerStream.
regulation: true, // "True": Dieser PowerStream soll vom Script reguliert werden
hasBat: true, // "true": Eine Batterie ist angeschlossen. Nur für PowerStream relevant.
battPozOn: 99, battPozOff: 92, // Wenn die Batterie bei battPozOn ist, Einspeisung auf MaxPower. Bei BattPozOff Normalbetrieb
battOnSwitchPrio: false, // Bei battPozOn wird in den Batterie-Prioritätsmodus gewechselt
lowBatLimitPozOn: 15, lowBatLimitPozOff: 25,// Bei Unterschreiten der Batterieladung von "lowBatLimitPozOn" % ist die maximale Einspeiseleistung auf
lowBatLimit: 150, // "lowBatLimit" limitiert, bis der Ladezustand wieder bei "lowBatLimitPozOff" ist
},
/#######################################################################
{
seriennummer: "XXXXXXXXXXXXX",
name: "DELTA Max",
typ: "DM",
subscribe: false, // "true": Alle Daten für dieses Gerät werden angefragt. "false": Es werden keine Statusdaten abgefragt
},
/#######################################################################
{
seriennummer: "XXXXXXXXXXXXX",
name: "SmartPlug 1",
typ: "SM",
subscribe: true, // "true": Alle Daten für dieses Gerät werden angefragt. "false": Es werden keine Statusdaten abgefragt
},*/
//#######################################################################
],
//SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P", // State, der den aktuellen Gesamtverbrauch in Watt anzeigt
//**
// Erweiterte Einstellungen:
//****
Regulation: true, // 'false' stellt das Setzen der Einspeiseleistung ab
RegulationState: "", // Wenn angegeben, kann mit diesem State die Regulation ein- und ausgeschaltet werden (Wird automatisch unter 0_userdata.0.ecoflow angelegt)
RegulationOffPower: -1, // Wird die Regulation per State abgestellt, wird die Einspeiseleistung des ersten Powerstreams auf diesen Wert gesetzt (-1 = keine Änderung)
RegulationMultiPsMode: 0, // Wenn mehrere PS reguloert werden sollen. "balance" = 0 oder "serial" = 1
BasePowerOffset: 30, // Wird vom aktuellen Verbrauch abgezogen, um die Einspeiseleistung zu berechnen
MaxPower: 600, // Der höchstmögliche Wert in Watt für die Einspeiseleistung
MinValueMin: 3, // Der Zeitraum in Minuten, aus dem der niedrigste Gesamtverbrauchswert geholt werden soll
MinValueAg: 0, // Art der Ermittlung des kleinsten Wertes: 0 = Minimalwert, 1 = Durchschnittswert
ReconnectMin: 30, // Zeit in Minuten, nach der die Anwendung neu gestartet wird, wenn keine neuen Daten eintreffen
statesPrefix: "0_userdata.0.ecoflow", // Hier werden die ecoFlow States angelegt
//latitude: latitude, // Breitengrad des Standortes (wird automatisch eingesetzt)
//longitude: longitude, // Längengrad des Standortes (wird automatisch eingesetzt)
Debug: true,
PlotCmdID: 99999,
};
///
///
function log(msg) {
console.log(msg);
}
const protoSource2 = syntax = "proto3"; message Message { repeated Header header = 1 ; bytes payload = 2; } message Header { bytes pdata = 1 [proto3_optional = false]; int32 src = 2 [proto3_optional = true]; int32 dest = 3 [proto3_optional = true]; int32 d_src = 4 [proto3_optional = true]; int32 d_dest = 5 [proto3_optional = true]; int32 enc_type = 6 [proto3_optional = true]; int32 check_type = 7 [proto3_optional = true]; int32 cmd_func = 8 [proto3_optional = true]; int32 cmd_id = 9 [proto3_optional = true]; int32 data_len = 10 [proto3_optional = true]; int32 need_ack = 11 [proto3_optional = true]; int32 is_ack = 12 [proto3_optional = true]; int32 seq = 14 [proto3_optional = true]; int32 product_id = 15 [proto3_optional = true]; int32 version = 16 [proto3_optional = true]; int32 payload_ver = 17 [proto3_optional = true]; int32 time_snap = 18 [proto3_optional = true]; int32 is_rw_cmd = 19 [proto3_optional = true]; int32 is_queue = 20 [proto3_optional = true]; int32 ack_type = 21 [proto3_optional = true]; string code = 22 [proto3_optional = true]; string from = 23 [proto3_optional = true]; string module_sn = 24 [proto3_optional = true]; string device_sn = 25 [proto3_optional = true]; } message InverterHeartbeat { optional uint32 inv_err_code = 1; optional uint32 inv_warn_code = 3; optional uint32 pv1_err_code = 2; optional uint32 pv1_warn_code = 4; optional uint32 pv2_err_code = 5; optional uint32 pv2_warning_code = 6; optional uint32 bat_err_code = 7; optional uint32 bat_warning_code = 8; optional uint32 llc_err_code = 9; optional uint32 llc_warning_code = 10; optional uint32 pv1_statue = 11; optional uint32 pv2_statue = 12; optional uint32 bat_statue = 13; optional uint32 llc_statue = 14; optional uint32 inv_statue = 15; optional int32 pv1_input_volt = 16; optional int32 pv1_op_volt = 17; optional int32 pv1_input_cur = 18; optional int32 pv1_input_watts = 19; optional int32 pv1_temp = 20; optional int32 pv2_input_volt = 21; optional int32 pv2_op_volt = 22; optional int32 pv2_input_cur = 23; optional int32 pv2_input_watts = 24; optional int32 pv2_temp = 25; optional int32 bat_input_volt = 26; optional int32 bat_op_volt = 27; optional int32 bat_input_cur = 28; optional int32 bat_input_watts = 29; optional int32 bat_temp = 30; optional uint32 bat_soc = 31; optional int32 llc_input_volt = 32; optional int32 llc_op_volt = 33; optional int32 llc_temp = 34; optional int32 inv_input_volt = 35; optional int32 inv_op_volt = 36; optional int32 inv_output_cur = 37; optional int32 inv_output_watts = 38; optional int32 inv_temp = 39; optional int32 inv_freq = 40; optional int32 inv_dc_cur = 41; optional int32 bp_type = 42; optional int32 inv_relay_status = 43; optional int32 pv1_relay_status = 44; optional int32 pv2_relay_status = 45; optional uint32 install_country = 46; optional uint32 install_town = 47; optional uint32 permanent_watts = 48; optional uint32 dynamic_watts = 49; optional uint32 supply_priority = 50; optional uint32 lower_limit = 51; optional uint32 upper_limit = 52; optional uint32 inv_on_off = 53; optional uint32 wireless_err_code = 54; optional uint32 wireless_warn_code = 55; optional uint32 inv_brightness = 56; optional uint32 heartbeat_frequency = 57; optional uint32 rated_power = 58; } message InverterHeartbeat2 { int32 X_Unknown_1 = 1; int32 X_Unknown_2 = 2; int32 X_Unknown_3 = 3; int32 X_Unknown_4 = 4; int32 X_Unknown_5 = 5; int32 X_Unknown_6 = 6; int32 X_Unknown_7 = 7; int32 X_Unknown_8 = 8; int32 X_Unknown_9 = 9; int32 X_Unknown_10 = 10; int32 X_Unknown_11 = 11; int32 X_Unknown_12 = 12; int32 X_Unknown_13 = 13; int32 X_Unknown_14 = 14; int32 X_Unknown_15 = 15; int32 X_Unknown_16 = 16; int32 X_Unknown_17 = 17; int32 X_Unknown_18 = 18; int32 X_Unknown_19 = 19; int32 X_Unknown_20 = 20; int32 X_Unknown_21 = 21; int32 X_Unknown_22 = 22; int32 X_Unknown_23 = 23; int32 X_Unknown_24 = 24; int32 X_Unknown_25 = 25; int32 X_Unknown_26 = 26; int32 X_Unknown_27 = 27; int32 X_Unknown_28 = 28; int32 X_Unknown_29 = 29; int32 X_Unknown_30 = 30; int32 X_Unknown_31 = 31; int32 X_Unknown_32 = 32; int32 X_Unknown_33 = 33; int32 X_Unknown_34 = 34; int32 X_Unknown_35 = 35; int32 X_Unknown_36 = 36; int32 X_Unknown_37 = 37; int32 X_Unknown_38 = 38; int32 X_Unknown_39 = 39; int32 X_Unknown_40 = 40; int32 X_Unknown_41 = 41; int32 X_Unknown_42 = 42; int32 X_Unknown_43 = 43; int32 X_Unknown_44 = 44; int32 X_Unknown_45 = 45; int32 X_Unknown_46 = 46; int32 X_Unknown_47 = 47; int32 X_Unknown_48 = 48; int32 X_Unknown_49 = 49; int32 X_Unknown_50 = 50; int32 X_Unknown_51 = 51; int32 X_Unknown_52 = 52; } message setMessage { setHeader header = 1; } message setHeader { setValue pdata = 1 [proto3_optional = true]; int32 src = 2 [proto3_optional = true]; int32 dest = 3 [proto3_optional = true]; int32 d_src = 4 [proto3_optional = true]; int32 d_dest = 5 [proto3_optional = true]; int32 enc_type = 6 [proto3_optional = true]; int32 check_type = 7 [proto3_optional = true]; int32 cmd_func = 8 [proto3_optional = true]; int32 cmd_id = 9 [proto3_optional = true]; int32 data_len = 10 [proto3_optional = true]; int32 need_ack = 11 [proto3_optional = true]; int32 is_ack = 12 [proto3_optional = true]; int32 seq = 14 [proto3_optional = true]; int32 product_id = 15 [proto3_optional = true]; int32 version = 16 [proto3_optional = true]; int32 payload_ver = 17 [proto3_optional = true]; int32 time_snap = 18 [proto3_optional = true]; int32 is_rw_cmd = 19 [proto3_optional = true]; int32 is_queue = 20 [proto3_optional = true]; int32 ack_type = 21 [proto3_optional = true]; string code = 22 [proto3_optional = true]; string from = 23 [proto3_optional = true]; string module_sn = 24 [proto3_optional = true]; string device_sn = 25 [proto3_optional = true]; } message setValue { optional int32 value = 1; } message permanent_watts_pack { optional int32 permanent_watts = 1; } message supply_priority_pack { optional int32 supply_priority = 1; } message bat_lower_pack { optional int32 lower_limit = 1; } message bat_upper_pack { optional int32 upper_limit = 1; } message PowerItem { optional uint32 timestamp = 1; optional sint32 timezone = 2; optional uint32 inv_to_grid_power = 3; optional uint32 inv_to_plug_power = 4; optional int32 battery_power = 5; optional uint32 pv1_output_power = 6; optional uint32 pv2_output_power = 7; } message PowerPack { optional uint32 sys_seq = 1; repeated PowerItem sys_power_stream = 2; //repeated plug_heartbeat_pack sys_power_stream = 2; } message PowerAckPack { optional uint32 sys_seq = 1; } message node_massage { optional string sn = 1; optional bytes mac = 2; } message mesh_child_node_info { optional uint32 topology_type = 1; optional uint32 mesh_protocol = 2; optional uint32 max_sub_device_num = 3; optional bytes parent_mac_id = 4; optional bytes mesh_id = 5; repeated node_massage sub_device_list = 6; } message EnergyItem { optional uint32 timestamp = 1; optional uint32 watth_type = 2; repeated uint32 watth = 3; } message EnergyTotalReport { optional uint32 watth_seq = 1; optional EnergyItem watth_item = 2; } message BatchEnergyTotalReport { optional uint32 watth_seq = 1; repeated EnergyItem watth_item = 2; } message EnergyTotalReportAck { optional uint32 result = 1; optional uint32 watth_seq = 2; optional uint32 watth_type = 3; } message EventRecordItem { optional uint32 timestamp = 1; optional uint32 sys_ms = 2; optional uint32 event_no = 3; repeated float event_detail = 4; } message EventRecordReport { optional uint32 event_ver = 1; optional uint32 event_seq = 2; repeated EventRecordItem event_item = 3; } message EventInfoReportAck { optional uint32 result = 1; optional uint32 event_seq = 2; optional uint32 event_item_num = 3; } message ProductNameSet { optional string name = 1; } message ProductNameSetAck { optional uint32 result = 1; } message ProductNameGet {} message ProductNameGetAck { optional string name = 3; } message RTCTimeGet {} message RTCTimeGetAck { optional uint32 timestamp = 1; optional int32 timezone = 2; } message RTCTimeSet { optional uint32 timestamp = 1; optional int32 timezone = 2 [(nanopb).default = 0]; } message RTCTimeSetAck { optional uint32 result = 1; } message country_town_message { optional uint32 country = 1; optional uint32 town = 2; } message time_task_config { optional uint32 task_index = 1; optional time_range_strategy time_range = 2; optional uint32 type = 3; } message time_task_delet { optional uint32 task_index = 1; } message time_task_config_post { optional time_task_config task1 = 1; optional time_task_config task2 = 2; optional time_task_config task3 = 3; optional time_task_config task4 = 4; optional time_task_config task5 = 5; optional time_task_config task6 = 6; optional time_task_config task7 = 7; optional time_task_config task8 = 8; optional time_task_config task9 = 9; optional time_task_config task10 = 10; optional time_task_config task11 = 11; } message time_task_config_ack { optional uint32 task_info = 1; } message rtc_data { optional int32 week = 1 [(nanopb).default = 0]; optional int32 sec = 2 [(nanopb).default = 0]; optional int32 min = 3 [(nanopb).default = 0]; optional int32 hour = 4 [(nanopb).default = 0]; optional int32 day = 5 [(nanopb).default = 0]; optional int32 month = 6 [(nanopb).default = 0]; optional int32 year = 7 [(nanopb).default = 0]; } message time_range_strategy { optional bool is_config = 1; optional bool is_enable = 2; optional int32 time_mode = 3; optional int32 time_data = 4; optional rtc_data start_time = 5; optional rtc_data stop_time = 6; } message plug_ack_message { optional uint32 ack = 1; } message plug_heartbeat_pack { optional uint32 err_code = 1 [(nanopb).default = 0]; optional uint32 warn_code = 2 [(nanopb).default = 0]; optional uint32 country = 3 [(nanopb).default = 0]; optional uint32 town = 4 [(nanopb).default = 0]; optional int32 max_cur = 5 [(nanopb).default = 0]; optional int32 temp = 6 [(nanopb).default = 0]; optional int32 freq = 7 [(nanopb).default = 0]; optional int32 current = 8 [(nanopb).default = 0]; optional int32 volt = 9 [(nanopb).default = 0]; optional int32 watts = 10 [(nanopb).default = 0]; optional bool switch = 11 [(nanopb).default = false]; optional int32 brightness = 12 [(nanopb).default = 0]; optional int32 max_watts = 13 [(nanopb).default = 0]; optional int32 heartbeat_frequency = 14 [(nanopb).default = 0]; optional int32 mesh_enable = 15 [(nanopb).default = 0]; } message plug_switch_message { optional uint32 plug_switch = 1; } message brightness_pack { optional int32 brightness = 1 [(nanopb).default = 0]; } message max_cur_pack { optional int32 max_cur = 1 [(nanopb).default = 0]; } message max_watts_pack { optional int32 max_watts = 1 [(nanopb).default = 0]; } message mesh_ctrl_pack { optional uint32 mesh_enable = 1 [(nanopb).default = 0]; } message ret_pack { optional bool ret_sta = 1 [(nanopb).default = false]; } enum CmdFunction { Unknown = 0; PermanentWattsPack = 129; SupplyPriorityPack = 130; }
;
const mqttDaten = {};
const musterSetAC = {
header: {
pdata: {
value: 1300,
},
src: 32,
dest: 53,
dSrc: 1,
dDest: 1,
checkType: 3,
cmdFunc: 20,
cmdId: 129,
dataLen: 3,
needAck: 1,
seq: 1651831507,
version: 19,
payloadVer: 1,
from: 'ios',
deviceSn: 'ABCxxxxxxx123'
}
};
// @ts-ignore
async function main() {
async function getEcoFlowMqttData(email, password) { const options = { hostname: 'api.ecoflow.com', path: '/auth/login', method: 'POST', headers: { 'Host': 'api.ecoflow.com', 'lang': 'de-de', 'platform': 'android', 'sysversion': '11', 'version': '4.1.2.02', 'phonemodel': 'SM-X200', 'content-type': 'application/json', 'user-agent': 'okhttp/3.14.9' } };
const data = {
appVersion: "4.1.2.02",
email: email,
os: "android",
osVersion: "30",
password: Buffer.from(password).toString('base64'),
scene: "IOT_APP",
userType: "ECOFLOW"
};
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
let token, userid;
try {
let response = await httpsRequest(options, data);
responseData = JSON.parse(response);
token = responseData.data.token;
userid = responseData.data.user.userId;
} catch (error) {
//throw new Error("Une erreur s'est produite:",error);
console.log("ERROR",response);
}
if (!token) return "ERROR";
options.path = `/iot-auth/app/certification?userId=${userid}`;
options.method = 'GET';
options.headers.authorization = `Bearer ${token}`;
try {
response = await httpsRequest(options);
response = JSON.parse(response);
mqttDaten.Passwort = response.data.certificatePassword;
mqttDaten.Port = response.data.port;
mqttDaten.UserID = userid;
mqttDaten.User = response.data.certificateAccount;
mqttDaten.URL = response.data.url
mqttDaten.protocol = response.data.protocol;
mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
} catch (error) {
log(response, mqttDaten);
throw new Error("Une erreur s'est produite lors de la détermination des données d'accès. Veuillez vérifier les données d'accès.");
}
}
//################ MQTT Verbindung ################## async function setupMQTTConnection(sn, valueWATT) { //console.log("mqttDaten",mqttDaten); //log("Nouvelle connexion MQTT",mqttDaten) // Verbindung herstellen const options = { port: mqttDaten.Port, clientId: mqttDaten.clientID, username: mqttDaten.User, password: mqttDaten.Passwort, protocol: mqttDaten.protocol }; const client = await mqtt.connect("mqtt://" + mqttDaten.URL, options); // Event-Handler für Verbindungsaufbau client.on('connect', function () { console.log('Connecté au courtier Ecoflow MQTT'); setAC(sn,valueWATT); setTimeout(() => { client.end(); }, "3000"); isMqttConnected = true });
// Event-Handler für getrennte Verbindung
client.on('close', () => {
console.log("Le client MQTT est déconnecté");
isMqttConnected = false;
});
// Callback für Fehler
client.on('error', function (error) {
log('Fehler bei der Ecoflow MQTT-Verbindung:' + error, 'warn'); //
});
client.on('reconnect', function () {
console.log('Reconnecting to Ecoflow MQTT broker...'); //
});
// Weitere Event-Handler hier...
return client;
}
await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort); client = await setupMQTTConnection("HW51ZEH4SF5R2556",0); //setAC("SERIAL NUMBER PWS",100); // Disconnect from MQTT broker //console.log(client); } main();
function SendProto(protomsg, topic) { //return const root = protobuf.parse(protoSource2).root; const PowerMessage = root.lookupType("setMessage"); const message = PowerMessage.create(JSON.parse(protomsg)); const messageBuffer = PowerMessage.encode(message).finish(); //log("Modifizierter Hex-String:" + Buffer.from(messageBuffer).toString("hex")); //log("topic:" + topic); client.publish(topic, messageBuffer, { qos: 1 }, function (error) { if (error) { console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error); } else { if (ConfigData.Debug) log('Le message MQTT a été publié avec succès.'); // } }); }
function setAC(asn, Value) { log("set Ac => " + Value + " Watts") if (!ConfigData.Regulation) { //return } let updatedMusterSetAC = musterSetAC; if (Value <= -1) { delete updatedMusterSetAC.item.meta; delete updatedMusterSetAC.item.ValByte; } else { updatedMusterSetAC.header.pdata.value = Value updatedMusterSetAC.header.dataLen = getVarintByteSize(Value) } updatedMusterSetAC.header.seq = Date.now() updatedMusterSetAC.header.deviceSn = asn //log(JSON.stringify(updatedMusterSetAC)) //setState(ConfigData.statesPrefix + '.app' + mqttDaten.UserID + '' + asn + '_thing_property_set.setAC', Value.toString(), true) SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + asn + '/thing/property/set'); }
function getVarintByteSize(number) { let byteSize = 0; while (number >= 128) { byteSize++; number >>= 7; // Rechtsschiebeoperation um 7 Bits } byteSize++; byteSize++; return byteSize; }`
package.json file :
{ "dependencies": { "https": "^1.0.0", "mqtt": "^5.0.3", "protobufjs": "^7.2.4" } }
it connects to https ecoflow to obtain the credeentials for mqqt, then it connect to mqqt, it publishes a message with the desired output then closes the connection....
simpler: protonmsg is {"header":{"pdata":{"value":0},"src":32,"dest":53,"dSrc":1,"dDest":1,"checkType":3,"cmdFunc":20,"cmdId":129,"dataLen":2,"needAck":1,"seq":1692616321954,"version":19,"payloadVer":1,"from":"ios","deviceSn":"serial pws"}}
seq is the timestamp
the message is : setMessage { header: { pdata: { value: 0 }, src: 32, dest: 53, dSrc: 1, dDest: 1, checkType: 3, cmdFunc: 20, cmdId: 129, dataLen: 2, needAck: 1, seq: 1692616147669, version: 19, payloadVer: 1, from: 'ios', deviceSn: 'pWs sERIAL number' } } it eondes is : messageBuffer <Buffer 0a 3c 0a 02 08 00 10 20 18 35 20 01 28 01 38 03 40 14 48 81 01 50 02 58 01 70 ae e1 b6 be 01 80 01 13 88 01 01 ba 01 03 69 6f 73 ca 01 10 48 57 35 31 ... 12 more bytes>
and publish it
the encoding is done with https://protobuf.dev/programming-guides/encoding/ who also exists in python
in google collab is something like this : `import google.protobuf from powerstream_pb2 import setMessage import time current_timestamp = int(time.time()) protonmsg = { "header": { "pdata": {"value": 0}, "src": 32, "dest": 53, "d_src": 1, # Corrected field name from "dSrc" to "d_src" "d_dest": 1, "check_type": 3, # Corrected field name from "checkType" to "check_type" "cmd_func": 20, "cmd_id": 129, "data_len": 2, "need_ack": 1, "seq": current_timestamp, "version": 19, "payload_ver": 1, "from_value": "ios", "device_sn": "999999" } }
message = setMessage() message.header.pdata.value = protonmsg["header"]["pdata"]["value"] message.header.src = protonmsg["header"]["src"] message.header.dest = protonmsg["header"]["dest"] message.header.d_src = protonmsg["header"]["d_src"] message.header.d_dest = protonmsg["header"]["d_dest"] message.header.check_type = protonmsg["header"]["check_type"] message.header.cmd_func = protonmsg["header"]["cmd_func"] message.header.cmd_id = protonmsg["header"]["cmd_id"] message.header.data_len = protonmsg["header"]["data_len"] message.header.need_ack = protonmsg["header"]["need_ack"] message.header.seq = protonmsg["header"]["seq"] message.header.version = protonmsg["header"]["version"] message.header.payload_ver = protonmsg["header"]["payload_ver"]
message.header.device_sn = protonmsg["header"]["device_sn"]
import binascii
encoded_message = message.SerializeToString()
hex_encoded_message = binascii.hexlify(encoded_message).decode('utf-8') print(hex_encoded_message)`
0a2c0a0208001020183520012801380340144881015002580170a1b48da706800113880101ca0106393939393939
@mattwells @tolwi is it enough to advance ?
I just realized another Power Stream bug...when it is in mode storage priority and the battery is 100% it limits the solar it injects in the network to the maximul output value...not logic at all....so it will not be enought to be able to change the output value it is necessary to be able to change the power stream mode also....
This Bug is also in the EcoFlow App. After a Call with the EcoFlow Support i Know it is due to the new Firmware.
I didnt suceed to implement it in HomeAssistant. Maybe the Creator of the Integration has an Idea 😀
@sebjosb do you know an ecoflow official forum where we can post bugs and ideas of evolutions?
I think there is a Ecoflow Forum on Facebook. Unfortunately im not on Facebook....
@bogdancs92 There is API and Forum Info on https://developer.ecoflow.com/us
BUT... it's disappointing. The official API does not support PowerStream (and also not all settings for the power stations). The Facebook forum is near-dead to dead. But have a look yourself, maybe it changes some time.
I just realized another Power Stream bug...when it is in mode storage priority and the battery is 100% it limits the solar it injects in the network to the maximul output value...
Looking at my device, the newest firmware (1.0.0.154)seems to have fixed that. But: I never go to 100%, My limit is 79% (non LiFePo unit). Maybe the bug still exists at exactly 100%.
@rgerhards I am in 10.0.0.154. Storage mode. Delta 2 at 100% Solar input is limited by the parameter MAX AC. It has no logi why. I have done multiple tests. AS soon as I move it to 100 it limits the output at 100 even the solar 1 + solar 2 is 300. And if i put it at 600 it passes the output at 300 the correct amount solar 1 + solar 2. So to benefit at maximum of my solar power I will have to change each morning the output to 600 to be sure that when the battery charges no watts are wasted....:'(
Thank you for the idea..I wil chanbge the limit to 99% and see if the limit still exists...
@bogdancs92 I have just seen that the EcoFlow Developer FB group now seems to have someone from EcoFlow at least participating a bit. If I understood correctly, he hints that the official API will get PowerStream support by end of August - which is not far away. I'll keep monitoring the group and let you know if there is anything new.
Waiting for the coming official API I have made a node.js server to change the ouptut based on the work from the IOBroker:
https://github.com/bogdancs92/ecoflow-powerstream-nodejs
Now I have to look how do I call this from HA automatismes.
It seems to work. I change the mode and the ouput power with a rest command from HA to my node server....
I will keep the PWS in priority power supply with an output of 10 as long as the d2 is not fully charged...when charged I will pass it in mode storage priority and i will pass the output to 500w
scenarios: now in the morning when d2 battery <5% i pass output at 10w (it is only in this mode that the Ecojoko plugs works) when d2 battery >98% I pass in storage mode and output 500 at 21h I pass in mode power suply priority and output at 120w
if battery fully charged and i want to use it either :
@mattwells @tolwi is it enough to advance ?
All of the protobuf for Ecoflow has been added to this repo already and can be viewed here https://github.com/tolwi/hassio-ecoflow-cloud/tree/main/custom_components/ecoflow_cloud/mqtt/proto I will warn you that the protobuf that they are using in the IOBroker isn't not as complete as mine although will work.
"All" that needs to happen is to update the powerstream.py numbers and selects to send over the payload and change the integration to send serial not json. Everything that is needed is already in the repo I just haven't had time/motivation to do it.
Hello, Could you show me how to modify the output watt and storage mode with the python command (execute under python for test without homeassistant), I suppose it is : def send_set_message(self, mqtt_state: dict[str, Any], command: dict)
How to test ? Please.
Hello, Could you show me how to modify the output watt and storage mode with the python command (execute under python for test without homeassistant), I suppose it is : def send_set_message(self, mqtt_state: dict[str, Any], command: dict)
How to test ? Please.
As far as I know, there is no easy way for that yet. I'm using bogdancs92's solution: https://github.com/bogdancs92/ecoflow-powerstream-nodejs
Hello, Could you show me how to modify the output watt and storage mode with the python command (execute under python for test without homeassistant), I suppose it is : def send_set_message(self, mqtt_state: dict[str, Any], command: dict) How to test ? Please.
As far as I know, there is no easy way for that yet. I'm using bogdancs92's solution: https://github.com/bogdancs92/ecoflow-powerstream-nodejs
I'm using it too, running very well I have documented it a bit how I got bogdancs92's solution running: https://github.com/bogdancs92/ecoflow-powerstream-nodejs/issues/1
@Nid01, @giovanne123 , Thank you for your reply.
Indeed, it is complicate for the moment, I am going to try through nodejs. I didn't understand what I have to write in TOKEN=??? and TOKEN_VAL=???
Thank you again.
Hello @Polyphe , you can write whatever you want as long as it corresponds...between the env file and your url call...it is just for security because there is no authentication... for exemple : TOKEN=blabla TOKEN_VAL=toto
your url should have https://xx.XX.XX?blabla=toto&....
Thank you @bogdancs92, @Nid01 , @giovanne123 . It works now with your help. For your information, in .env file, I put between "", the password so that it works... Next step is to try with python as the rest of the code is in python (it is no win ;) )
This feature would definitely be the killer feature of this repo.
I can’t wait to use it, so I keep an eye on this issue. 👀
I would also love to see this feature implemented.
imho the only thing that really needs to be controlled is the "PowerStream Other Loads"
because the pain in the *** for now is, that either the battery is empty after two hours, and therefore useless, or the solar panels are limited to 50W which is my base consumption in the night, which makes the PV useless right now.
the switch between battery prio or power prio can be timed and is good imho.
Hallo, Still ist is implement here, you can use this solution : https://github.com/bogdancs92/ecoflow-powerstream-nodejs/issues/1
This solution works very fine. I implemented with 0 Watt 0 watt feed-in/consumption from the network.
@gilbert76 , which interval are you using for adjust AC Output? (I currently use 10sec, plan to further play with it but bad weather currently for testing)
I check if the consum of energy or the feed is over 50Watt for 1 minute I set the AC Output new.
Here my Automation
alias: 00 - PowerStream-AC-Set description: "" trigger:
Here my address : Gilbert.donofrioATfreenet.de I speak Italian, German, Greek and English.
Hey Guys, i am a starter in the programming World. How did you integrate the .js script into Homeassistant? Did you set up a separate system for this? Thanks for your help!!!
The JS script is a docker for it own what you can pull the command over website. Howto you can fin it here: https://github.com/bogdancs92/ecoflow-powerstream-nodejs/issues/1
You can send command manual or you send it over Home Assistant which can set the AC Output automatically
oh Okay, thanks! I have looked for the command, but couldnt find it. Could you please send me the Link to the command?
i am not using home assistant in a docker, but Home Assistant Operating System. Therefore setting up a separate raspberry just for this is at the moment for me a bit oversized effort.
I think i wait for the feature to be implemented here or by EcoFlow... ;-)
docker not needed. for the solution only node (also on OS level should work with the repo's "node server.js". But I haven't used. I have everything in docker, therefore explained that way)
But Ecoflow_Cloud support would be fine...
I check if the consum of energy or the feed is over 50Watt for 1 minute I set the AC Output new.
Here my Automation
alias: 00 - PowerStream-AC-Set description: "" trigger:
- platform: numeric_state entity_id: sensor.shelly_3m_verbrauch for: hours: 0 minutes: 1 seconds: 0 above: 50 enabled: true
- platform: numeric_state entity_id: sensor.shelly_3m_einspeisung for: hours: 0 minutes: 1 seconds: 0 above: 50 enabled: true
platform: state entity_id:
- input_boolean.powerstream_ac_set from: "off" to: "on" condition:
condition: or conditions:
condition: and conditions:
condition: time after: "18:00:00" before: "07:00:00" weekday:
- mon
- tue
- wed
- thu
- fri
- sat
- sun
condition: or conditions:
- condition: numeric_state entity_id: sensor.shelly_3m_verbrauch above: 50
- condition: numeric_state entity_id: sensor.shelly_3m_einspeisung above: 50
condition: state entity_id: input_boolean.powerstream_ac_set state: "on" action:
- service: input_boolean.turn_off data: {} target: entity_id:
- input_boolean.powerstream_ac_set
- service: input_number.set_value data_template: value: >- {{ states('sensor.powerstream_inverter_output_watts') |float(0)|round(-1) + states('sensor.shelly_summe')|float(0)|round(-1) }} target: entity_id: input_number.ecoflow_powerstream_ac_output
- service: input_text.set_value data: value: "0" target: entity_id: input_text.ecoflow_powerstream_set_power_suplymode
- service: rest_command.ecoflow_powerstream_ac_set_rest data: {} enabled: true mode: single
Here my address : Gilbert.donofrioATfreenet.de I speak Italian, German, Greek and English. @gilbert76 could you format your automation configuration via code, please? The way it is formatted now, it seems not functional and not very readable, at least for me.
Hello, is it possible to implement a feature to change the output of the PowerStream in HomeAssistant?
Many thanks
Sebastian