Automating Heat Pump Flow Temperature with Home Assistant cover image

Automating Heat Pump Flow Temperature with Home Assistant

Simon Bennett January 4, 2025

If you're running a heat pump, getting the flow temperature right is crucial for efficiency. Too high and you're wasting energy. Too low and your home won't be warm enough. The good news is that with Home Assistant, you can automate this entirely using weather compensation curves.

The Mathematics Behind Flow Temperature

Your heat pump's flow temperature needs to vary based on the outdoor temperature. When it's mild outside, you need less heat, so the flow temperature can be lower. This relationship isn't linear though - it follows the physics of heat transfer through radiators.

Key Formula: Heat Load Calculation

First, we calculate how much heat your building needs right now:

Heat Load = Design Heat Loss × (Indoor Target - Outdoor Current) / (Indoor Design - Outdoor Design)

For example, if your home has a 6kW heat loss at -3°C design temperature:

  • At 10°C outside: Heat Load = 6 × (21 - 10) / (21 - (-3)) = 2.75kW
  • At 0°C outside: Heat Load = 6 × (21 - 0) / (21 - (-3)) = 5.25kW

Radiator Output and Flow Temperature

Radiators don't output heat linearly with temperature. They follow this relationship:

Actual Output = Rated Output × (ΔT_actual / 50)^1.3

Where ΔT is the difference between mean water temperature and room temperature.

To find the required flow temperature:

  1. Calculate the ratio: Output Needed / Radiator Capacity
  2. Find required ΔT: 50 × (ratio)^(1/1.3)
  3. Mean water temp = Room temp + ΔT
  4. Flow temp = Mean water temp + (Heat pump ΔT / 2)

Home Assistant Template Configuration

Here's a complete Home Assistant template sensor that calculates the optimal flow temperature based on current conditions:

template:
  - sensor:
      - name: "Heat Pump Flow Temperature Target"
        unique_id: heat_pump_flow_temp_target
        unit_of_measurement: "°C"
        device_class: temperature
        state: >
          {% set design_heat_loss = 6.0 %}           {# kW at design conditions #}
          {% set design_outdoor_temp = -3.0 %}       {# Design outdoor temp °C #}
          {% set design_indoor_temp = 21.0 %}        {# Design indoor temp °C #}
          {% set radiator_output_dt50 = 10.0 %}      {# Total radiator output at ΔT50 in kW #}
          {% set hp_delta_t = 5.0 %}                 {# Heat pump flow-return difference °C #}

          {# Get current conditions from your sensors #}
          {% set target_indoor = states('sensor.target_indoor_temperature') | float(21) %}
          {% set current_outdoor = states('sensor.outdoor_temperature') | float(10) %}

          {# Calculate design temperature difference #}
          {% set design_temp_diff = design_indoor_temp - design_outdoor_temp %}

          {# Calculate current temperature difference #}
          {% set current_temp_diff = target_indoor - current_outdoor %}

          {# Calculate heat load ratio #}
          {% set heat_load_ratio = current_temp_diff / design_temp_diff %}

          {# Calculate actual heat load needed #}
          {% set actual_heat_load = design_heat_loss * heat_load_ratio %}

          {# Only calculate if heating is needed #}
          {% if actual_heat_load > 0 %}
            {# Calculate radiator performance ratio #}
            {% set radiator_ratio = actual_heat_load / radiator_output_dt50 %}

            {# Calculate required delta T using radiator equation #}
            {% set required_dt = 50 * (radiator_ratio ** (1/1.3)) %}

            {# Calculate mean water temperature #}
            {% set mean_water_temp = target_indoor + required_dt %}

            {# Calculate flow temperature #}
            {% set flow_temp = mean_water_temp + (hp_delta_t / 2) %}

            {# Clamp between reasonable limits #}
            {{ [flow_temp, 55] | min | round(1) }}
          {% else %}
            {# No heating needed #}
            {{ target_indoor }}
          {% endif %}

      - name: "Heat Pump Current Heat Load"
        unique_id: heat_pump_current_load
        unit_of_measurement: "kW"
        device_class: power
        state: >
          {% set design_heat_loss = 6.0 %}
          {% set design_outdoor_temp = -3.0 %}
          {% set design_indoor_temp = 21.0 %}
          {% set target_indoor = states('sensor.target_indoor_temperature') | float(21) %}
          {% set current_outdoor = states('sensor.outdoor_temperature') | float(10) %}

          {% set design_temp_diff = design_indoor_temp - design_outdoor_temp %}
          {% set current_temp_diff = target_indoor - current_outdoor %}
          {% set heat_load_ratio = current_temp_diff / design_temp_diff %}
          {% set actual_heat_load = design_heat_loss * heat_load_ratio %}

          {{ [actual_heat_load, 0] | max | round(2) }}

      - name: "Heat Pump Estimated COP"
        unique_id: heat_pump_estimated_cop
        state: >
          {% set flow_temp = states('sensor.heat_pump_flow_temperature_target') | float(35) %}
          {% set outdoor_temp = states('sensor.outdoor_temperature') | float(10) %}
          {% set lift = flow_temp - outdoor_temp %}

          {# COP estimation based on temperature lift #}
          {% if lift <= 15 %}
            {{ 6.0 }}
          {% elif lift <= 22 %}
            {{ 6.0 - (lift - 15) * (1.0 / 7) }}
          {% elif lift <= 30 %}
            {{ 5.0 - (lift - 22) * (0.5 / 8) }}
          {% elif lift <= 34 %}
            {{ 4.5 - (lift - 30) * (0.5 / 4) }}
          {% elif lift <= 38 %}
            {{ 4.0 - (lift - 34) * (0.5 / 4) }}
          {% elif lift <= 45 %}
            {{ 3.5 - (lift - 38) * (0.5 / 7) }}
          {% elif lift <= 52 %}
            {{ 3.0 - (lift - 45) * (0.5 / 7) }}
          {% elif lift <= 60 %}
            {{ 2.5 - (lift - 52) * (0.5 / 8) }}
          {% elif lift <= 70 %}
            {{ 2.0 - (lift - 60) * (0.5 / 10) }}
          {% else %}
            {{ 1.5 }}
          {% endif %}

      - name: "Heat Pump Electrical Consumption"
        unique_id: heat_pump_electrical_consumption
        unit_of_measurement: "kW"
        device_class: power
        state: >
          {% set heat_load = states('sensor.heat_pump_current_heat_load') | float(0) %}
          {% set cop = states('sensor.heat_pump_estimated_cop') | float(3) %}

          {% if cop > 0 %}
            {{ (heat_load / cop) | round(2) }}
          {% else %}
            {{ 0 }}
          {% endif %}

      - name: "Heat Pump Flow Rate Required"
        unique_id: heat_pump_flow_rate_required
        unit_of_measurement: "L/min"
        state: >
          {% set heat_load = states('sensor.heat_pump_current_heat_load') | float(0) %}
          {% set hp_delta_t = 5.0 %}  {# Your heat pump's delta T #}

          {# Q = m × c × ΔT, where c = 4.18 kJ/kg·K for water #}
          {# Flow rate in kg/s = Q / (4.18 × ΔT) #}
          {# Convert to L/min (1 kg/s ≈ 60 L/min for water) #}

          {% if hp_delta_t > 0 %}
            {% set flow_rate_kg_s = heat_load / (4.18 * hp_delta_t) %}
            {% set flow_rate_l_min = flow_rate_kg_s * 60 %}
            {{ flow_rate_l_min | round(1) }}
          {% else %}
            {{ 0 }}
          {% endif %}

Customizing for Your System

To adapt these templates for your heat pump system, you'll need to modify these parameters:

  1. design_heat_loss: Your home's heat loss at design conditions (from your MCS calculation)
  2. design_outdoor_temp: Your local design outdoor temperature (typically -3°C in the UK)
  3. design_indoor_temp: Your design indoor temperature (usually 21°C)
  4. radiator_output_dt50: Total output of all your radiators at ΔT50
  5. hp_delta_t: Your heat pump's flow-return temperature difference (typically 5°C)

Integrating with Your Heat Pump

Once you have the target flow temperature calculated, you need to send it to your heat pump. The method varies by manufacturer:

Samsung (via SmartThings)

automation:
  - alias: "Update Samsung Heat Pump Flow Temperature"
    trigger:
      - platform: state
        entity_id: sensor.heat_pump_flow_temperature_target
    action:
      - service: climate.set_temperature
        target:
          entity_id: climate.samsung_heat_pump
        data:
          target_temp_low: "{{ (states('sensor.heat_pump_flow_temperature_target') | float / 0.5) | round * 0.5 }}"

Mitsubishi (via MELCloud)

automation:
  - alias: "Update Mitsubishi Heat Pump Flow Temperature"
    trigger:
      - platform: state
        entity_id: sensor.heat_pump_flow_temperature_target
    action:
      - service: water_heater.set_temperature
        target:
          entity_id: water_heater.mitsubishi_heat_pump
        data:
          temperature: "{{ (states('sensor.heat_pump_flow_temperature_target') | float / 0.5) | round * 0.5 }}"

Modbus-enabled Heat Pumps

automation:
  - alias: "Update Heat Pump Flow Temperature via Modbus"
    trigger:
      - platform: time_pattern
        minutes: "/5"  # Update every 5 minutes
    action:
      - service: modbus.write_register
        data:
          hub: heat_pump
          address: 40001  # Check your heat pump's modbus register map
          value: "{{ ((states('sensor.heat_pump_flow_temperature_target') | float / 0.5) | round * 0.5 * 10) | int }}"

Advanced Features

Adding Solar Gain Compensation

If you have solar sensors, you can reduce flow temperature when the sun is heating your home:

{% set solar_gain = states('sensor.solar_irradiance') | float(0) %}
{% set solar_reduction = (solar_gain / 1000) * 2 %}  {# 2°C reduction per kW/m² #}
{% set adjusted_flow_temp = flow_temp - solar_reduction %}

Night Setback with Gradual Recovery

For overnight temperature reduction without the morning spike:

{% set hour = now().hour %}
{% if 22 <= hour or hour < 6 %}
  {% set setback = 2.0 %}  {# 2°C setback at night #}
{% elif 6 <= hour < 8 %}
  {% set setback = 2.0 - ((hour - 6) / 2 * 2.0) %}  {# Gradual recovery #}
{% else %}
  {% set setback = 0 %}
{% endif %}
{% set target_indoor = target_indoor - setback %}

Weather Forecast Integration

Anticipate heating needs based on upcoming weather:

{% set forecast_temp_2h = state_attr('weather.home', 'forecast')[0].temperature %}
{% set trend = forecast_temp_2h - current_outdoor %}
{% set anticipation_factor = trend * 0.5 %}  {# Adjust flow temp by half the trend #}
{% set flow_temp = flow_temp - anticipation_factor %}

Monitoring and Optimization

Create a dashboard to monitor your system's performance:

type: vertical-stack
cards:
  - type: gauge
    entity: sensor.heat_pump_flow_temperature_target
    name: Target Flow Temperature
    min: 20
    max: 55
    severity:
      green: 20
      yellow: 35
      red: 45

  - type: entities
    entities:
      - sensor.heat_pump_current_heat_load
      - sensor.heat_pump_estimated_cop
      - sensor.heat_pump_electrical_consumption
      - sensor.heat_pump_flow_rate_required

  - type: history-graph
    entities:
      - sensor.heat_pump_flow_temperature_target
      - sensor.outdoor_temperature
    hours_to_show: 24

Benefits of Automated Flow Temperature Control

  1. Improved Efficiency: Always running at the optimal flow temperature can improve seasonal COP by 20-30%
  2. Consistent Comfort: No overshooting or undershooting room temperature
  3. Reduced Wear: Lower average flow temperatures extend heat pump lifespan
  4. Energy Savings: Typically 15-25% reduction in heating costs
  5. Silent Operation: Lower flow temperatures mean quieter operation

Troubleshooting Common Issues

Flow temperature oscillating wildly? Add damping to smooth out changes:

{% set previous = states('sensor.heat_pump_flow_temperature_target') | float(35) %}
{% set damping = 0.3 %}  {# 30% of new value, 70% of previous #}
{% set smoothed = (flow_temp * damping) + (previous * (1 - damping)) %}

Heat pump cycling on/off? Ensure minimum flow temperature during mild weather:

{% set minimum_flow = 28 %}  {# Prevent cycling #}
{{ [flow_temp, minimum_flow] | max }}

Rooms not reaching temperature? Check your radiator sizing calculation - you may need to adjust the radiator_output_dt50 value upward.

Conclusion

Automating flow temperature control with Home Assistant transforms your heat pump from a dumb on/off system into an intelligent, continuously optimizing heating system. The templates provided here give you a solid foundation that you can customize for your specific setup.

Remember: heat pumps work best when they run continuously at the lowest possible flow temperature. These automations help achieve exactly that, maximizing both comfort and efficiency.

Want a ready-made solution? Warm Energy does all this automatically, learning your home's specific characteristics and optimizing for both comfort and cost.