IDM heatpump integration via modbus (pure HA)

IDM Heatpump Modbus integration
I like to share my integration of the IDM AERO 12 Heatpump. The concepts shared here, should also work with other IDM heatpumps.

General thoughts

A lot of terms used here are in German, hope this is OK, as long as I expect that IDM-Heatpumps are mainly sold in Austria, Switzerland and Germany.

Warranty
This is a hobby project, so there is no gurantee, support or warranty at all. You are responsible if you use parts of this project.

Prepare IDM Heatpump to eanble Modbus
You need to enter the IDM settings and switch to “Fachmann Ebene” by entering the expert code (first level is OK, if you don’t know the code, look in the internet or ask your supplier).
Go to ‘Gebäudeleittechnik’, enter the setting ‘Modbus TCP’ and enable it.
2024-03-09 13_33_38-Navigator Pro

If you can’t manage this, consider to not continue with this project or ask the IDM-Support, I had really good experiences.
There is also a page ‘GLT Monitor’ to see all the available numbers and monitor/debug the values. See also these documents:

Important Note:
Make sure to not write too often if the settings via modbus as some of them are stored in EEPROM, see on page 12 modbus description!

Homeassistant
Let’s assume that you have HA already running and you have some experiences with it.
I use Visual Studio code to connect to my HA config via SSH (not described here).
I personally don’t like to put all stuff into ‘configuration.yaml’, I also don’t like to spread a project over multiple files like ‘automation.yaml’. Fortunately there is a solution to properly seperate your projects: packages.
I created a subfolder “packages” and placed my projects there. You can have the same items as in configuration.yaml in each subfile, this does a perfect job to separate the different projects. You can simply switch them on/off by just comenting one line.
Packages
This should also work with your current setup, to not mix up this new code with you existing one.

Integration
This integration uses only the standard HA configurations and integrations, there is no need to use HACS or sourcecode - just configurations, using standard HA components.

Modbus
This integration uses modbus to communicate with the IDM heatpump.
Unfortunately the IDM modbus interface uses a different Endian-Format (Big-Endian, Little-Endian). So you need to provide the option swap: word when reading float values.
When writing back such float values, you need to convert the float value by swapping the bytes again. This look ugly (see in automation definitions), but works good. If someone knows a better method, please let me know.
! Make sure to use your IP-address of your IDM device (see modbus: configuration) !

Integration

PV integration
This project needs to know the current surplus PV power. So this package needs some kind of external sensors. In my setup my converter is a solaredge system, so you might need to adapt/rename these sensors (see my configuration below).

Features
All the features described here are working great for me and my setup, you might have other ideas/preferences, so just disable or modify these parts. Only use all the features if you really understand how they work.
I’m not a heating expert, I did not found more documentation on how the heating works, so these assumptions mentioned here might be incomplete/incorrect.
The overall idea is to have a better overview on what the heatpump is doing and better understand how it works. Whenever there is enough PV power I want to use this to store energy in my buffer (850 l).
Typically the heatpump runs if there is need to do so. This means if the warm-water is below 48° (depending on the settings in IDM heatpump) it starts preparing water till appr. 53°. And if the heating is on, it will start if the heating water is no more sufficient to provide the needed temperature (Vorlauf). This is working without any integration - this is what the heatpump does itself.
If you want to ‘store’ the remaining PV power, we need to tell the heatpump to prepare warm water (more than currently needed) and store it for later use. Fortunately there is a parameter that IDM designed for that: 74. In short: if you signal that there is remaining power, the heating will use it.
In detail: the IDM will only start if there is enough energy. For my device this is about 2.1 to 2.8 kW, see the page ‘Photovoltaik/PV Leistung’ Value ‘Leistung Vorrang’. This value depends on several parameters (not sure) but for sure on the current temperature outside - 2.1 in summer, 2.8 in winter. Typically the heating starts using PV if the provided value is 500W higher than what is expected (to be sure there is enough energy). Then it will run at least 10 minutes - no matter if there is enough energy or not. It will also not start again prior a 10 minutes delay to have not too much toggles (bad for lifetime).

Happy Path:
The happy (naive) path is just provide the PV power and the heatpump uses it. It does adjust the used power, so the more you provide the more it uses - for my model 1.5-4 KW. If the water is warm enough, see settings on page ‘Photovoltaik’ (please discuss these values with your supplier/expert to not damage the device), the heatpump will stop. It will also stop ‘most times’ if the power is too low. Sometimes it keeps on running even if there is no more power reported !?
Unhappy Path:
Let’s assume there are 3000W available, the heatpump starts… If you now want to make a coffee, the available power will drop to e.g. 800W - heatpump will probably stop. Even if this is only for 2 minutes, as described above it will not start again for 10 minutes. Wouldn’t it be great if it keeps running? Coffee is just an example for any consumer like oven, washing machine, dryer, dish washer etc…
I have a battery, on rainy days sometimes I have constantly 2000W, Battery is full, but heatpump does not start, because will start when e.g. 2,4 kW + 500W at 2,9kW. But it would be fine to let it run and use the battery that can be relaoded later.
So I introduced a buffer value “Puffer für WP Überschuss”. If this value is 0W you have the happy path, the heatpump will just see what PV is delivering. If it is +1000W, the heatpump will leave a 1000W unused so this can be used for other things. If it is negative, like -2000, the system reports more power than available (might use the battery if available) but will let the hetpump run (or not stop if there is some shadow).
There is also a maximum value ‘Maximal WP Überschuss’ that limit the power usable by heatpump. The remaining power reported will include also the power the heatpump really uses, so even if providing ‘artificial’ values, the heatpump sees a response if it changes the used power.

I know, quite complex, let’s look at some examples (I add a 700W if HP is off to make it start):

| Use case                  | PV Power | Buffer | Max  | HP Usage | PV to HP|
-----------------------------------------------------------------------------
| HP is off (+700 to start) | 2800     |      0 | 5000 |        0 |    3500 |
| HP starts                 | 2800     |      0 | 5000 |     1100 |    1700 |
| HP is running             | 2800     |      0 | 5000 |     2300 |     500 |
| high PV                   | 5500     |      0 | 5000 |     2300 |    2700 |
| dawning                   | 2350     |      0 | 5000 |     2300 |      50 |
| WP adjust usage           | 2300     |      0 | 5000 |     2150 |     150 |
| dawning                   | 2000     |      0 | 5000 |     2050 |       0 |
| WP stops                  | 1800     |      0 | 5000 |        0 |    2500 |

| high PV limited           | 5000     |      0 | 3000 |     2800 |     200 |
| high PV positive buffer   | 5000     |   2100 | 3000 |     2800 |     100 |

| Force run                 | 2000     |  -4000 | 3000 |        0 |    3700 |
| HP is running             | 2000     |  -4000 | 3000 |     2200 |     800 |
| Coffee 3100W              | -1100    |  -4000 | 3000 |     2200 |     700 |

| simple case HP is running | 2800     |      0 |    0 |     2300 |     500 |

Call me crazy, but it works for my needs. To have kind of a normal behaviour, just set buffer to 0 and max to 4000. Then you will have the plain PV control as designed by IDM.
To make it even worse you can use a dummy value ‘Dummy Value 2 Überschuss’ (this is not tied to real PV power at all), they are used if the switch ‘Übertrage reale PV Werte zu WP’ is off. Typicall set it to 0.

Additional
‘Wärmepumpe ein/aus automatisch’:
shall start the heatpump once a day (after 10am) if the battery is already 70+% loaded, no matter if there is currently enough power. There should be some power, because it was enough to load the battery.
This works perfectly for summer time to avoid having the heatpump starting between 8pm and 8am in the night (without PV). So typically the HP runs around 1pm fills up warm-water to 63° which is enough till the next day :slight_smile: .
This does not really work for winter time when the heating is on :frowning: . However even when heating is needed, it makes sense to let the HP run when it is warmer and high chance of PV.
‘Anforderung Heizen/Warmwasser’ is experimental.

Let’s discuss how to do it better, or which kind of drugs might be helpful for this crazy guy.

======================================================

Code

content of packages\idm.yaml:

input_boolean:
  use_pv_values:
    name: Übertrage reale PV Werte zu WP
    initial: false
  use_prefer_wp_before_battery:
    name: Bevorzuge Wärmepumpe vor Batterie
    initial: true
  wp_on_off_automatically:
    name: Wärmepumpe ein/aus automatisch
    initial: true
  wp_did_run_today:
    name: Wärmepumpe lief heute schon
    initial: true

