Klimakonwektor Vaillant VA 2 i modbus

Witam
Czytałem wątek o kotłach Vaillant ale nie znalazłem rozwiązania swojego problemu z klimakonwektorem.
Zastępuję grzejniki klimakonwektorami aby obniżyć temp. zasilania CO na pompie ciepła. Kupiłem klimakonwektory Vaillant VA 2 CN.
Aby nimi sterować mogę kupić dedykowany regulator ale jak to pogodzić z HA?

Nie kupowałem na razie termostatów: “Vaillant VA 2-WC C Regulator elektroniczny przewodowy” bo nie wiem czy uda mi się to zintegrować.
Do oryginalnego regulatora skłania mnie estetyka i możliwość sterowania manualnego bezpośrednio pod klapką klimakonwektora ale nie chcę rezygnować z integracji. Rozumiem, że kupując ww. regulator oraz konwerter RS485 do UART z ESP tworzę połączenie WiFi i dodając modbus w HA po zaadresowaniu każdego klimakonwektora mam możliwość sterowania nimi przez HA?
Fakt wyjdzie ponad 400 zł do każdej sztuki nie wiem jednak czy uda mi się to uruchomić a dodatkowo muszę mieć sterowanie zarówno grzania jak i chłodzenia jednym i tym samym zaworem.

Tanią opcją z pewnością jest dokupienie chińczyka z Tuyą (rozwiązanie z Elektrody), podpięcie pod sterownik i aby uniknąć chmury może się uda jak w sterowaniu podłogówką - wymiana softu/modułu Wifi na ESP ale stracę jednak pomiary temperatury z klimakonwektora czyli temp. powietrza na wejściu wentylatora i zasilania z CO.

Czytałem wątki o piecach i klimie Vaillanta ale nie znalazłem nic o klimakonwektorach może ktoś coś już walczył w sposób bardziej zaawansowany niż na Elektrodzie?

Pozdrawiam
Wojtek

Już poprawiam:

https://www.elektroda.pl/rtvforum/viewtopic.php?p=20205941#20205941

https://www.elektroda.pl/rtvforum/topic3795583-30.html

https://www.elektroda.pl/rtvforum/viewtopic.php?p=20959092#20959092

Gdzie linki do tego co czytałeś na Elektroda? Jakim protokołem komunikuje się ten sprzęt ze światem? Jesli to ModBUS to potrzebna jest tabela rejestrów.

Vaillant, czyli zapewne eBus

Podłączyłem się do sterownika i próbuję czytać za pomocą Modbus Monitor XPF ale pierwszy raz używam tego softu i nie bardzo cokolwiek czyta:

08:22:02:987401,Poll Once: False
08:22:03:228188,Client-STARTING
08:22:03:245901,Connection obtained for channel 'RTU:COM6'
08:22:03:245982,Client-RUNNING
08:22:03:275344,RTU TX,01 04 00 00 00 01 31 CA,Read Input Registers,SID:1,FC:4,ADR:0,CNT:1
08:22:03:336558,RTU RX,01 84 01 82 C0
08:22:03:337103, Illegal Function,ERR:1,SID:1 FC:84,Sample Input Register,300001,1,1,0,INT16,ABCD_BE,30001,,
08:22:03:353697,RTU TX,01 02 00 00 00 01 B9 CA,Read Inputs Registers,SID:1,FC:2,ADR:0,CNT:1
08:22:03:416074,RTU RX,01 82 01 81 60
08:22:03:416414, Illegal Function,ERR:1,SID:1 FC:82,Discrete Input Register,100001,1,1,0,BIT,ABCD_BE,1,,
08:22:03:447650,RTU TX,01 01 00 00 00 01 FD CA,Read Coils,SID:1,FC:1,ADR:0,CNT:1
08:22:04:449505,Timeout, 3 retries remaining
08:22:04:449545,RTU TX,01 01 00 00 00 01 FD CA,Read Coils,SID:1,FC:1,ADR:0,CNT:1
08:22:05:450613,Timeout, 2 retries remaining
08:22:05:450687,RTU TX,01 01 00 00 00 01 FD CA,Read Coils,SID:1,FC:1,ADR:0,CNT:1
08:22:06:453182,Timeout, 1 retries remaining
08:22:06:453777,Response Time Out
08:22:07:485918,RTU TX,01 01 00 00 00 10 3D C6,Read Coils,SID:1,FC:1,ADR:0,CNT:16
08:22:08:487628,Timeout, 3 retries remaining
08:22:08:487668,RTU TX,01 01 00 00 00 10 3D C6,Read Coils,SID:1,FC:1,ADR:0,CNT:16
08:22:09:490717,Timeout, 2 retries remaining
08:22:09:490754,RTU TX,01 01 00 00 00 10 3D C6,Read Coils,SID:1,FC:1,ADR:0,CNT:16
08:22:10:495694,Timeout, 1 retries remaining
08:22:10:495890,Response Time Out
08:22:10:526854,RTU TX,01 04 00 00 00 01 31 CA,Read Input Registers,SID:1,FC:4,ADR:0,CNT:1
08:22:10:596685,RTU RX,01 84 01 82 C0
08:22:10:596980, Illegal Function,ERR:1,SID:1 FC:84,Sample Input Register,300001,1,1,0,INT16,ABCD_BE,30001,,
08:22:10:620731,RTU TX,01 02 00 00 00 01 B9 CA,Read Inputs Registers,SID:1,FC:2,ADR:0,CNT:1
08:22:10:677374,RTU RX,01 82 01 81 60
08:22:10:677707, Illegal Function,ERR:1,SID:1 FC:82,Discrete Input Register,100001,1,1,0,BIT,ABCD_BE,1,,
08:22:10:698763,RTU TX,01 01 00 00 00 01 FD CA,Read Coils,SID:1,FC:1,ADR:0,CNT:1
08:22:11:708324,Timeout, 3 retries remaining
08:22:11:708392,RTU TX,01 01 00 00 00 01 FD CA,Read Coils,SID:1,FC:1,ADR:0,CNT:1
08:22:12:709903,Timeout, 2 retries remaining
08:22:12:709979,RTU TX,01 01 00 00 00 01 FD CA,Read Coils,SID:1,FC:1,ADR:0,CNT:1
08:22:13:711921,Timeout, 1 retries remaining
08:22:13:712171,Response Time Out

Jeśli ebus to tym zestawem nic nie odczytam. Czyli aby podjąć próbę jakiejkolwiek komunikacji po ebus muszę kupić adapter.
Złącze CN23 na sterowniku ma opis “MODBUS RTU TO UPPER UNIT” stąd próba komunikacji zwykłym adapterem RS485

Jak ma taki opis to jest to modbus (ebus jest w kotłach gazowych grupy vaillant, dlatego napisałem że zapewne ebus, ale z opisu wynika ze w tym przypadku nie). Komunikację masz raczej zestawioną poprawnie. To wygląda raczej na nieprawidłowy typ rejestru - w logu widać “Read Input Registers” i “Read Coils” natomiast zaglądając tutaj:

Wygląda na to że tam również może być

Modbus function code 0x03 (read holding registers).

Tu jest wątek na ten temat:

Supported function codes:
1: Read Coils
2: Read Discrete Input
3: Read Holding Register
4: Read Input Register
5: Write Single Coil
6: Write Single Register
15: Write Multiple Coils
16: Write Multiple Registers

Da się w jakiś sposób w tym programie zmienić typ odczytywanego rejestru z “Input Registers” na “Holding Registers” ? Może to rozwiąże problem z “illegal function” (o ile już nie zmieniałeś).

Jutro powinny dojść konwertery i poskładam adapter z tego linka:
Może będzie prościej cokolwiek odczytać.

Sorki, że późno odpowiadam ale buduję wiatę pod panele (to będzie pewnie kolejny mój problem z integracją falownika) i dopiero późnym wieczorem nadrabiam zaległości przy kompie.

