Integrating Zehnder ComfoAir 350 using ComfoConnect KNX C

This is an alternative solution to the ComfoAir binding which uses a serial port.
I preferred to have the ComoAir directly controlled via the KNX bus and thus I bought the Zehnder ComfoConnect KNX C.
I am sharing my items and sitemap configuration, maybe its useful to other openHABeners:

Sitemap integration:

		Text item=Lueftung_Airflow_Msg icon="fan" label="Lüftung "{
        	Frame label="Control" {
        		Switch item=Lueftung_Auto_Mode label="Automatik"
				Setpoint item=Lueftung_Fan_Level step=1 minValue=0 maxValue=4
				Switch item=Lueftung_Temperature_profile_mode label="temperature profile mode" mappings=[0=adaptiv, 1=fest, 2=setpoint]
				Switch item=Lueftung_Temperature_profile label="temperature profile" mappings=[0=normal, 1=cool, 2=warm]
				Setpoint item=Lueftung_Temperature_set_point minValue=16 maxValue=24 step=0.5
				Switch item=Lueftung_Chart_Period label="Chart Period" icon="chart" mappings=[0="Hour", 1="Day", 2="Week"]
        	Frame label="Status" {
        		Text item=Lueftung_Filterwechsel_Msg
        		Text item=Lueftung_Airflow_Msg
        		Text item=Lueftung_Status_Msg
        		Text item=Lueftung_StatusErr_Msg
				Chart item=gLueftung_Chart_Airflow period=h refresh=600 visibility=[Lueftung_Chart_Period==0, Lueftung_Chart_Period=="NULL"]
				Chart item=gLueftung_Chart_Airflow period=D refresh=3600 visibility=[Lueftung_Chart_Period==1]
				Chart item=gLueftung_Chart_Airflow period=W refresh=3600 visibility=[Lueftung_Chart_Period==2]
        	Frame label="Temperature" {
        		Text item=Lueftung_Temperatur_Extract_Msg
        		Text item=Lueftung_Temperatur_Exhaust_Msg
        		Text item=Lueftung_Temperatur_Outdoor_Msg
        		Text item=Lueftung_Temperatur_Supply_Msg
			Frame label="Humidity" {
				Text item=Lueftung_Humidity_Extract_Msg
				Text item=Lueftung_Humidity_Exhaust_Msg
				Text item=Lueftung_Humidity_Outdoor_Msg
				Text item=Lueftung_Humidity_Supply_Msg
				Chart item=gLueftung_Chart_Humidity period=h refresh=600 visibility=[Lueftung_Chart_Period==0, Lueftung_Chart_Period=="NULL"]
				Chart item=gLueftung_Chart_Humidity period=D refresh=3600 visibility=[Lueftung_Chart_Period==1]
				Chart item=gLueftung_Chart_Humidity period=W refresh=3600 visibility=[Lueftung_Chart_Period==2]


Group gLueftung  "Lüftungsanlage"    <pie>

Group gLueftung_Chart_Airflow 
Group gLueftung_Chart_Humidity
Number Lueftung_Chart_Period     "Chart Period"

Switch Lueftung_Auto_Mode          "Auto Modus"                            	(gLueftung) { knx="1.003:30/1/0+<1.003:30/1/1" }
String Lueftung_Filterwechsel_Msg  "Filtertausch [%s]"       	<clock>		(gLueftung)
String Lueftung_Airflow_Msg        "Airflow [%s]" 				<fan>		(gLueftung)
String Lueftung_Status_Msg         "Status [%s]"                <settings>	(gLueftung)
String Lueftung_StatusErr_Msg      "Error Status [%s]"     		<settings>	(gLueftung)

String Lueftung_Temperatur_Room_Msg "Room [%s]"			<temperature> (gLueftung)
String Lueftung_Temperatur_Extract_Msg "Extract [%s]"	<temperature> (gLueftung) 
String Lueftung_Temperatur_Exhaust_Msg "Exhaust [%s]"	<temperature> (gLueftung)
String Lueftung_Temperatur_Outdoor_Msg "Outdoor [%s]"	<temperature> (gLueftung)
String Lueftung_Temperatur_Supply_Msg "Supply [%s]"		<temperature> (gLueftung)

String Lueftung_Humidity_Room_Msg "Room [%s]"			<humidity> (gLueftung)
String Lueftung_Humidity_Extract_Msg "Extract [%s]"	    <humidity> (gLueftung) 
String Lueftung_Humidity_Exhaust_Msg "Exhaust [%s]"	    <humidity> (gLueftung)
String Lueftung_Humidity_Outdoor_Msg "Outdoor [%s]"	    <humidity> (gLueftung)
String Lueftung_Humidity_Supply_Msg "Supply [%s]"		<humidity> (gLueftung)

//Number Lueftung_Control     "Steuerung"                             <settings> (gLueftung) {comfoair="activate"}
//Number Lueftung_Komfortemperatur "Zieltemperatur [%.1f °C]"         <temperature> (gLueftung) {comfoair="target_temperatur"}
//Number Lueftung_Bypass      "Bypass [MAP(]"        <selfBypass> (gLueftung) {comfoair="bypass_mode"}

Number Lueftung_Fan_Level       "Stufe [%d]"   <fan>   	                            (gLueftung)	{ knx="5.010:30/1/2+<5.010:30/1/3"}
Number Lueftung_Airflow         "Airflow [%d h]"              				        (gLueftung, gLueftung_Chart_Airflow) { autoupdate="true", knx="<(10)13.002:30/1/4" }
Number Lueftung_Filterwechsel   "Filterwechsel [%d h]"              				(gLueftung) { autoupdate="true", knx="<(300)7.007:30/1/5" }
Number Lueftung_Status          "Status [%d]"                       				(gLueftung) { autoupdate="true", knx="<(300)5.001:30/1/7" }
Number Lueftung_Temperature_profile_mode  "Temperatur profile mode"      <settings> (gLueftung) { knx="5.010:30/1/8+<5.010:30/1/9" }
Number Lueftung_Temperature_profile  	  "Temperatur profile"           <settings> (gLueftung) { knx="5.010:30/1/10+<5.010:30/1/11" }
Number Lueftung_Temperature_set_point     "Temperatur [%.1f]"         <temperature> (gLueftung) { knx="30/1/12+<30/1/13" }
//Switch Lueftung_StatusErr      "Error Status [%d]"                 (gLueftung) { autoupdate="true", knx="<(30)1.003:30/1/6" }

Number Lueftung_Temperatur_Room "Room [%.1f °C]"                     (gLueftung) { autoupdate="true", knx="<(100)30/1/20" }
Number Lueftung_Temperatur_Extract "Extract [%.1f °C]"               (gLueftung) { autoupdate="true", knx="<(100)30/1/21" }
Number Lueftung_Temperatur_Exhaust "Exhaust [%.1f °C]"               (gLueftung) { autoupdate="true", knx="<(100)30/1/22" }
Number Lueftung_Temperatur_Outdoor "Outdoor [%.1f °C]"               (gLueftung) { autoupdate="true", knx="<(100)30/1/23" }
Number Lueftung_Temperatur_Supply "Supply [%.1f °C]"                 (gLueftung) { autoupdate="true", knx="<(100)30/1/24" }

Number Lueftung_Humidity_Room "Room [%d %%]"                     (gLueftung) { autoupdate="true", knx="<(100)5.001:30/1/30" }
Number Lueftung_Humidity_Extract "Extract [%d %%]"               (gLueftung, gLueftung_Chart_Humidity) { autoupdate="true", knx="<(100)5.001:30/1/31" }
Number Lueftung_Humidity_Exhaust "Exhaust [%d %%]"               (gLueftung) { autoupdate="true", knx="<(100)5.001:30/1/32" }
Number Lueftung_Humidity_Outdoor "Outdoor [%d %%]"               (gLueftung) { autoupdate="true", knx="<(100)5.001:30/1/33" }
Number Lueftung_Humidity_Supply "Supply [%d %%]"                 (gLueftung, gLueftung_Chart_Humidity) { autoupdate="true", knx="<(100)5.001:30/1/34" }


import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import java.lang.Math
var boolean autoChangeInProgress = false

rule "Airflow Message"
	System started
	Item Lueftung_Airflow changed
	if( (Lueftung_Airflow.state instanceof DecimalType) ){
		var String msg = Lueftung_Airflow.state.format("%d") + " m" + "\u00b3" +"/h"

rule "Filterwechsel Message"
	System started
	Item Lueftung_Filterwechsel changed
    if( (Lueftung_Filterwechsel.state instanceof DecimalType) ){
		var Number Filterwechsel = Lueftung_Filterwechsel.state as DecimalType
		var Number days = Filterwechsel/24
		var String msg = days.intValue + " Tage"
  		postUpdate(Lueftung_Filterwechsel_Msg, msg)


rule "Status and Error Status Message"
	System started
	Item Lueftung_Status changed
	Item Lueftung_StatusErr received update
    if( (Lueftung_Status.state instanceof DecimalType) ){
		var Number Status = Lueftung_Status.state as DecimalType
		if (Status.intValue == 0) {
  			postUpdate(Lueftung_Status_Msg, "ok, Status: " + Status.intValue)
  		} else {
  			postUpdate(Lueftung_Status_Msg, "Not ok check ComfoConnect KNX C manual, status val: " + Status.intValue)
//  	if ( (Lueftung_StatusErr.state instanceof DecimalType) ){
//  		var Number StatusErr = Lueftung_StatusErr.state as DecimalType
//		if (StatusErr.intValue == 0) {
//  			postUpdate(Lueftung_StatusErr_Msg, "no Error")
//  		} else {
//  			postUpdate(Lueftung_StatusErr_Msg, "Error: check ComfoConnect KNX C manual, status val: " + StatusErr.intValue )
//  		}
//  	}

rule "Temperature Message"
	System started
	Item Lueftung_Temperatur_Room changed
	Item Lueftung_Temperatur_Extract changed
	Item Lueftung_Temperatur_Exhaust changed
	Item Lueftung_Temperatur_Outdoor changed
	Item Lueftung_Temperatur_Supply changed
    	if( (Lueftung_Temperatur_Room.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Temperatur_Room_Msg, Lueftung_Temperatur_Room.state.format("%.1f") + "°C")
    	if( (Lueftung_Temperatur_Extract.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Temperatur_Extract_Msg, Lueftung_Temperatur_Extract.state.format("%.1f") + "°C")
    	if( (Lueftung_Temperatur_Exhaust.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Temperatur_Exhaust_Msg, Lueftung_Temperatur_Exhaust.state.format("%.1f") + "°C")
    	if( (Lueftung_Temperatur_Outdoor.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Temperatur_Outdoor_Msg, Lueftung_Temperatur_Outdoor.state.format("%.1f") + "°C")
    	if( (Lueftung_Temperatur_Supply.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Temperatur_Supply_Msg, Lueftung_Temperatur_Supply.state.format("%.1f") + "°C")

rule "Humidity Message"
	System started
	Item Lueftung_Humidity_Room changed
	Item Lueftung_Humidity_Extract changed
	Item Lueftung_Humidity_Exhaust changed
	Item Lueftung_Humidity_Outdoor changed
	Item Lueftung_Humidity_Supply changed
    	if( (Lueftung_Humidity_Room.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Humidity_Room_Msg, Lueftung_Humidity_Room.state.format("%d") + "%")
    	if( (Lueftung_Humidity_Extract.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Humidity_Extract_Msg, Lueftung_Humidity_Extract.state.format("%d") + "%")
    	if( (Lueftung_Humidity_Exhaust.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Humidity_Exhaust_Msg, Lueftung_Humidity_Exhaust.state.format("%d") + "%")
    	if( (Lueftung_Humidity_Outdoor.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Humidity_Outdoor_Msg, Lueftung_Humidity_Outdoor.state.format("%d") + "%")
    	if( (Lueftung_Humidity_Supply.state instanceof DecimalType) ){
    		postUpdate(Lueftung_Humidity_Supply_Msg, Lueftung_Humidity_Supply.state.format("%d") + "%")

rule "Filterlaufzeit"
	System started
	Item Lueftung_Filterlaufzeit changed
    	if( Lueftung_Filterlaufzeit.state instanceof DecimalType ){
		var Number laufzeit = Lueftung_Filterlaufzeit.state as DecimalType
		var Number weeks = Math::floor( (laufzeit/168).doubleValue )
		var Number days = Math::floor( ((laufzeit-(weeks*168))/24).doubleValue)
		var String msg = ""
		if( weeks > 0 ){
			if( weeks == 1 ) msg = weeks.intValue + " Woche"
			else msg = weeks.intValue + " Wochen"
    		if( days > 0 ){
    			if( msg.length > 0 ) msg = msg + ", "
    			if( days == 1 ) msg = msg + days.intValue + " Tag"
    			else msg = msg + days.intValue + " Tage"

persistence config:

Strategies {
	// for rrd charts, we need a cron strategy
	everyMinute : "0 * * * * ?"

Items {
	DemoSwitch,NoOfLights,Window_GF_Toilet,Heating* : strategy = everyChange, everyMinute, restoreOnStartup
	// let's only store temperature values in rrd
	Temperature*,Weather_Chart* : strategy = everyMinute, restoreOnStartup

	Heizung_Chart* : strategy = everyMinute, restoreOnStartup

	Lueftung_Airflow, Lueftung_Humidity_Extract, Lueftung_Humidity_Supply: strategy = everyMinute, restoreOnStartup
	gLueftung_Chart_Airflow, gLueftung_Chart_Humidity : strategy = everyMinute, restoreOnStartup	

	SMA_TotalPac : strategy = everyMinute, restoreOnStartup
	gSMA_Chart : strategy = everyMinute, restoreOnStartup	

Hints or ideas to improve the setup are very welcome.


As a matter of principle: thank you!
I bought a 350 and (I can’t remember) either an Ethernet or KNX interface … short story: still packed up as house I am building is not ready for it yet.

Hello Max,
Here is the link to the device:

Just checked: its the same gear you’ve got…
What a time saver! thanks.

I assume that all of you have a ComfoAir 350Q - not the “old” model without “Q” - right?

Linkt to “old” version: Zehnder ComfoAir 350

If that is true has anyone experience whether the ComfoConnect KNX C works with the “old” version?

Hello JohnnyX,
yes I have the 350Q.
Don’t know if these are compatible, just contact Zehnder directly, they are usually quite helpful.

I would love support for the ComfoConnect LAN C for the Q Series

There is a python based integration in home assistant and it would be great if a pro could outline how this can be migrated to a openhab binding :-/

1 Like

Indeed, they are helpful, here is the answer literally:
Leider gibt es für Ihre Anlage kein KNX Modul. Das Gateway ist nur mit der neuen Modellreihe ComfoAir Q kompatibel.

That’s not good :frowning_face:
For me it is still not understandable that so many expensive house equipment parts don’t support KNX.

I’m getting this machine, and have OH 2.2 running.
My home will be mostly on z-wave protocol, can yuo suggest a budget solution for a knx power supply and knx ip gateway to use the Zehnder with knx interface?

thank you

I have this ABB KNX power supply:
And I use
as ip gateway on my server (same were openhab runs on).
I use this usb - KNX interface:

there is also an non KNX version of comfoconnect --> Comfoconnect LAN C


did anybody update this Example to the KNX 2 Binding of Openhab 2.3? I have several issues with the Multiswitches (profile mode and temperature profile). Would be very glad if someone has a .things example because the bus-connection with some datatypes seems to be the main issue.

Best wishes from a first time poster,


I’m working on the same issue. Did you find a solution? Do you have a solution for the auto mode? This thing makes me crazy!

Greetings Torsten

Sorry I cant help.
I am still on openhab 2.2 and the old knx binding. I will update when the next release arrives.
However, it would be good that once you find the solution that you post it.

Hi Marco
I have an Comfoair Q450 TR, and i bought the KNX modul. Comfoconnect KNX.
I use ETS5 software for KNX.
I can’t find the applikation (.knxprod) file.
Where did you get yours?

you can find the KNX applikation for comfoairQ here: (Downloads -> Software -> KNX)

How can I connect my ComfoconnectKNX with my Openhab directly via KNX? I have the ComfoconnectKNX in my ETS5, but there is no openhab server to connect with.
What do I have to do in the ETS5?

Hello Lars,
use the files provided above in this thread.
The first one is the .items file describing all the sensor and control items. You need to adapt the KNX addresses matching your ETS Configuration.
After adapting check the openhab log (e.g ssh -p 8101 openhab@yourip ) if there are any errors/warnings.
Once this is working use the provided sitemap template.
Finally add a rules file if needed.
Hope that helps

Hello Michael,
it is here: