Integracja z rekuperatorem Defro DRX po MODBUS

Hej,

Nie widziałem nigdzie takiego tematu, więc postanowiłem założyć nowy i opisać jak zintegrować rekuperację na Defro. Wzorowałem się na wiedzy dostępnej w Internecie o rekuperatorze Thessli - podziękowania dla wszystkich użytkowników zaangażowanych w ten projekt bo było mi po prostu dużo łatwiej. Dodatkowo też skorzystałem z dokumentacji od samego producenta. Przejdźmy do konkretów.

Dashboard:

Co potrzebujemy?

  1. Konwerter MODBUS RS485 TCP przez Ethernet lub Wifi - ja skorzystałem z Elfin EW11A
  2. Zasilacz do konwertera: w moim przypadku 5-36V 5W - patrz komentarz poniżej

Problemy:

  1. Producent płytki Tech Sterowniki dla rekuperatora Defro przygotował jedynie komunikację (A/B) i nie ma wyprowadzonego zasilania, więc zgodnie z oficjalną informacją jaką uzyskałem należy zastosować osobny zasilacz…
  2. Brakuje obsługi jednego przypadku użycia - jeśli na ekranie sterownika zostanie zmieniony bieg (funkcja na 1-4 godziny), to takie ustawienie bierze priorytet i z ze strony MODBUS nie ma żadnego rejestru, który by, po pierwsze, pozwolił odczytać to ustawienie (brak informacji poza biegiem), oraz po drugie pozwolił nadpisać rejestr (zmiana biegu przez integrację nie będzie zaakceptowana). Zgłosiłem to do Defro. Drobna sprawa ale jednak dobrze by było mieć to obsłużone.

Pierwsze kroki:
Jak już mamy konwerter to trzeba go podpiąć do płytki rekuperatora. Można kupić gotowy przewód lub zarobić samemu. W moim przypadku wykorzystałem zwykłą skrętkę utp 5e. Następnie musimy się połączyć z konwerterem i skonfigurować ustawienia sieciowe. Szczegóły pomijam bo jest dużo informacji w internecie jak to poprawnie zrobić.

Konwerter konfigurujemy zgodnie z ustawieniami na sterowniku Defro (domyślne ustawienia):

Do tego potrzebujemy ustawienia serwera TCP:

Konfiguracja w Home Assistant:
Ustawiamy integrację MODBUS (można jedynie przez YAML):