Na razie to nie wiesz co i gdzie odczytać, więc skutek będzie podobny jak w przypadku Modbus Monitor. Słabo to widzę aby bez dokumentacji odnaleźć, rozszyfrować i odkryć przeznaczenie rejestrów.

Zobaczymy pewnie nie będzie łatwo a z pewnością bez kontrolera, na którym mógłbym zmieniać ustawienia i ewentualnie je odczytywać wręcz niemożliwe.
Komunikacja jest - podłączyłem ESP i sniffera przez USB.
AI napisał mi kawałek kodu do ESP który wysyła do Vaillanta zapytania o kolejne rejestry.
Modbussniffer loguje to sobie do pliku .
Jak skończą to wyszukam na które Vaillant nie odpowiedział błędem 83.

Próbuj :+1: . Moje obawy budzi opis złącza “MODBUS RTU TO UPPER UNIT” , sugeruje to raczej przeznaczenie niezgodne z Twoimi oczekiwaniami i służy do komunikacji między jednostkami w “kaskadzie”.

Jak się nie uda będę próbował wpiąć się równolegle do kontrolera (jeśli go kupię) lub ewentualnie gdy będzie to możliwe sam konwerter do ebus i kontroler z wyświetlaczem i enkoderem na ESP jako termostat. ESP odpytał wszystkie adresy a sniffer zapisał 10 MB tekstu wstępnie widziałem, że pojawiły się odpowiedzi np. 9600 co by sugerowało ustawienie prędkości portu. Od chińczyka jedzie też na próbę termostat z Tuya, ktory będzie do przeróbki na ESPHome.

Uporałem się (prawie) z fotowoltaiką więc powrót do Vaillanta.
Na razie eksperymenty na sucho więc nie do końca steruje mi obrotami w trybie grzania i chłodzenia ale AI podpowiedziała, że w trybie grzania wyłączył mi nadmuch aby nie chłodzić pomieszczenia.
Fragment, który działa:

select:
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Tryb pracy klimakonwektora"
    id: vaillant_mode
    address: 1601
    value_type: U_WORD
    optionsmap:
      "Wyłączony (OFF)": 0
      "Wentylacja (Fan Only)": 1
      "Chłodzenie (Cool)": 2
      "Ogrzewanie (Heat)": 3
      "Automatyczne (Auto)": 4

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Bieg wentylatora"
    id: vaillant_fan_mode
    address: 1603
    value_type: U_WORD
    optionsmap:
      "Niski (Low)": 2
      "Średni (Medium)": 3
      "Wysoki (Max)": 4

number:
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Zadana temperatura"
    id: vaillant_setpoint
    address: 1602
    value_type: U_WORD
    mode: BOX
    min_value: 17
    max_value: 30
    step: 1
    unit_of_measurement: "°C"

binary_sensor:
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Status pompy (R1613)"
    address: 1613
    register_type: holding
    lambda: |-
      return (x == 1);
    icon: "mdi:pump"

To najprawdopodobniej pozwoli na kiepskie ale jednak sterowanie klimakonwektorem bez termostatu.
Jednak musiałem zminimalizować wydatki i kupiłem sterowniki od majfriendów więc będę przerabiał Tuye na ESPHome jak większość swoich termostatów a po modbusie zostanie jeden najmniejszy na którym ręczna regulacja nie jest konieczna.

Pierwszy klimakonwektor uruchomiony może jeszcze wszystko nie hula idealnie ale podstawowe funkcje działają.
Stwierdziłem, że nie chce mi się podłączać sterownik Tuya i nim sterować nadmuchem i zawórem bo niestety nie ma czujnika temperatury czynnika więc będzie grzał i nieświadomie chłodził lub na odwrót. Zatem ESP32C3Supermini zamiast oryginalnego modułu bo potrzebne są dwa UARTy.
Moduł zasilacza i przekaźników od sterownika “do kosza” zasilanie ze złącza, na którym normalnie chodziłby sterownik Vailant i może nie idealnie ale za 100 zł i odrobinę dłubania działa.
Sam tego nie pisałem AI darmowe po wielu poprawkach dało radę.

Mała edycja aby nie generować kolejnego posta.

