Integration of heat pump THZ 5.5 eco from tecalor (Stiebel Eltron) into Home Assistant

Hi together,
Does anyone know how the configuration must be done for this topic?

I installed Home Assistant on a Raspberry Pi 3 64 Bit and connected it over TCP/IP with the heat pump THZ 5.5 eco from tecalor. To realize TCP/IP Protocoll an Internet Service Gateway (Software 12.1.0) from tecalor is installed with modbus enabled.

I want to include the ISG module in my home assistant. Up to now I get following 2 error messages in my Logs:

Setup failed for stiebel_eltron: Invalid config. 4. August 2022 um 22:03:23 – (FEHLER) setup.py"

Invalid config for [stiebel_eltron]: expected a dictionary for dictionary value @ data[‘stiebel_eltron’]. Got [OrderedDict([(‘name’, ‘THZ5.5eco’)])]. (See /config/configuration.yaml, line 32)

My entries in configuration.yaml:

> modbus:
>  - name: isgweb
>     close_comm_on_error: true
>     delay: 5
>     timeout: 5
>     type: tcp
>     host: (IP of Internet Service Gateway)
>     port: 502
> 
> stiebel_eltron:
>   - name: THZ5.5eco

Hi Matthias,

I have written a MODBUS to MQTT wrapper that I currently try to integrate into Home Assistant. My ISG web is modified a bit, in order to make some more values accessible via MODBUS. The howto for this was available on http://www.loxwiki.eu/ but right now the page seems down.

If you’re interested I can publish the wrapper on Github.

Regards,
Manuel

Helo @MatthiasH, I am also running a Tecalor THZ 5.5 eco (Stiebel Eltron) heat pump. It is not working with the Stiebel Eletron Integration. I am just using the Modbus Integration with the following config within the configuration.yaml. It does not yet include all registers, and I am not yet able to set values within the holding register, but maybe it helps you a little. The config is also not including HK2 (Heizkreis 2 / Heating circuit 2), because it is not in use in my heat pump. The register values are documented in this Stiebel Software Documentation.