# Modbus through TCP
# Elfin EW11A - Defro DRX V

  - name: DefroBMS
    host: 192.168.1.xxx
    port: 9999
    type: tcp
    delay: 5
    timeout: 15
    sensors:
      - name: "Defro - Wydatek Nawiewu"
        address: 100
        slave: 1
        device_class: volume_flow_rate
        unit_of_measurement: m³/h
        state_class: measurement
        scan_interval: 15
        unique_id: "defro_wydatek_nawiewu"
        
      - name: "Defro - Wydatek Wywiewu"
        slave: 1
        address: 101
        device_class: volume_flow_rate
        unit_of_measurement: m³/h
        state_class: measurement
        scan_interval: 15
        unique_id: "defro_wydatek_wywiewu"
        
      - name: "Defro - Bieg Nawiewu"
        address: 102
        slave: 1
        input_type: holding
        unique_id: "defro_bieg_nawiewu"
        
      - name: "Defro - Bieg Wywiewu"
        slave: 1
        address: 103
        input_type: holding
        unique_id: "defro_bieg_wywiewu"
        
      - name: "Defro - Bieg Zadany"
        address: 157
        slave: 1
        input_type: holding
        scan_interval: 15
        unique_id: "defro_bieg_zadany"

      - name: "Defro - Stan Filtrów"
        slave: 1
        address: 170
        unique_id: "defro_stan_filtrow"
        
      - name: "Defro - Temperatura Czerpnia"
        address: 104
        slave: 1
        unit_of_measurement: °C
        scale: 0.1
        precision: 1
        device_class: temperature
        state_class: measurement
        unique_id: "defro_temperatura_czerpnia"
        
      - name: "Defro - Temperatura Wyrzutnia"
        address: 105
        slave: 1
        unit_of_measurement: °C
        scale: 0.1
        precision: 1
        device_class: temperature
        state_class: measurement
        unique_id: "defro_temperatura_wyrzutnia"
        
      - name: "Defro - Temperatura Nawiew"
        address: 106
        slave: 1
        unit_of_measurement: °C
        scale: 0.1
        precision: 1
        device_class: temperature
        state_class: measurement
        unique_id: "defro_temperatura_nawiew"
        
      - name: "Defro - Temperatura Wywiew"
        address: 107
        slave: 1
        unit_of_measurement: °C
        scale: 0.1
        precision: 1
        device_class: temperature
        state_class: measurement
        unique_id: "defro_temperatura_wywiew"
        
      - name: "Defro - Temperatura Kontroli Nagrzewnicy"
        address: 109
        slave: 1
        unit_of_measurement: °C
        scale: 0.1
        precision: 1
        device_class: temperature
        state_class: measurement
        unique_id: "defro_temperatura_kontroli_nagrzewnicy"
        
      - name: "Defro - Przepływ Biegu 1"
        address: 164
        slave: 1
        input_type: holding
        unique_id: "defro_przeplyw_biegu_1"
        
      - name: "Defro - Przepływ Biegu 2"
        address: 165
        slave: 1
        input_type: holding
        unique_id: "defro_przeplyw_biegu_2"
        
      - name: "Defro - Przepływ Biegu 3"
        address: 166
        slave: 1
        input_type: holding
        unique_id: "defro_przeplyw_biegu_3"
        
      - name: "Defro - Błędy"
        address: 139
        slave: 1
        input_type: holding
        data_type: uint16
        unique_id: "defro_bledy"
        
      - name: "Defro - Bypass Dolna Temperatura"
        address: 173
        slave: 1
        input_type: holding
        unique_id: "defro_dolna_temperatura"
        
      - name: "Defro - Bypass Temperatura Komfortu"
        address: 174
        slave: 1
        input_type: holding
        unique_id: "defro_temperatura_komfortu"
        
    binary_sensors:
      - name: "Defro - Bypass Stan"
        address: 133
        slave: 1
        input_type: holding
        unique_id: "defro_bypass_stan"
        
      - name: "Defro - Urlop Stan"
        address: 137
        slave: 1
        input_type: holding
        unique_id: "defro_urlop_stan"
        
    switches:
      - name: "Defro - Bypass"
        address: 141
        slave: 1
        write_type: holding
        command_on: 1
        command_off: 0
        verify:
            address: 141
            input_type: holding
            state_on: 1
            state_off: 0
        unique_id: "defro_bypass"
        
      - name: "Defro - Tygodniówka"
        address: 156
        slave: 1
        write_type: holding
        command_on: 1
        command_off: 0
        verify:
            address: 156
            input_type: holding
            state_on: 1
            state_off: 0
        unique_id: "defro_tygodniowka"
        
      - name: "Defro - Flow Balancing"
        address: 168
        slave: 1
        write_type: holding
        command_on: 1
        command_off: 0
        verify:
            address: 168
            input_type: holding
            state_on: 1
            state_off: 0
        unique_id: "defro_flow_balancing"
        
      - name: "Defro - Urlop"
        address: 145
        slave: 1
        write_type: holding
        command_on: 60
        command_off: 0
        verify:
            address: 137
            input_type: holding
            state_on: 1
            state_off: 0
        unique_id: "defro_urlop"
        
      - name: "Defro - Okna"
        address: 158
        slave: 1
        write_type: holding
        command_on: 1
        command_off: 0
        verify:
            address: 158
            input_type: holding
            state_on: 1
            state_off: 0
        unique_id: "defro_okna"

Brakuje kilku funkcji, z których po prostu nie korzystam ale jak ktoś chce to może łatwo dodać: palenisko, tryb tygodniowy, GWC, jakieś dodatkowe ustawienia na przykład przy bypassie.

Dla głównego sensora od wentylatorów zrobiłem osobny YAML: /homeassistant/template/defro_fan_sensor.yaml