Do termostatu wcisnąłem odczyt czujnika temperatury z Vaillanta jako bardziej wiarygodną i precyzyjną. Teoretycznie powinien się jeszcze odczytać stan wentylatora i zaworu, który mógłbym użyć do odczytu aktywnego stanu ale coś nie zatrybiło.

esphome:
  name: fcusypialnia3
  friendly_name: FCU-Sypialnia3

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf
    
logger:
#  level: NONE
  
api:
  encryption:
    key: !secret api_key
#  reboot_timeout: 1h
    
ota:
  - platform: esphome
    password: !secret ota_password
    
wifi:
  networks:
    - ssid: **************
      password: !secret wifi_password     

  power_save_mode: NONE
  manual_ip:
    static_ip: 192.168.1.100
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.1
    dns2: 91.234.8.8
  ap:
    ssid: FCUSypialnia3
    password: !secret wifi_ap_password

#  reboot_timeout: 1h

captive_portal:

web_server:
  port: 80
  version: 3
  auth:
    username: !secret web_username
    password: !secret web_password

time:
  - id: time_provider
    platform: homeassistant
    on_time_sync:
      - logger.log: "Synchronized system clock"
    
status_led:
    pin:
      number: GPIO8
      
text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP Address
    ssid:
      name: SSID
    mac_address:
      name: Mac Address
    scan_results:
      name: Scan Results

button:
  - platform: restart
    name: Restart
  
uart:
  - id: modbus_uart
    rx_pin: GPIO6
    tx_pin: GPIO7
    baud_rate: 9600
    data_bits: 8
    parity: NONE
    stop_bits: 1
  - id: mcu_uart
    rx_pin: GPIO3
    tx_pin: GPIO4
    baud_rate: 9600
  
tuya:
  uart_id: mcu_uart  
  time_id: time_provider

modbus:
  uart_id: modbus_uart
  id: modbus1
  
modbus_controller:
  - id: vaillant_va2
    modbus_id: modbus1
    update_interval: 10s
    setup_priority: -10
    command_throttle: 0ms
    address: 1       # Adres klimakonwektora

sensor:
  - platform: tuya
    name: "Temperatura panel"
    device_class: "temperature"
    sensor_datapoint: 24
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    
  - platform: tuya
    name: "Temperatura"
    device_class: "temperature"
    id: tuya_DP124
    sensor_datapoint: 124
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    internal: true
    
  - platform: tuya
    name: "Temp. zadana panel"
    sensor_datapoint: 16
    on_value:
      then:
        - lambda: |-
            auto call = id(vaillant_setpoint).make_call();
            call.set_value(x); 
            call.perform();
            ESP_LOGD("custom", "Przesłano nową temperaturę zadaną do Vaillanta: %.1f", x);

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Temperatura Vailant" 
    id: temp_vaillant    
    register_type: holding    
    address: 1606 
    value_type: U_WORD        
    accuracy_decimals: 1
    unit_of_measurement: "°C" 
    filters:
      - lambda: return 25.0 + (x - 90.0) * 0.5;    
    on_value:
      then:
        - lambda: |-
            // 1. Ręcznie ustawiamy stan wirtualnego sensora Tuya DP124
            id(tuya_DP124).publish_state(x);
            
            // 2. Ręcznie nadpisujemy aktualną temperaturę w termostacie
            id(tuya_panel).current_temperature = x;
            
            // 3. Rozgłaszamy nowy stan termostatu do Home Assistant
            id(tuya_panel).publish_state();
   
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Temp. CO" ###  0...240 odpowiada -20...100 st. C oblicz (Temp. +5)*2+30
    register_type: holding    
    address: 1607              
    value_type: U_WORD        
    accuracy_decimals: 1
    unit_of_measurement: "°C"   
    filters:
#      - multiply: 0.1      
      - lambda: return 25.0 + (x - 90.0) * 0.5;
      
