Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delta 2 (MQTT) #6

Open
Ne0-Hack3r opened this issue Nov 28, 2022 · 85 comments
Open

Delta 2 (MQTT) #6

Ne0-Hack3r opened this issue Nov 28, 2022 · 85 comments

Comments

@Ne0-Hack3r
Copy link

I've started down the road of trying to integrate Delta 2 data with HA via MQTT...

I already have mosquito broker setup as per the instructions from @lwsrbrts in ISSUE #1 and my tweaks for SHP per ISSUE #5 so I simply added the following to mosquito.conf:

# Delta2 Data
topic "" in 0 ecoflow/D2/data /app/device/property/<D2 Serial Number in UPPER CASE>

After seeing the ../D2/data topic on the local broker via MQTT Explorer I used a power shell script to gather messages and log them over the course of several minutes and captured a number of short messages with 1-5 key/value pairs each similar to this:

{"id":1330299617613842767,"version":"1.0","timestamp":1669603258,"moduleType":"1","params":{"pd.carTemp":36}}
{"id":1330299618754038533,"version":"1.0","timestamp":1669603258,"moduleType":"2","params":{"bms_bmsStatus.minCellTemp":26,"bms_bmsStatus.vol":53494,"bms_bmsStatus.f32ShowSoc":89.7}}
{"id":1330299618642890337,"version":"1.0","timestamp":1669603258,"moduleType":"1","params":{"pd.carTemp":35}}
{"id":1330299626203776316,"version":"1.0","timestamp":1669603259,"moduleType":"3","params":{"inv.acInVol":123113}}
{"id":1330299627343973305,"version":"1.0","timestamp":1669603259,"moduleType":"2","params":{"bms_emsStatus.paraVolMin":52491,"bms_emsStatus.paraVolMax":54491}}
{"id":1330299627232824896,"version":"1.0","timestamp":1669603259,"moduleType":"2","params":{"bms_bmsStatus.maxCellVol":3343,"bms_bmsStatus.vol":53491,"bms_bmsStatus.f32ShowSoc":89.7}}
{"id":1330299634793709708,"version":"1.0","timestamp":1669603260,"moduleType":"5","params":{"mppt.carOutVol":46,"mppt.outVol":53331,"mppt.carTemp":36}}
{"id":1330299635933905399,"version":"1.0","timestamp":1669603260,"moduleType":"1","params":{"pd.carTemp":36}}
{"id":1330299635822756850,"version":"1.0","timestamp":1669603260,"moduleType":"3","params":{"inv.acInAmp":66,"inv.acInVol":123084}}
{"id":1330299634793710818,"version":"1.0","timestamp":1669603260,"moduleType":"1","params":{"pd.carTemp":35}}
{"id":1330299687473516144,"version":"1.0","timestamp":1669603266,"moduleType":"1","params":{"pd.carTemp":35}}
{"id":1330299695952298376,"version":"1.0","timestamp":1669603267,"moduleType":"2","params":{"bms_bmsStatus.vol":53490,"bms_bmsStatus.f32ShowSoc":89.7,"bms_bmsStatus.minCellVol":3342}}
{"id":1330299694923253762,"version":"1.0","timestamp":1669603267,"moduleType":"1","params":{"pd.carTemp":36}}
{"id":1330299696063449385,"version":"1.0","timestamp":1669603267,"moduleType":"5","params":{"mppt.carOutVol":46,"mppt.inVol":942,"mppt.outVol":53331,"mppt.carTemp":36}}
{"id":1330299695952300772,"version":"1.0","timestamp":1669603267,"moduleType":"3","params":{"inv.acInAmp":74,"inv.acInVol":123504}}
{"id":1330299747603057983,"version":"1.0","timestamp":1669603273,"moduleType":"1","params":{"pd.carTemp":36}}
{"id":1330299756081840390,"version":"1.0","timestamp":1669603274,"moduleType":"2","params":{"bms_emsStatus.f32LcdShowSoc":89.7,"bms_emsStatus.paraVolMin":52494,"bms_emsStatus.paraVolMax":54494}}
{"id":1330299755052794531,"version":"1.0","timestamp":1669603274,"moduleType":"2","params":{"bms_bmsStatus.remainCap":9626,"bms_bmsStatus.minCellTemp":26,"bms_bmsStatus.vol":53490,"bms_bmsStatus.f32ShowSoc":89.7}}
{"id":1330299756192990623,"version":"1.0","timestamp":1669603274,"moduleType":"1","params":{"pd.carTemp":35}}

After parsing and sorting a bit I came up with the following list of "unique" params:

bms_bmsStatus.amp
bms_bmsStatus.f32ShowSoc
bms_bmsStatus.maxCellVol
bms_bmsStatus.minCellTemp
bms_bmsStatus.minCellVol
bms_bmsStatus.remainCap
bms_bmsStatus.vol
bms_emsStatus.f32LcdShowSoc
bms_emsStatus.paraVolMax
bms_emsStatus.paraVolMin
inv.acInAmp
inv.acInVol
mppt.carOutVol
mppt.carTemp
mppt.inAmp
mppt.inVol
mppt.outVol
pd.carTemp

I also grabbed the payload from ..\get_reply in response to "lastestquotas" from the EF MQTT server (sent to ..\get by the D2 app when it opens) and discovered these unique keys:

bms_bmsStatus.amp             
bms_bmsStatus.bmsFault        
bms_bmsStatus.bqSysStatReg    
bms_bmsStatus.cellId          
bms_bmsStatus.cycles          
bms_bmsStatus.designCap       
bms_bmsStatus.errCode         
bms_bmsStatus.f32ShowSoc      
bms_bmsStatus.fullCap         
bms_bmsStatus.inputWatts      
bms_bmsStatus.maxCellTemp     
bms_bmsStatus.maxCellVol      
bms_bmsStatus.maxMosTemp      
bms_bmsStatus.minCellTemp     
bms_bmsStatus.minCellVol      
bms_bmsStatus.minMosTemp      
bms_bmsStatus.num             
bms_bmsStatus.openBmsIdx      
bms_bmsStatus.outputWatts     
bms_bmsStatus.remainCap       
bms_bmsStatus.remainTime      
bms_bmsStatus.soc             
bms_bmsStatus.soh             
bms_bmsStatus.sysVer          
bms_bmsStatus.tagChgAmp       
bms_bmsStatus.temp            
bms_bmsStatus.type            
bms_bmsStatus.vol             
bms_emsStatus.bmsIsConnt      
bms_emsStatus.bmsModel        
bms_emsStatus.bmsWarState     
bms_emsStatus.chgAmp          
bms_emsStatus.chgCmd          
bms_emsStatus.chgRemainTime   
bms_emsStatus.chgState        
bms_emsStatus.chgVol          
bms_emsStatus.dsgCmd          
bms_emsStatus.dsgRemainTime   
bms_emsStatus.emsIsNormalFlag 
bms_emsStatus.f32LcdShowSoc   
bms_emsStatus.fanLevel        
bms_emsStatus.lcdShowSoc      
bms_emsStatus.maxAvailNum     
bms_emsStatus.maxChargeSoc    
bms_emsStatus.maxCloseOilEb   
bms_emsStatus.minDsgSoc       
bms_emsStatus.minOpenOilEb    
bms_emsStatus.openBmsIdx      
bms_emsStatus.openUpsFlag     
bms_emsStatus.paraVolMax      
bms_emsStatus.paraVolMin      
inv.acDipSwitch               
inv.acInAmp                   
inv.acInFreq                  
inv.acInVol                   
inv.cfgAcEnabled              
inv.cfgAcOutFreq              
inv.cfgAcOutVol               
inv.cfgAcWorkMode             
inv.cfgAcXboost               
inv.chargerType               
inv.chgPauseFlag              
inv.dcInAmp                   
inv.dcInTemp                  
inv.dcInVol                   
inv.dischargeType             
inv.errCode                   
inv.fanState                  
inv.FastChgWatts              
inv.inputWatts                
inv.invOutAmp                 
inv.invOutFreq                
inv.invOutVol                 
inv.invType                   
inv.outputWatts               
inv.outTemp                   
inv.reserved                  
inv.SlowChgWatts              
inv.standbyMins               
inv.sysVer                    
mppt.acStandbyMins            
mppt.beepState                
mppt.carOutAmp                
mppt.carOutVol                
mppt.carOutWatts              
mppt.carStandbyMin            
mppt.carState                 
mppt.carTemp                  
mppt.cfgAcEnabled             
mppt.cfgAcOutFreq             
mppt.cfgAcOutVol              
mppt.cfgAcXboost              
mppt.cfgChgType               
mppt.cfgChgWatts              
mppt.chgPauseFlag             
mppt.chgState                 
mppt.chgType                  
mppt.dc24vState               
mppt.dc24vTemp                
mppt.dcChgCurrent             
mppt.dcdc12vAmp               
mppt.dcdc12vVol               
mppt.dcdc12vWatts             
mppt.dischargeType            
mppt.faultCode                
mppt.inAmp                    
mppt.inVol                    
mppt.inWatts                  
mppt.mpptTemp                 
mppt.outAmp                   
mppt.outVol                   
mppt.outWatts                 
mppt.powStandbyMin            
mppt.res                      
mppt.scrStandbyMin            
mppt.swVer                    
mppt.x60ChgType               
pd.beepMode                   
pd.brightLevel                
pd.carState                   
pd.carTemp                    
pd.carUsedTime                
pd.carWatts                   
pd.chgDsgState                
pd.chgPowerAC                 
pd.chgPowerDC                 
pd.chgSunPower                
pd.dcInUsedTime               
pd.dcOutState                 
pd.dsgPowerAC                 
pd.dsgPowerDC                 
pd.errCode                    
pd.ext3p8Port                 
pd.ext4p8Port                 
pd.extRj45Port                
pd.icoBytes                   
pd.invUsedTime                
pd.lcdOffSec                  
pd.model                      
pd.mpptUsedTime               
pd.qcUsb1Watts                
pd.qcUsb2Watts                
pd.remainTime                 
pd.reserved                   
pd.soc                        
pd.standbyMin                 
pd.sysVer                     
pd.typec1Temp                 
pd.typec1Watts                
pd.typec2Temp                 
pd.typec2Watts                
pd.typecUsedTime              
pd.usb1Watts                  
pd.usb2Watts                  
pd.usbqcUsedTime              
pd.usbUsedTime                
pd.wattsInSum                 
pd.wattsOutSum                
pd.wifiAutoRcvy               
pd.wifiRssi                   
pd.wifiVer                    
pd.wireWatts                  

It appears the D2 works differently than the DP. The DP will send a large message with the status of 'everything' for a given module (bmsMaster, ems, inv, ...) on a fairly frequent basis. SHP appears to operate in a similar way. The D2 appears to send only small messages with what seem to be status "updates" for specific data points which would seem to imply the app gets the status of 'everything' when it opens and then only receives update messages past that point... I'm still new to HA and MQTT and I'm not an "app developer" so some of my speculations here (based on initial observation) may well be incorrect...

For DP and SHP we can create automations in HA to trigger on specific payloads for specific params in messages such as "bmsMaster.errCode = 0" (for DP) or "id = 2" (for SHP) and, based on those triggers, re-publish the entire message to a unique sub-topic (such as ../SHP/circuits) then use an mqtt sensor to pull all the key/value pairs into attributes of an entity in HA. Once those attributes exist in HA custom sensors can be made to track specific attributes (such as sensor.shp_circuit_1_power) or the attribute values can be used directly in various places like {{ state_attr('sensor.shp_circuits','infoList')[10].chWatt | round(0) }}

