tolwi / hassio-ecoflow-cloud

EcoFlow Cloud Integration for Home Assistant
416 stars 74 forks source link

PowerStream change Output watts #124

Open sebjosb opened 1 year ago

sebjosb commented 1 year ago

Hello, is it possible to implement a feature to change the output of the PowerStream in HomeAssistant?

Many thanks

Sebastian

StefanMoellenhoff commented 1 year ago

I would be highly interested in this feature as well. Thank you for the great work!

jeff1975 commented 1 year ago

Ich hätte auch großes Interesse an dieser Funktion. Vielen Dank für die tolle Arbeit!

madjuju commented 1 year ago

Me too

ddhyland commented 1 year ago

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?

mattwells commented 1 year ago

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.

mattwells commented 1 year ago

@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.

ddhyland commented 1 year ago

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 commented 1 year ago

@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.

bogdancs92 commented 1 year ago

@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 👯

bogdancs92 commented 1 year ago

here is the js script from https://forum.iobroker.net/topic/66743/ecoflow-connector-script-zur-dynamischen-leistungsanpassung 1692266227129-ecoflow-connector_v112.js.zip

sebjosb commented 1 year ago

Thanks!! How could the Script be integrated in Homeassitant, because this is Java Script?

bogdancs92 commented 1 year ago

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 :)

bogdancs92 commented 1 year ago

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

bogdancs92 commented 1 year ago

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; } ;

sebjosb commented 1 year ago

This could work. Did you try it out?

bogdancs92 commented 1 year ago

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; }`

bogdancs92 commented 1 year ago

package.json file :

{ "dependencies": { "https": "^1.0.0", "mqtt": "^5.0.3", "protobufjs": "^7.2.4" } }

bogdancs92 commented 1 year ago

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....

bogdancs92 commented 1 year ago

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

bogdancs92 commented 1 year ago

the encoding is done with https://protobuf.dev/programming-guides/encoding/ who also exists in python

bogdancs92 commented 1 year ago

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" } }

Create a setMessage instance and populate its fields

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.from" = protonmsg["header"]["from_value"] # Use from_value instead of from

message.header.device_sn = protonmsg["header"]["device_sn"]

import binascii

Serialize the message to binary

encoded_message = message.SerializeToString()

Convert the binary to hexadecimal

hex_encoded_message = binascii.hexlify(encoded_message).decode('utf-8') print(hex_encoded_message)`

0a2c0a0208001020183520012801380340144881015002580170a1b48da706800113880101ca0106393939393939

bogdancs92 commented 1 year ago

@mattwells @tolwi is it enough to advance ?

bogdancs92 commented 1 year ago

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....

sebjosb commented 1 year ago

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 😀

bogdancs92 commented 1 year ago

@sebjosb do you know an ecoflow official forum where we can post bugs and ideas of evolutions?

sebjosb commented 1 year ago

I think there is a Ecoflow Forum on Facebook. Unfortunately im not on Facebook....

rgerhards commented 1 year ago

@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.

rgerhards commented 1 year ago

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%.

bogdancs92 commented 1 year ago

@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...

rgerhards commented 1 year ago

@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.

bogdancs92 commented 1 year ago

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.

bogdancs92 commented 1 year ago

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 commented 1 year ago

@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.

Polyphe commented 1 year ago

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.

Nid01 commented 1 year ago

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

giovanne123 commented 1 year ago

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

Polyphe commented 1 year ago

@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.

bogdancs92 commented 1 year ago

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&....

Polyphe commented 1 year ago

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 ;) )

AdrianPal commented 1 year ago

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. 👀

Snooz82 commented 1 year ago

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.

gilbert76 commented 1 year ago

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.

image

image

giovanne123 commented 1 year ago

@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)

gilbert76 commented 1 year ago

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.

sebjosb commented 1 year ago

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!!!

gilbert76 commented 1 year ago

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

sebjosb commented 1 year ago

oh Okay, thanks! I have looked for the command, but couldnt find it. Could you please send me the Link to the command?

Snooz82 commented 1 year ago

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... ;-)

giovanne123 commented 1 year ago

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...

Nid01 commented 1 year ago

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.