binary_sensor:
 # --- CZUJNIKI WYCIĄGNIĘTE Z REJESTRU 1619 ---
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Status ogrzewania"
    register_type: holding
    address: 1619
    # Sprawdzamy Bit 8 (wartość 256)
    lambda: |-
      return ((int)x & 0x0100) != 0; 
    icon: "mdi:radiator"

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Status zaworu 3d"
    register_type: holding
    address: 1619
    # Sprawdzamy Bit 9 (wartość 512)
    lambda: |-
      return ((int)x & 0x0200) != 0;
    icon: "mdi:valve"


# 2. NASTAWA TEMPERATURY
number:
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Temp. zadana Vaillant"
    id: vaillant_setpoint
    address: 1602
    value_type: U_WORD
    mode: BOX
    min_value: 17
    max_value: 30
    step: 0.5
    unit_of_measurement: "°C"

switch:
  - platform: tuya
    name: "ON-OFF Tuya"
    id: panel_power_switch
    switch_datapoint: 1              # DP 1: Zasilanie panelu
    on_turn_off:
      then:
        - lambda: |-
            auto call = id(vaillant_mode).make_call();
            call.set_option("Wyłączony"); // wysyła 0 do Modbus 1601
            call.perform();
    on_turn_on:
      then:
        - lambda: |-
            auto call = id(vaillant_mode).make_call();
            std::string current_tuya_mode = id(panel_tryb).current_option();
            
            if (current_tuya_mode == "Chłodzenie") call.set_option("Chłodzenie");   
            else if (current_tuya_mode == "Ogrzewanie") call.set_option("Ogrzewanie"); 
            else if (current_tuya_mode == "Wentylacja") call.set_option("Wentylacja"); 
            else call.set_option("Wentylacja"); 
            call.perform();
        
  - platform: tuya
    name: "Blokada rodzicielska"
    id: panel_child_lock
    entity_category: config     
    switch_datapoint: 40
    device_class: "switch"    
    icon: "mdi:lock"


climate:
  - platform: tuya
    name: "Termostat"
    id: tuya_panel
    switch_datapoint: 1              # DP 1: Zasilanie ON/OFF
    target_temperature_datapoint: 16 # DP 16: Zadana temperatura
    current_temperature_datapoint: 124 # DP 24: Temperatura w pokoju
#    temperature_multiplier: 0.1
    supports_cool: true              
    supports_heat: true              
    active_state:
      datapoint: 2
      cooling_value: 0               
      heating_value: 1               
      fanonly_value: 2 
    fan_mode:
      datapoint: 28
      low_value: 0                   
      medium_value: 1                
      high_value: 2                  
      auto_value: 3                  
    visual:
      min_temperature: 18 °C      
      max_temperature: 28 °C      
#      temperature_step: 0.5°C      
      temperature_step:
        target_temperature: 0.5
        current_temperature: 0.1 
select:
  - platform: tuya
    name: "Tryb Pracy Tuya"
    id: panel_tryb
    enum_datapoint: 2         # DP2 (0=Cool, 1=Heat, 2=Fan)
    options:
      0: "Chłodzenie"
      1: "Ogrzewanie"
      2: "Wentylacja"
    on_value:
      then:
        - lambda: |-
            if (id(panel_power_switch).state) {
              auto call = id(vaillant_mode).make_call();
              call.set_option(x); 
              call.perform();
            }

# --- REJESTRY WYBORU MODBUS DLA VAILLANTA ---
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Tryb pracy Vaillant"
    id: vaillant_mode
    address: 1601
    value_type: U_WORD
    optionsmap:
      "Wyłączony": 0
      "Wentylacja": 1
      "Chłodzenie": 2
      "Ogrzewanie": 3

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Nadmuch Vaillant"
    id: vaillant_fan
    address: 1603
    value_type: U_WORD
    optionsmap:
      "Wyłączony": 0          
      "Niski": 2
      "Średni": 3
      "Wysoki": 4
      "Auto": 5
   
  - platform: tuya
    name: "Nadmuch Tuya"
    id: panel_wentylator
    enum_datapoint: 28             # DP28 dla biegów
    options:
      0: "Niski"
      1: "Średni"
      2: "Wysoki"
      3: "Auto"
    on_value:
      then:
        - lambda: |-
            auto call = id(vaillant_fan).make_call();
            call.set_option(x); 
            call.perform();


      


      