# Input Registers Values of Tecalor THZ 5.5 Eco via configuration.yaml
modbus:
  - type: tcp
    host: 192.168.178.10 # IP-Address of Tecalor THZ 5.5 Eco ISG Web/Plus
    port: 502
    name: "THZ 5.5 Eco"
    close_comm_on_error: true
    delay: 5
    timeout: 5
    sensors:
      - name: "Heizung Raumisttemperatur HK1"
        unique_id: tecalor.wpTHZ-55eco.input.30001
        slave: 1
        address: 0
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Raumsolltemperatur HK1"
        unique_id: tecalor.wpTHZ-55eco.input.30002
        slave: 1
        address: 1
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Raumfeuchte HK1"
        unique_id: tecalor.wpTHZ-55eco.input.30003
        slave: 1
        address: 2
        input_type: input
        unit_of_measurement: "%"
        device_class: humidity
        scale: 0.1
        precision: 1
      - name: "Heizung Außentemperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30007
        slave: 1
        address: 6
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Ist-Temperatur HK1"
        unique_id: tecalor.wpTHZ-55eco.input.30008
        slave: 1
        address: 7
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Soll-Temperatur HK1"
        unique_id: tecalor.wpTHZ-55eco.input.30009
        slave: 1
        address: 8
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Vorlauftemperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30012
        slave: 1
        address: 11
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Rücklauftemperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30013
        slave: 1
        address: 12
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Druck"
        unique_id: tecalor.wpTHZ-55eco.input.30014
        slave: 1
        address: 13
        input_type: input
        unit_of_measurement: "bar"
        device_class: pressure
        scale: 0.1
        precision: 1
      - name: "Heizung Volumenstrom"
        unique_id: tecalor.wpTHZ-55eco.input.30015
        slave: 1
        address: 14
        input_type: input
        unit_of_measurement: "l/m"
        scale: 0.1
        precision: 1
      - name: "Heizung Warmwasser Ist-Temperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30016
        slave: 1
        address: 15
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Warmwasser Soll-Temperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30017
        slave: 1
        address: 16
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Zuluft Ist-Lüfterdrehzahl"
        unique_id: tecalor.wpTHZ-55eco.input.30018
        slave: 1
        address: 17
        input_type: input
        unit_of_measurement: "Hz"
        device_class: frequency
        scale: 0.1
        precision: 1
      - name: "Heizung Zuluft Soll-Volumenstrom"
        unique_id: tecalor.wpTHZ-55eco.input.30019
        slave: 1
        address: 18
        input_type: input
        unit_of_measurement: "m³/h"
        scale: 0.1
        precision: 1
      - name: "Heizung Abluft Ist-Lüfterdrehzahl"
        unique_id: tecalor.wpTHZ-55eco.input.30020
        slave: 1
        address: 19
        input_type: input
        unit_of_measurement: "Hz"
        device_class: frequency
        scale: 0.1
        precision: 1
      - name: "Heizung Abluft Soll-Volumenstrom"
        unique_id: tecalor.wpTHZ-55eco.input.30021
        slave: 1
        address: 20
        input_type: input
        unit_of_measurement: "m³/h"
        scale: 0.1
        precision: 1
      - name: "Heizung Abluftfeuchte"
        unique_id: tecalor.wpTHZ-55eco.input.30022
        slave: 1
        address: 21
        input_type: input
        unit_of_measurement: "%"
        device_class: humidity
        precision: 1
      - name: "Heizung Ablufttemperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30023
        slave: 1
        address: 22
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Ablufttaupunkt"
        unique_id: tecalor.wpTHZ-55eco.input.30024
        slave: 1
        address: 23
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Heißgastemperatur"
        unique_id: tecalor.wpTHZ-55eco.input.30028
        slave: 1
        address: 27
        input_type: input
        unit_of_measurement: "°C"
        device_class: temperature
        scale: 0.1
        precision: 1
      - name: "Heizung Hochdruck"
        unique_id: tecalor.wpTHZ-55eco.input.30029
        slave: 1
        address: 28
        input_type: input
        unit_of_measurement: "bar"
        device_class: pressure
        scale: 0.01
        precision: 1
      - name: "Heizung Niederdruck"
        unique_id: tecalor.wpTHZ-55eco.input.30030
        slave: 1
        address: 29
        input_type: input
        unit_of_measurement: "bar"
        device_class: pressure
        scale: 0.01
        precision: 1
      - name: "Heizung Verdichterstarts"
        unique_id: tecalor.wpTHZ-55eco.input.30031
        slave: 1
        address: 30
        input_type: input
        precision: 1
      - name: "Heizung Verdichterdrehzahl"
        unique_id: tecalor.wpTHZ-55eco.input.30032
        slave: 1
        address: 31
        input_type: input
        unit_of_measurement: "Hz"
        device_class: frequency
        precision: 1
      - name: "Heizung Betriebsstatus"
        unique_id: tecalor.wpTHZ-55eco.input.32001
        slave: 1
        address: 2000
        input_type: input
        precision: 1
      - name: "Heizung SG-Ready Status"
        unique_id: tecalor.wpTHZ-55eco.input.35001
        slave: 1
        address: 5000
        input_type: input
        precision: 1
binary_sensor:
  - platform: template
    sensors:
      heizung_status_schaltprogramm:
        friendly_name: "Heizung Status Schaltprogramm"
        unique_id: tecalor.wpTHZ-55eco.input.32001.0
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(1) > 0 }}"
      heizung_status_verdichter:
        friendly_name: "Heizung Status Verdichter"
        unique_id: tecalor.wpTHZ-55eco.input.32001.1
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(2) > 0 }}"
      heizung_status_heizen:
        friendly_name: "Heizung Status Heizen"
        unique_id: tecalor.wpTHZ-55eco.input.32001.2
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(4) > 0 }}"
      heizung_status_kuehlen:
        friendly_name: "Heizung Status Kuehlen"
        unique_id: tecalor.wpTHZ-55eco.input.32001.3
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(8) > 0 }}"
      heizung_status_warmwasserbereitung:
        friendly_name: "Heizung Status Warmwasserstatus"
        unique_id: tecalor.wpTHZ-55eco.input.32001.4
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(16) > 0 }}"
      heizung_status_nacherwaermung:
        friendly_name: "Heizung Status Nacherwaermung"
        unique_id: tecalor.wpTHZ-55eco.input.32001.5
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(32) > 0 }}"
      heizung_status_service:
        friendly_name: "Heizung Status Service"
        unique_id: tecalor.wpTHZ-55eco.input.32001.6
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(64) > 0 }}"
      heizung_status_evu_sperre:
        friendly_name: "Heizung Status EVU Sperre"
        unique_id: tecalor.wpTHZ-55eco.input.32001.7
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(128) > 0 }}"
      heizung_status_filterwechsel_beide:
        friendly_name: "Heizung Status Filterwechsel Beide"
        unique_id: tecalor.wpTHZ-55eco.input.32001.8
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(256) > 0 }}"
      heizung_status_lueftung:
        friendly_name: "Heizung Status Lueftung"
        unique_id: tecalor.wpTHZ-55eco.input.32001.9
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(512) > 0 }}"
      heizung_status_heizkreispumpe:
        friendly_name: "Heizung Status Heizkreispumpe"
        unique_id: tecalor.wpTHZ-55eco.input.32001.10
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(1024) > 0 }}"
      heizung_status_abtauen_verdampfer:
        friendly_name: "Heizung Status Abtauen Verdampfer"
        unique_id: tecalor.wpTHZ-55eco.input.32001.11
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(2048) > 0 }}"
      heizung_status_filterwechsel_abluft:
        friendly_name: "Heizung Status Filterwechsel Abluft"
        unique_id: tecalor.wpTHZ-55eco.input.32001.12
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(4096) > 0 }}"
      heizung_status_filterwechsel_zuluft:
        friendly_name: "Heizung Status Filterwechsel Zuluft"
        unique_id: tecalor.wpTHZ-55eco.input.32001.13
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(8192) > 0 }}"
      heizung_status_aufheizprogramm:
        friendly_name: "Heizung Status Aufheizprogramm"
        unique_id: tecalor.wpTHZ-55eco.input.32001.14
        value_template: "{{ states('sensor.heizung_betriebsstatus')|int|bitwise_and(16384) > 0 }}"

Does someone know how to write to the holding register?

Hi holzhannes, I read your issue a little bit late, but if you should not have a solution for your request up to now here is an example:
You can add a service action in an automation. In THZ 5.5eco (InternetServiceGateway) the ventilation is modbus register 1017. I added following code in automation.yaml to start ventilation during the day:

- id: "1666982605621"
  alias: Lüftung_ON
  description: ""
  trigger:
    - platform: time
      at: 01:00:00
    - platform: time
      at: 05:00:00
    - platform: time
      at: "10:00:00"
    - platform: time
      at: "15:00:00"
    - platform: time
      at: "20:00:00"
  condition: []
  action:
    - service: modbus.write_register
      data:
        address: 1017
        value: "1"
  mode: single

Has someone a working solution for Tecalor / Stiebel heatpumps without ISG?
Running a fhem container for this purpose and want to get rid of it^^

I have a Tecalor THZ heat pump running and I am testing Can Bus, while ISG pins are plugged parallel to my Can Bus wires. I adapted ‘ESP32 NodeMCU Development Board’ with attached ’ MCP2515 CAN Bus Module’. My Home Assistant is running on a Raspberry Pi 4. In the directory /config/esphome I stored the file ‘esphome-web-288ccc.yaml’ with the content below. When the log file is running in ESPHome I see all the messages running between ISG and THZ.
Up to now I am not able to trigger a sensor request. THZ is not answering to my requests. May be the reason is, that I defined my Can Bus node 0x180. I guess, THZ will only answer to requests from node ID 0x6a2, what is used by ISG. So, I have to try this option also.

Content of esphome-web-288ccc.yaml:

esphome:
  name: esphome-web-288ccc
  friendly_name: ESPHome Web 288ccc

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "yV2hpkUxilwEFPZXT8U5eD/HoyxdB3LsZJaGcwYAsZg="

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-288Ccc"
    password: "vWVSNEiAnHZA"

sensor:
  - platform: template
    id: hzg_aussentemp
    name: "HZG AUSSENTEMP"
    state_class: "measurement"
    device_class: temperature
    unit_of_measurement: "°C"
    icon: "mdi:thermometer-lines"
    update_interval: 30min

  - platform: template
    id: hzg_hk1_solltemp_tag
    name: "HZG HK1 SOLLTEMP TAG"
    state_class: "measurement"
    device_class: temperature
    unit_of_measurement: "°C"
    icon: "mdi:thermometer-lines"
    update_interval: 1min

  - platform: template
    id: hzg_ww_solltemp_tag
    name: "HZG WW SOLLTEMP TAG"
    state_class: "measurement"
    device_class: temperature
    unit_of_measurement: "°C"
    icon: "mdi:thermometer-lines"
    update_interval: 1min

  - platform: template
    id: hzg_betriebsart
    name: "HZG BETRIEBSART"
    state_class: "measurement"
    update_interval: 10min

captive_portal:
time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      # alle 3 Minuten (180 s) am CAN Bus nach dem Sensor-Status fragen:
      - seconds: /60
        then:
          # Byte 0:   0x3: 1. Teil der CAN Bus ID der Heizung
          # Byte 1:   0x1: 1 steht für Anfrage an den CAN Bus
          # Byte 2/3: 0x00: gehört mit der 3 oben zur CAN Bus ID der Heizung
          #                 ergibt zusammen 0x180 hex(3*8 + 00)
          # Byte 4/5: 0xfa: ist immer so bei thz ???????? 
          # Byte 6-9: 0x00,0x0c: ID Außentemp, siehe elsterTabelle.inc
          # Byte 10-13: reserviert für die Antwort später (in °C *10)

          #Außentemperatur 1/10 °C et dec value - ok /10
          - canbus.send: 
              can_id: 0x680
              data: [0x31,0x00,0xfa,0x00,0x0c,0x00,0x00]
          
          - lambda: |-
              ESP_LOGD("myesp", "Timer-Anfrage von 0x680 (ESP) an 0x180: [0x31,0x00,0xfa,0x00,0x0c,0x00,0x00]");

          - delay: 200ms
  
# hier kommen die Antworten vom CAN Bus:
spi:
  id: McpSpi
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

