This post is part of the Komfovent in Home Assistant series.
We recently got balanced ventilation installed, and discovered that our ventilation unit has Modbus TCP/IP support. I even found a thread on the Home Assistant community talking about interfacing the C6 controller, which our unit has, into Home Assistant 😃
Here is my implementation 👇
Table of contents
Network
The ventilation unit is in the attic, and needs cabled network. Not a problem — I was already working on getting two CAT6 cables and a fiber up there 🙂
I’ll explain getting network to the attic in a future post.
Home Assistant
What follows is my Home Assistant configuration, for the ventilation unit itself, templates, scripts, inputs, and automations that I use. There is a lot going on here, but if you are familiar with Home Assistant; it should make some sense to you 🙂
Modbus
Getting the actual data from the ventilation unit is done with Modbus over TCP, as configured below. I used the PDF document above to get the data I needed, I also found a very useful thread on the Home Assistant community.
The scale
and precision
properties are used to convert the values received from the unit — it’s all documented on the Home Assistant Modbus integration page.
I’m pulling most values every 30 seconds, set with scan_interval
, while some statistical data is pulled every 5 minutes (300 seconds).
Some values take up more than one Modbus register; for those you will need to set the count
property.
modbus:
- name: komfovent
type: tcp
host: 192.168.1.38
port: 502
switches:
- name: "Komfovent Eco Mode"
address: 2
command_on: 1
command_off: 0
verify:
- name: "Komfovent Constant heat recovery"
address: 216
command_on: 1
command_off: 0
verify:
sensors:
- name: "Komfovent Power"
address: 0
scan_interval: 30
- name: "Komfovent Mode"
address: 4
scan_interval: 30
- name: "Komfovent Status bit mask"
address: 899
scan_interval: 30
- name: "Komfovent Supply temperature"
address: 901
scan_interval: 30
unit_of_measurement: °C
scale: 0.1
precision: 1
device_class: temperature
- name: "Komfovent Extract temperature"
address: 902
scan_interval: 30
unit_of_measurement: °C
scale: 0.1
precision: 1
device_class: temperature
- name: "Komfovent Outdoor temperature"
address: 903
scan_interval: 30
unit_of_measurement: °C
scale: 0.1
precision: 1
device_class: temperature
- name: "Komfovent Supply Fan Intensivity"
address: 909
scan_interval: 30
unit_of_measurement: '%'
scale: 0.1
precision: 1
- name: "Komfovent Extract Fan Intensivity"
address: 910
scan_interval: 30
unit_of_measurement: '%'
scale: 0.1
precision: 1
- name: "Komfovent Heat exchanger"
address: 911
scan_interval: 30
unit_of_measurement: '%'
scale: 0.1
precision: 0
- name: "Komfovent Filter Impurity"
address: 916
scan_interval: 300
unit_of_measurement: '%'
- name: "Komfovent Power consumption"
address: 920
scan_interval: 30
unit_of_measurement: W
device_class: power
- name: "Komfovent Heating power"
address: 921
scan_interval: 30
unit_of_measurement: W
device_class: power
- name: "Komfovent Heat Recovery"
address: 922
scan_interval: 30
unit_of_measurement: W
device_class: power
- name: "Komfovent Heat exchanger efficiency"
address: 923
scan_interval: 30
scale: 1
precision: 0
unit_of_measurement: '%'
- name: "Komfovent Energy saving"
address: 924
scan_interval: 30
unit_of_measurement: '%'
- name: "Komfovent Power consumption Month"
address: 928
scan_interval: 300
unit_of_measurement: kWh
data_type: 'int32'
precision: 0
scale: 0.001
device_class: energy
- name: "Komfovent Power consumption Total"
address: 930
scan_interval: 300
unit_of_measurement: kWh
data_type: 'int32'
precision: 0
scale: 0.001
state_class: total_increasing
device_class: energy
- name: "Komfovent Heating Recovery Month"
address: 940
scan_interval: 300
unit_of_measurement: kWh
data_type: 'int32'
precision: 0
scale: 0.001
device_class: energy
Templates
To convert some of the values received I have a few templates:
- Komfovent Mode Text
- Converts the received mode integer to a human readable string
- Ventilation info
- Combines multiple ventilation sensor values into a single entity
- Used by our Dakboard digital calendar
- Komfovent status …
- Converts the received status bit mask into boolean status states
- Komfovent mode is …
- Converts mode into four boolean status entities
- Used for button states on the Lovelace dashboard
template:
- sensor:
- name: "Komfovent Mode Text"
state: >
{%- if states.sensor.komfovent_mode.state | int == 0 %}
Standby
{%- elif states.sensor.komfovent_mode.state | int == 1 %}
Away
{%- elif states.sensor.komfovent_mode.state | int == 2 %}
Normal
{%- elif states.sensor.komfovent_mode.state | int == 3 %}
Intensive
{%- elif states.sensor.komfovent_mode.state | int == 4 %}
Boost
{%- elif states.sensor.komfovent_mode.state | int == 5 %}
Kitchen
{%- elif states.sensor.komfovent_mode.state | int == 6 %}
Fireplace
{%- elif states.sensor.komfovent_mode.state | int == 7 %}
Override
{%- elif states.sensor.komfovent_mode.state | int == 8 %}
Holiday
{%- elif states.sensor.komfovent_mode.state | int == 9 %}
Air quality
{%- elif states.sensor.komfovent_mode.state | int == 10 %}
Off
{% else %}
fail
{%- endif %}
icon: hass:account-cog
- name: "Ventilation info"
state: "{{ states.sensor.komfovent_mode_text.state }}"
attributes:
heat: "{{ states.sensor.komfovent_heating_power_w.state }}"
outside: "{{ states.sensor.komfovent_outdoor_temperature_c.state }}"
- binary_sensor:
- name: "Komfovent status Starting"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(1) > 0 }}"
- name: "Komfovent status Stopping"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(2) > 0 }}"
- name: "Komfovent status Fan"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(4) > 0 }}"
- name: "Komfovent status Rotor"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(8) > 0 }}"
- name: "Komfovent status Heating"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(16) > 0 }}"
- name: "Komfovent status Cooling"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(32) > 0 }}"
- name: "Komfovent status Heating denied"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(64) > 0 }}"
- name: "Komfovent status Cooling denied"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(128) > 0 }}"
- name: "Komfovent status Flow down"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(256) > 0 }}"
- name: "Komfovent status Free heating"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(512) > 0 }}"
- name: "Komfovent status Free cooling"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(1024) > 0 }}"
- name: "Komfovent status Alarm fail"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(2048) > 0 }}"
- name: "Komfovent status Alarm warning"
state: "{{ states.sensor.komfovent_status_bit_mask.state | int | bitwise_and(4096) > 0 }}"
- name: "Komfovent mode is Away"
state: "{{ states.sensor.komfovent_mode.state | int == 1 }}"
- name: "Komfovent mode is Normal"
state: "{{ states.sensor.komfovent_mode.state | int == 2 }}"
- name: "Komfovent mode is Intensive"
state: "{{ states.sensor.komfovent_mode.state | int == 3 }}"
- name: "Komfovent mode is Boost"
state: "{{ states.sensor.komfovent_mode.state | int == 4 }}"
Scripts
For changing states on the ventilation unit, aka. writing registers, I use scripts:
- Komfovent mode Away/Normal/Intensive/Boost
- Changes the ventilation mode
- Komfovent temperature night/day time
- Sets night or day time temperatures
- Temperature is set per mode, so three registers are written; normal, intensive, and fireplace mode
- There is a one second delay between each mode register write
script:
komfovent_set_away:
alias: Komfovent mode Away
sequence:
- service: modbus.write_register
data:
address: 4
hub: komfovent
unit: 1
value: 1
komfovent_set_normal:
alias: Komfovent mode Normal
sequence:
- service: modbus.write_register
data:
address: 4
hub: komfovent
unit: 1
value: 2
komfovent_set_intensive:
alias: Komfovent mode Intensive
sequence:
- service: modbus.write_register
data:
address: 4
hub: komfovent
unit: 1
value: 3
komfovent_set_boost:
alias: Komfovent mode Boost
sequence:
- service: modbus.write_register
data:
address: 4
hub: komfovent
unit: 1
value: 4
komfovent_set_night_temp:
alias: Komfovent temperature night time
variables:
temperature: "{{ ((states.input_number.ventilation_set_night_temp.state | float) * 10) | int }}"
sequence:
- service: modbus.write_register
data:
address: 109 # Normal
hub: komfovent
unit: 1
value: "{{ temperature }}"
- delay: 1
- service: modbus.write_register
data:
address: 115 # Intensive
hub: komfovent
unit: 1
value: "{{ temperature }}"
- delay: 1
- service: modbus.write_register
data:
address: 134 # Fireplace
hub: komfovent
unit: 1
value: "{{ temperature }}"
komfovent_set_day_temp:
alias: Komfovent temperature day time
variables:
temperature: "{{ ((states.input_number.ventilation_set_day_temp.state | float) * 10) | int }}"
sequence:
- service: modbus.write_register
data:
address: 109 # Normal
hub: komfovent
unit: 1
value: "{{ temperature }}"
- delay: 1
- service: modbus.write_register
data:
address: 115 # Intensive
hub: komfovent
unit: 1
value: "{{ temperature }}"
- delay: 1
- service: modbus.write_register
data:
address: 134 # Fireplace
hub: komfovent
unit: 1
value: "{{ temperature }}"
Inputs
I use a few inputs for the scripts and automations:
- Komfovent auto away
- A manual switch to enable/disable the auto away automation
- Komfovent away/normal/night/day time
- Time inputs for changing the mode, and adjusting set temperatures
- Komfovent temperature night/day time
- Temperature inputs for night and day time
- Range from 15 –> 25, with steps of 0,1
input_boolean:
ventilation_auto_away:
name: Komfovent auto away
icon: mdi:calendar-clock
input_datetime:
ventilation_set_away_time:
name: Komfovent away time
has_date: false
has_time: true
icon: mdi:home-export-outline
ventilation_set_normal_time:
name: Komfovent normal time
has_date: false
has_time: true
icon: mdi:home-account
ventilation_set_night_time:
name: Komfovent night time
has_date: false
has_time: true
icon: mdi:weather-night
ventilation_set_day_time:
name: Komfovent day time
has_date: false
has_time: true
icon: mdi:weather-sunny
input_number:
ventilation_set_night_temp:
name: Komfovent temperature night time
min: 15
max: 25
step: 0.1
unit_of_measurement: °C
icon: mdi:weather-night
ventilation_set_day_temp:
name: Komfovent temperature day time
min: 15
max: 25
step: 0.1
icon: mdi:weather-sunny
unit_of_measurement: °C
Automations
Automations is really where the magic happens 🚀
Ventilation mode Intensive/Normal
We normally run the system in Normal mode, but if the CO₂ level in either the living room, or on the 2nd floor, goes above 1000 ppm for 15 minutes; the mode is changed to Intensive. Meaning that the fans increase from 50% to 70%.
After the CO₂ level drops below 1000 ppm, in both area, for 15 minutes; it returns to Normal mode.
To minimize interference with other modes; it only changes if the current mode is either Normal or Intensive, respectively.
Ventilation auto Away/Normal
On weekday mornings, the system is put in Away mode. Then; in the afternoon it is set back to Normal.
Both away and normal times are set by input_datetime
entities, currently set to 8:00 and 15:00.
But, during Away mode; if the CO₂ level in either the living room, or on the 2nd floor, goes above 800 ppm; it will immediately change back to Normal mode. This prevents bad air quality if people are home during a weekday.
To minimize interference with other modes; it only changes if the current mode is either Normal or Away, respectively.
Ventilation auto time Night/Day
I’m experimenting with lowering the temperature during the night, and on weekdays when the house is empty.
Both times and temperatures are adjustable with input_datetime
and input_number
entities. Currently I have it set for 20°C at night, and 22° at day time. Night time is set to start at 23:00, and end at 9:00.
But since Away mode is active during weekdays, and the temperature is set per mode, the day time temperature doesn’t become active until Normal mode is set, which currently happens at 15:00.
- id: '1635867520743'
alias: Ventilation mode Intensive
description: ''
trigger:
- platform: numeric_state
entity_id: sensor.netatmo_stue_co2
above: '1000'
for:
hours: 0
minutes: 15
seconds: 0
milliseconds: 0
- platform: numeric_state
entity_id: sensor.netatmo_2_etg_co2
above: '1000'
for:
hours: 0
minutes: 15
seconds: 0
milliseconds: 0
condition:
- condition: state
entity_id: sensor.komfovent_mode
state: '2'
action:
- service: script.komfovent_set_intensive
mode: single
- id: '1635867596635'
alias: Ventilation mode Normal
description: ''
trigger:
- platform: numeric_state
entity_id: sensor.netatmo_stue_co2
below: '1000'
for:
hours: 0
minutes: 15
seconds: 0
milliseconds: 0
- platform: numeric_state
entity_id: sensor.netatmo_2_etg_co2
below: '1000'
for:
hours: 0
minutes: 15
seconds: 0
milliseconds: 0
condition:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.netatmo_stue_co2
below: '1000'
- condition: numeric_state
entity_id: sensor.netatmo_2_etg_co2
below: '1000'
- condition: state
entity_id: sensor.komfovent_mode
state: '3'
action:
- service: script.komfovent_set_normal
mode: single
- id: '1635968110386'
alias: Ventilation auto Away
description: ''
trigger:
- platform: time
at: input_datetime.ventilation_set_away_time
condition:
- condition: and
conditions:
- condition: state
entity_id: input_boolean.ventilation_auto_away
state: 'on'
- condition: time
weekday:
- mon
- tue
- wed
- thu
- fri
- condition: state
entity_id: sensor.komfovent_mode
state: '2'
action:
- service: script.komfovent_set_away
mode: single
- id: '1635968206887'
alias: Ventilation auto Normal
description: ''
trigger:
- platform: time
at: input_datetime.ventilation_set_normal_time
- platform: numeric_state
entity_id: sensor.netatmo_stue_co2
above: '800'
for:
hours: 0
minutes: 0
seconds: 0
milliseconds: 0
- platform: numeric_state
entity_id: sensor.netatmo_2_etg_co2
above: '800'
for:
hours: 0
minutes: 0
seconds: 0
milliseconds: 0
condition:
- condition: and
conditions:
- condition: state
entity_id: input_boolean.ventilation_auto_away
state: 'on'
- condition: time
weekday:
- mon
- tue
- wed
- thu
- fri
- condition: state
entity_id: sensor.komfovent_mode
state: '1'
action:
- service: script.komfovent_set_normal
mode: single
- id: '1636644751808'
alias: Ventilation auto time Night
description: ''
trigger:
- platform: time
at: input_datetime.ventilation_set_night_time
condition:
- condition: state
entity_id: input_boolean.ventilation_auto_away
state: 'on'
action:
- service: script.komfovent_set_night_temp
mode: single
- id: '1636644785089'
alias: Ventilation auto time Day
description: ''
trigger:
- platform: time
at: input_datetime.ventilation_set_day_time
condition:
- condition: state
entity_id: input_boolean.ventilation_auto_away
state: 'on'
action:
- service: script.komfovent_set_day_temp
mode: single
Lovelace
Below is the configuration for my Ventilation card. I won’t go into details here — but looking at the image above, and the configuration below, you should get an understanding of what is happening.
type: vertical-stack
title: Ventilation
cards:
- type: horizontal-stack
cards:
- type: entity
entity: sensor.komfovent_mode_text
name: Mode
- type: entity
entity: sensor.komfovent_heat_recovery_w
icon: mdi:refresh-circle
name: Recovery
- type: horizontal-stack
cards:
- type: gauge
entity: sensor.komfovent_heating_power_w
min: 0
max: 1000
name: Heating power
- type: gauge
entity: sensor.komfovent_energy_saving
min: 0
max: 100
name: Energy saving
- type: gauge
entity: sensor.komfovent_heat_exchanger_efficiency
min: 0
max: 100
name: Exchanger eff.
- type: horizontal-stack
cards:
- type: button
icon: hass:home-export-outline
name: Away
tap_action:
action: more-info
show_state: false
hold_action:
action: call-service
service: script.komfovent_set_away
service_data: {}
target: {}
entity: binary_sensor.komfovent_mode_is_away
- type: button
tap_action:
action: more-info
hold_action:
action: call-service
service: script.komfovent_set_normal
service_data: {}
target: {}
icon: hass:home-account
name: Normal
entity: binary_sensor.komfovent_mode_is_normal
- type: button
tap_action:
action: more-info
icon: hass:weather-windy
hold_action:
action: call-service
service: script.komfovent_set_intensive
service_data: {}
target: {}
name: Intensive
entity: binary_sensor.komfovent_mode_is_intensive
- type: button
tap_action:
action: more-info
name: Boost
icon: hass:fan
hold_action:
action: call-service
service: script.komfovent_set_boost
service_data: {}
target: {}
show_icon: true
show_name: true
entity: binary_sensor.komfovent_mode_is_boost
- type: entities
entities:
- entity: switch.komfovent_eco_mode
name: Economy
icon: mdi:piggy-bank-outline
- entity: sensor.komfovent_supply_temperature_c
name: Supply temperature
icon: hass:home-import-outline
- entity: sensor.komfovent_extract_temperature_c
name: Exhaust temperature
icon: hass:home-export-outline
- entity: sensor.komfovent_outdoor_temperature_c
name: Outdoor temperature
icon: hass:weather-partly-cloudy
- entity: sensor.komfovent_supply_fan_intensivity
name: Supply fan
icon: hass:home-import-outline
- entity: sensor.komfovent_extract_fan_intensivity
name: Exhaust fan
icon: hass:home-export-outline
- entity: sensor.komfovent_filter_impurity
name: Filter clogging
icon: hass:air-filter
- entity: sensor.komfovent_power_consumption_w
name: Power consumption
icon: mdi:transmission-tower-export
- entity: sensor.komfovent_power_consumption_month_kwh
icon: mdi:calendar-end
name: Power usage, month
- entity: sensor.komfovent_heating_recovery_month_kwh
icon: mdi:calendar-refresh
name: Heat recovery, month
state_color: false
show_header_toggle: false
- type: entity-filter
entities:
- entity: binary_sensor.komfovent_status_starting
name: Starting
icon: hass:play
- entity: binary_sensor.komfovent_status_stopping
name: Stopping
icon: hass:stop
- entity: binary_sensor.komfovent_status_fan
name: Fan
icon: hass:fan
- entity: binary_sensor.komfovent_status_rotor
name: Rotor
icon: hass:autorenew
- entity: binary_sensor.komfovent_status_heating
name: Heating
icon: hass:radiator
- entity: binary_sensor.komfovent_status_cooling
name: Cooling
- entity: binary_sensor.komfovent_status_heating_denied
name: Heat denied
icon: hass:radiator-off
- entity: binary_sensor.komfovent_status_cooling_denied
name: Cooling denied
- entity: binary_sensor.komfovent_status_flow_down
name: Low flow
icon: hass:fan-minus
- entity: binary_sensor.komfovent_status_free_heating
name: Free heat
icon: hass:thermometer-plus
- entity: binary_sensor.komfovent_status_free_cooling
name: Free cooling
icon: hass:thermometer-minus
- entity: binary_sensor.komfovent_status_alarm_fail
name: Fail
icon: hass:alert
- entity: binary_sensor.komfovent_status_alarm_warning
name: Warning
icon: hass:alert
state_filter:
- 'on'
card:
type: glance
state_color: false
show_state: false
title: Status
- type: entities
entities:
- entity: input_boolean.ventilation_auto_away
name: Enable
icon: hass:calendar-clock
- entity: input_datetime.ventilation_set_away_time
name: Away mode (weekdays)
icon: hass:home-export-outline
- entity: input_datetime.ventilation_set_normal_time
name: Normal mode (weekdays)
icon: hass:home-account
title: Scheduling
Dakboard
We have a Dakboard digital calendar in the kitchen, where I also show some data from Home Assistant.
From the ventilation system; three values are shown:
- Mode, in a human readable string
- Power used by the electric heater, in watts
- Outside supply air temperature
I have a blog post where I explain how I get data from Home Assistant, into Dakboard.
Ending note
Interfacing the ventilation system with Home Assistant has opened up so many possibilities, and allows me to use the system in clever ways 🙂 I will explore further in future blog posts 🖖
Last commit 2024-11-11, with message: Add lots of tags to posts.