W trybie grzania gdy Tco na wyjściu klimakonwektora jest za niska nadmuch nie włącza się i wtedy w logach pojawia się błąd bo sterownik usiłuje włączyć wiatrak a Vaillant wie, że nie powinien chodzić. Jeszcze nie zacząłem sezonu ale gdy tylko włączę ogrzewanie przyjdzie pora na testy.
Jest jeszcze kwestia wizualizacji w HA bo termostat cały czas pokazuje, że grzeje lub chłodzi - taka przypadłość integracji climate z Tuya -i nie wykrywa stanu idle czyli osiągnięcia żądanej temperatury. AI podpowiada, że można to uzyskać w samym HA ale na razie odpuściłem.

Kolejną wersję już poprawioną wklejam jako kolejny post.
Aktywność w kwestii grzania działa i wyłuskana z rejestru Vaillanta.
Grzanie - załączony elektrozawór
Chłodzenie - załączony elektrozawór+pompa kondensatu - do sprawdzenia latem.
Na klima konwektorze na DIP switchu S2 jeśli klima konwektor jest przy podłodze wskazane jest zmienić korektę temp. na 1 stopień z domyślnych 3 stopni.
Aktualny yaml:

esphome:
  name: fcusypialnia3
  friendly_name: FCU-Sypialnia3

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf
    
logger:
#  level: NONE
  
api:
  encryption:
    key: !secret api_key
#  reboot_timeout: 1h
    
ota:
  - platform: esphome
    password: !secret ota_password
    
wifi:
  networks:
    - ssid: *********
      password: !secret wifi_password     

  power_save_mode: NONE
  manual_ip:
    static_ip: 192.168.1.100
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.1
    dns2: 91.234.8.8
  ap:
    ssid: FCUSypialnia3
    password: !secret wifi_ap_password

#  reboot_timeout: 1h

captive_portal:

web_server:
  port: 80
  version: 3
  auth:
    username: !secret web_username
    password: !secret web_password

time:
  - id: time_provider
    platform: homeassistant
    on_time_sync:
      - logger.log: "Synchronized system clock"
    
status_led:
    pin:
      number: GPIO8
      
text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP Address
    ssid:
      name: SSID
    mac_address:
      name: Mac Address
    scan_results:
      name: Scan Results

button:
  - platform: restart
    name: Restart
  
uart:
  - id: modbus_uart
    rx_pin: GPIO6
    tx_pin: GPIO7
    baud_rate: 9600
    data_bits: 8
    parity: NONE
    stop_bits: 1
  - id: mcu_uart
    rx_pin: GPIO3
    tx_pin: GPIO4
    baud_rate: 9600
  
tuya:
  uart_id: mcu_uart  
  time_id: time_provider

modbus:
  uart_id: modbus_uart
  id: modbus1
  
modbus_controller:
  - id: vaillant_va2
    modbus_id: modbus1
    update_interval: 10s
    setup_priority: -10
    command_throttle: 0ms
    address: 1       # Adres ID klimakonwektora ustawiony na przełącznikach DIP

