tolwi / hassio-ecoflow-cloud

EcoFlow Cloud Integration for Home Assistant
281 stars 47 forks source link

Addition of Smart Home Panel #217

Open Ne0-Hack3r opened 4 months ago

Ne0-Hack3r commented 4 months ago

These changes are based on the branch created by @tolwi 3 months ago but with the changes incorporated into the current main branch. I have tested against my own SHP running firmware version 6.0.0.25 on Home Assistant Core 2024.2.2 with Home Assistant OS 11.5

This includes all the main sensors but does not include power sensors that are derived from calculations of other sensors or cumulative energy sensors for use with the energy dashboard that source from the power sensors for individual circuits as highlighted on the image below.

image

We can discuss how best to incorporate both the circled energy sensors which are reported by the SHP itself as well as the calculated power and energy sensors in this integration.

Also: There is a bit of an issue with the charge/discharge sliders getting set to an initial value upon startup (or restart of HA). The SHP does not appear to report those readings except when they change. My workaround for the YAML package was an automation that would post that information to a separate ../status topic with a retained flag and then source the state for those sliders from there...

  - id: 'EF_SHP_Limits'
    alias: SHP - Limits
    description: 'Re-Publish SHP Charge/Discharge Limits to new Topic'
    mode: single
    trigger:
    - platform: mqtt
      topic: ecoflow/SHP/data
      payload: 30
      value_template: '{{ value_json.params[''id''] }}'
    condition: []
    action:
    - service: mqtt.publish
      data:
        topic: ecoflow/SHP/limits
        payload: '{{trigger.payload}}'
        retain: true

This "works" because I am using a local mosquito broker but it is not ideal and it not the proper approach for an integration. Perhaps we can discuss how best to address this. One possible solution is to initially send a request for 'latestQuotas' on ../get and then parse the ../get_reply to set initial values. This did not seem to be easily accomplished within a YAML package. Perhaps it is more easily done within an integration? I am new to coding for an integration so eager to both learn and contribute.

Ne0-Hack3r commented 3 months ago

Examples of calculated power sensors from my YAML package: ** wondering the best way to add these to the integration.

template:
  - sensor:
    - name: "SHP Grid Power"
      unique_id: shp_grid_power
      unit_of_measurement: "W"
      device_class: power
      state_class: measurement
      state: >-
        {% if is_state('binary_sensor.shp_grid_available','on') %}
          {% set home = int(states('sensor.shp_all_ch_power')) %}
          {% set pro1 = int(states('sensor.shp_pro1_power'))   %}
          {% set pro2 = int(states('sensor.shp_pro2_power'))   %}
          {% if is_state('binary_sensor.shp_pro1_ac_charging','on') %}{% set pro1 = (pro1 * -1) %}{%endif%}
          {% if is_state('binary_sensor.shp_pro2_ac_charging','on') %}{% set pro2 = (pro2 * -1) %}{%endif%}
          {{ home - pro1 - pro2 }}
        {% else %}
          0
        {% endif %}

    - name: "SHP ALL PRO Output"
      unique_id: shp_all_pro_output
      unit_of_measurement: "W"
      device_class: power
      state_class: measurement
      state: >-
        {{(int(states('sensor.shp_pro1_output'))+int(states('sensor.shp_pro2_output')))|round(0)}}

    - name: "SHP ALL PRO Charging Power"
      unique_id: shp_all_pro_charging_power
      unit_of_measurement: "W"
      device_class: power
      state_class: measurement
      state: >-
        {{int(states('sensor.shp_pro1_charging_power'))+int(states('sensor.shp_pro2_charging_power'))}}

mqtt:
  sensor:
  - name: "SHP ALL Circuit Power"
    object_id: shp_all_ch_power
    unique_id: shp_all_ch_power
    unit_of_measurement: "W"
    device_class: power
    state_class: measurement
    state_topic: "ecoflow/SHP/data"
    qos: 0
    value_template: >-
      {% set l = value_json.params.infoList[:10] %}
      {% if l is defined %}{{l|sum(attribute='chWatt')|round(0)}}{%else%}{{this.state}}{%endif%}

Example Calculated Energy Sensors (riemann sum integration) ** Not sure how to add creation of these "helpers" to the integration or if there is a better approach.

sensor: 
- name: SHP Circuit 1 Energy
  unique_id: shp_ch1_energy
  source: sensor.shp_ch1_power
  round: 3
  platform: integration
  unit_prefix: k

