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]
			}
		}

Items:

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(bypass_de.map):%s]"        <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" }

rules:

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"
when
	System started
	or
	Item Lueftung_Airflow changed
then
	if( (Lueftung_Airflow.state instanceof DecimalType) ){
		var String msg = Lueftung_Airflow.state.format("%d") + " m" + "\u00b3" +"/h"
  		postUpdate(Lueftung_Airflow_Msg,msg)
  	}
end


rule "Filterwechsel Message"
when
	System started
	or
	Item Lueftung_Filterwechsel changed
then
    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)
  	}

end


rule "Status and Error Status Message"
when
	System started
	or
	Item Lueftung_Status changed
	or
	Item Lueftung_StatusErr received update
then
    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 )
//  		}
//  	}
end


rule "Temperature Message"
when
	System started
	or
	Item Lueftung_Temperatur_Room changed
	or
	Item Lueftung_Temperatur_Extract changed
	or
	Item Lueftung_Temperatur_Exhaust changed
	or
	Item Lueftung_Temperatur_Outdoor changed
	or
	Item Lueftung_Temperatur_Supply changed
then
    	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")
    	}
end

rule "Humidity Message"
when
	System started
	or
	Item Lueftung_Humidity_Room changed
	or
	Item Lueftung_Humidity_Extract changed
	or
	Item Lueftung_Humidity_Exhaust changed
	or
	Item Lueftung_Humidity_Outdoor changed
	or
	Item Lueftung_Humidity_Supply changed
then
    	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") + "%")
    	}
end


 /*
rule "Filterlaufzeit"
when
	System started
	or
	Item Lueftung_Filterlaufzeit changed
    then
    	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"
    		}
    
    		postUpdate(Lueftung_Filterlaufzeit_Message,msg)
    	}
    end

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.

2 Likes

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:
http://www.zehnder-systems.de/produkte-und-systeme/komfortable-wohnraumlueftung/zehnder-comfoconnect-knx-c
Cheers,
Marco

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:
https://new.abb.com/low-voltage/products/building-automation/product-range/abb-i-bus-knx/products/power-supply
And I use https://github.com/knxd/knxd
as ip gateway on my server (same were openhab runs on).
I use this usb - KNX interface:
http://shop.busware.de/product_info.php/cPath/1_34/products_id/59

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

Hi,

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,

Daniel

Hello,
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.
Regards
Marco

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?
Regards

you can find the KNX applikation for comfoairQ here:
https://www.zehnder-systems.de/downloads/csy (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
Marco

Hello Michael,
it is here:
https://www.zehnder-systems.ch/de/file/177310/download?token=Z_M6QlbElEQGmSZ01c1zzx7paS_x027IO7_YAgUVl-A
Regards