sensor:
  - platform: tuya
    name: "Temperatura panel"
    device_class: "temperature"
    sensor_datapoint: 24
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    
  - platform: tuya
    name: "Temperatura"
    device_class: "temperature"
    id: tuya_DP124
    sensor_datapoint: 124
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    internal: true
    
  - platform: tuya
    name: "Temp. zadana panel"
    sensor_datapoint: 16
    on_value:
      then:
        - lambda: |-
            auto call = id(vaillant_setpoint).make_call();
            call.set_value(x); 
            call.perform();
            ESP_LOGD("custom", "Przesłano nową temperaturę zadaną do Vaillanta: %.1f", x);

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Temperatura Vailant" 
    id: temp_vaillant    
    register_type: holding    
    address: 1606 
    value_type: U_WORD        
    accuracy_decimals: 1
    unit_of_measurement: "°C" 
    filters:
      - lambda: return 25.0 + (x - 90.0) * 0.5;    
    on_value:
      then:
        - lambda: |-
            // 1. Ręcznie ustawiamy stan wirtualnego sensora Tuya DP124
            id(tuya_DP124).publish_state(x);
            
            // 2. Ręcznie nadpisujemy aktualną temperaturę w termostacie
            id(tuya_panel).current_temperature = x;
            
            // 3. Rozgłaszamy nowy stan termostatu do Home Assistant
            id(tuya_panel).publish_state();

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Temp. CO" ###  0...240 odpowiada -20...100 st. C oblicz (Temp. +5)*2+30
    register_type: holding    
    address: 1607              
    value_type: U_WORD        
    accuracy_decimals: 1
    unit_of_measurement: "°C"   
    filters:
      - lambda: return 25.0 + (x - 90.0) * 0.5;

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Stan 1619" 
    register_type: holding    
    address: 1619              
    value_type: U_WORD        
    accuracy_decimals: 0
    # STAN AKTYWNOŚCI BEZPOŚREDNIO DO KOMPONENTU CLIMATE
    on_value:
      then:
        - lambda: |-
            int raw_val = (int)x;
            
            // 1. Wyciągamy stany logiczne z bitów na podstawie Twoich testów
            bool valve_open = (raw_val & 512) != 0;   // Bit 9 (waga 512) - Zawór otwarty
            bool pump_on = (raw_val & 2048) != 0;    // Bit 11 (waga 2048) - Pompa kondensatu
            
            // 2. Jeśli urządzenie jest całkowicie wyłączone (OFF) na panelu
            if (id(tuya_panel).mode == climate::CLIMATE_MODE_OFF) {
              id(tuya_panel).action = climate::CLIMATE_ACTION_OFF;
            }
            // 3. Jeśli zawór jest zamknięty (lub rejestr wynosi 0) -> Wymuszamy IDLE (Bezczynność)
            else if (!valve_open) {
              id(tuya_panel).action = climate::CLIMATE_ACTION_IDLE;
            }
            // 4. Jeśli zawór jest otwarty, decydujemy o akcji na podstawie pompy skroplin
            else {
              if (pump_on) {
                id(tuya_panel).action = climate::CLIMATE_ACTION_COOLING; // Aktywne chłodzenie
              } else {
                id(tuya_panel).action = climate::CLIMATE_ACTION_HEATING; // Aktywne grzanie
              }
            }
            
            // 5. Rozgłaszamy wymuszony stan do Home Assistant i panelu Tuya
            id(tuya_panel).publish_state();

binary_sensor:
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Vaillant: Status zaworu 3d"
    id: v_status_valve
    register_type: holding
    address: 1619
    # Weryfikacja wartości logicznej: czy w liczbie z rejestru ukryta jest waga 512 (Bit 9)
    lambda: |-
      return ((int)x & 512) != 0;
    icon: "mdi:valve"

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Vaillant: Status pompy kond"
    id: v_status_pump
    register_type: holding
    address: 1619
    # Weryfikacja wartości logicznej: czy w liczbie z rejestru ukryta jest waga 2048 (Bit 11)
    lambda: |-
      return ((int)x & 2048) != 0;
    icon: "mdi:pump"

  - platform: template
    name: "Klimakonwektor: Aktywne Ogrzewanie"
    id: fcu_active_heating
    device_class: running
    # Grzejemy, gdy zawór jest otwarty (512), a pompa kondensatu stoi (brak 2048)
    lambda: |-
      return id(v_status_valve).state && !id(v_status_pump).state;
    icon: "mdi:fire"

  - platform: template
    name: "Klimakonwektor: Aktywne Chłodzenie"
    id: fcu_active_cooling
    device_class: running
    # Chłodzimy, gdy zawór jest otwarty (512) oraz pompa kondensatu pracuje (2048)
    lambda: |-
      return id(v_status_valve).state && id(v_status_pump).state;
    icon: "mdi:snowflake"

