DEYE ESP GBB OPTIMIZER ver 0.02

Opis integracjii GbbOptimizer z Homeassistant
Zakładam, że Homeassistant potrafisz obsługiwać w stopniu średniozaawansowanym. Masz uruchomione/zainstalowane wszystkie dodatki (miedzy innymi Mosquitto Broker. Umiesz w ESP itp.)

Integracja opiera się na  komunikacji Gbb <-> HA poprzez protokół MQTT

Pierwsza czynność to uruchomienie komunikacji z MQTT
Adresy serwerów MQTT: https://gbboptimizer.gbbsoft.pl/Manual?Filters.PageNo=14
W HA stwórz plik /share/mosquitto/GbbOptimizer.conf (nazwa pliku nie ma znaczenia. Możesz mieć jeden plik dla kilku integracji (np z serwerem Supla itp)

*****************************************************************
connection GbbOptimizer_<plantID>
remote_username <plantID>
remote_password <plantToken>
address <adres mqtt patrz tutaj>:8883
bridge_capath /etc/ssl/certs
 topic # both 2 ha_gbb/ <plantID>/ha_gbb/
*****************************************************************
gdzie <plantID> zastąp numerem PlantId a <plantToken> zastąp PlantToken (bez znaków "<" ani ">")

Poprawnie napisany plik, po restarcie brokera pokaże nam topic ha_gbb/ESS (pod warunkiem, że odczekałeś aż GbbOptimizer coś do Ciebie wysłał (poprawnie skonfigurowany GbbOptimizer wysyła dane co godzinę).

Kolejny etap to napisanie automatyzacji, która wyśle dane z naszej instalacji do GbbOptimizera.
Wysyłamy obowiązkowo:
"soc_perc" (lub "V" jeżeli zaznaczono 'Steruj poprzez V a nie przez SOC (Deye)')
"loads_total_kWh" 
"fromgrid_total_kWh"
"togrid_total_kWh"
"pv_total_kWh"
Dodatkowo możemy „poinformować” GbbOptimizer o dodatkowych obciążeniach naszego domu, których system nie będzie brał pod uwagę w obliczeniach zużycia.
"ev_charge_total_kWh" (ładowanie auta)
"hp_total_kWh" (pompa ciepła)
"other1_total_kWh” (auto żony)
"other2_total_kWh" (wóz sąsiada)

Kod automatyzacji:
*****************************************************************
alias: GbbOptimizer - publikacja mqtt
description: ""
trigger:
  - platform: time_pattern
    seconds: "1"
action:
  - service: mqtt.publish
    data:
      qos: "0"
      retain: false
      payload_template: |2-
                {
                  "loads_total_kWh": {{ states.sensor.sunsynk_day_load_power.state | float(-1)  }},
                  "fromgrid_total_kWh": {{ states.sensor.sunsynk_day_grid_import.state | float(-1)  }},
                  "pv_total_kWh": {{ states.sensor.sunsynk_day_pv_energy.state | float(-1) }},
                  "soc_perc": {{ states.sensor.sunsynk_battery_soc.state | float(-1) }},
                  "togrid_total_kWh": {{ states.sensor.sunsynk_day_grid_export.state | float(-1) }},
                  "other2_total_kWh": {{ states.sensor.gosund_1_energy_today.state | float(-1) }},
                  "other1_total_kWh": {{ states.sensor.ygrek_prezesa_energy_added.state | float(-1) }},
                  "ev_charge_total_kWh": {{ states.sensor.smartwb_energy.state | float(-1) }},
                  "hp_total_kWh": {{ states.sensor.piec_zuzycie_dzienne.state | float(-1) }},
                }
      topic: ha_gbb/sensor
*****************************************************************
Automatyzacja co minutę wyśle dane z naszej instalacji do GbbOptimizera. Encje musisz podmienić na swoje ale jest duża szansa, że jeśli używasz kodu do ESP ze strony hybrydaplus.pl to będą się zgadzać.

Teraz utwórz 4 „pomocniki” - wszystkie to input_boolean. Mają one reprezentować stany przesyłane w payload topica ha_gbb/ESS dla „Operation”
(w poniższej automatyzacji podglądnij jakie możesz dać im nazwy)
⁃	Charge
⁃	Discharge
⁃	Normal
⁃	DisableCharge
Z payload będziesz jeszcze „wyciągać” ChargeLimitW, SOC, i jak sobie dopiszesz InputLimitW (nie jest Ci do niczego potrzebny w Deye)

W kolejnym etapie utwórz 2 sceny ustawiające:
Grid Charge (encja włączająca ładowanie z grid) - EDIT: NIE DODAJEMY TEJ ENCJI
Mode Grid Charge Time1-6 (encja włączająca poszczególne timery)
Toggle System Timer (encja włączająca używanie timerów)
Scena 1 - wszystko włączone (dla Charge)
Scena 2 - włączone tylko System timer (dla Normal, Discharge i DisableCharge)

Teraz kolej na automatyzacje załączająca odpowiednią scenę
(Zwróć uwagę na to jak nazwałem input.boolean. Jak nazwiesz tak samo będziesz miał mniej roboty.
Automatyzacja 1:
*****************************************************************
alias: GBB - aktywuj scenę  Discharge, DisableCharge, Normal
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.gbboptimizer_output_source_discharge_battery
      - input_boolean.gbboptimizer_output_source_start_disablecharge
      - input_boolean.gbboptimizer_output_source_start_normal
    from: "off"
    to: "on"
condition: []
action:
  - service: scene.turn_on
    metadata: {}
    target:
      entity_id: scene.gbb_normal
mode: single
*****************************************************************
Automatyzacja 2
*****************************************************************
alias: GBB - aktywuj scenę Charge
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.gbboptimizer_output_source_priority_battery
    from: "off"
    to: "on"
condition: []
action:
  - service: scene.turn_on
    metadata: {}
    target:
      entity_id: scene.gbb_charge
mode: single
*****************************************************************
Na koniec ostatnia automatyzacja. Musisz w niej dostosować encje, choć jest duża szansa, że będziesz mieć takie same.

*****************************************************************
alias: >-
  GBB  -  Mqtt, PAYLOADS - Ustawienie wartości SOC, ChargeLimitW i obsługa
  Operation
description: >
  Ustawienie encji number.sunsynk_system_mode_soc_time1 na wartość SOC, encji
  number.sunsynk_system_mode_power_time1 na wartość ChargeLimitW, oraz
  odpowiedniego input_boolean w zależności od wartości Operation z payload MQTT
trigger:
  - platform: mqtt
    topic: ha_gbb/EMS
condition: []
action:
  - service: number.set_value
    data_template:
      entity_id: number.sunsynk_system_mode_soc_time1
      value: "{{ trigger.payload_json['SOC'] }}"
  - service: number.set_value
    data_template:
      entity_id: number.sunsynk_system_mode_soc_time2
      value: "{{ trigger.payload_json['SOC'] }}"
  - service: number.set_value
    data_template:
      entity_id: number.sunsynk_system_mode_soc_time3
      value: "{{ trigger.payload_json['SOC'] }}"
  - service: number.set_value
    data_template:
      entity_id: number.sunsynk_system_mode_soc_time4
      value: "{{ trigger.payload_json['SOC'] }}"
  - service: number.set_value
    data_template:
      entity_id: number.sunsynk_system_mode_soc_time5
      value: "{{ trigger.payload_json['SOC'] }}"
  - service: number.set_value
    data_template:
      entity_id: number.sunsynk_system_mode_soc_time6
      value: "{{ trigger.payload_json['SOC'] }}"
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload_json['ChargeLimitW'] is not none }}"
        sequence:
          - service: number.set_value
            data_template:
              entity_id: number.sunsynk_system_mode_power_time1
              value: >
                {% set abs_charge_limit = trigger.payload_json['ChargeLimitW'] |
                abs %} {% if abs_charge_limit > 8000 %}
                  8000
                {% else %}
                  {% if abs_charge_limit < 0 %}
                    0
                  {% else %}
                    {{ abs_charge_limit }}
                  {% endif %}
                {% endif %}
          - service: number.set_value
            data_template:
              entity_id: number.sunsynk_system_mode_power_time2
              value: >
                {% set abs_charge_limit = trigger.payload_json['ChargeLimitW'] |
                abs %} {% if abs_charge_limit > 8000 %}
                  8000
                {% else %}
                  {% if abs_charge_limit < 0 %}
                    0
                  {% else %}
                    {{ abs_charge_limit }}
                  {% endif %}
                {% endif %}
          - service: number.set_value
            data_template:
              entity_id: number.sunsynk_system_mode_power_time3
              value: >
                {% set abs_charge_limit = trigger.payload_json['ChargeLimitW'] |
                abs %} {% if abs_charge_limit > 8000 %}
                  8000
                {% else %}
                  {% if abs_charge_limit < 0 %}
                    0
                  {% else %}
                    {{ abs_charge_limit }}
                  {% endif %}
                {% endif %}
          - service: number.set_value
            data_template:
              entity_id: number.sunsynk_system_mode_power_time4
              value: >
                {% set abs_charge_limit = trigger.payload_json['ChargeLimitW'] |
                abs %} {% if abs_charge_limit > 8000 %}
                  8000
                {% else %}
                  {% if abs_charge_limit < 0 %}
                    0
                  {% else %}
                    {{ abs_charge_limit }}
                  {% endif %}
                {% endif %}
          - service: number.set_value
            data_template:
              entity_id: number.sunsynk_system_mode_power_time5
              value: >
                {% set abs_charge_limit = trigger.payload_json['ChargeLimitW'] |
                abs %} {% if abs_charge_limit > 8000 %}
                  8000
                {% else %}
                  {% if abs_charge_limit < 0 %}
                    0
                  {% else %}
                    {{ abs_charge_limit }}
                  {% endif %}
                {% endif %}
          - service: number.set_value
            data_template:
              entity_id: number.sunsynk_system_mode_power_time6
              value: >
                {% set abs_charge_limit = trigger.payload_json['ChargeLimitW'] |
                abs %} {% if abs_charge_limit > 8000 %}
                  8000
                {% else %}
                  {% if abs_charge_limit < 0 %}
                    0
                  {% else %}
                    {{ abs_charge_limit }}
                  {% endif %}
                {% endif %}
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload_json['Operation'] == 'Normal' }}"
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.gbboptimizer_output_source_start_normal
            data: {}
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.gbboptimizer_output_source_discharge_battery
                - input_boolean.gbboptimizer_output_source_start_disablecharge
                - input_boolean.gbboptimizer_output_source_priority_battery
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload_json['Operation'] == 'Discharge' }}"
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.gbboptimizer_output_source_discharge_battery
            data: {}
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.gbboptimizer_output_source_start_normal
                - input_boolean.gbboptimizer_output_source_start_disablecharge
                - input_boolean.gbboptimizer_output_source_priority_battery
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload_json['Operation'] == 'DisableCharge' }}"
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.gbboptimizer_output_source_start_disablecharge
            data: {}
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.gbboptimizer_output_source_start_normal
                - input_boolean.gbboptimizer_output_source_discharge_battery
                - input_boolean.gbboptimizer_output_source_priority_battery
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload_json['Operation'] == 'Charge' }}"
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.gbboptimizer_output_source_priority_battery
            data: {}
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.gbboptimizer_output_source_start_normal
                - input_boolean.gbboptimizer_output_source_discharge_battery
                - input_boolean.gbboptimizer_output_source_start_disablecharge
            data: {}

*****************************************************************
Pozostaje Ci jeszcze dopisać odpowiednia automatyzację (i pewnie scenę) która się uruchomi gdy w payload dostaniesz info, że cena jest poniżej zera. Na dziś jej nie ma bo nie jest potrzebna.

Tyle!