- name: SHP Circuit 2 Energy
  unique_id: shp_ch2_energy
  source: sensor.shp_ch2_power
  round: 3
  platform: integration
  unit_prefix: k

  . . .

- name: SHP Grid Energy
  unique_id: shp_grid_energy
  source: sensor.shp_grid_power
  round: 3
  platform: integration
  unit_prefix: k

Example of energy sensors reported by SHP itself

  - name: SHP Grid kWh Today
    object_id: shp_grid_kwh
    unique_id: shp_grid_kwh
    unit_of_measurement: "kWh"
    device_class: energy
    state_class: total
    state_topic: "ecoflow/SHP/data"
    qos: 0
    value_template: >-
      {% set d = value_json.params['gridDayWatth'] %}
      {% if d is defined %}{{(d/1000)|round(2)}}{%else%}{{this.state}}{%endif%}

  - name: SHP Battery kWh Today
    object_id: shp_batt_kwh
    unique_id: shp_batt_kwh
    unit_of_measurement: "kWh"
    device_class: energy
    state_class: total
    state_topic: "ecoflow/SHP/data"
    qos: 0
    value_template: >-
      {% set d = value_json.params['backupDayWatth'] %}
      {% if d is defined %}{{(d/1000)|round(2)}}{%else%}{{this.state}}{%endif%}

  - name: SHP Circuit 1 Battery Energy
    object_id: shp_ch1_batt_energy
    unique_id: shp_ch1_batt_energy
    state_topic: "ecoflow/SHP/data"
    qos: 0
    device_class: energy
    unit_of_measurement: "Wh"
    state_class: total
    value_template: >-
      {% if value_json.params.id == 49 %}
        {% set d = value_json.params.watth[0] %}
        {% if d is defined %}
          {{d|sum|round(0)}}
        {%else%}{{this.state}}{%endif%}
      {%else%}{{this.state}}{%endif%}
    last_reset_value_template: >-
      {% if value_json.params.id == 49 %}
        {% set t = value_json.params.rtc %}
        {% if t is defined %}
          {{(as_timestamp(strptime(t,'%Y-%m-%d'))|as_datetime()).isoformat()}}
        {%else%}{{state_attr(this.state,'last_reset')}}{%endif%}
      {%else%}{{state_attr(this.state,'last_reset')}}{%endif%}

   . . . 

The above can be added as normal Energy Entities though, if we choose to add the battery energy per circuit, that may require some customization if we want to preserve the RTC timestamp reported by the SHP for those sensors.

Ne0-Hack3r commented 3 months ago

@tolwi

I'm getting a strange issue when trying to change the value on the selects for circuit mode:

2024-03-07 00:45:39.167 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'powCh': 0, 'ctrlSta': 0, 'ctrlMode': 0, 'priority': 6}
2024-03-07 00:45:39.167 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'powCh': 0, 'ctrlSta': 0, 'ctrlMode': 0, 'priority': 7}
2024-03-07 00:45:39.167 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'powCh': 1, 'ctrlSta': 0, 'ctrlMode': 0, 'priority': 8}
2024-03-07 00:45:39.168 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'powCh': 1, 'ctrlSta': 0, 'ctrlMode': 0, 'priority': 9}
2024-03-07 00:45:39.168 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'powCh': 1, 'ctrlSta': 0, 'ctrlMode': 0, 'priority': 2}
2024-03-07 00:45:39.168 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'powCh': 1, 'ctrlSta': 0, 'ctrlMode': 0, 'priority': 4}
2024-03-07 00:45:39.168 DEBUG (MainThread) [custom_components.ecoflow_cloud.devices.smart_home_panel] ModeDictSelectEntity._update_value = {'sta': 1, 'ctrlMode': 1}
if self._update_value(values[0].value):
File "/config/custom_components/ecoflow_cloud/devices/smart_home_panel.py", line 33, in _update_value
return super()._update_value({"sta": val['ctrlSta'], "ctrlMode": val['ctrlMode']})

I'm not quite sure I'm understanding the flow here but it appears that both updates from the device and the change to the displayed value of the select are being processed by this same piece of code but the key names are different between what is used in the device update and the payload posted to ../set

Any suggestions on how to reconcile this?

nickwinger commented 1 week ago

Waiting for the SHP Integration :) will it be possible to switch the delta pros charging on/off ?

parkee commented 1 week ago