For D2, the messages arrive with a unique "moduleType" but may contain one or several different parameters... The moduleType could be used by an automation to re-publish to a unique topic per module... but at that point I'm a bit lost in terms of how to structure mqtt sensors to pull the specific data/attributes into HA...

Perhaps someone here with more HA/MQTT background can suggest the best approach?

@Ne0-Hack3r Ne0-Hack3r mentioned this issue Nov 28, 2022
@Ne0-Hack3r
Copy link
Author

I think I've come up with a sort of "work around" for this type of messaging that is similar to what @lwsrbrts came up with for DP and I adapted for use with SHP... Like SHP (and unlike DP) the Delta 2 messages have an identifier... For SHP this is an "id" in the payload that is unique to the module in question... for D2 this is a "moduleType" in the payload...

Created the following automations to re-publish payloads with certain module types to new topics:

- id: 'EF_D2_pd'
  alias: D2 - pd
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/data
    payload: 1
    value_template: '{{value_json.moduleType}}'
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/pd
      payload: '{{trigger.payload}}'
  mode: single

- id: 'EF_D2_bms'
  alias: D2 - bms
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/data
    payload: 2
    value_template: '{{value_json.moduleType}}'
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/bms
      payload: '{{trigger.payload}}'
  mode: single

- id: 'EF_D2_inv'
  alias: D2 - inv
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/data
    payload: 3
    value_template: '{{value_json.moduleType}}'
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/inv
      payload: '{{trigger.payload}}'
  mode: single

- id: 'EF_D2_mppt'
  alias: D2 - mppt
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/data
    payload: 5
    value_template: '{{value_json.moduleType}}'
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/mppt
      payload: '{{trigger.payload}}'
  mode: single

BTW: I assume moduleType '4' is for the extra battery but I don't have one to prove that... It's also possible that could appear under moduelType '2' as, unlike DP, both 'bms' and 'ems' keys appear under this same moduleType...

Then, created MQTT sensors to pull the above into sensor attributes in HA:

- name: D2-pd
    state_topic: "ecoflow/D2/pd"
    qos: 0
    value_template: "on"
    json_attributes_template: "{{ value_json.params | tojson}}"
    json_attributes_topic: "ecoflow/D2/pd"

  - name: D2-inv
    state_topic: "ecoflow/D2/inv"
    qos: 0
    value_template: "on"
    json_attributes_template: "{{ value_json.params | tojson}}"
    json_attributes_topic: "ecoflow/D2/inv"

  - name: D2-bms
    state_topic: "ecoflow/D2/bms"
    qos: 0
    value_template: "on"
    json_attributes_template: "{{ value_json.params | tojson}}"
    json_attributes_topic: "ecoflow/D2/bms"

  - name: D2-mppt
    state_topic: "ecoflow/D2/mppt"
    qos: 0
    value_template: "on"
    json_attributes_template: "{{ value_json.params | tojson}}"
    json_attributes_topic: "ecoflow/D2/mppt"

Then, not knowing which keys will appear in a given message payload for a given moduleType, I came up with this workaround to build out custom sensors for specific data points:

# D2 - Battery
  - name: "D2 Main Battery SOC"
    unit_of_measurement: "%"
    device_class: battery
    state_class: measurement
    state: >- 
      {% if state_attr('sensor.d2_bms','bms_bmsStatus.f32ShowSoc') is not none %}   
        {{ state_attr('sensor.d2_bms','bms_bmsStatus.f32ShowSoc') }}
      {% else %}
        {{ states('sensor.D2_Main_Battery_SOC') }}
      {% endif %}

  - name: "D2 Main Battery Voltage"
    unit_of_measurement: "V"
    device_class: voltage
    state_class: measurement
    state: >- 
      {% if state_attr('sensor.d2_bms','bms_bmsStatus.vol') is not none %}   
        {{ (int(state_attr('sensor.d2_bms','bms_bmsStatus.vol')) / 1000) }}
      {% else %}
        {{ states('sensor.D2_Main_Battery_Voltage') }}
      {% endif %}

... If the given attribute is not available in a given message the sensor stays set to it's last known state but if it is available (and not 'none') the state gets set to the value of that attribute (with whatever modifications I want like taking the voltage in mv and converting it to volts).

Still a bit tedious to identify which data points and build out sensors for each but at least it looks like this can now be done... It would certainly be nice if they would just give us a proper, locally accessible API or a secure way to enable a local MQTT broker on these devices... This is way better than nothing but I'm no fan of having all this tied to the cloud and it's starting to feel like a lot of layers and latency...

@Ne0-Hack3r
Copy link
Author

Ne0-Hack3r commented Dec 2, 2022

I'm exploring a different approach which may work as alternative for D2 (as well as DP and SHP - though the payload for 'lastestQuotas' on SHP is HUGE)...

In the case of all 3 devices (SHP, DP, D2) when the app opens it publishes to ../get requesting 'status of everything' like this:

{"from":"Android","id":"101493325","moduleType":0,"operateType":"latestQuotas","params":{},"version":"1.0"}

The device will respond by posting a large payload to ../get_reply with JSON containing the status of 'everything'...

In this alternative approach, I created two automations. One that triggers every 10 seconds to ask for status and another to grab the response from ../get_reply and post it to ../status

- id: EF_D2_GetStatus
  alias: D2 - Get Status
  description: ''
  trigger:
  - platform: time_pattern
    seconds: "/10"
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/get
      payload: '{"from":"HA","id":"999000123","moduleType":0,"operateType":"latestQuotas","params":{},"version":"1.0"}'

- id: EF_D2_status
  alias: D2 - status
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/get_reply
  condition:
  - condition: template
    value_template: '{{ ''latestQuotas'' in (trigger.payload) }}'
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/status
      payload: '{{trigger.payload}}'
  mode: single

I then created an MQTT sensor that sets state based on the value of the 'online' key and pulls the rest of the payload from 'quataMap' into attributes of the sensor...

  - name: D2-status
    state_topic: "ecoflow/D2/status"
    qos: 0
    value_template: >-
      {%if (value_json.data.online) == 1 %} online {%else%} offline {%endif%}
    json_attributes_template: "{{ value_json.data.quotaMap | tojson}}"
    json_attributes_topic: "ecoflow/D2/status"

At that point all the data is in HA and is updated every 10 seconds. You can display select attributes in the Templates section of Developer Tools and use them as the basis for custom sensors, triggers, etc.

The following:

{{ state_attr('sensor.d2_status','friendly_name') }} = {{ states('sensor.d2_status') }}

BMS
Fan Level        : {{ state_attr('sensor.d2_status','bms_emsStatus.fanLevel') }}
Discharge Limit  : {{ state_attr('sensor.d2_status','bms_emsStatus.minDsgSoc') }}%
Charge Limit     : {{ state_attr('sensor.d2_status','bms_emsStatus.maxChargeSoc') }}%
Input Power      : {{ state_attr('sensor.d2_status','bms_bmsStatus.inputWatts') }}W
Output Power     : {{ state_attr('sensor.d2_status','bms_bmsStatus.outputWatts') }}W

BATTERY
State of Charge  : {{ state_attr('sensor.d2_status','bms_bmsStatus.f32ShowSoc') }}%
Pack Temperature : {{ state_attr('sensor.d2_status','bms_bmsStatus.temp') }}C
Min Cell Temp    : {{ state_attr('sensor.d2_status','bms_bmsStatus.minCellTemp') }}C
Max Cell Temp    : {{ state_attr('sensor.d2_status','bms_bmsStatus.maxCellTemp') }}C
Pack Voltage     : {{ state_attr('sensor.d2_status','bms_bmsStatus.vol') /1000 }}V
Min Pack Voltage : {{ state_attr('sensor.d2_status','bms_emsStatus.paraVolMin') /1000 }}V
Max Pack Voltage : {{ state_attr('sensor.d2_status','bms_emsStatus.paraVolMax') /1000 }}V
Min Cell Voltage : {{ state_attr('sensor.d2_status','bms_bmsStatus.minCellVol') /1000 }}V
Max Cell Voltage : {{ state_attr('sensor.d2_status','bms_bmsStatus.maxCellVol') /1000 }}V
Design Capacity  : {{ state_attr('sensor.d2_status','bms_bmsStatus.designCap') /1000 }}Ah
Full Capacity    : {{ state_attr('sensor.d2_status','bms_bmsStatus.fullCap') /1000 }}Ah
Remain Capacity  : {{ state_attr('sensor.d2_status','bms_bmsStatus.remainCap') /1000 }}Ah
Battery Cycles   : {{ state_attr('sensor.d2_status','bms_bmsStatus.cycles') }}

Should produce output like this:
image

@Ne0-Hack3r
Copy link
Author

I feel like the above approach is a bit cleaner than constantly triggering multiple automations on constant messages flowing into .../data and having to re-publish those to various other topics and then have an MQTT sensor for each topic...

However, it does mean updates are limited to how often the automation in HA triggers to ask for 'status' (every 10 seconds in my example) and does turn this monitoring into more of a 'polling' operation...

With this approach you don't need ../data in mosquito.conf but instead need:

topic "" in 0 ecoflow/D2/get_reply /app/<UID>/<SN>/thing/property/get_reply
topic "" both 0 ecoflow/D2/get /app/<UID>/<SN>/thing/property/get

I'd like to get some feedback on this approach (at least conceptually if not specific to the Delta 2) vs parsing each inbound message to ../data and re-publishing to various other topics before pulling, separately, into HA and also any opinions on if 10 seconds is too often or not often enough, etc.

@Ne0-Hack3r
Copy link
Author

While testing this alternative, I also played with having the automation that publishes ../get 'status of everything' trigger on any message published to ../data

In this case updates are constant but so are requests asking for 'everything' - the rationale is that if something got published to ../data then 'something' changed so let's update 'everything' (to our single sensor in HA) and go from there... the potential drawback I see if that every time the device publishes an update to SOC, for example, HA will publish a request to mosquitto, which forward to public MQTT, which is picked up by the device, which then publishes a full payload with 'all status' that is then forwarded to Mosquitto read by HA, republished to Mosquitto under ../status and then read, again, by HA into the MQTT sensor entity...

In the case of DP, all the keys for status of a given module (bmsMaster for example) are sent together for each update so while you could use this alternative for DP is somewhat less compelling as you end up with 'all status' for each in one of 5-7 MQTT sensor entities in HA (though it still could be nice to have them all in one). With D2 it's a bit more convoluted since any given message contains only changes of status for 1 or a few attributes (simpler to handle in a purpose built script, more convoluted to get into usable form for HA)...

I have not decided which approach I will ultimately take in building out custom sensors to monitor D2 and I have yet to work on using HA to make changes to settings by publishing payloads to ../set

Ultimately all of this would be more elegant built as a formal HA integration but I'm not sure I'm ready to think about tackling that at this point...

@Ne0-Hack3r
Copy link
Author

Just a quick update to say that I've refined my D2 integration further... I ended up going with 2 automations for MQTT such that one publishes a requests for 'state of everything' to ..\get whenever 'any' state change is published to ..\data (by the device) and another that triggers on receiving the list of all states on ..\get_reply and re-publishes the payload containing 'latestQuotas' to ..\status

I've also got a number of MQTT sensors setup (working from data in ..\status) as well as automations that update input_numbers and input_lists based on various payloads received on ..\set or publish appropriate payloads to ..\set to change settings (on the device) when inputs are changed on the HA dashboard... I do have a bit of an issue to figure out though where changing a setting in the app results in the automation to change input on the dashboard executing which then triggers the automation to publish the same change to ../set (because the input on the dashboard changed)... At this point it just causes the device to beep twice because it was told to change the setting twice (by app and HA) to the 'same value' (if beep is turned on)... but it "works"... I just find it annoying because it is ugly/kludge and I want to figure out how to employ some kind of logic to prevent redundant commands to change the same setting to the same value...

Here is what I have working, thus far in HA, for Delta 2

image

@jegres1709
Copy link

jegres1709 commented Dec 8, 2022

Hey,
I was almost about to send back D2 due to missing support in HA till I saw your issue and the amazing progress!
Could you share your automations and sensors with us to change the settings in HA?

I would like to combine these information with an external solar generator and a zigbee socket to charge Delta 2 efficiently.

@Ne0-Hack3r
Copy link
Author

Hey, I was almost about to send back D2 due to missing support in HA till I saw your issue and the amazing progress! Could you share your automations and sensors with us to change the settings in HA?

Here is what I did to trigger a publish to ../get when a change is published to ../data and also to re-publish the response on ../get-reply to ../status -- at that point you can build out MQTT sensors using ecoflow/D2/status as the topic...

# Automations for D2
automation:

# Delta 2 - Status

- id: ef_d2_get_status
  alias: D2 - Get Status
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/data
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/get
      payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":0,"operateType":"latestQuotas","params":{},"version":"1.0"}'

- id: ef_d2_set_status
  alias: D2 - Set Status
  description: ''
  trigger:
  - platform: mqtt
    topic: ecoflow/D2/get_reply
  condition:
  - condition: template
    value_template: '{{ ''latestQuotas'' in (trigger.payload) }}'
  action:
  - service: mqtt.publish
    data:
      topic: ecoflow/D2/status
      payload: '{{trigger.payload}}'
  mode: single

I'm still working on building out sensors and refining automations... but here is an example MQTT sensor that uses the data published to the ../status topic to pull SOC into HA...

# MQTT for D2
mqtt:

  # Standard MQTT Sensors
  sensor:

  - name: D2 Main Battery SOC
    object_id: d2_soc
    unique_id: d2_soc
    unit_of_measurement: "%"
    device_class: battery
    state_class: measurement
    state_topic: "ecoflow/D2/status"
    qos: 0
    value_template: "{{ value_json.data.quotaMap['bms_bmsStatus.f32ShowSoc'] }}"

@jegres1709
Copy link

jegres1709 commented Dec 9, 2022

Thank you! the automations and mqtt senosr to get the status are working like a charme!
Maybe I misunderstood your post regarding the possibility to change the settings on the device via HA (e.g. charge limit)
But on the left side of the screenshot it seems you get it working to publish commands to the mqtt-server to make these changes.
I would also like to have such overview of D2 settings and uninstall the android app after that

@Ne0-Hack3r
Copy link
Author

Thank you! the automations and mqtt senosr to get the status are working like a charme! Maybe I misunderstood your post regarding the possibility to change the settings on the device via HA (e.g. charge limit) But on the left side of the screenshot it seems you get it working to publish commands to the mqtt-server to make these changes. I would also like to have such overview of D2 settings and uninstall the android app after that

I am working on refining the process to control and change settings... it's a matter of capturing appropriate payloads from ../set as you change settings on the app and then publishing them (potentially with some alteration) back to ../set from HA using automations and input_numbers/lists, etc. Right now I have an issue where if I change something in the app and my automation then changes the slider in HA, then another automation that works in reverse kicks off to change it (to the same thing) by publishing the same command again.. I'm, not sure, yet, how to prevent the double trigger without shutting down the ability to change settings from HA...

@jegres1709
Copy link

just played around to change some values (e.g. input power) and i´m facing a problem with publishing in /set.
the value changes ocacionally and will be overrided by the last andoid app command in MQTT explorer e.g.

{
"from": "Android",
"id": "237821030",
"moduleType": 2,
"operateType": "dsgCfg",
"params": {
"minDsgSoc": 30
},
"version": "1.0"
}

so i have to switch to another value and back again in hope the change will be applied.

@Ne0-Hack3r
Copy link
Author

... i´m facing a problem with publishing in /set. the value changes ocacionally and will be overrided by the last andoid app command in MQTT explorer

I encountered some oddities in the beginning and have adjusted a few things and, at this point, don't seem to have an issue...

  1. be sure to use a unique ID for "remote_clientid" in mosquite.conf (I used power shell to generate a GUID and used that)

  2. Use a unique 'from' identifier when publishing the payload to ../set

  3. Use a unique random ID when publishing the payload to ../set

I don't know for certain if all of the above are "required" (though you will have issues for sure if your remote_clientid is not unique)... But it does make it much easier to troubleshoot if you're watching the broker and can see if a command came from "Android", "IOS", or "HA" (what I use to designate 'home assistant') and, at one point, I suspected sending every payload with the same "ID" (like '999999999') was causing some issues (at least for Delta 2) which may or may not be an issue for every device and/or setting (depends on how the device itself actually interprets these payloads)...

You'll notice in one of the examples above I use the following for the payload:

payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":0,"operateType":"latestQuotas","params":{},"version":"1.0"}'

This means each time the payload is posted by HA to ../set it will be "FROM: HA" and use a unique ID between 999910000-999999999

Hope that helps...

@jegres1709
Copy link

jegres1709 commented Dec 20, 2022

point 2 and 3 was already implemented in my config.
I added point 1 to my mosquitto.conf:

connection ecoflow-bridge
address mqtt.ecoflow.com:8883
remote_clientid XXX
remote_username app-XXX
remote_password XXX
try_private true
bridge_insecure false
bridge_protocol_version mqttv311
bridge_tls_version tlsv1.2
bridge_cafile /share/mosquitto/cacert.pem
topic "" in 0 ecoflow/D2/get_reply /app/XXX/XXX/thing/property/get_reply
topic "" both 0 ecoflow/D2/get /app/XXX/XXX/thing/property/get
topic "" in 0 ecoflow/D2/set `/app/XXX/XXX/thing/property/set

in MQTT explorer I can see the changes sent, but it will not be applied..idk whats wrong now
grafik

EDIT: found it! should be:
topic "" **both 0** ecoflow/D2/set /app/XXX/XXX/thing/property/set

@Ne0-Hack3r
Copy link
Author

EDIT: found it! should be: topic "" **both 0** ecoflow/D2/set /app/XXX/XXX/thing/property/set

Yup... that will do it... set to "in" the payloads published to mosquito from HA won't be sent to mqtt.ecoflow.com...

@Ne0-Hack3r
Copy link
Author

NOTE: I made a final attempt to move away from constantly publishing a request for 'latestQuotas' to ../get and then re-publishing the response (with 'state of everything') to ../status for use by my MQTT sensors... While this does "work" the problem is that D2 only publishes the state of most things when/if they change so you end up with a lot of sensors in "limbo" until everything on D2 has been exercised... This makes for an ugly dashboard full of gauges in "unknown" state...

I'm not sure how "inefficient" it might be or how much (if any) extra power I'm using by querying for 'state of everything' every time any change is published to ../data (/app/device/property/SN) but the result is having all sensors constantly up to date and not having any in "unknown" state when sensors are reloaded and the dashboard appears quite responsive...

... still feels a touch kludgy or somehow "inelegant" so if anyone else comes up with a better way to handle this for Delta 2, please let me know!

@jegres1709
Copy link

jegres1709 commented Dec 23, 2022

Are you able to control the fan of DP2 ?
As I can see in /status there is Bms emsStatus.fanLevel 0 and Inv.fanState 0 even if the fan is roaring.
Would like to set the fan to 0 if the input is e.g. under 100W..because the temperature is still in acceptable range

@Ne0-Hack3r
Copy link
Author

Ne0-Hack3r commented Dec 24, 2022

Are you able to control the fan of DP2 ? As I can see in /status there is Bms emsStatus.fanLevel 0 and Inv.fanState 0 even if the fan is roaring. Would like to set the fan to 0 if the input is e.g. under 100W..because the temperature is still in acceptable range

I found that as well... With the most recent firmware I see those two keys in both 'latestQuotas' and sometimes in the payloads published to ../data but, thus far, I've only ever seen them be zero no matter what the fans are doing on the Delta 2...

As far as manipulating the fan speed, I doubt that is possible... There are no settings in the app for any of the devices I'm aware of to do that... I do have a sensor to display fan speed in HA for Delta Pro (which works) but can't "control" the speed... For D2, thus far I have not found any place that the unit actually updates to say what speed fan are running at or even if they are running at all...

If you or someone else discover something, please let me know. I would at least like a sensor to monitor fan speed over time and be able to compare that with temperatures, loads, etc.

@jegres1709
Copy link

yes...
only OperatrionType is missing in this case, maybe we could make changes if it´s known

@jegres1709
Copy link

Which firmware version do you have on delta2 ?
The app is offering to upgrade from 1.0.2.0 to 1.0.3.18 and I'm not sure do that

@Ne0-Hack3r
Copy link
Author

I'm running the most current 1.0.0.64 firmware on Delta 2 with WiFi firmware of 1.0.3.18 and that is what I used to develop my MQTT package and dashboards for HA...

@jegres1709
Copy link

jegres1709 commented Jan 5, 2023

Thank you ! I have just updated it to the newest version, and it works very well

EDIT1: after the firmware upgrade is :

   trigger:
   - platform: mqtt
     topic: ecoflow/D2/data

working very well!

I used before:

  - platform: time_pattern
    seconds: /1

because the values were not updated so quickly on version 1.0.2.0, but it was not satisfying

EDIT2:

this change causes a higher number of write attempts --> wearout of SSD/HDD an a higher CPU usage

@Ne0-Hack3r
Copy link
Author

this change causes a higher number of write attempts --> wearout of SSD/HDD an a higher CPU usage

This is good to know... I'm running HA in a VM on a large Hyper-V server so I'm not too concerned at this point... however, I still feel my approach of constantly asking for "state of everything" is a bit of a kludge... Perhaps someone can suggest a better approach... The main issue with just pulling from ../data is that the state of many sensors are not updated frequently (if at all) via that topic so anytime sensors are reloaded a bunch of them can remain in 'unknown' state for a protracted period of time...

@jegres1709
Copy link

maybe this issue can help to improve your version ;)
seems it´s possible to change the mqtt server to run that local and go away from ecoflow mqtt
nielsole/ecoflow-bt-reverse-engineering#2

@Ne0-Hack3r
Copy link
Author

maybe this issue can help to improve your version ;) seems it´s possible to change the mqtt server to run that local and go away from ecoflow mqtt nielsole/ecoflow-bt-reverse-engineering#2

Interesting.... I'm not sure I totally understand how to implement that but, assuming the data/topics are laid out the same and messages are reported the same as using the public MQTT, I'm not sure it resolves the challenge with regard to pulling this into HA... Perhaps there is a way, in Home Assistant, to call a script or trigger an automation that pulls the comprehensive quota data "once" and populates the "current state" when/if sensors get reset (or HA is restarted)... I'd have to dig back into it but there may also be some states that never get updated (though I think most, maybe all, do update when/if changed)...

@jegres1709
Copy link

jegres1709 commented Apr 9, 2023

Just install the android app and put in your wifi-password and the ip of your mqtt server. its working on port 8883, so there is some config needed, if not already done.
I set here my local mosquitto broker. Now Delta2 is sending the data directly to it.

Wifi-Reconnects seem also gone away.

for now I tested some commands like switch USB on/off etc and it responds so much better and faster, then with official mqtt server. The "old" commands are working fine here.
/sys/80/R33XXXXXXXX/thing/property/set is the topic to send commands. on set_reply you will get an ACK.

grafik
all the data will be send successively in 6 blocks by the device in sys/80/R33XXXXXX/thing/property/post
and look like this:

{
  "id": 6673458,
  "version": "1.0",
  "time": 6673458,
  "moduleType": 5,
  "typeCode": "mpptStatus",
  "params": {
    "inVol": 1939,
    "inWatts": 0,
    "outVol": 52385,
    "carOutVol": 56,
    "carState": 0,
    "dcChgCurrent": 8000,
    "beepState": 1,
    "cfgAcEnabled": 1,
    "cfgAcXboost": 1,
    "cfgAcOutFreq": 50,
    "cfgChgWatts": 100
  }
}

{
  "id": 7533768,
  "version": "1.0",
  "time": 7533768,
  "moduleType": 4,
  "typeCode": "bmsSlaveStatus",
  "params": {
    "soc": 44,
    "vol": 52732,
    "amp": 0,
    "f32ShowSoc": 43.9,
    "inputWatts": 0,
    "outputWatts": 0,
    "remainTime": 4322
  }
}
{
  "id": 6744148,
  "version": "1.0",
  "time": 6744148,
  "moduleType": 3,
  "typeCode": "invStatus",
  "params": {
    "invOutVol": 229091,
    "invOutAmp": 20
  }
}

{
  "id": 7259268,
  "version": "1.0",
  "time": 7259268,
  "moduleType": 2,
  "typeCode": "bmsStatus",
  "params": {
    "soc": 24,
    "vol": 52719,
    "minCellVol": 3292,
    "f32ShowSoc": 23.8,
    "inputWatts": 0,
    "outputWatts": 0
  }
}
{
  "id": 536890164,
  "version": "1.0",
  "time": 7857378,
  "moduleType": 2,
  "typeCode": "emsStatus",
  "params": {
    "bmsModel": 2,
    "lcdShowSoc": 35,
    "dsgRemainTime": 1270,
    "f32LcdShowSoc": 34.6,
    "paraVolMin": 51726,
    "paraVolMax": 53726,
    "minDsgSoc": 10,
    "minOpenOilEb": 0,
    "maxCloseOilEb": 100
  }
}
{
  "id": 7717168,
  "version": "1.0",
  "time": 7717168,
  "moduleType": 1,
  "typeCode": "pdStatus",
  "params": {
    "soc": 24,
    "wattsOutSum": 0,
    "wattsInSum": 0,
    "remainTime": -1281,
    "dcOutState": 0,
    "usb1Watts": 0,
    "typec1Watts": 0,
    "carWatts": 0,
    "standbyMin": 30,
    "lcdOffSec": 10
  }
}

States of some switches are missing here. (e.g. USB switch//dc24vState)
don´t know if it´s enough to work with and to solve the problem with pulling, but for use in HA it has to be adjusted anyways.

@jegres1709
Copy link

jegres1709 commented Apr 10, 2023

here are some entries from my configuration.yaml :

mqtt:
binary_sensor:
    - name: "local_D2 AC Output Enabled"
      object_id: local_d2_ac_enabled
      unique_id: local_d2_ac_enabled
      qos: 0
      state_topic: "/sys/80/R33XXXXXXXXXX/thing/property/post"
      value_template: >-
        {% set d = value_json['params']['cfgAcEnabled'] %}
        {% if d is defined %}{{(d==1)|iif('ON','OFF')}}{%endif%}

Problems:

  1. I have some troubles to get the right data (slave, main), because they appear in more then one block (e.g. bms, ems)
    1. outputWatts
      Solution: I took the value from moduleType 3, which is the inverter, like before. was not in the block, but seems to updating anyways
    - name: local_d2_power_export_ac
      unique_id: local_d2_power_export_ac
      state_topic: "/sys/80/R331XXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "W"
      value_template: >-
        {% if value_json['moduleType'] == 3 %}
          {{value_json['params']['outputWatts']}}    
        {% endif %}
  • f32ShowSoc
    Solution:
    - name: local_d2_power_state_of_charge_main
      unique_id: local_d2_power_state_of_charge_main
      state_topic: "/sys/80/R331XXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: >-
        {% if value_json['moduleType'] == 2 %}
          {{value_json['params']['f32ShowSoc']}}   
        {% endif %}

    - name: local_d2_power_state_of_charge_slave
      unique_id: local_d2_power_state_of_charge_slave
      state_topic: "/sys/80/R331XXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: >-
        {% if value_json['moduleType'] == 4 %}
          {{value_json['params']['f32ShowSoc']}}   
        {% endif %}

    - name: local_d2_power_state_of_charge_gesamt
      unique_id: local_d2_power_state_of_charge_gesamt
      state_topic: "/sys/80/R331XXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: "{{ value_json['params']['f32LcdShowSoc'] }}"
  • inputWatts
    Solution: same as for outputWatts. I took the value from moduleType 3, which is the inverter,
    - name: local_d2_power_import
      unique_id: local_d2_power_import
      state_topic: "/sys/80/R331XXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: W
      value_template: >-
        {% if value_json['moduleType'] == 3 %}
          {{value_json['params']['inputWatts']}}   
        {% endif %}
  • maxChargeSoc is not found. Should appear in emsStatus section.
    Solution: will be updated after manual change

  • dc24vState is not found. Should appear in mpptStatus section, but missing.
    Solution: it´s dcOutState now..

@Ne0-Hack3r
Copy link
Author

Just install the android app and put in your wifi-password and the ip of your mqtt server. its working on port 8883, so there is some config needed, if not already done. I set here my local mosquitto broker. Now Delta2 is sending the data directly to it.

Are you talking about the Ecoflow App itself or one of the other apps mentioned in the thread? And are you saying mosquito needs to be reconfigured to operate on port 8883, over TLS, instead of 1883 (no TLS) as I currently have it setup (by default)?

And by WiFi-password are you referring to the password for the ESSID (wLAN) the D2 is connected to, the credentials for the EcoFlow login (from the native app) or the password for MQTT credentials?

Might you be willing to share a redacted version of your mosquitto.conf?

Sorry if theses questions seem dense. I'm not that familiar with Python or Android development and haven't really had time to dig into this approach in depth. I read through the linked thread one time and didn't entirely follow the process to get D2 connected to mosquito. Once I've got that accomplished I think I should be okay as far as adapting my HA packages to work locally... so if you can assist me with the shortest, clearest path to that end it would be greatly appreciated.

I'll be happy to share the adapted YAML, etc. on my repo once I've got a handle on this approach... Hoping the same will work for SHP and DP as well...

Also: I'm assuming once D2 is talking to local MQTT broker (mosquito) it could then be bridged to the cloud MQTT if one wanted to allow for remote control or sending data to EcoFlow (basically the reverse of what we did in the first place to bridge public MQTT to private broker) ??

If I can gain 100% control locally via HA of all my stuff I'm not sure I'd ever choose to connect with the cloud server any longer. However, you never know if support, updates, etc. may be needed and there might be a need to either reset to "normal" or pass data through...

In reading through the thread it sounds like certificate checks may have to be bypassed and probing for firmware updates "faked" or "ignored" ??

@jegres1709
Copy link

jegres1709 commented Apr 11, 2023

here is a little manual:

  1. install the com.companyname.XamarinBluetoothScanner-Signed.apk from BluetoothScanner.zip from the other thread.
  2. activate bluetooth on your smartphone
  3. start the app
    1. select the device in the top
    2. full in your wifi-password, you want the device to be connected to (same network as your smartphone)
    3. fill in IP or name of the new MQTT-Broker
    4. fill in the mqtt-port
      1. port 1883 should work too, but i didn´t try it. It was not possible to change the port in the previous app by ipalchuk, but now it is
      2. for port 8883 your mqtt broker needs to be configured. i had to create a CA file, certificate file and a private key file.
        Require Certificate is disabled in my Mosquitto GUI config. i´ve not changed mosquitto.conf for now.
    5. select checkbox if you want to change the broker
    6. click on "SEND" in the top of the app
    7. Device should respond with "Res: True 53-33-53-11" and in the body you will see the new mqtt broker, if everything was ok. if you get "Res: false", then click again the "SEND" button.
    8. if you want to disable BLE for saving energy or to make it saver, you can send the comman 33-53-53-51 in the app and apply it with "SEND" button. BLE will be turned on again after you turn off/on your device
  4. check your MQTT broker for incoming messages from your device on the topic sys/80/R33XXXXXX/thing/property/post
    data comes in blocks :
Details

{
  "id": 6673458,
  "version": "1.0",
  "time": 6673458,
  "moduleType": 5,
  "typeCode": "mpptStatus",
  "params": {
    "inVol": 1939,
    "inWatts": 0,
    "outVol": 52385,
    "carOutVol": 56,
    "carState": 0,
    "dcChgCurrent": 8000,
    "beepState": 1,
    "cfgAcEnabled": 1,
    "cfgAcXboost": 1,
    "cfgAcOutFreq": 50,
    "cfgChgWatts": 100
  }
}

{
  "id": 7533768,
  "version": "1.0",
  "time": 7533768,
  "moduleType": 4,
  "typeCode": "bmsSlaveStatus",
  "params": {
    "soc": 44,
    "vol": 52732,
    "amp": 0,
    "f32ShowSoc": 43.9,
    "inputWatts": 0,
    "outputWatts": 0,
    "remainTime": 4322
  }
}
{
  "id": 6744148,
  "version": "1.0",
  "time": 6744148,
  "moduleType": 3,
  "typeCode": "invStatus",
  "params": {
    "invOutVol": 229091,
    "invOutAmp": 20
  }
}

{
  "id": 7259268,
  "version": "1.0",
  "time": 7259268,
  "moduleType": 2,
  "typeCode": "bmsStatus",
  "params": {
    "soc": 24,
    "vol": 52719,
    "minCellVol": 3292,
    "f32ShowSoc": 23.8,
    "inputWatts": 0,
    "outputWatts": 0
  }
}
{
  "id": 536890164,
  "version": "1.0",
  "time": 7857378,
  "moduleType": 2,
  "typeCode": "emsStatus",
  "params": {
    "bmsModel": 2,
    "lcdShowSoc": 35,
    "dsgRemainTime": 1270,
    "f32LcdShowSoc": 34.6,
    "paraVolMin": 51726,
    "paraVolMax": 53726,
    "minDsgSoc": 10,
    "minOpenOilEb": 0,
    "maxCloseOilEb": 100
  }
}
{
  "id": 7717168,
  "version": "1.0",
  "time": 7717168,
  "moduleType": 1,
  "typeCode": "pdStatus",
  "params": {
    "soc": 24,
    "wattsOutSum": 0,
    "wattsInSum": 0,
    "remainTime": -1281,
    "dcOutState": 0,
    "usb1Watts": 0,
    "typec1Watts": 0,
    "carWatts": 0,
    "standbyMin": 30,
    "lcdOffSec": 10
  }
}

here are all of my sensors from configuration.yaml that i´m using:
I copied almost all sensors from old config and only renamed them from sensor.d2 to sensor.local_d2 and reused them also for energy tracking etc for my tiny dashboard

Details

#local ecoflow
mqtt:
  sensor:
    - name: local_d2_power_export_ac
      unique_id: local_d2_power_export_ac
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "W"
      value_template: >-
        {% if value_json['moduleType'] == 3 %}
          {{value_json['params']['outputWatts']}}    
        {% endif %}

    - name: local_d2_power_export_car
      unique_id: local_d2_power_export_car
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: W
      value_template: "{{ value_json['params']['carWatts'] }}"

    - name: local_d2_power_charge_input
      unique_id: local_d2_power_charge_input
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      unit_of_measurement: "W"
      value_template: "{{ value_json['params']['cfgChgWatts'] }}"

    - name: local_d2_power_import
      unique_id: local_d2_power_import
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: W
      value_template: >-
        {% if value_json['moduleType'] == 3 %}
          {{value_json['params']['inputWatts']}}   
        {% endif %}

      # sensor for values of power output of Ecoflow D2 on AC
    - name: local_d2_power_export_gesamt
      unique_id: local_d2_power_export_gesamt
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: W
      value_template: >-
        {{ states('sensor.local_d2_power_export_ac')|float + states('sensor.local_d2_power_export_car')|float + states('sensor.local_d2_power_export_typec1')|float + states('sensor.local_d2_power_export_usb1')|float }}

      # sensor for values of power output of Ecoflow D2 on typec1
    - name: local_d2_power_export_typec1
      unique_id: local_d2_power_export_typec1
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: W
      value_template: "{{ value_json['params']['typec1Watts'] }}"

      # sensor for values of power output of Ecoflow D2 on usb1
    - name: local_d2_power_export_usb1
      unique_id: local_d2_power_export_usb1
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: W
      value_template: "{{ value_json['params']['usb1Watts'] }}"

      # sensor for values of charge limit of Ecoflow D2
    - name: local_d2_power_charge_limit
      unique_id: local_d2_power_charge_limit
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: "{{ value_json['params']['maxChargeSoc'] }}"

      # sensor for values of discharge limit of Ecoflow D2
    - name: local_d2_power_discharge_limit
      unique_id: local_d2_power_discharge_limit
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: "{{ value_json['params']['minDsgSoc'] }}"

      # sensor for values of state of charge of Ecoflow D2
    - name: local_d2_power_state_of_charge
      unique_id: local_d2_power_state_of_charge
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: >-
        {% if value_json['moduleType'] == 2 %}
          {{value_json['params']['f32ShowSoc']}}   
        {% endif %}

      # sensor for values of state of charge of Ecoflow D2
    - name: local_d2_power_state_of_charge_slave
      unique_id: local_d2_power_state_of_charge_slave
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: >-
        {% if value_json['moduleType'] == 4 %}
          {{value_json['params']['f32ShowSoc']}}   
        {% endif %}

      # sensor for values of state of charge of Ecoflow D2
    - name: local_d2_power_state_of_charge_gesamt
      unique_id: local_d2_power_state_of_charge_gesamt
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      qos: 0
      unit_of_measurement: "%"
      value_template: "{{ value_json['params']['f32LcdShowSoc'] }}"

binary_sensor:

    - name: "local_D2 AC Output Enabled"
      object_id: local_d2_ac_enabled
      unique_id: local_d2_ac_enabled
      qos: 0
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      value_template: >-
        {% set d = value_json['params']['cfgAcEnabled'] %}
        {% if d is defined %}{{(d==1)|iif('ON','OFF')}}{%endif%}

    - name: "local_D2 DC Output Enabled"
      object_id: local_d2_dc_enabled
      unique_id: local_d2_dc_enabled
      qos: 0
      state_topic: "/sys/80/R331XXXXXXXXXXX/thing/property/post"
      value_template: >-
        {% set d = value_json['params']['carState'] %}
        {% if d is defined %}{{(d==1)|iif('ON','OFF')}}{%endif%}

    - name: "local_D2 USB Output Enabled"
      object_id: local_d2_usb_enabled
      unique_id: local_d2_usb_enabled
      qos: 0
      state_topic: "/sys/80/R331ZEB4ZEAL0791/thing/property/post"
      value_template: >-
        {% set d = value_json['params']['dcOutState'] %}
        {% if d is defined %}{{(d==1)|iif('ON','OFF')}}{%endif%}


- platform: template
  switches:
      local_d2_ac_out:
        friendly_name: "local_d2 AC Output"
        value_template: "{{ states('binary_sensor.local_d2_ac_enabled') }}"
        turn_on:
          - service: mqtt.publish
            data:
              topic: /sys/80/R331ZEB4ZEAL0791/thing/property/set
              payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":5,"operateType":"acOutCfg","params":{"enabled":1,"out_voltage":-1,"out_freq":255,"xboost":255},"version":"1.0"}'
        turn_off:
          - service: mqtt.publish
            data:
              topic: /sys/80/R331ZEB4ZEAL0791/thing/property/set
              payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":5,"operateType":"acOutCfg","params":{"enabled":0,"out_voltage":-1,"out_freq":255,"xboost":255},"version":"1.0"}'
        icon_template: >-
          {% if is_state('binary_sensor.local_d2_ac_enabled','on') %}
            mdi:power-plug
          {% else %}
            mdi:power-plug-off
          {% endif %}

      local_d2_dc_out:
        friendly_name: "local_d2 DC Output"
        value_template: "{{ states('binary_sensor.local_d2_dc_enabled') }}"
        turn_on:
          - service: mqtt.publish
            data:
              topic: /sys/80/R331ZEB4ZEAL0791/thing/property/set
              payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":5,"operateType":"mpptCar","params":{"enabled":1},"version":"1.0"}'
        turn_off:
          - service: mqtt.publish
            data:
              topic: /sys/80/R331ZEB4ZEAL0791/thing/property/set
              payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":5,"operateType":"mpptCar","params":{"enabled":0},"version":"1.0"}'
        icon_template: >-
          {% if is_state('binary_sensor.local_d2_dc_enabled','on') %}
            mdi:power-plug
          {% else %}
            mdi:power-plug-off
          {% endif %}
          
      local_d2_usb_out:
        friendly_name: "local_d2 USB Output"
        value_template: "{{ states('binary_sensor.local_d2_usb_enabled') }}"
        turn_on:
          - service: mqtt.publish
            data:
              topic: /sys/80/R331ZEB4ZEAL0791/thing/property/set
              payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":1,"operateType":"dcOutCfg","params":{"enabled":1},"version":"1.0"}'
        turn_off:
          - service: mqtt.publish
            data:
              topic: /sys/80/R331ZEB4ZEAL0791/thing/property/set
              payload: '{"from":"HA","id":"{{999900000+(range(10000,99999)|random)}}","moduleType":1,"operateType":"dcOutCfg","params":{"enabled":0},"version":"1.0"}'
        icon_template: >-
          {% if is_state('binary_sensor.local_d2_usb_enabled','on') %}
            mdi:power-plug
          {% else %}
            mdi:power-plug-off
          {% endif %}


  # Sensor for Riemann sum of energy export of Ecoflow D2 MAIN AC (W -> Wh)
  - platform: integration
    source: sensor.local_d2_power_export_ac
    name: d2_energy_export_ac_sum
    unit_prefix: k
    round: 2
    method: left

  # Sensor for Riemann sum of energy import of Ecoflow D2 MAIN (W -> Wh)
  - platform: integration
    source: sensor.local_d2_power_import
    name: d2_energy_import_sum
    unit_prefix: k
    round: 2
    method: left

  # Sensor for Riemann sum of energy export gesamt of Ecoflow D2 MAIN (W -> Wh)
  - platform: integration
    source: sensor.local_d2_power_export_gesamt
    name: d2_power_export_gesamt_sum
    unit_prefix: k
    round: 2
    method: left

  # Sensor for Riemann sum of energy export of Ecoflow D2 MAIN Car (W -> Wh)
  - platform: integration
    source: sensor.local_d2_power_export_car
    name: d2_energy_export_car_sum
    unit_prefix: k
    round: 2
    method: left

  # Sensor for Riemann sum of energy export of Ecoflow D2 MAIN typec1 (W -> Wh)
  - platform: integration
    source: sensor.local_d2_power_export_typec1
    name: d2_energy_export_typec1_sum
    unit_prefix: k
    round: 2
    method: left

  # Sensor for Riemann sum of energy export of Ecoflow D2 MAIN usb1 (W -> Wh)
  - platform: integration
    source: sensor.local_d2_power_export_usb1
    name: d2_energy_export_usb1_sum
    unit_prefix: k
    round: 2
    method: left

utility_meter:
  d2_energy_import_daily:
    source: sensor.d2_energy_import_sum
    name: D2 Energy Import Daily
    cycle: daily
  d2_energy_export_gesamt_daily:
    source: sensor.d2_power_export_gesamt_sum
    name: D2 Energy Export gesamt Daily
    cycle: daily
  d2_energy_export_ac_daily:
    source: sensor.d2_energy_export_ac_sum
    name: D2 Energy Export AC Daily
    cycle: daily
  d2_energy_export_car_daily:
    source: sensor.d2_energy_export_car_sum
    name: D2 Energy Export Car Daily
    cycle: daily
  d2_energy_export_typec1_monthly:
    source: sensor.d2_energy_export_typec1_sum
    name: D2 Energy Export TypeC1 Monthly
    cycle: monthly
  d2_energy_export_usb1_monthly:
    source: sensor.d2_energy_export_usb1_sum
    name: D2 Energy Export USB1 Monthly
    cycle: monthly


if you open the official app, Delta2 will connect to the official server again and the procedure from point 3 needs to be repeated.

I'll be happy to share the adapted YAML, etc. on my repo once I've got a handle on this approach... Hoping the same will work for SHP and DP as well...

you could try it, if it also works with SHP. I have still no SHP at home, why I could´t test it :)

Also: I'm assuming once D2 is talking to local MQTT broker (mosquito) it could then be bridged to the cloud MQTT if one wanted to allow for remote control or sending data to EcoFlow (basically the reverse of what we did in the first place to bridge public MQTT to private broker) ??

ipalchuk mentioned it in his thread, that it could be better to bridge the connection to official mqtt server

If I can gain 100% control locally via HA of all my stuff I'm not sure I'd ever choose to connect with the cloud server any longer. However, you never know if support, updates, etc. may be needed and there might be a need to either reset to "normal" or pass data through...

that the same thing I think. For my usecase I don´t need the official server and the official app at all.

In reading through the thread it sounds like certificate checks may have to be bypassed and probing for firmware updates "faked" or "ignored" ??

thats also a point ipalchuk mentioned. i´m running this configuration since yesterday and yes the device is sending periodicaly his module versions etc., but I didn´t see any problems so far. if problems will appear due to this, we need to write a script for HA to answer some device requests.

@Ne0-Hack3r
Copy link
Author

@jegres1709 thanks for the detailed summary and example YAML... I'll start playing with this approach as soon as I can find some time...

One challenge I have is that my Android "devices" are virtual (emulation on PC) so I don't have a ready way to attempt this over BT... I'll have to see if I can side load that apk on an old fire tablet or something...

If just opening the native app resets the device to point back to the EF cloud server for WiFi that means that once this is setup you need to transition to using HA exclusively. That could be a pain for updates and native testing of new features... I'm wondering if there is some way to have HA "reset" this pointer under given conditions or even periodically... I can just see opening the native app to check something and then losing all subsequent data into HA until this is detected and reset... I suspect such a solution would involve using BT capabilities in HA (which would involve additional hardware for me since my HA instance is in a VM).

One interesting aspect of this is that EF could (if they choose) supply a local option for the official API that uses the same/similar set of MQTT/JSON topics/payloads as the cloud approach. An API that exposes the data we all care about the most that is accessible locally without internet or cloud server (and fully sanctioned by Ecoflow) is, I think, what everyone wants (at least those of us integrating with HA if not many others that would prefer a non-cloud-centric approach).

@SamA699
Copy link

SamA699 commented May 28, 2023

Hello, I am absolute newbie on github and no native speaker/writer .... sorry for my english :) ...
I'm interessted in local bluetooth access to my Ecoflow DELTA 2.
I now have Home-Assistant running in VM on my Synology (with a lot of Tasmota and Shelly device running well) and MQTT running.
I bought an M5-Stack-Atom lite with Bluetooth Proxy running. I've tested the BTEcowflow.exe on my Laptop and could become contact to my Delta 2 ... but now I don't know how to proceed. :(
It seems, that the M5 can't get contact to the Delta 2, nothing is shown in Home Assistant.
Have I to change something in the configuration.yaml?
What else I have to do? Please HELP! :)
My main interesst is, to show the accu state in HA with local access :)
If there is a way to do a local access with MQTT and Home-Assistant, this were great, also.

@SamA699
Copy link

SamA699 commented May 29, 2023

Can I please get access too @Ne0-Hack3r?

@SamA699
Copy link

SamA699 commented May 30, 2023

Hello @Ne0-Hack3r?,
follow your steps above I now have my D2 pointing to my local MQTT-Server on 1883, but i can not see any incomming message.
Can you please explain - for newiebs - how you finally got it to work without using TLS on Mosquitto and how you used the PowerShell script for the HTTP intercept and sent the following JSON in response to the D2 cert request??

These ar the loggs in mosquitto-broker:
New connection from 192.XXX.XXX.XXX:61135 on port 1883.
Client disconnected due to protocol error.

UPDATE: Thanks to @jegres1709 and his description I finally got it!
All extern connections to Ecoflow-Server are disabled by our Router!
Ecoflow Delta 2 runs local in Home-Assistant. Thanks to everybody!!!

@giovanne123
Copy link

@Ne0-Hack3r, can I please get access too to your repo/scripts... will start investigation for Delta 2 (Max and maybe Powerstream later).

@SamA699
Copy link

SamA699 commented Jun 2, 2023

interesting observation: I blocked the internet connection of my Delta2 in FritzBox router. Connected to the device with official app via BT, but the connection to my mqtt broker is still existing. so a prallel use of both methods is still possible as long as the D2 does not have an internet connection. Sure you can not connect with official app remotely, but it´s still enough to change some things like lab-functions etc.

So my needs are absolutely satisfied. My HA dashboard is working like a charme again and in a local network only. Exactly what I was looking for. With disabling of BT you would have much more security.

Screenshot_20230411_224540_Home Assistant.jpg

If you are interested in the code i could share this too.

@jegres1709 Könntest du mir bitte deinen Code zusenden? Hast du auch eine Möglichkeit gefunden, das Display zu dimmen?
Vielen Dank

@giovanne123
Copy link

interesting observation: I blocked the internet connection of my Delta2 in FritzBox router. Connected to the device with official app via BT, but the connection to my mqtt broker is still existing. so a prallel use of both methods is still possible as long as the D2 does not have an internet connection. Sure you can not connect with official app remotely, but it´s still enough to change some things like lab-functions etc.
So my needs are absolutely satisfied. My HA dashboard is working like a charme again and in a local network only. Exactly what I was looking for. With disabling of BT you would have much more security.
Screenshot_20230411_224540_Home Assistant.jpg
If you are interested in the code i could share this too.

@jegres1709 Könntest du mir bitte deinen Code zusenden? Hast du auch eine Möglichkeit gefunden, das Display zu dimmen? Vielen Dank

@jegres1709, I am interested in the code, too ;-)

@jegres1709
Copy link

sure. dashboard looks little different now. You will also need some HACS lovelace extension like "vertical-stack-in-card", mushroom-chips-card.

image

Details

type: custom:vertical-stack-in-card
cards:
  - type: custom:mushroom-template-card
    primary: >-
      {% if is_state('device_tracker.espressif', 'home') %}      Total: {{
      states('sensor.local_d2_power_state_of_charge_gesamt') }}% | Main: {{
      states('sensor.local_d2_power_state_of_charge') }}%  | Slave: {{
      states('sensor.local_d2_power_state_of_charge_slave') }}%  {% else %}
      Offline | Total: {{ states('sensor.local_d2_power_state_of_charge_gesamt')
      }}% {% endif %}
    secondary: >-
      {% if is_state('device_tracker.espressif', 'home') %} Outlet:
      {{states('switch.steckdose_ecoflow_switch') }}  |   Charge:
      {{states('sensor.local_d2_power_charge_input') }}W |   In:
      {{states('sensor.local_d2_power_import') }}W |   Out:
      {{states('sensor.local_d2_power_export_gesamt') }}W {% else %}  {% endif
      %}
    icon: >
      {% set battery_level =
      (states('sensor.local_d2_power_state_of_charge_gesamt') | int / 10) |
      round(0) | int * 10 %}

      {% if is_state('sensor.local_d2_power_charge_limit', '100' ) %}
        {% if battery_level > 0 %}
          mdi:battery-charging-{{ battery_level }}
        {% else %}
          mdi:battery-charging-outline
        {% endif %}
      {% else %}
        {% if battery_level == 100 %}
          mdi:battery
        {% elif battery_level > 0 %}
          mdi:battery-{{ battery_level }}
        {% else %}
          mdi:battery-alert-variant-outline
        {% endif %}
      {% endif %}

                    
    entity: switch.steckdose_ecoflow_switch
    icon_color: >-
      {% set battery_level =
      states('sensor.local_d2_power_state_of_charge_gesamt') | int %}

      {% if battery_level > 90 %} 
        green
      {% elif battery_level > 60 %}
        light-green
      {% elif battery_level > 50 %}
        lime
      {% elif battery_level > 40 %}
        yellow
      {% elif battery_level > 30 %}
        amber
      {% elif battery_level > 20 %}
        orange
      {% elif battery_level > 10 %}
        deep-orange
      {% else %}
        red
      {% endif %} 
                    
  - type: custom:mushroom-chips-card
    chips:
      - type: template
        entity: sensor.gesamtleistung_power
        content: '{{ states(entity) | round(1) }} W'
        icon: mdi:lightning-bolt
        icon_color: 72, 143, 194
        tap_action:
          action: more-info
      - type: template
        entity: sensor.shelly_25_channel_2_power
        content: '{{ states(entity) | round(1) }} W'
        icon: mdi:solar-power-variant
        icon_color: 255, 152, 0
        tap_action:
          action: more-info
    alignment: center
  - type: custom:mushroom-chips-card
    chips:
      - type: template
        entity: switch.local_d2_usb_out
        content: USB Out
        icon: mdi:usb-port
        icon_color: |
          {% if is_state('switch.local_d2_usb_out', 'on') %}
          green
          {% else %}
          red
          {% endif %}
        tap_action:
          action: toggle
      - type: template
        entity: switch.local_d2_ac_out
        content: AC Out
        icon: mdi:power-socket-de
        icon_color: |
          {% if is_state('switch.local_d2_ac_out', 'on') %}
          green
          {% else %}
          red
          {% endif %}
        tap_action:
          action: toggle
      - type: template
        entity: switch.local_d2_dc_out
        content: DC Out
        icon: mdi:current-dc
        icon_color: |
          {% if is_state('switch.local_d2_dc_out', 'on') %}
          green
          {% else %}
          red
          {% endif %}
        tap_action:
          action: toggle
    alignment: center
  - type: energy-distribution
    link_dashboard: false

@giovanne123
Copy link

giovanne123 commented Jun 2, 2023

sure. dashboard looks little different now. You will also need some HACS lovelace extension like "vertical-stack-in-card", mushroom-chips-card.

image

Details

Thanks, I will give it a try when my Delta 2 Max arrives and start looking in next step for Powerstream ;-)

@jegres1709, do I still need the access to the repo from @Ne0-Hack3r ? Unfortunatelly he does not respond, do you have the needed files/scripts/code elsewhere?

@jegres1709
Copy link

jegres1709 commented Jun 2, 2023

little offtopic:
if you don´t want to buy the powerstream and just want to realize zero feed-in with e.g. your balcony solar equipment and delta , you can take a look into my Node-Red automation:

image

Details

[{"id":"3df11bd37c6c8465","type":"tab","label":"Local_Ecoflow","disabled":false,"info":"","env":[]},{"id":"1a3268c94a11053c","type":"inject","z":"3df11bd37c6c8465","name":"Inject 2s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"4","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":100,"y":100,"wires":[["259a27be912a49c7"]]},{"id":"07a3379985dbbd30","type":"api-current-state","z":"3df11bd37c6c8465","name":"Gesamtleistung Power >= 60W","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"60","halt_if_type":"num","halt_if_compare":"gte","entity_id":"sensor.gesamtleistung_power","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":660,"y":200,"wires":[["3a8b14f36a8d6fd0","d75ead8b372b8d75"],["f27f5fa3c53c63ef","869a0ef1276fd201","8a2e81d3a7b9e4ac"]]},{"id":"f27f5fa3c53c63ef","type":"api-current-state","z":"3df11bd37c6c8465","name":"Gesamtleistung Power is not 0","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.gesamtleistung_power","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1360,"y":80,"wires":[["9fe603e31ff64650"],[]]},{"id":"9fe603e31ff64650","type":"change","z":"3df11bd37c6c8465","name":"","rules":[{"t":"set","p":"var1","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1630,"y":80,"wires":[["769b6589677608ac"]]},{"id":"769b6589677608ac","type":"change","z":"3df11bd37c6c8465","name":"sum","rules":[{"t":"set","p":"payload","pt":"msg","to":"$flowContext('var2') - $flowContext('var1') - $flowContext('var3') - 12","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1820,"y":140,"wires":[["b3eaca6f582a6939"]]},{"id":"f5e433fbab83c36d","type":"change","z":"3df11bd37c6c8465","name":"","rules":[{"t":"set","p":"var2","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1630,"y":140,"wires":[["769b6589677608ac"]]},{"id":"b3eaca6f582a6939","type":"switch","z":"3df11bd37c6c8465","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"50","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1990,"y":140,"wires":[["afe76745c25b66a7","6ad06aac758ad8f9","afbbe38d7823cd84"]]},{"id":"92ae397f3ed9544e","type":"api-current-state","z":"3df11bd37c6c8465","name":"Gesamtleistung Power <= -10W","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"-10","halt_if_type":"num","halt_if_compare":"lte","entity_id":"sensor.gesamtleistung_power","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":660,"y":100,"wires":[["35aa14b4512186e2"],[]]},{"id":"259a27be912a49c7","type":"api-current-state","z":"3df11bd37c6c8465","name":"Solar über 80W?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"80","halt_if_type":"num","halt_if_compare":"gte","entity_id":"sensor.shelly_25_channel_2_power","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":310,"y":100,"wires":[["92ae397f3ed9544e","07a3379985dbbd30"],["81954b8e9fe53334"]]},{"id":"36c18ebf2e5752cf","type":"api-current-state","z":"3df11bd37c6c8465","name":"Gesamtleistung Power >= 46W","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"46","halt_if_type":"num","halt_if_compare":"gte","entity_id":"sensor.gesamtleistung_power","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1290,"y":400,"wires":[["7f3db6f94e7878e3","3a8b14f36a8d6fd0","d75ead8b372b8d75"],[]]},{"id":"35aa14b4512186e2","type":"api-current-state","z":"3df11bd37c6c8465","name":"Steckdose an?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"true","halt_if_type":"bool","halt_if_compare":"is","entity_id":"switch.steckdose_ecoflow_switch","state_type":"habool","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":990,"y":20,"wires":[["f27f5fa3c53c63ef","869a0ef1276fd201","8a2e81d3a7b9e4ac"],["f532c0ec0a2c2a50"]]},{"id":"f532c0ec0a2c2a50","type":"api-call-service","z":"3df11bd37c6c8465","name":"Steckdose einschalten","server":"4b1912ab.49dcdc","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_on","areaId":[],"deviceId":["f6a442d2e24b369fe131e7349867f58e"],"entityId":["switch.steckdose_ecoflow_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":990,"y":100,"wires":[["35aa14b4512186e2"]]},{"id":"2c28723dd5d3773d","type":"api-call-service","z":"3df11bd37c6c8465","name":"Steckdose ausschalten","server":"4b1912ab.49dcdc","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_off","areaId":[],"deviceId":["f6a442d2e24b369fe131e7349867f58e"],"entityId":["switch.steckdose_ecoflow_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":2070,"y":540,"wires":[[]]},{"id":"3c02fe5bae2f7f3d","type":"api-current-state","z":"3df11bd37c6c8465","name":"Steckdose an?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"true","halt_if_type":"bool","halt_if_compare":"is","entity_id":"switch.steckdose_ecoflow_switch","state_type":"habool","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1820,"y":540,"wires":[["2c28723dd5d3773d"],[]]},{"id":"7f3db6f94e7878e3","type":"api-current-state","z":"3df11bd37c6c8465","name":"Akkustand über 20%?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"20","halt_if_type":"num","halt_if_compare":"gte","entity_id":"sensor.local_d2_power_state_of_charge_gesamt","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1560,"y":460,"wires":[["3c02fe5bae2f7f3d"],["8c4e7e32aef980ef"]],"icon":"font-awesome/fa-battery-1"},{"id":"ecd1f3716214a367","type":"api-call-service","z":"3df11bd37c6c8465","name":"Steckdose einschalten","server":"4b1912ab.49dcdc","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_on","areaId":[],"deviceId":["f6a442d2e24b369fe131e7349867f58e"],"entityId":["switch.steckdose_ecoflow_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":2060,"y":460,"wires":[[]]},{"id":"8c4e7e32aef980ef","type":"api-current-state","z":"3df11bd37c6c8465","name":"Steckdose an?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"true","halt_if_type":"bool","halt_if_compare":"is","entity_id":"switch.steckdose_ecoflow_switch","state_type":"habool","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1820,"y":460,"wires":[[],["ecd1f3716214a367"]],"icon":"node-red-contrib-home-assistant-websocket/ha-entity-switch.svg"},{"id":"a7f2d64034a97ac9","type":"change","z":"3df11bd37c6c8465","name":"","rules":[{"t":"set","p":"var3","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1630,"y":200,"wires":[["769b6589677608ac"]]},{"id":"e0f60c0b0a717f72","type":"api-current-state","z":"3df11bd37c6c8465","name":"local_D2 DC Out an?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"true","halt_if_type":"bool","halt_if_compare":"is","entity_id":"switch.local_d2_dc_out","state_type":"habool","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"seconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":140,"y":420,"wires":[["f584c8ff9d411a6f"],["66e088ed56775779"]]},{"id":"66e088ed56775779","type":"api-current-state","z":"3df11bd37c6c8465","name":"local_D2 USB Out an?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"true","halt_if_type":"bool","halt_if_compare":"is","entity_id":"switch.local_d2_usb_out","state_type":"habool","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"seconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":140,"y":500,"wires":[["06e506ab5feeb5d5"],["3c02fe5bae2f7f3d"]]},{"id":"9a75a9f05003c229","type":"template","z":"3df11bd37c6c8465","name":"Charge Limit auf 100% setzen","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"from\":\"HA\",\"id\":\"{{payload}}\",\"moduleType\":2,\"operateType\":\"upsConfig\",\"params\":{\"maxChgSoc\": 100},\"version\":\"1.0\"}","output":"yaml","x":2400,"y":100,"wires":[["885efd87fab7b55e"]]},{"id":"afe76745c25b66a7","type":"random","z":"3df11bd37c6c8465","name":"random","low":"999999999","high":"999900000","inte":"true","property":"payload","x":2150,"y":100,"wires":[["9a75a9f05003c229"]]},{"id":"472b5dd11e6977d9","type":"template","z":"3df11bd37c6c8465","name":"Charge Limit auf 20% setzen","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"from\":\"HA\",\"id\":\"{{payload}}\",\"moduleType\":2,\"operateType\":\"upsConfig\",\"params\":{\"maxChgSoc\": 20},\"version\":\"1.0\"}","output":"yaml","x":2040,"y":340,"wires":[["6b325b401f51da0e"]]},{"id":"d355547fdea62c1c","type":"random","z":"3df11bd37c6c8465","name":"random","low":"999999999","high":"999900000","inte":"true","property":"payload","x":1800,"y":340,"wires":[["472b5dd11e6977d9"]]},{"id":"afbbe38d7823cd84","type":"template","z":"3df11bd37c6c8465","name":"Charge Input auf Überschuss setzen","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"from\":\"HA\",\"id\":\"{{payload1}}\",\"moduleType\":5,\"operateType\":\"acChgCfg\",\"params\":{\"chgWatts\": {{payload}},\"chgPauseFlag\": 255},\"version\":\"1.0\"}","output":"yaml","x":2420,"y":180,"wires":[["fabb7237c56f5f31"]]},{"id":"6ad06aac758ad8f9","type":"random","z":"3df11bd37c6c8465","name":"random","low":"999910000","high":"999999999","inte":"true","property":"payload1","x":2160,"y":200,"wires":[["afbbe38d7823cd84"]]},{"id":"59494b3bd8ee69d3","type":"template","z":"3df11bd37c6c8465","name":"Charge Input auf 120W setzen","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"from\":\"HA\",\"id\":\"{{payload1}}\",\"moduleType\":5,\"operateType\":\"acChgCfg\",\"params\":{\"chgWatts\": 100,\"chgPauseFlag\": 255},\"version\":\"1.0\"}","output":"yaml","x":2050,"y":400,"wires":[["869d7d7198135d60"]]},{"id":"61fd7b66611b1d36","type":"random","z":"3df11bd37c6c8465","name":"random","low":"999910000","high":"999999999","inte":"true","property":"payload1","x":1800,"y":400,"wires":[["59494b3bd8ee69d3"]]},{"id":"81954b8e9fe53334","type":"api-current-state","z":"3df11bd37c6c8465","name":"local_D2 AC Out an?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"true","halt_if_type":"bool","halt_if_compare":"is","entity_id":"switch.local_d2_ac_out","state_type":"habool","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"seconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":140,"y":340,"wires":[["f249369efc5077a0"],["e0f60c0b0a717f72"]]},{"id":"3a8b14f36a8d6fd0","type":"api-current-state","z":"3df11bd37c6c8465","name":"Charge Limit auf 20% ?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"20","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.local_d2_power_charge_limit","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1570,"y":340,"wires":[[],["d355547fdea62c1c"]]},{"id":"d75ead8b372b8d75","type":"api-current-state","z":"3df11bd37c6c8465","name":"Charge Input auf 120W ?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"100","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.local_d2_power_charge_input","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1570,"y":400,"wires":[[],["61fd7b66611b1d36"]]},{"id":"6b325b401f51da0e","type":"mqtt out","z":"3df11bd37c6c8465","name":"","topic":"/sys/80/R331ZEB4ZEAL0791/thing/property/set","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"ec973db6908c7b25","x":2420,"y":340,"wires":[]},{"id":"869d7d7198135d60","type":"mqtt out","z":"3df11bd37c6c8465","name":"","topic":"/sys/80/R331ZEB4ZEAL0791/thing/property/set","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"ec973db6908c7b25","x":2420,"y":400,"wires":[]},{"id":"885efd87fab7b55e","type":"mqtt out","z":"3df11bd37c6c8465","name":"","topic":"/sys/80/R331ZEB4ZEAL0791/thing/property/set","qos":"0","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"ec973db6908c7b25","x":2780,"y":100,"wires":[]},{"id":"fabb7237c56f5f31","type":"mqtt out","z":"3df11bd37c6c8465","name":"","topic":"/sys/80/R331ZEB4ZEAL0791/thing/property/set","qos":"0","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"ec973db6908c7b25","x":2780,"y":180,"wires":[]},{"id":"869a0ef1276fd201","type":"api-current-state","z":"3df11bd37c6c8465","name":"local_D2 Power Import not 1","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"1","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.local_d2_power_import","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1360,"y":140,"wires":[["f5e433fbab83c36d"],[]]},{"id":"8a2e81d3a7b9e4ac","type":"api-current-state","z":"3df11bd37c6c8465","name":"local_D2 Power Export not 1","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"1","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.local_d2_power_export_gesamt","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1360,"y":200,"wires":[["a7f2d64034a97ac9"],[]]},{"id":"a9897250059f3768","type":"api-current-state","z":"3df11bd37c6c8465","name":".local_d2_power_export_ac for 15min 0W ?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.local_d2_power_export_ac","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"15","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":690,"y":340,"wires":[["6810a6a577a18f7b"],["36c18ebf2e5752cf"]]},{"id":"6810a6a577a18f7b","type":"api-call-service","z":"3df11bd37c6c8465","name":"D2 AC off","server":"4b1912ab.49dcdc","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.local_d2_ac_out"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1020,"y":340,"wires":[["36c18ebf2e5752cf"]]},{"id":"a6362d9a7c7f9a7d","type":"api-current-state","z":"3df11bd37c6c8465","name":".local_d2_power_export_dc for 15min 0W ?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.local_d2_power_export_car","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"15","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":690,"y":420,"wires":[["9c1cb22f4c2517e9"],["36c18ebf2e5752cf"]]},{"id":"9c1cb22f4c2517e9","type":"api-call-service","z":"3df11bd37c6c8465","name":"D2 DC off","server":"4b1912ab.49dcdc","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.local_d2_dc_out"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1020,"y":420,"wires":[["36c18ebf2e5752cf"]]},{"id":"be0ee2492058d92d","type":"api-current-state","z":"3df11bd37c6c8465","name":".local_d2_power_export_USB C for 15min 0W ?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.local_d2_power_export_typec1","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"15","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":700,"y":480,"wires":[["bd2989e7f11c24e0"],["36c18ebf2e5752cf"]]},{"id":"9ac2f77dca105360","type":"api-call-service","z":"3df11bd37c6c8465","name":"D2 USB off","server":"4b1912ab.49dcdc","version":5,"debugenabled":false,"domain":"homeassistant","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.local_d2_usb_out"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1030,"y":500,"wires":[["36c18ebf2e5752cf"]]},{"id":"bd2989e7f11c24e0","type":"api-current-state","z":"3df11bd37c6c8465","name":".local_d2_power_export_USB3 for 15min 0W ?","server":"4b1912ab.49dcdc","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.local_d2_power_export_usb1","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"num"}],"for":"15","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":700,"y":520,"wires":[["9ac2f77dca105360"],["36c18ebf2e5752cf"]]},{"id":"06e506ab5feeb5d5","type":"delay","z":"3df11bd37c6c8465","name":"","pauseType":"delay","timeout":"1700","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":400,"y":500,"wires":[["be0ee2492058d92d"]]},{"id":"f249369efc5077a0","type":"delay","z":"3df11bd37c6c8465","name":"","pauseType":"delay","timeout":"1700","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":400,"y":340,"wires":[["a9897250059f3768"]]},{"id":"f584c8ff9d411a6f","type":"delay","z":"3df11bd37c6c8465","name":"","pauseType":"delay","timeout":"1700","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":400,"y":420,"wires":[["a6362d9a7c7f9a7d"]]},{"id":"4b1912ab.49dcdc","type":"server","name":"Home Assistant","addon":true},{"id":"ec973db6908c7b25","type":"mqtt-broker","name":"","broker":"192.168.2.147","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]


@jegres1709
Copy link

jegres1709 commented Jun 2, 2023

sure. dashboard looks little different now. You will also need some HACS lovelace extension like "vertical-stack-in-card", mushroom-chips-card.
image
Details

Thanks, I will give it a try when my Delta 2 Max arrives and start looking in next step for Powerstream ;-)

@jegres1709, do I still need the access to the repo from @Ne0-Hack3r ? Unfortunatelly he does not respond, do you have the needed files/scripts/code elsewhere?

code for what? the code for the dashboard is in the "Details"
do you need the entries for your configuration.yaml in HA? --> #6 (comment)

@giovanne123
Copy link

giovanne123 commented Jun 2, 2023

sure. dashboard looks little different now. You will also need some HACS lovelace extension like "vertical-stack-in-card", mushroom-chips-card.
image
Details

Thanks, I will give it a try when my Delta 2 Max arrives and start looking in next step for Powerstream ;-)
@jegres1709, do I still need the access to the repo from @Ne0-Hack3r ? Unfortunatelly he does not respond, do you have the needed files/scripts/code elsewhere?

code for what? the code for the dashboard is in the "Details" do you need the entries for your configuration.yaml in HA? --> #6 (comment)

Thanks, yes I think that is the part where I need to dig deeper after I have my Delta available.
(HA Dashboard is clear but what I meant was the code to generate the sensors/entities used in HA (Dashboard) but that is in your explanation with switching the mqtt broker 👍 )

@Ne0-Hack3r
Copy link
Author

SamA699

Done

@Ne0-Hack3r
Copy link
Author

giovanne123

Done

@radeonorama
Copy link

radeonorama commented Oct 18, 2023

Hey @Ne0-Hack3r , could you send me an invite. I'd really like to have my delta connecting to a local mqtt. I have major connectivity issues where I'm located typically off grid so has integration becomes a pain. Thanks!

@Ne0-Hack3r
Copy link
Author

@radeonorama - invite sent... bear in mind that Delta 2 and Delta 2 Max are the only Delta models that can be hacked, over BLE, to do local MQTT and that process now requires a BLE auth packet to be formed and sent upon connection. The Delta Pro, Smart Home panel (and I presume the Delta Mini/Max) communicate with the back end over TCP via byte stream which, without an open port, is a problem. I've still found no way to gain 'local' control of my SHP (only MQTT via the cloud server).

@radeonorama
Copy link

Thanks v much for this! The dashboards you have made available are amazingly detailed and kick the sh*t out of the EF official app data!

I just need to find the time to do the same with the wave 2!

@Yrkis
Copy link

Yrkis commented Nov 13, 2023

Hey @Ne0-Hack3r , could you send me an invite.

@Ne0-Hack3r
Copy link
Author

@Yrkis - done.

@mmiller7
Copy link

mmiller7 commented Dec 3, 2023

I've started down the road of trying to integrate Delta 2 data with HA via MQTT...

[...]

It appears the D2 works differently than the DP. The DP will send a large message with the status of 'everything' for a given module (bmsMaster, ems, inv, ...) on a fairly frequent basis. SHP appears to operate in a similar way. The D2 appears to send only small messages with what seem to be status "updates" for specific data points which would seem to imply the app gets the status of 'everything' when it opens and then only receives update messages past that point... I'm still new to HA and MQTT and I'm not an "app developer" so some of my speculations here (based on initial observation) may well be incorrect...

For DP and SHP we can create automations in HA to trigger on specific payloads for specific params in messages such as "bmsMaster.errCode = 0" (for DP) or "id = 2" (for SHP) and, based on those triggers, re-publish the entire message to a unique sub-topic (such as ../SHP/circuits) then use an mqtt sensor to pull all the key/value pairs into attributes of an entity in HA. Once those attributes exist in HA custom sensors can be made to track specific attributes (such as sensor.shp_circuit_1_power) or the attribute values can be used directly in various places like {{ state_attr('sensor.shp_circuits','infoList')[10].chWatt | round(0) }}

For D2, the messages arrive with a unique "moduleType" but may contain one or several different parameters... The moduleType could be used by an automation to re-publish to a unique topic per module... but at that point I'm a bit lost in terms of how to structure mqtt sensors to pull the specific data/attributes into HA...

Perhaps someone here with more HA/MQTT background can suggest the best approach?

I'm still VERY early in the process, but this is so far matching up very well with my newly acquired River 2 (base model) unit.

And yeah...it seems VERY different from my Delta Max 2000 (original, not "2") which closely followed the Delta Pro implementation

@mmiller7
Copy link

mmiller7 commented Dec 3, 2023

Does the Delta2 take a MQTT message for setting the RTC from the app?

I'd say 90+% of this Delta 2 thread has applied to my River 2, but I'm stuck with the message where the app sets the clock which seems to be potentially tied to getting correct responses back from the River 2...maybe related to its schedule/automation portion of its responses?

{"from":"Android","id":"127261337","moduleType":5,"operateType":"setRtcTime","params":{"sec":12,"min":38,"week":7,"month":12,"hour":0,"year":2023,"day":3},"version":"1.0"}

Specifically there is a "week" field that was "1" on December 2nd and is now "7" on December 3rd. Its clearly not "week of year" nor "week of month", and I am unsure what it might be.

@Ne0-Hack3r
Copy link
Author

Ne0-Hack3r commented Dec 6, 2023

Does the Delta2 take a MQTT message for setting the RTC from the app?

I'd say 90+% of this Delta 2 thread has applied to my River 2, but I'm stuck with the message where the app sets the clock which seems to be potentially tied to getting correct responses back from the River 2...maybe related to its schedule/automation portion of its responses?

{"from":"Android","id":"127261337","moduleType":5,"operateType":"setRtcTime","params":{"sec":12,"min":38,"week":7,"month":12,"hour":0,"year":2023,"day":3},"version":"1.0"}

Specifically there is a "week" field that was "1" on December 2nd and is now "7" on December 3rd. Its clearly not "week of year" nor "week of month", and I am unsure what it might be.

@mmiller7 the "week" is "day of week" but is a bit unintuitive...

"week": {{now().isoweekday() + 1 if now().weekday() < 6 else 1}},

I have not found it to be necessary, thus far, with D2, D2M, or DP to post this RTC payload... For D2, data is continually posted to the /app/device/property/{sn} topic with status of sensors (as they "change")... You can still post to ../get for 'latestQuotas' and a paylod with status of all sensors will be posted, by the device, to ../get_reply

I am not on the latest firmware for D2/D2M as I hacked mine over BLE to report directly to my local MQTT. The prior update added a BLE auth packet that had to be reverse engineered and this next update may also add some form of encryption (at least for D2M)... seems like it is getting harder over time to keep up this reverse engineering game...

@mmiller7
Copy link

For D2, data is continually posted to the /app/device/property/{sn} topic with status of sensors (as they "change")

Odd, mine doesn't seem to do this unless the app is open.

I'm wondering if this is a firmware change, since my Delta Max 2000 used to publish on-change to the data/property one but after an update last night no longer publishes unless the app is open, which is the same behavior I see with the D2.

I'm not using Bluetooth, just WiFi thru their servers...it does seem like things are changing a fair bit over time.

Might have to modify mine so that it attempts to query using the latestquotas...I was hoping to minimize spamming their servers by letting the data flow in on its own.

@Ne0-Hack3r
Copy link
Author

For D2, data is continually posted to the /app/device/property/{sn} topic with status of sensors (as they "change")

Odd, mine doesn't seem to do this unless the app is open.

I'm wondering if this is a firmware change, since my Delta Max 2000 used to publish on-change to the data/property one but after an update last night no longer publishes unless the app is open, which is the same behavior I see with the D2.

I'm not using Bluetooth, just WiFi thru their servers...it does seem like things are changing a fair bit over time.

Might have to modify mine so that it attempts to query using the latestquotas...I was hoping to minimize spamming their servers by letting the data flow in on its own.

@mmiller7 - If you are using the native app and then closing it that will keep future updates from posting to the broker unless you restart the broker after the app is closed.

@harambe88
Copy link

@Ne0-Hack3r, would you please give me access to your repo? I have been looking for accessing Ecoflow Delta 2 for a long time. You've done an amazing work!! Thank you!

@Ne0-Hack3r
Copy link
Author

@harambe88

Done...

Note that https://github.com/tolwi/hassio-ecoflow-cloud is full HACS integration that has support for Delta 2 as well.

Also, be aware that some of the hacks to obtain local control of D2 and D2M may no longer work with later firmware so cloud based MQTT may be the only solution moving forward. I have not had time to look into what roadblocks new firmware introduces on the BLE side or what it might take to overcome those (assuming it is even possible).

And, finally, you may also want to know that the official EcoFlow supported API is now public and includes Delta 2 as well.

More info here
https://developer.ecoflow.com/

Facebook group here
https://www.facebook.com/groups/1405868123482532

@harambe88
Copy link

@Ne0-Hack3r Thank you very much!

@viguera
Copy link

viguera commented Apr 21, 2024

@Ne0-Hack3r - can you please provide access to your repo? I'm working on setting up my SHP + dual DPs + HA.

Thanks.

@Ne0-Hack3r
Copy link
Author

viguera

Apologies. I have been away from GitHub for a while. Invite sent.

@viguera

@vxiProg
Copy link

vxiProg commented Oct 13, 2024

@Ne0-Hack3r Could you please invite me to your repo? Much appreciated, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests