[Project] Lambda Heat Pump Integration via Modbus TCP

Hi everyone,

I would like to share my configuration for integrating Lambda Heat Pumps (https://www.lambda-wp.at/) into Home Assistant. Since Lambda uses a standard Modbus TCP protocol, we can achieve a deep integration without any extra hardware, allowing for real-time monitoring of efficiency (COP), temperatures, and operating states.

Prerequisites

  1. Modbus TCP Enabled: Ensure Modbus TCP is activated in your Lambda controller settings.
  2. Static IP: It is highly recommended to set a DHCP Reservation (Static IP) for your heat pump in your router settings to ensure Home Assistant doesn’t lose the connection after a reboot.
  3. Documentation: You can find the official Modbus register map here: Lambda Modbus Protocol (PDF)

Configuration

The setup is split into two parts: the Modbus sensors and Template sensors for calculated values like “Spread” (Spreizung) and human-readable states.

1. Modbus Sensor Setup (modbus.yaml or configuration.yaml)

This block defines the connection and pulls the raw data from the registers.

# Example configuration for Modbus TCP
modbus:
  - name: lambda_heatpump
    type: tcp
    host: YOUR_HEAT_PUMP_IP  # Replace with your actual IP address, e.g., 192.168.1.133
    port: 502
    delay: 2
    timeout: 5

    sensors:
      # --- Status & Environment ---
      - name: "Lambda Operating State"
        unique_id: lambda_op_state_fixed
        address: 1
        input_type: holding
        scan_interval: 30

      - name: "Lambda Ambient Temp"
        unique_id: lambda_ambient_temp_fixed
        address: 2
        input_type: holding
        data_type: int16
        scale: 0.1
        precision: 1
        unit_of_measurement: "°C"
        device_class: temperature
        scan_interval: 60

      # --- Temperatures & Flow ---
      - name: "Lambda Flow Common"
        unique_id: lambda_flow_common_fixed
        address: 5002
        input_type: holding
        data_type: int16
        scale: 0.1
        precision: 1
        unit_of_measurement: "°C"
        device_class: temperature
        scan_interval: 45

      - name: "Lambda Return EG"
        unique_id: lambda_return_eg_fixed
        address: 5003
        input_type: holding
        data_type: int16
        scale: 0.1
        precision: 1
        unit_of_measurement: "°C"
        device_class: temperature
        scan_interval: 45

      - name: "Lambda Return NG"
        unique_id: lambda_return_ng_fixed
        address: 5103
        input_type: holding
        data_type: int16
        scale: 0.1
        precision: 1
        unit_of_measurement: "°C"
        device_class: temperature
        scan_interval: 45

      - name: "Lambda Return OG"
        unique_id: lambda_return_og_fixed
        address: 5203
        input_type: holding
        data_type: int16
        scale: 0.1
        precision: 1
        unit_of_measurement: "°C"
        device_class: temperature
        scan_interval: 45

      # --- Efficiency & Power ---
      - name: "Lambda COP Internal"
        unique_id: lambda_cop_intern_fixed
        address: 1013
        input_type: holding
        data_type: int16
        scale: 0.01
        precision: 2
        unit_of_measurement: "COP"
        state_class: measurement
        scan_interval: 60
        nan_value: 0.0
        
      - name: "Lambda Power Input"
        unique_id: lambda_power_input_fixed
        address: 1012
        input_type: holding
        data_type: int16
        scale: 1
        precision: 0
        unit_of_measurement: "W"
        device_class: power
        state_class: measurement
        scan_interval: 60
        
      - name: "Lambda Actual Heating Capacity"
        unique_id: lambda_actual_heating_capacity_fixed
        address: 1011
        input_type: holding
        data_type: int16
        scale: 0.1
        precision: 1
        unit_of_measurement: "kW"
        device_class: power
        state_class: measurement
        scan_interval: 60

2. Template Sensors (template.yaml)

These sensors calculate the temperature delta (spread) between flow and return for different floors and translate the numeric operating state into readable text.

- sensor:
    # Calculation of temperature spread for different zones
    - name: "Spread Ground Floor (EG)"
      unique_id: spreizung_eg
      unit_of_measurement: "K" 
      state: >
        {% set flow = states('sensor.lambda_flow_common') | float(none) %}
        {% set return = states('sensor.lambda_return_eg') | float(none) %}
        {% if flow is not none and return is not none %}
          {% set diff = (flow - return) | round(1) %}
          {% if -20 < diff < 60 %} {{ diff }} {% else %} {{ states('sensor.spreizung_eg') | float(0) }} {% endif %}
        {% else %} 0.0 {% endif %}

    - name: "Spread Next Floor (NG)"
      unique_id: spreizung_ng
      unit_of_measurement: "K"
      state: >
        {% set flow = states('sensor.lambda_flow_common') | float(none) %}
        {% set return = states('sensor.lambda_return_ng') | float(none) %}
        {% if flow is not none and return is not none %}
          {% set diff = (flow - return) | round(1) %}
          {% if -20 < diff < 60 %} {{ diff }} {% else %} {{ states('sensor.spreizung_ng') | float(0) }} {% endif %}
        {% else %} 0.0 {% endif %}

    - name: "Spread Upper Floor (OG)"
      unique_id: spreizung_og
      unit_of_measurement: "K"
      state: >
        {% set flow = states('sensor.lambda_flow_common') | float(none) %}
        {% set return = states('sensor.lambda_return_og') | float(none) %}
        {% if flow is not none and return is not none %}
          {% set diff = (flow - return) | round(1) %}
          {% if -20 < diff < 60 %} {{ diff }} {% else %} {{ states('sensor.spreizung_og') | float(0) }} {% endif %}
        {% else %} 0.0 {% endif %}

    # Translation of Operating Mode
    - name: "Lambda Operating Mode Text"
      unique_id: lambda_mode_text_template
      state: >
        {% set modes = {0: 'OFF', 1: 'AUTOMATIC', 2: 'MANUAL', 3: 'ERROR'} %}
        {{ modes.get(states('sensor.lambda_operating_state') | int(0), 'Unknown') }}
      icon: >
        {% set state = states('sensor.lambda_operating_state') | int(0) %}
        {% if state == 0 %} mdi:power-off
        {% elif state == 1 %} mdi:refresh-auto
        {% elif state == 2 %} mdi:hand-pointing-up
        {% elif state == 3 %} mdi:alert-octagon
        {% else %} mdi:help-circle {% endif %}      
        
    # Stable COP sensor for clean Long Term Statistics graphing
    - name: "Lambda COP Graph"
      unique_id: cop_grafik_stable
      unit_of_measurement: "COP"
      state_class: measurement
      state: >
        {{ states('sensor.lambda_cop_internal') | float(0.0) }}
      availability: >
        {{ true }}

Features of this setup

  • Resilience: The template sensors include safety checks to prevent unrealistic spikes in your graphs if a Modbus read fails.
  • Human Readable: Converts numeric codes (0, 1, 2) into clear status text (OFF, AUTO, etc.).
  • Calculated Metrics: Automatically calculates the temperature delta (Spread), which is crucial for balancing your heating circuits.

I hope this helps anyone looking to integrate their Lambda system!