input_number:
  dummy_value_1:
    name: "Dummy Value 1 Power"
    initial: 0
    min: -10000
    max: 10000
    step: 100
    mode: box
    unit_of_measurement: "W"
    icon: mdi:lightning-bolt
  dummy_value_2:
    name: "Dummy Value 2 Überschuss"
    initial: 0
    min: -10000
    max: 10000
    step: 100
    mode: box
    unit_of_measurement: "W"
    icon: mdi:lightning-bolt
  wp_buffer:
    name: "Puffer für WP Überschuss"
    initial: 0
    min: -10000
    max: 10000
    step: 100
    mode: box
    unit_of_measurement: "W"
    icon: mdi:lightning-bolt
  wp_max_power:
    name: "Maximal WP Überschuss"
    initial: 2700
    min: -10000
    max: 10000
    step: 100
    mode: box
    unit_of_measurement: "W"
    icon: mdi:lightning-bolt
  idm_stopanforderungheizen:
    name: "Stop Anforderung Heizen"
    initial: 60
    min: 25
    max: 65
    step: 1
    mode: slider
    unit_of_measurement: "C"
    icon: mdi:thermometer-alert
  idm_stopanforderungwarmwasser:
    name: "Stop Anforderung Warmwasser"
    initial: 60
    min: 25
    max: 65
    step: 1
    mode: slider
    unit_of_measurement: "C"
    icon: mdi:thermometer-alert

switch:
- platform: template
  switches:
    idm_anforderungheizen:
      unique_id: "idm_anforderungheizen"
      value_template: "{{ is_state('sensor.idm_AnforderungHeizen', '1') }}"
      turn_on:
      - service: modbus.write_register
        data:
          hub: idm_wp
          unit: 1
          address: 1710
          value: 1
      turn_off:
      - service: modbus.write_register
        data:
          hub: idm_wp
          unit: 1
          address: 1710
          value: 0
- platform: template
  switches:
    idm_anforderungwarmwasser:
      unique_id: "idm_anforderungwarmwasser"
      value_template: "{{ is_state('sensor.idm_AnforderungWarmWasser', '1') }}"
      turn_on:
      - service: modbus.write_register
        data:
          hub: idm_wp
          unit: 1
          address: 1712
          value: 1
      turn_off:
      - service: modbus.write_register
        data:
          hub: idm_wp
          unit: 1
          address: 1712
          value: 0


# NEEDS RESTART !!!
modbus:
  - name: "idm_wp"
    type: tcp
    # use your own IP-Address
    host: 192.168.1.54
    port: 502
    delay: 1 # after reconnect
    # deprecated -> retries: 5
    # retry_on_empty: true
    # message_wait_milliseconds: 500
    # timeout: 10
    sensors:
    - name: "idm_gemittelteAussentemperatur"
      unique_id: "idm_gemittelteaussentemperatur"
      slave: 1
      scale: 1
      offset: 0
      unit_of_measurement: °C
      address: 1002
      data_type: float32
      scan_interval: 30
      precision: 1
      swap: word
    - name: "idm_Aussentemperatur"
      unique_id: "idm_aussentemperatur"
      slave: 1
      scale: 1
      offset: 0
      unit_of_measurement: °C
      address: 1000
      data_type: float32
      scan_interval: 30
      precision: 1
      swap: word
    - name: "idm_Leistungsaufnahme"
      unique_id: idm_Leistungsaufnahme
      slave: 1
      scale: 1
      offset: 0
      address: 4122
      data_type: float32
      scan_interval: 5
      precision: 3
      swap: word
      unit_of_measurement: kW
      device_class: power
    - name: "idm_AktuellerPhotovoltaikUeberschuss"
      unique_id: idm_AktuellerPhotovoltaikUeberschuss
      slave: 1
      scale: 1
      offset: 0
      address: 74
      data_type: float32
      scan_interval: 5
      precision: 3
      swap: word
      unit_of_measurement: kW
      device_class: power
    - name: "idm_AktuellePhotovoltaikProduktion"
      unique_id: idm_AktuellePhotovoltaikProduktion
      slave: 1
      scale: 1
      offset: 0
      unit_of_measurement: kW
      address: 78
      data_type: float32
      scan_interval: 30
      precision: 3
      swap: word
    - name: "idm_Vorlauftemperatur"
      unique_id: "idm_vorlauftemperatur"
      slave: 1
      scale: 1
      offset: 0
      unit_of_measurement: °C
      address: 1350
      data_type: float32
      scan_interval: 30
      precision: 1
      swap: word
    - name: "idm_Waermespeichertemperatur"
      unique_id: "idm_waermespeichertemperatur"
      slave: 1
      scale: 1
      offset: 0
      unit_of_measurement: °C
      address: 1008
      data_type: float32
      scan_interval: 30
      precision: 1
      swap: word
    - name: "idm_TrinkwassertemperaturUnten"
      unique_id: "idm_trinkwassertemperaturunten"
      slave: 1
      scale: 1
      offset: 0
      unit_of_measurement: °C
      address: 1012
      data_type: float32
      scan_interval: 30
      precision: 1
      swap: word
    - name: "idm_AnforderungHeizen"
      unique_id: idm_anforderungsheizen
      slave: 1
      address: 1710
      data_type: uint16
      scan_interval: 5
    - name: "idm_AnforderungWarmWasser"
      unique_id: idm_anforderungwarmwasser
      slave: 1
      address: 1712
      data_type: uint16
      scan_interval: 5

automation:
- id: solaredge_to_idm
  alias: SolarEdge to IDM
  description: Update von PV Überschuss
  trigger:
  - platform: state
    entity_id:
    - sensor.pv_uberschuss_fur_wp
    - input_boolean.use_pv_values
    # - sensor.pv_leistung_fur_wp
    from:
  condition:
    # Check if input values are defined
    - condition: template
      value_template: "{{ is_number(states('sensor.pv_uberschuss_fur_wp')) }}"
  action:
  - service: modbus.write_register
    data:
      hub: idm_wp
      unit: 1
      address: 74
      value: '[ {{ unpack(pack(states(''sensor.pv_uberschuss_fur_wp'')|float, ">f"), ">H", offset=2) }}, 
      {{  unpack(pack(states(''sensor.pv_uberschuss_fur_wp'')|float, ">f"), ">H") }} ]'
  # This is not needed, because WP dows not use this value !?
  # - service: modbus.write_register
  #   data:
  #     hub: idm_wp
  #     unit: 1
  #     address: 78
  #     value: >
  #       [ {{ unpack(pack(states("sensor.pv_leistung_fur_wp")|float, ">f"), ">H", offset=2) }}, 
  #         {{ unpack(pack(states("sensor.pv_leistung_fur_wp")|float, ">f"), ">H") }} ]
  mode: queued

### Automatische Wärmepumpensteuerung ###
# setzt das Flag, welches angibt, ob die WP schon lief zurĂźck, d.h. vor 10:00 Uhr soll die WP ohenhin nicht laufen
- id: reset_did_wp_run_totay_automation
  alias: reset did wp run totay
  description: 'Resets the WP did run flag once daily'
  trigger:
  - platform: time
    at: '10:00:00'
  condition: []
  action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.wp_did_run_today
  mode: queued

# wenn WP mind. 1000W zieht, läuft sie, und das Flag WP ist gelaufen wird schon mal gesetzt
- id: update_did_wp_run_totay_automation
  alias: update did wp run totay
  description: 'Updates the WP did run flag when WP is running'
  trigger:
  - platform: state
    entity_id:
    - sensor.idm_leistungsaufnahme
  condition:
    #alias: "WP did not run today (Flag is off) AND WP is running"
    and:    
      - condition: state
        entity_id: input_boolean.wp_did_run_today
        state: "off"  
      - condition: numeric_state
        entity_id: sensor.idm_leistungsaufnahme
        value_template: "{{ state.state|float * 1000 }}"
        above: 1000
  action:
  - service: input_boolean.turn_on
    target:
      entity_id: input_boolean.wp_did_run_today
  mode: queued

# Wenn WP noch nicht gelaufen, und Speicher mind. 70% voll ist, kann die WP laufen
- id: start_wp_automation
  alias: Start WP
  condition:
    and:
      - condition: state
        entity_id: input_boolean.wp_on_off_automatically
        state: "on"  
      - condition: state
        entity_id: input_boolean.wp_did_run_today
        state: "off"
      - condition: state
        entity_id: input_boolean.use_pv_values
        state: "off"
      - condition: numeric_state
        entity_id: sensor.solaredge_battery1_state_of_charge
        above: 70     
  trigger:
  - platform: state
    entity_id:
    - sensor.solaredge_battery1_state_of_charge
  action:
  - service: input_boolean.turn_on
    target:
      entity_id: input_boolean.use_pv_values
  mode: queued

# 'WP ist gelaufen' ausschalten, wenn start_wp kommt, um nicht gleich wieder auszuschalten
- id: start_wp_auto_switchoff_wp_did_run_today
  condition:
    - condition: template
      value_template: "{{ trigger.from_state.state == 'off' and trigger.to_state.state == 'on' }}"
  trigger:
  - platform: state
    entity_id:
    - input_boolean.use_pv_values
  action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.wp_did_run_today
  mode: queued

# wenn die WP schon lief und sie fertig ist (nicht mehr läuft), soll sie nicht mehr laufen 
- id: stop_wp_automation
  alias: Stop WP
  condition:
    and:
      - condition: state
        entity_id: input_boolean.wp_on_off_automatically
        state: "on"  
      - condition: state
        entity_id: input_boolean.wp_did_run_today
        state: "on"
      - condition: numeric_state
        entity_id: sensor.idm_leistungsaufnahme
        value_template: "{{ state.state|float(0) * 1000 }}"
        below: 100
  trigger:
  - platform: state
    entity_id:
    - sensor.idm_leistungsaufnahme
    - input_boolean.wp_did_run_today
  action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.use_pv_values
  mode: queued

# ausschalten der Heizanforderung bei erreichen der Zieltemperatur
- id: stop_wp_heizanforderung
  alias: Stop WP HeizAnforderung
  condition:
    - condition: state
      entity_id: sensor.idm_AnforderungHeizen
      state: "1"  
    - condition: template
      value_template: "{{ states('sensor.idm_Waermespeichertemperatur')|float >= states('input_number.idm_stopanforderungheizen')|float }}"      
  trigger:
  - platform: state
    entity_id:
    - sensor.idm_Waermespeichertemperatur
    - sensor.idm_AnforderungHeizen
  action:
  - service: modbus.write_register
    data:
      hub: idm_wp
      unit: 1
      address: 1710
      value: 0
  mode: queued

# ausschalten der WarmWasserAnforderung bei erreichen der Zieltemperatur
- id: stop_wp_warmwasseranforderung
  alias: Stop WP WarmwasserAnforderung
  condition:
    - condition: state
      entity_id: sensor.idm_AnforderungWarmWasser
      state: "1"  
    - condition: template
      value_template: "{{ states('sensor.idm_trinkwassertemperaturunten')|float >= states('input_number.idm_stopanforderungwarmwasser')|float }}"      
  trigger:
  - platform: state
    entity_id:
    - sensor.idm_trinkwassertemperaturunten
    - sensor.idm_AnforderungWarmWasser
  action:
  - service: modbus.write_register
    data:
      hub: idm_wp
      unit: 1
      address: 1712
      value: 0
  mode: queued

The UI: (see .storage\lovelace.dashboard_warmepumpe)

{
  "version": 1,
  "minor_version": 1,
  "key": "lovelace.dashboard_warmepumpe",
  "data": {
    "config": {
      "views": [
        {
          "title": "Home",
          "cards": [
            {
              "type": "entities",
              "entities": [
                {
                  "entity": "input_number.dummy_value_1"
                },
                {
                  "entity": "input_number.dummy_value_2"
                },
                {
                  "entity": "input_boolean.wp_on_off_automatically"
                },
                {
                  "entity": "input_boolean.wp_did_run_today"
                },
                {
                  "entity": "input_boolean.use_pv_values"
                },
                {
                  "entity": "input_boolean.use_prefer_wp_before_battery"
                },
                {
                  "entity": "sensor.aktuelle_erzeugung"
                },
                {
                  "entity": "sensor.aktueller_verbrauch"
                },
                {
                  "entity": "sensor.aktueller_uberschuss"
                },
                {
                  "entity": "input_number.wp_max_power"
                },
                {
                  "entity": "input_number.wp_buffer"
                },
                {
                  "entity": "sensor.pv_uberschuss_fur_wp"
                },
                {
                  "entity": "sensor.idm_aktuellerphotovoltaikueberschuss"
                },
                {
                  "entity": "sensor.idm_leistungsaufnahme"
                },
                {
                  "entity": "sensor.idm_trinkwassertemperaturunten"
                },
                {
                  "entity": "sensor.idm_waermespeichertemperatur"
                },
                {
                  "entity": "sensor.idm_aussentemperatur"
                },
                {
                  "entity": "switch.idm_anforderungheizen"
                },
                {
                  "entity": "input_number.idm_stopanforderungheizen"
                },
                {
                  "entity": "switch.idm_anforderungwarmwasser"
                },
                {
                  "entity": "input_number.idm_stopanforderungwarmwasser"
                }
              ]
            },
            {
              "type": "grid",
              "cards": [
                {
                  "type": "gauge",
                  "entity": "sensor.aktuelle_erzeugung",
                  "max": 10000,
                  "needle": true,
                  "name": "Erzeugung",
                  "min": 0,
                  "severity": {
                    "green": 5000,
                    "yellow": 2000,
                    "red": 0
                  }
                },
                {
                  "type": "gauge",
                  "entity": "sensor.solaredge_battery1_power",
                  "needle": true,
                  "severity": {
                    "green": 100,
                    "yellow": -100,
                    "red": -5000
                  },
                  "min": -5000,
                  "max": 5000,
                  "name": "Power"
                },
                {
                  "type": "gauge",
                  "entity": "sensor.solaredge_battery1_state_of_charge",
                  "needle": true,
                  "severity": {
                    "green": 70,
                    "yellow": 20,
                    "red": 0
                  },
                  "name": "State of Charge"
                },
                {
                  "type": "gauge",
                  "entity": "sensor.solaredge_m1_ac_power",
                  "name": "Einspeisung",
                  "needle": true,
                  "severity": {
                    "green": 200,
                    "yellow": -200,
                    "red": -8000
                  },
                  "min": -8000,
                  "max": 8000
                },
                {
                  "type": "gauge",
                  "entity": "sensor.aktueller_verbrauch",
                  "name": "Verbrauch",
                  "needle": true,
                  "severity": {
                    "green": 0,
                    "yellow": 300,
                    "red": 1000
                  },
                  "max": 6000
                },
                {
                  "type": "gauge",
                  "entity": "sensor.idm_leistungsaufnahme",
                  "unit": "KW",
                  "name": "Wärmepumpe",
                  "max": 4,
                  "needle": true,
                  "severity": {
                    "green": 0,
                    "yellow": 2,
                    "red": 3
                  },
                  "min": 0
                }
              ]
            },
            {
              "type": "history-graph",
              "entities": [
                {
                  "entity": "sensor.idm_aktuellerphotovoltaikueberschuss"
                },
                {
                  "entity": "sensor.idm_leistungsaufnahme"
                },
                {
                  "entity": "sensor.idm_trinkwassertemperaturunten"
                },
                {
                  "entity": "sensor.idm_waermespeichertemperatur"
                }
              ],
              "hours_to_show": 5,
              "title": "WP FĂźllen"
            },
            {
              "type": "markdown",
              "content": "[WP history](/history?entity_id=sensor.idm_aktuellerphotovoltaikueberschuss%2Csensor.idm_leistungsaufnahme%2Csensor.idm_trinkwassertemperaturunten%2Csensor.idm_waermespeichertemperatur&start_date={{ as_timestamp(now() - timedelta(hours=6)) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ') }})                "
            }
          ]
        },
        {
          "title": "Power",
          "path": "power",
          "icon": "mdi:air-filter",
          "badges": [],
          "cards": []
        },
        {
          "badges": [
            {
              "entity": "sensor.solaredge_ac_power"
            },
            {
              "entity": "sensor.solaredge_battery1_power"
            },
            {
              "entity": "sensor.solaredge_m1_ac_power"
            },
            {
              "entity": "sensor.solaredge_battery1_state_of_charge"
            }
          ],
          "cards": []
        }
      ]
    }
  }
}

Calculation of pv_uberschuss_fur_wp (kW)
This is really specific to my setup, just to give you an idea where/how I calculate this value, this is added here. You probably need to create you own calculation!