canbus:
  - platform: mcp2515
    id: my_mcp2515
    spi_id: McpSpi
    cs_pin: GPIO5
    can_id: 0x680
    use_extended_id: false
    bit_rate: 20kbps
    on_frame:

      # Telegramme von CAN ID 0x6a2 (ISG) in die Sensoren einarbeiten
      - can_id: 0x6a2
        then:
          # - lambda: ist Kennung für folgenden C++ Code
          # Byte 0:   0xd: Empfänger-ID des ESP32
          # Byte 1:   0x2: 1 steht für Anfrage an den CAN Bus
          # Byte 2/3: 0x00: gehört mit 'd' oben zur CAN Bus ID des ESP32
          #                 ergibt zusammen 0x680 hex(d*8 + 0)
          # Byte 4/5: 0xfa
          # Byte 6-9: 0x00,0x0c: Sensor-ID lt. ElsterTable.inc
          # Byte 10-13: Sensorwert
          - lambda: |-
              //int wert0 =int(x[0]);
              //int wert1 =int(x[1]);
              //int wert2 =int(x[2]);
              //int wert3 =int(x[3]);
              //int wert4 =int(x[4]);
              //int wert5 =int(x[5]);
              //int wert6 =int(x[6]);
              //ESP_LOGD("myesp", "von 0x6a2 (ISG) Hex: %2.2x %2.2x %x %2.2x %2.2x %2.2x %2.2x", wert0, wert1, wert2, wert3, wert4, wert5, wert6);

              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));
              
              if( iart == 2 ) {

                float sensorwert;

                switch( sensor_id ){

                  default:
                    ESP_LOGD("myesp", "Antwort von 0x6a2 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                            , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              } else if(iart == 0) {
                ESP_LOGD("myesp", "Schreiben von 0x6a2 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                        , ziel, iart, fa_kenng, sensor_id, antwort);
              } else {
                ESP_LOGD("myesp", "Anfrage von 0x6a2 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x"
                        , ziel, iart, fa_kenng, sensor_id );
              } // if( iart == 2)

      # Telegramme von CAN ID 0x180 (THZ) in die Sensoren einarbeiten
      - can_id: 0x180
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ) {
                  case 0x0003:
                    if( ziel == 0x680 ){
                      sensorwert = antwort / 10;
                      id(hzg_ww_solltemp_tag).publish_state(sensorwert);
                      ESP_LOGD("myesp", "hzg_ww_solltemp_tag (0x%4.4x): %f°C", sensor_id, sensorwert);
                    }
                    break;

                  case 0x000c:
                    if( ziel == 0x680 ){
                      sensorwert = antwort / 10;
                      id(hzg_aussentemp).publish_state(sensorwert);
                      ESP_LOGD("myesp", "hzg_aussentemp(0x%4.4x): %f°C", sensor_id, sensorwert);
                    }
                    break;

                  case 0x0012:
                    if( ziel == 0x680 ){
                      sensorwert = antwort;
                      id(hzg_betriebsart).publish_state(sensorwert);
                      ESP_LOGD("myesp", "hzg_betriebsart(0x%4.4x): %f°C", sensor_id, sensorwert);
                    }
                    break;

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x180 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else if(iart == 0) {
              //  ESP_LOGD("myesp", "Schreiben von 0x180 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              //} else {
              //  ESP_LOGD("myesp", "Anfrage von 0x180 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      # Telegramme von CAN ID 0x680 (ESP32)
      - can_id: 0x680
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ) {
                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x680 (ESP) an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              } else if(iart == 0) {
                ESP_LOGD("myesp", "Schreiben von 0x680 (ESP) an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                        , ziel, iart, fa_kenng, sensor_id, antwort);
              } else {
                ESP_LOGD("myesp", "Anfrage von 0x680 (ESP) an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                        , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)


      - can_id: 0x6a1
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x6a1 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x6a1 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x301
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  case 0x0005:
                    if( ziel == 0x680 ){
                      sensorwert = antwort / 10;
                      id(hzg_hk1_solltemp_tag).publish_state(sensorwert);
                      ESP_LOGD("myesp", "hzg_hk1_solltemp_tag (0x%4.4x): %f°C", sensor_id, sensorwert);
                    }
                    break;

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x301 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x301 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x302
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x302 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x302 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x514
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x514 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x514 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x480
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x480 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x480 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x601
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x601 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x601 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x602
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x602 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x602 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x603
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x603 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x603 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x604
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x604 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x604 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)

      - can_id: 0x605
        then:
          - lambda: |-  
              int ziel = (int(x[0] & 0xf0) << 3) + int(x[1]);
              int iart = int(x[0] & 0x0f);
              int fa_kenng =int(x[2]);
              int sensor_id = int( (x[3]<<8) + x[4] );
              float antwort = float(int( (x[5]<<8) + x[6] ));

              if( iart == 2 ) {
                float sensorwert;

                switch( sensor_id ){

                  default:
                    if( ziel == 0x6a2 )
                      ESP_LOGD("myesp", "Antwort von 0x605 an 0x%x fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
                              , ziel, fa_kenng, sensor_id, antwort);
                    break;
                } // switch( sensor_id )

              //} else {
              //  ESP_LOGD("myesp", "von 0x605 an 0x%x art: %d fa_kenng: %2.2x sensor_id: 0x%4.4x Wert: %.0f"
              //          , ziel, iart, fa_kenng, sensor_id, antwort);
              } // if( iart == 2)
1 Like

Same here. Do you have anything done in the meantime?

I have the same heat pump and would love to interface it without the ISG. Did you manage to get this running?

1 Like

You have seen this?
Or have you found a other solution?

Hi @MatthiasH, @holzhannes and all,

did you or anyone else make any progress on this, maybe?

Since I have the exact same heat pump at home and about to connect it to MCP2515/ESP32 (have no ISG), I am very interested to read about the CAN IDs and the detailed setup.

I was able to hook up the hardware to the CAN bus yesterday and plan to dive into the data details soon (waiting for the 4k7 resistor to arrive).

Happy to share my progress in case I am able to get it done.

Hi @Exezz78 & @saschae @wiwallo @Bernd2378,
I did last week the soldering with my Tecalor THZ 5.5 eco + ESP32 & MCP2515 CAN Interface.
I’m making progress since I was able to trigger register readouts from multiple can adresses and to decode the messages bus (mainly 0x6A1 is asking 0x180 and 0x301 for different values - return address seems still little off, maybe a bug in my decoder):

However, the adresses, mapping table and meanings of e.g. “PROGRAMMSCHALTER” seem to fit partially only. (e.g. mode “Automtaik” ix 0x0B00 instead of 0x0200 and saw a lot of “unknown” registers already on the bus).
My work is based on the repository below. Would you be interested in jointly completing the adaption of the mapping tables? I think about forking the repo and adapt it for the THZ (+ bugfixing and cleanup). I plan to go through all menu items on the THZ and monitor the bus to find the correct registers and their meaning - there a joint efford would be great.

Hi croessi,

that´s great! Thanks for sharing!

I have been trying the repo you shared but found the one from kr0ner based on THZ 504 even closer to the THZ 5.5 CAN IDs.

Several features ran out out the box for me (e.g. water & room temp, ventilation incl. change in settings, …) although there are still some missing/wrongly/not existing assigned IDs guess due to the difference in the heat pump features (screenshot attached).

Happy to contribute to a 5.5 specific repo where I can (my knowledge is very limited and based on try & error mostly).

Hi @Exezz78,
thanks I had a look at the repo from @kr0ner and I think he did a very similar thing. However, he seems to be more skilled and the repo already has many things I had in mind - so I’ll contribute there to integrate my thz 5.5 findings - Ideally even without forking. Let’s team up there - I’ll create a branch and share it here.

2 Likes