Any chance this is going to be merged soon? Any help is needed on this? I really need the integration so I'll try to fork the repo and run it on my HA over the weekend.

nickwinger commented 1 week ago

FYI: I just managed to setup a MQTT SmartHomePanel Sensor in HomeAssistant with the official way.

Just Setup the MQTT Integration with the server (mqtt-e.ecoflow.com, port: 8883), certificateAccount etc. and then put this in your configuration.yml:

(This is an example of extracting the battery information from that quota json) mqtt: sensor:

parkee commented 1 week ago

Thank you so much. I'll check it out as well. Honestly, all I really need is to be able to turn on/off EPS based on another sensor and charge batteries based on a sensor, schedule, and battery level. The only issue I see is that you can't have more than 1 MQTT configuration, so I believe the bridge is needed since I already use MQTT locally.

nickwinger commented 1 week ago

@parkee I'm new to MQTT, but as far as i understood if you want to have more MQTT Server/Sources you would have to use the Mosquitto-Broker Add-On ?

nickwinger commented 1 week ago

@parkee i just switched the EPS Mode on mine

If you have setup Ecoflow in the HomeAssistant MQTT Integration like i did, then you can try to publish in the Configuration Page:

Topic: /open/${certificateAccount}/${sn}/set

PayLoad: { "id": 123456789, "moduleType": 1, "operateType": "TCP", "version": "1.0", "sn": "SP10ZAW5ZE9E0052", "params": { "cmdSet": 11, "id": 24, "eps": 0 } }

parkee commented 1 week ago

Yea, I guess so. I'm already using it since my energy meter shares the data via MQTT. I just need to read how to set up the bridge so I can integrate my Ecoflow SHP as well.

Thank you so much for your tips. I'm gonna play with it either this evening or this weekend. This PR also contains a lot of useful information on how to interact with the panel.

nickwinger commented 1 week ago

@parkee

I setup a switch for the Channel 5 to go Grid mode on/off: (you can do the same for the EPS, however in my get quota i don't find the eps entry like in the documentation, don't know why):

SN_FOR_SHP = SerialNumber of SmartHomePanel certificateAccount = i used the JAVA Examples from the Ecoflow-Site to retrieve the certificateAccount in the response to "getMQTTCertification"

`mqtt:

parkee commented 1 week ago

@nickwinger I'll have to wait a bit. I applied to get access to the dev portal, but it says I need to wait up to 5 working days. I've never seen anything like this before. Usually, API access is instant. But since there's an official API now (I am not sure when it was introduced, but I'm pretty sure it didn't exist a year ago), I believe creating a completely new integration would make sense. Let's see if I can put enough effort into it.

Nid01 commented 1 week ago

@nickwinger I'll have to wait a bit. I applied to get access to the dev portal, but it says I need to wait up to 5 working days. I've never seen anything like this before. Usually, API access is instant. But since there's an official API now (I am not sure when it was introduced, but I'm pretty sure it didn't exist a year ago), I believe creating a completely new integration would make sense. Let's see if I can put enough effort into it.

@Mark-Hicks created the repository https://github.com/Mark-Hicks/ecoflow-api and already shared some progress on Facebook https://www.facebook.com/groups/1405868123482532/permalink/1628868904515785 and I am also working on my attempt in https://github.com/Nid01/EcoFlow-IoT-Open/tree/EcoFlow_IoT_Open. When I'm focused enough in the next few days I should be able to implement the configuration of device parameters.

nickwinger commented 1 week ago

Yes, the official API is nice and beside the lack of good documentation and a way to get the certificate Account without having to run the java example it works very good. The only problem i see is as with the tuya stuff, being forced to go over the cloud having an internet connection instead of communicating with the device locally. In an emergency event, what the smart home panel was designed for, some may not have internet for some time.

Mark-Hicks commented 1 week ago

Yes, the official API is nice and beside the lack of good documentation and a way to get the certificate Account without having to run the java example it works very good. The only problem i see is as with the tuya stuff, being forced to go over the cloud having an internet connection instead of communicating with the device locally. In an emergency event, what the smart home panel was designed for, some may not have internet for some time.

Example code is here: https://github.com/Mark-Hicks/ecoflow-api-examples

All Ecoflow devices that use the app can connect using Bluetooth or ad hoc WiFi and SHP will function as configured in an outage. That doesn't necessarily help in terms our own integrations depending on the Ecoflow cloud API server but, unfortunately, Ecoflow has never shown any interest in creating a local TCP/BLE base API.