template:
  - sensor:
    - name: "aktueller Verbrauch"
      unique_id: "aktueller_verbrauch"
      unit_of_measurement: "W"
      state: >
        {% if states('sensor.solaredge_ac_power')|is_number and
              states('sensor.solaredge_m1_ac_power')|is_number 
        %}
          {{ states('sensor.solaredge_ac_power')|float - states('sensor.solaredge_m1_ac_power')|float }}
        {%- else -%}
          {{ -1.0 }}
        {%- endif %}
      icon: mdi:home-import-outline  
      state_class: measurement
      device_class: power
    - name: "aktuelle Erzeugung"
      unique_id: "aktuelle_erzeugung"
      unit_of_measurement: "W"
      # state: "{{ ( states('sensor.solaredge_ac_power')|float + states('sensor.solaredge_battery1_power')|float ) }}"
      state: >
        {% if states('sensor.solaredge_ac_power')|is_number and
              states('sensor.solaredge_battery1_power')|is_number 
        %}
          {{ ( states('sensor.solaredge_ac_power')|float + states('sensor.solaredge_battery1_power')|float ) }}
        {%- else -%}
          {{ -1.0 }}
        {%- endif %}
      icon: mdi:lightning-bolt  
      state_class: measurement
      device_class: power
    - name: "aktueller Überschuss"
      unique_id: "aktueller_uberschuss"
      unit_of_measurement: "W"
      state: >
        {% if states('sensor.solaredge_m1_ac_power')|is_number and
              states('sensor.solaredge_battery1_power')|is_number 
        %}
          {% if is_state("sensor.solaredge_battery1_status", "Discharging") -%}
            {{ ( states('sensor.solaredge_m1_ac_power')|float  + states('sensor.solaredge_battery1_power')|float ) }}
          {%- else -%}
            {{ states('sensor.solaredge_m1_ac_power') }}
          {%- endif %}
        {%- else -%}
          {{ -1.0 }}
        {%- endif %}
      icon: mdi:lightning-bolt  
      state_class: measurement
      device_class: power
    - name: "PV Leistung fĂźr WP"
      unique_id: "pv_leistung_fur_wp"
      unit_of_measurement: "kW"
      state: >
        {% if is_state("input_boolean.use_pv_values", "on") %}
          {{ ( max(states('sensor.aktuelle_erzeugung')|float - states('input_number.wp_buffer')|float, 0) / 1000 ) }}
        {%- else -%}
          {{ ( states('input_number.dummy_value_1')|float / 1000 ) }}
        {%- endif %}
      icon: mdi:lightning-bolt  
      state_class: measurement
      device_class: power      
    - name: "PV Überschuss für WP"
      unique_id: "pv_uberschuss_fur_wp"
      unit_of_measurement: "kW"
      # ggf. wird während die Batterie geladen wird dennoch die Batterie-Power der WP zur Verfßgung gestellt (abzßglich eines Puffers von 100)       
      # x = available power for WP
      # maxpower = limit power for WP, might include a 700 extra to trigger start
      # aktpower = current power that the WP uses
      # y = the power that we want to give to WP (the current usage is subtracted)
      # z = the power that we can give to WP (respecting the available power)
      state: >
        {% if is_state("input_boolean.use_pv_values", "on") %}
          {% set x = (states('sensor.aktueller_uberschuss')|float(0) - states('input_number.wp_buffer')|float)|float %}
          {% if is_state("input_boolean.use_prefer_wp_before_battery", "on") %}
            {% set batchargepower = states('sensor.solaredge_battery1_power')|float(0) - 100 %}
            {% if batchargepower > 0 %}
              {% set x = x + batchargepower %}
            {% endif %}
          {% endif %}
          {% set x = max(x,0) %}
          {% set maxpower = states('input_number.wp_max_power')|float %}
          {% set aktpower = states('sensor.idm_leistungsaufnahme')|float(0) * 1000 %}
          {% if aktpower <= 0 %}
            {% set maxpower = maxpower + 700 %}
          {% endif %}
          {% set y = maxpower - aktpower %}
          {% set y = max(y,0) %}
          {% set z = min(x, y) %}
          {{ z / 1000 }}
        {% else %}
          {{ ( states('input_number.dummy_value_2')|float / 1000 ) }}
        {% endif %}
      icon: mdi:lightning-bolt  
      state_class: measurement
      device_class: power
10 Likes

Hi, I have successfully written the IDM parameters 74 and 76 to the modbus register.

Now I would like to write the parameter 86 to the modbus register. If I do it exactly as I did with the previous parameters, I get an implausible value.

Can someone help me how to get a plausible value into the IDM control? I think the formatting of the value is wrong, but unfortunately I don’t have the knowledge needed to do it correctly.

Thanks

I did not find any description of Param 86!? Where did you get the information on that and how is it defined (a float or an integer?)

Thanks a lot!
Are you happy with integration and generally the IDM heatpump?
i am considering to buy one (ALM 8).

Yes, I think it has quite a good quality and worked now for 2 years without any issue.
There is a good interface that enables you to monitor and manage the heatpump.

1 Like

We have moved into our new home and picked an IDM SLM Aero 3-11.
I am new to HA and I am getting into it slowly.

@BerndK I’d like to ask you for help, since I am struggling to follow your instructions. My first goal is to collect the data from the heat pump and get into my InfluxDB.
Second would be to connect the hp to my PV, since my “Wechselrichter” from SMA doesn’t work with it :frowning:

@PizziH I think the description is quite detailed - or is there any better somewhere?
Just ask a dedicated question, then I might have an answer.

Hi @BerndK,

im using parts of your integration on my new home assistant setup and it works fine.
Unfortunatly im also not able to write paramter 86 on the modbus register.

modbus_idm

You are not using the battery soc?

Thx Basti

@oxxel85 No I’m not using the SOC. I use the SOC of my Battery directly. AFAIK there is no Battery in my heatpump. I think this is that the IDM UI can present same overview of all the important values. That might be fancy, but I can see all these values in HA - I’m not asking the heating for the battery!
Do you really need to write that value?
If so, I can’t see any reason why this should not work with the standard “- service: modbus.write_register”.

1 Like

Thanks in advance @BerndK

I am implementing what you did step by step in our house and I was quite happy when I was able to read some stats straight from my heat pump.
That was the beginning. Now with lots of sunshine and way too much energy from the roof I am planning to do the next step.

I will come back with questions about your code I guess. The whole project is very promising and If it still works for you I am really optimistic it will work for me as well.

@BerndK
First of all thanks for all the work BerndK. I am getting there but I got some questions and it would be great if you could assist me.

I was able to write parameter 74 to the IDM (my model is a SLM 3-11). I was just a bit worried because the shown value on my HA dashboard did start flickering between 0 and the actual value. I didn’t take the time but I guess it did change every 5 seconds.
Since we shouldn’t write too often into the EEPROM, I did stop it again.
Is this a normal behavior or what could be wrong? Is it the same with you HP?

In the webinterface from the HP, I have put these settings in.


Is it correct to pick the value “Gebäudeleittechnik” and how do these settings affect the automation I want to set up? Or do I need to switch it to “Digitaleingang” ?

I do not have a PV battery and I am not planning to get one soon, so I would like to exclude anything related with it.

If possible I’d like to make sure that on sunny days, when the sun sets our warm water buffer (825l) is filled with 54°C water.

Not sure why the value is flickering!? From my understanding this value should not flicker. Perhaps there is something wrong with your automation(s). Perhaps there are multiple automations that write the same value?
Which value do you show? Do you read back the written value via modbus?
Looking at the documentation there should be no issue with writing this value frequently, because this seems to be not persistent (not in eeprom). Other values like the current mode, or target temperature are persistent. See the modbus doku, some values are marked with a ‘*’ - 74 is not.
Your settings are looking good.
So when writing about 3.0 KW to 74 should start the heatpump after about 60secs.
I personally load the buffer up to 63 degrees to store more energy. AFAIK the maximum is 65. However, if 54 is enough to avoid loading during night this is ok. The lower teperature the less work the heatpump has to do, the longer it will live.
In theory the heatpump should increase and decrease power according to the provided PV power. I was not perfectly happy with that, so I am using 1712 ‘anforderungwarmwasser’. This will let the HP with about 3.5 KW for about an hour. That will fill the buffer - regardless of the current PV power. In my setup it is not mandatory to use the exact current PV power, because the battery does buffer the volatile PV power.

Thank you again @BerndK for your reply.

I dont know what happened, but it is not flickering anymore.
After I realized that all my automations were gone I did some research and now they are all in one place. That is sorted.

Since this week is very sunny I did activate the script again today and it did what it should. I just had it running for an hour and the two temperates (Trinkwasser and Wärmespeicher) have reached the set temperature.
Now they are - of course - slowly decreasing.
When will it start again? Is there a setting I haven’t seen?

And can I make sure that is will run the last time an hour before sunset or something like that?

When will it start again?
This is my summer setuo - so once a day after 10am and when the battery is 70+ it starts. This is typically enough till next day.
For winter the same rules are in place (when heating is on, the buffer will not stay at 50°+), I sometimes trigger the loading manually depending on sun, battery, car, temperature etc…
But you can implement any other logic that fits in your setup.