# NASTAWA TEMPERATURY
number:
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Temp. zadana Vaillant"
    id: vaillant_setpoint
    address: 1602
    value_type: U_WORD
    mode: BOX
    min_value: 17
    max_value: 30
    step: 0.5
    unit_of_measurement: "°C"

switch:
  - platform: tuya
    name: "ON-OFF Tuya"
    id: panel_power_switch
    switch_datapoint: 1              # DP 1: Zasilanie panelu
    on_turn_off:
      then:
        - lambda: |-
            auto call = id(vaillant_mode).make_call();
            call.set_option("Wyłączony"); // wysyła 0 do Modbus 1601
            call.perform();
    on_turn_on:
      then:
        - lambda: |-
            auto call = id(vaillant_mode).make_call();
            std::string current_tuya_mode = id(panel_tryb).current_option();
            
            if (current_tuya_mode == "Chłodzenie") call.set_option("Chłodzenie");   
            else if (current_tuya_mode == "Ogrzewanie") call.set_option("Ogrzewanie"); 
            else if (current_tuya_mode == "Wentylacja") call.set_option("Wentylacja"); 
            else call.set_option("Wentylacja"); 
            call.perform();
        
  - platform: tuya
    name: "Blokada rodzicielska"
    id: panel_child_lock
    entity_category: config     
    switch_datapoint: 40
    device_class: "switch"    
    icon: "mdi:lock"

# --- INTEGRACJA KLIMATYZACJI / TERMOSTATU ---
climate:
  - platform: tuya
    name: "Termostat"
    id: tuya_panel
    switch_datapoint: 1              # DP 1: Zasilanie ON/OFF
    target_temperature_datapoint: 16 # DP 16: Zadana temperatura
    current_temperature_datapoint: 124 # DP 24: Temperatura w pokoju
    supports_cool: true              
    supports_heat: true              
    active_state:
      datapoint: 2
      cooling_value: 0               
      heating_value: 1               
      fanonly_value: 2 
    fan_mode:
      datapoint: 28
      low_value: 0                   
      medium_value: 1                
      high_value: 2                  
      auto_value: 3                  
    visual:
      min_temperature: 18 °C      
      max_temperature: 28 °C      
      temperature_step: 1 °C      


select:
  - platform: tuya
    name: "Tryb Pracy Tuya"
    id: panel_tryb
    enum_datapoint: 2         # DP2 (0=Cool, 1=Heat, 2=Fan)
    options:
      0: "Chłodzenie"
      1: "Ogrzewanie"
      2: "Wentylacja"
    on_value:
      then:
        - lambda: |-
            if (id(panel_power_switch).state) {
              auto call = id(vaillant_mode).make_call();
              call.set_option(x); 
              call.perform();
            }

# --- REJESTRY WYBORU MODBUS DLA VAILLANTA ---
  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Tryb pracy Vaillant"
    id: vaillant_mode
    address: 1601
    value_type: U_WORD
    optionsmap:
      "Wyłączony": 0
      "Wentylacja": 1
      "Chłodzenie": 2
      "Ogrzewanie": 3

  - platform: modbus_controller
    modbus_controller_id: vaillant_va2
    name: "Nadmuch Vaillant"
    id: vaillant_fan
    address: 1603
    value_type: U_WORD
    optionsmap:
      "Wyłączony": 0          
      "Niski": 2
      "Średni": 3
      "Wysoki": 4
      "Auto": 5
   
  - platform: tuya
    name: "Nadmuch Tuya"
    id: panel_wentylator
    enum_datapoint: 28             # DP28 dla biegów
    options:
      0: "Niski"
      1: "Średni"
      2: "Wysoki"
      3: "Auto"
    on_value:
      then:
        - lambda: |-
            auto call = id(vaillant_fan).make_call();
            call.set_option(x); 
            call.perform();


    
1 Like

O kurcze, fantastycznie, że ktoś już działa w tym temacie. Ja również mam klimakonwektor Vaillant, też nie mam kontrolera i raczej też skuszę się na komunikację MODBUS. Tym bardziej, że mam czujniki Xiaomi Mi Monitor 2 po Bluetooth to je mogę potraktować jako czujnik temperatury.