- fan:
    - name: "Defro - Fan Mode"
      unique_id: "fan.defro_fan_mode"
      state: >-
        {{ is_state('binary_sensor.defro_status', 'on') }}
      percentage: >-
        {% set gear = states('sensor.defro_bieg_zadany_max') | int(0) %}
        {% if gear == 1 %}
          {{ (states('sensor.defro_przeplyw_biegu_1') | float(0) / 450 * 100) | round(1) }}
        {% elif gear == 2 %}
          {{ (states('sensor.defro_przeplyw_biegu_2') | float(0) / 450 * 100) | round(1) }}
        {% elif gear == 3 %}
          {{ (states('sensor.defro_przeplyw_biegu_3') | float(0) / 450 * 100) | round(1) }}
        {% else %}
          0
        {% endif %}
      speed_count: 3

      turn_on:
        - action: modbus.write_register
          data:
            slave: 1
            address: 157
            value: >-
              {% set p = percentage | int %}
              {% if p <= 0 %} 0
              {% elif p <= 33 %} 1
              {% elif p <= 66 %} 2
              {% else %} 3
              {% endif %}
            hub: DefroBMS

      turn_off:
        - action: modbus.write_register
          data:
            slave: 1
            address: 157
            value: 0
            hub: DefroBMS

      set_percentage:
        - action: modbus.write_register
          data:
            slave: 1
            address: 157
            value: >-
              {% set p = percentage | int %}
              {% if p <= 0 %} 0
              {% elif p <= 33 %} 1
              {% elif p <= 66 %} 2
              {% else %} 3
              {% endif %}
            hub: DefroBMS

      preset_modes:
        - "normalny"
        - "urlop"
        - "okna"

      preset_mode: >-
        {% if is_state('switch.defro_urlop', 'on') %}
          urlop
        {% elif is_state('switch.defro_okna', 'on') %}
          okna
        {% else %}
          normalny
        {% endif %}

      set_preset_mode:
        - choose:
            - conditions: "{{ preset_mode == 'urlop' }}"
              sequence:
                - action: switch.turn_off
                  target:
                    entity_id:
                      - switch.defro_okna
                - action: switch.turn_on
                  target:
                    entity_id: switch.defro_urlop

            - conditions: "{{ preset_mode == 'okna' }}"
              sequence:
                - action: switch.turn_off
                  target:
                    entity_id:
                      - switch.defro_urlop
                - action: switch.turn_on
                  target:
                    entity_id: switch.defro_okna

            - conditions: "{{ preset_mode == 'normalny' }}"
              sequence:
                - action: switch.turn_off
                  target:
                    entity_id:
                      - switch.defro_urlop
                      - switch.defro_okna

Pozostałe sensory są w kolejnym YAML: /homeassistant/template/defro_template_sensors.yaml

- sensor:
    - name: "Defro - Sprawność"
      unique_id: "sensor.defro_sprawnosc"
      unit_of_measurement: "%"
      state_class: "measurement"
      state: >-
        {% if is_state('binary_sensor.defro_bypass_stan', 'off') %}
          {% set t1 = states('sensor.defro_temperatura_czerpnia') | float(0) %}
          {% set t2 = states('sensor.defro_temperatura_nawiew') | float(0) %}
          {% set t3 = states('sensor.defro_temperatura_wywiew') | float(0) %}
          {% if (t3 - t1) != 0 %}
            {{ ((t2 - t1) / (t3 - t1) * 100) | round(1, default=0) }}
          {% else %}
            0
          {% endif %}
        {% else %}
          0
        {% endif %}

    - name: "Defro - Odzyskana Moc"
      unique_id: "sensor.defro_odzyskana_moc"
      unit_of_measurement: "W"
      state_class: "measurement"
      state: >-
        {% if is_state('binary_sensor.defro_bypass_stan', 'off') %}
          {% set strumienN = states('sensor.defro_wydatek_nawiewu') | float(0) %}
          {% set tcz = states('sensor.defro_temperatura_czerpnia') | float(0) %}
          {% set tn = states('sensor.defro_temperatura_nawiew') | float(0) %}
          {% set odzysk = ((tn - tcz) * strumienN * 1200) / 3600 %}
          {% if tcz < strumienN %}
            {{ odzysk | float | round(1, default=0) }}
          {% else %}
            {{ (odzysk * -1) | float | round(1, default=0) }}
          {% endif %}
        {% else %}
          0
        {% endif %}

    - name: "Defro - Wysterowanie Wywiew"
      unique_id: "sensor.defro_wysterowanie_wywiew"
      unit_of_measurement: "%"
      state_class: "measurement"
      state: >-
        {% set flowW = states('sensor.defro_wydatek_wywiewu') | float(0) %}
        {{ ((flowW * 100) / 450) | float | round(1, default=0) }}

    - name: "Defro - Wysterowanie Nawiew"
      unique_id: "sensor.defro_wysterowanie_nawiew"
      unit_of_measurement: "%"
      state_class: "measurement"
      state: >-
        {% set flowN = states('sensor.defro_wydatek_nawiewu') | float(0) %}
        {{ ((flowN * 100) / 450) | float | round(1, default=0) }}

    - name: "Defro - Błedy Stan"
      unique_id: "sensor.defro_bledy_stan"
      state: >-
        {% set val = states('sensor.defro_bledy') | int(0) %}
        {% set binary = '{0:b}'.format(val) %}
        {% set data = namespace(positions=[]) %}

        {% for i in range(binary | length) %}
          {% if binary[-1 - i] == '1' %}
            {% set data.positions = data.positions + [i + 1] %}
          {% endif %}
        {% endfor %}

        {% set errors = {
          1: "Błąd wentylatora wywiewu",
          2: "Błąd wentylatora nawiewu",
          3: "Błąd czujnika temperatury zewnętrznej",
          4: "Błąd czujnika temperatury wywiewu",
          5: "Błąd czujnika temperatury nawiewu",
          6: "Błąd czujnika temperatury wyrzut",
          7: "Błąd czujnika temperatury nawilżacza",
          10: "Błąd czujnika przepływu wywiewu",
          11: "Błąd czujnika przepływu nawiewu",
          12: "Błąd czujnika nawilżacza",
          13: "Błąd czujnika nagrzewnicy GWC",
          14: "Błąd czujnika temperatury nagrzewnicy nawilżacza",
          15: "Błąd wentylatora wywiewu przepływ",
          16: "Błąd wentylatora nawiewu przepływ"
        } %}

        {% set ns = namespace(active_errors=[]) %}
        {% for pos in data.positions %}
          {% if errors[pos] is defined %}
            {% set ns.active_errors = ns.active_errors + [errors[pos]] %}
          {% endif %}
        {% endfor %}

        {{ ns.active_errors if ns.active_errors else "OK" }}

    - name: "Defro - Bieg Zadany Max"
      unique_id: "defro_bieg_zadany_max"
      state: >-
        {{ [
          states('sensor.defro_bieg_nawiewu') | int(0),
          states('sensor.defro_bieg_wywiewu') | int(0)
        ] | max }}

- binary_sensor:
    - name: "Defro - Status"
      unique_id: "binary_sensor.defro_status"
      state: >-
        {% set val = states('sensor.defro_bieg_zadany') | int(0) %}
        {{ 'off' if val == 0 else 'on' }}

    - name: "Defro - Okna Stan"
      unique_id: "binary_sensor.defro_okna_stan"
      state: >-
        {{ is_state('switch.defro_okna', 'on') }}

    - name: "Defro - Awaria"
      unique_id: "binary_sensor.defro_awaria"
      state: >-
        {{ states('sensor.defro_bledy') | int(0) != 0 }}

Oczywiście do głównego pliku konfiguracyjnego /homeassistant/configuration.yaml musimy dodać odpowiednio:

template: !include_dir_merge_list template/
modbus: !include modbus.yaml

Kod dla vertical stack card:

type: vertical-stack
cards:
  - type: picture-elements
    elements:
      - type: state-label
        entity: sensor.defro_temperatura_czerpnia
        style:
          top: 33%
          left: 7%
          color: black
          font-family: system-ui
      - type: state-label
        entity: sensor.defro_temperatura_wywiew
        style:
          top: 33%
          left: 93%
          color: black
          font-family: system-ui
      - type: state-label
        entity: sensor.defro_temperatura_wyrzutnia
        style:
          top: 66.8%
          left: 7%
          color: black
          font-family: system-ui
      - type: state-label
        entity: sensor.defro_temperatura_nawiew
        style:
          top: 66.8%
          left: 93%
          color: black
          font-family: system-ui
      - type: state-label
        entity: sensor.defro_temperatura_kontroli_nagrzewnicy
        style:
          top: 21%
          left: 37%
          color: black
          font-family: system-ui
          font-size: smaller
      - type: state-label
        entity: sensor.defro_wydatek_nawiewu
        style:
          top: 82%
          left: 92%
      - type: state-label
        entity: sensor.defro_wysterowanie_nawiew
        style:
          top: 82%
          left: 66%
          color: black
          font-family: system-ui
          font-size: smaller
      - type: state-label
        entity: sensor.defro_wydatek_wywiewu
        style:
          top: 18%
          left: 92%
      - type: state-label
        entity: sensor.defro_wysterowanie_wywiew
        style:
          top: 82%
          left: 35%
          color: black
          font-family: system-ui
          font-size: smaller
      - type: state-label
        entity: sensor.defro_sprawnosc
        style:
          top: 72%
          left: 50%
          color: black
          font-family: system-ui
          font-size: smaller
      - type: state-label
        entity: sensor.defro_odzyskana_moc
        style:
          top: 29%
          left: 50%
          color: black
          font-family: system-ui
          font-size: smaller
      - type: state-label
        entity: sensor.defro_bieg_nawiewu
        style:
          top: 52%
          left: 65.5%
          color: black
          font-weight: bold
      - type: state-label
        entity: sensor.defro_bieg_wywiewu
        style:
          top: 52%
          left: 34.5%
          color: black
          font-weight: bold
      - type: state-label
        entity: sensor.shellyemminig4_7c2c6777361c_moc
        style:
          top: 19%
          left: 50%
          color: black
          font-family: system-ui
          font-size: smaller
    image: /local/defro/defro1v.png


Chciałbym jeszcze w przyszłości usprawnić działanie karty i pokazać bypass oraz może kilka innych ustawień na wzór integracji z Thessli ale to może kiedyś. :slightly_smiling_face:

Pozdrawiam,
Piotr

3 polubienia