Setup Modbus for Westaflex HVAC

I’m just ghost writing this – credit to Peter Aschinger @Peter_Aschinger for working through a real implementation, and producing the utility rules.

Here we describe how to use Modbus binding 2.x to allow openHAB to interact with a Westaflex WAC350 aircon, other models are probably similar.

WAC350 provides a serial port, so you’d probably use an RS485 USB dongle on your OH host, or an RS485 shield, then connect with twisted pair cable and RJ45 connector. For a long cable run, you may need to arrange a terminator resistor for your RS485 cable.

RJ45 Port 1 on the unit is reserved for the ‘FLEX’ controller panel, Port 2 is the one for external systems like openHAB, these are separate serial buses.

Follow the WAC manual to set up Port 2 serial parameters – typically 19200 baud, no parity, slave ID 1

We’ll also need to know the Modbus register map for this device, as found in the manual.

Sample of table

The WAC350 has several read-only ‘coils’ for binary signals, some writeable coils for control, read-only ‘input’ registers for numeric sensing, and writeable ‘holding’ registers for control.

That’s all we need to know to set up Modbus binding.

In this example we only use a few of the available registers.

We need to set up Things for the Modbus binding. A serial Bridge thing to define the basic slave, and poller Bridge things for each group of registers we want to access. Finally data Things that describe how to interpret each register.

You have a choice; you may add Things using PaperUI and edit parameters onscreen, for the OH system to save in its database.
Or you may add Things via a text file with your editor, say wac350.things. (You can also view these, but not edit, in PaperUI.)
Don’t try to mix these methods.

I’m going to list settings in the xxx.things style, as it’s more compact than providing a lot of PaperUI screenshots.

westaflex.things

// define the serial path to Westaflex
Bridge modbus:serial:wac350
     [ port="/dev/ttyS0", baud=19200, stopBits="1.0", parity="none", dataBits=8, encoding="rtu" ] {

     // define a block of coils to read
     Bridge poller wcoil [ start=17 length=1 refresh=10000 type="coil" ] {
              // selected coils of interest
              Thing data wc17 [ readStart="17", readValueType="bit", writeStart="17", writeType="coil" ]
     }

     // define block of inputs to read
     Bridge poller winputA [ start=0 length=10 refresh=5000 type="input" ] {
             // selected input reg
             Thing data wi00 [ readStart="0", readValueType="int16", readTransform="JS(divide10.js)" ]
             Thing data wi03 [ readStart="3", readValueType="int16", readTransform="JS(divide10.js)" ]
             Thing data wi06 [ readStart="6", readValueType="int16", readTransform="JS(divide10.js)" ]
             Thing data wi09 [ readStart="9", readValueType="int16", readTransform="JS(divide10.js)" ]
     }

     // define another block of inputs to read
     Bridge poller winputB [ start=12 length=5 refresh=5000 type="input" ] {
             Thing data wi13 [ readStart="13", readValueType="int16" ]
             Thing data wi14 [ readStart="14", readValueType="int16" ]
             Thing data wi15 [ readStart="15", readValueType="int16" ]
             Thing data wi16 [ readStart="16", readValueType="int16" ]
     }

     // define block of holdings to read/write
     Bridge poller wholdA [ start=0 length=2 refresh=10000 type="holding" ] {
             Thing data wh00 [ readStart="0", readValueType="int16", writeStart="0", writeValueType="int16", writeType="holding" ]
             Thing data wh01 [ readStart="1", readValueType="int16", writeStart="1", writeValueType="int16", writeType="holding" ]
     }

     // define another block of holdings to read/write
     Bridge poller wholdB [ start=300 length=10 refresh=10000 type="holding" ] {
              Thing data wh305 [ readStart="305", readValueType="uint16" ]
      }
}

Notice that some of the data Things use a read transform: some Westaflex registers contain e.g. 195 representing 19.5 degrees, so we tell the binding to use a little javascript to do the division as the binding updates the Item.
You will need to make sure the transformation service is installed in OH, and then create this file and put it in/conf/transform/

divide10.js

// Wrap everything in a function (no global variable pollution)
(function(inputData) {
// on read: the polled number as string
return parseFloat(inputData) / 10;
})(input)

Creating the data Things automatically makes various channels available for your Items.
At this stage, you should be polling your Westaflex over Modbus error free.

1 Like

To make the data available to openHAB rules and Uis, you must link Items to the Modbus channels.

westaflex.items

Group WAC350_Sensors "Westaflex sensors"

Number Zuluft "Zuluft [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputA:wi00:number" }
Number Abluft "Abluft [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputA:wi03:number" }
Number Fort_Luft "Fortluft [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputA:wi06:number" }
Number Aussenluft "Aussenluft [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputA:wi09:number" }
Number Abluftfeuchte "Abluftfeuchte [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputB:wi13:number" }
Number Bypassklappe "Bypassklappe [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputB:wi14:number" }
Number Luefter1 "Luefter1 [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputB:wi15:number" }
Number Luefter2 "Luefter2 [%.0f]" (WAC350_Sensors) { channel="modbus:data:winputB:wi16:number" }
Number LuefterStufe "LuefterStufe [%.0f]" (WAC350_Sensors) { channel="modbus:data:wholdA:wh00:number" }
Number ZuluftTempSet "Solltemperatur [%.0f]" (WAC350_Sensors) { channel="modbus:data:wholdA:wh01:number" }
Number FilterTimer "Filter Timer [%.0f]" { channel="modbus:data:wholdB:wh305:number" }
Switch FanStandby_Switch "FanStandby_Switch" { channel="modbus:data:wcoil:wc17:switch" }

The following Items are not linked to Modbus, may be useful in rules

westaflexplus.items

// additional Items for rules

// for calculated efficiency
Number Wirkungsgrad "Wirkungsgrad" (WAC350_Sensors)

// set your outdoor temp limits and a control
Switch Lueftung_min_max_Temp "Lueftung_min_max_Temp"
Number:Temperature AussenGrenzMax_Set "AussenGrenzMax_Set"
Number:Temperature AussenGrenzMin_Set "AussenGrenzMin_Set"

// set your timer controls
Switch WAC350_Zeitschaltung_01
Switch WAC350_Zeitschaltung_02
// timer hours and mins controls
Number Deko_WECKER_H "Start Stunde [%s]"
Number Deko_WECKER_M "Start Minute [%s]"
Number Deko_WECKER_Ho "Start Stunde [%s]"
Number Deko_WECKER_Mo "Start Minute [%s]"

etc.

Some rule samples, to control your HVAC

westaflex.rules

// Beginn der Datei
// globale Variablen
var Long lLStufe
var Timer Deko = null


rule "Aussenluft manuell"
when
    Item AussenTemp changed // your outdoor temp sensor
then
   if(Lueftung_min_max_Temp.state != ON) return;  // wenn Schalter aus(nicht an) , keine Aktionen
   
   if(!(AussenTemp.state instanceof Number)) {
        logWarn("Lüftung manuell", "AussenTemp.state not a Number: {}",AussenTemp.state)  // numerisch ?
        return;
   }
   if (AussenTemp.state >= AussenGrenzMax_Set.state && FanStandby_Switch.state != OFF) {
       // above max limit
       FanStandby_Switch.sendCommand(ON)
       logInfo("Lüftung manuell", "LuefterStufe ist jetzt: {} AussenTemp beträgt {}",LuefterStufe.state,AussenTemp.state )
       
   }
   else if (AussenTemp.state <= AussenGrenzMin_Set.state && FanStandby_Switch.state != ON) { // below min limit
       FanStandby_Switch.sendCommand(OFF)
       logInfo("Lüftung manuell", "LuefterStufe ist jetzt: {} AussenTemp beträgt {}",LuefterStufe.state,AussenTemp.state)
       
   }
end


rule "luefter geändert"
when
    Item LuefterStufe changed
then
    lLStufe = now.millis
end

rule "berechne Wirkungsgrad"  // calculate efficiency
when
    Item Abluft changed or
    Item Zuluft changed or
    Item Aussenluft changed
then
    if(lLStufe + 15*60*1000 < now.millis && LuefterStufe.state.toString != "0"){
    //Lüfter läuft bereits mindestens 15 Minuten auf der aktuellen Stufe

       if(!(Abluft.state instanceof Number)) {
           logWarn("Wirkungsgrad", "Abluft.state not a Number: {}",Abluft.state)
           return;
       }
       if(!(Zuluft.state instanceof Number)) {
           logWarn("Wirkungsgrad", "Zuluft.state not a Number: {}",Zuluft.state)
           return;
       }
       if(!(Aussenluft.state instanceof Number)) {
           logWarn("Wirkungsgrad", "Aussenluft.state not a Number: {}", Aussenluft.state)
           return;
       }
       val Number Aussenluft = (Aussenluft.state as Number)
       val Number nDivisor = (Abluft.state as Number) - Aussenluft //Wert fuer Berechung gem. Westaflex
       val Number nFaktor = (Zuluft.state as Number) - Aussenluft //Wert fuer Berechung gem. Westaflex
       if(nDivisor == 0) {
           logWarn("Wirkungsgrad", "Aussentemperatur = Ablufttemperatur, Division durch 0!")
           Wirkungsgrad.postUpdate(NULL)
       } else
           Wirkungsgrad.postUpdate((100*nFaktor/nDivisor).intValue) //Berechung gem. Westaflex
          
 }
end


rule "Wecker Deko"
    when
        Time cron "0 * * * * ?" //Abfrage jede Minute
    then

    if(WAC350_Zeitschaltung_01.state == ON) {
        var sollMinute = (Deko_WECKER_M.state as DecimalType).intValue
        var sollStunde = (Deko_WECKER_H.state as DecimalType).intValue
        var sollMinuteo = (Deko_WECKER_Mo.state as DecimalType).intValue
        var sollStundeo = (Deko_WECKER_Ho.state as DecimalType).intValue

        if (sollMinute == now.getMinuteOfHour && sollStunde == now.getHourOfDay) {
            LuefterStufe.sendCommand(1)
        } else if (sollMinuteo == now.getMinuteOfHour && sollStundeo == now.getHourOfDay) {
            LuefterStufe.sendCommand(0)
        }
    }
    
    if(WAC350_Zeitschaltung_02.state == ON) {
        var sollMinute = (Deko_WECKER_Ma.state as DecimalType).intValue
        var sollStunde = (Deko_WECKER_Ha.state as DecimalType).intValue
        var sollMinuteo = (Deko_WECKER_Maa.state as DecimalType).intValue
        var sollStundeo = (Deko_WECKER_Haa.state as DecimalType).intValue

        if (sollMinute == now.getMinuteOfHour && sollStunde == now.getHourOfDay) {
            LuefterStufe.sendCommand(2)
        } else if (sollMinuteo == now.getMinuteOfHour && sollStundeo == now.getHourOfDay) {
            LuefterStufe.sendCommand(1)
        }    
    }
end

:+1:
Dear @rossko57, thanks for making this Tutorial.
@all:
If you have any questions feel free to ask.

Br Peter

Hi There… long time a go in the deep files of OpenHab …
I managed to move my very old PaperUI Config to the actual Textfile config.
Maybe its helpful to someone.

modbus.things:

// define the serial path to Westaflex
Bridge modbus:serial:wac350
     [ port="/dev/ttyS0", baud=19200, stopBits="1.0", parity="none", dataBits=8, encoding="rtu" ] {

     // define a block of coils to read // Regular poll Fanstandby
     Bridge poller wcoil [ start=9, length=35, refresh=10000, type="coil" ] {
              // selected coils of interest
              Thing data wc09 [ readStart="9",  readValueType="bit", writeStart="9",  writeType="coil" ] //Geringe_Feuchte_Alarm
              Thing data wc16 [ readStart="16", readValueType="bit", writeStart="16", writeType="coil" ] //FilterTimeReset
              Thing data wc17 [ readStart="17", readValueType="bit", writeStart="17", writeType="coil" ] //FanStandby_Switch
              Thing data wc18 [ readStart="18", readValueType="bit", writeStart="18", writeType="coil" ] //System_Reset
              Thing data wc42 [ readStart="42", readValueType="bit", writeStart="42", writeType="coil" ] //Bypass Active
     } 

     // define block of inputs to read // Regular poll 0-10
     Bridge poller winputA [ start=0, length=10, refresh=60000, type="input" ] {
             // selected input reg
             Thing data wi00 [ readStart="0", readValueType="int16", readTransform="JS(divide10.js)" ] //Zuluft
             Thing data wi03 [ readStart="3", readValueType="int16", readTransform="JS(divide10.js)" ] //Abluft
             Thing data wi06 [ readStart="6", readValueType="int16", readTransform="JS(divide10.js)" ] //Fort_Luft
             Thing data wi09 [ readStart="9", readValueType="int16", readTransform="JS(divide10.js)" ] //Aussenluft
     }

     // define another block of inputs to read // Regular poll 12-16
     Bridge poller winputB [ start=12, length=5, refresh=60000, type="input" ] {
             Thing data wi13 [ readStart="13", readValueType="int16" ] //Abluftfeuchte 
             Thing data wi14 [ readStart="14", readValueType="int16" ] //Bypassklappe
             Thing data wi15 [ readStart="15", readValueType="int16" ] //Luefter1
             Thing data wi16 [ readStart="16", readValueType="int16" ] //Luefter2
     }

     // define block of holdings to read/write // Regular poll fanspeed
     Bridge poller wholdA [ start=0, length=2, refresh=10000, type="holding" ] {
             Thing data wh00 [ readStart="0", readValueType="int16", writeStart="0", writeValueType="int16", writeType="holding" ] //LuefterStufe
             Thing data wh01 [ readStart="1", readValueType="int16", writeStart="1", writeValueType="int16", writeType="holding" ] //ZuluftTempSet
     }

     // define another block of holdings to read/write // Regular poll
     Bridge poller wholdB [ start=300, length=10, refresh=600000, type="holding" ] {
              Thing data wh305 [ readStart="305", readValueType="uint16" ] //FilterTimer
      }
}

an here the items:
westaflex.items

Group     WAC350_Sensors           "Westaflex Sensors"

Number    Zuluft                   "Zuluft [%.0f]"                  (WAC350_Sensors)    {channel="modbus:data:wac350:winputA:wi00:number"}
Number    Abluft                   "Abluft [%.0f]"                  (WAC350_Sensors)    {channel="modbus:data:wac350:winputA:wi03:number"}
Number    Fort_Luft                "Fortluft [%.0f]"                (WAC350_Sensors)    {channel="modbus:data:wac350:winputA:wi06:number"}
Number    Aussenluft               "Aussenluft [%.0f]"              (WAC350_Sensors)    {channel="modbus:data:wac350:winputA:wi09:number"} (Item über LaCrosse Sensor da Fehler auf Westaflex Platine)
Number    Abluftfeuchte            "Abluftfeuchte [%.0f]"           (WAC350_Sensors)    {channel="modbus:data:wac350:winputB:wi13:number"} (Item über LaCrosse Sensor da Fehler auf Westaflex Platine)
Number    Bypassklappe             "Bypassklappe"                   (WAC350_Sensors)    {channel="modbus:data:wac350:winputB:wi14:number"}
Number    Luefter1                 "Luefter1 [%s %%]"               (WAC350_Sensors)    {channel="modbus:data:wac350:winputB:wi15:number"}
Number    Luefter2                 "Luefter2 [%s %%]"               (WAC350_Sensors)    {channel="modbus:data:wac350:winputB:wi16:number"}
Number    LuefterStufe             "LuefterStufe"                   (WAC350_Sensors)    {channel="modbus:data:wac350:wholdA:wh00:number"}
Number    ZuluftTempSet            "Solltemperatur"                 (WAC350_Sensors)    {channel="modbus:data:wac350:wholdA:wh01:number"}
Number    FilterTimer              "Filtertimer [%s h]"                                 {channel="modbus:data:wac350:wholdB:wh305:number"}
Number    Geringe_Feuchte_Alarm    "Alarm geringe Abluftfeuchte"                        {channel="modbus:data:wac350:wcoil:wc09:number"}
Switch    FanStandby_Switch        "FanStandby_Switch"                                  {channel="modbus:data:wac350:wcoil:wc17:switch"}
Switch    FiltertimerReset         "Reset Filtertimer"                                  {channel="modbus:data:wac350:wcoil:wc16:switch"}
Switch    System_Reset             "Reset Alarme"                                       {channel="modbus:data:wac350:wcoil:wc18:switch"}
Number    BypassActive             "Bypass Aktiv"                                       {channel="modbus:data:wac350:wcoil:wc42:number"}

This is great stuff. I have a Clivet Gaia HVAC unit which has also Modbus for internal functioning and they give all the control registers in the manual.
I have to try this in the future…

Here a few Screenshots of my UI:


57

2 Likes

And here comes a additional rule to having a reminder to change the filter elements inside the unit:

 // globale Variablen müssen zu Beginn der rules Datei deklariert werden!
var Timer tFilter = null

rule "Filterwarnung"

when
Item FilterTimer changed

then
if(tFilter === null && (FilterTimer.state >2000 )) {
    tFilter = createTimer(now.plusHours(12), [ |
        tFilter = null
    ])
    sendBroadcastNotification("Erinnerung !!  Filterwechsel bei 2160h! Aktueller Wert "+FilterTimer.state.toString+"h") //Allgemeine Warnung an alle 
    
    
}
end

Here another usefull rule:
“Control based on humidity”

var Timer tLuftfeuchtigkeitBad = null

rule "LuftfeuchtigkeitBad"
when
	Item InnenDGBadFeuchte changed
then
	val int lfBad=(InnenDGBadFeuchte.state as Number).intValue()
	
	if (lfBad<50) {
	
		// sollte die Lueftung wegen dem Bad auf Stufe 2 (Mittel) geschaltet worden sein und noch kein Timer laufen,
		// der die Lueftung wieder mit Nachlauf durch reaktivierung der Feuchtesteuerung auf Stufe Klein schaltet
		//if (LuefterStufe.state==2 && tLuftfeuchtigkeitBad===null && Lueftung_Zeit.state==OFF && Lueftung_Feuchte.state==OFF && WAC350_Durchschnitt_Feuchte_Haus.state==ON) {
        if (Lueftung_Bad.state==ON && tLuftfeuchtigkeitBad===null) {
			// ...starte nun einen Timer, der nach 60 Minuten die Feuchtesteuerung wieder aktiviert
            logInfo("Feuchtesteuerung mit Nachlauf:","Nachlauf 60min aktiviert!")
            sendBroadcastNotification("Feuchtesteuerung mit Nachlauf um " + now.toString("HH:mm") + " Uhr: Starte 60min Timer")            //Pushnachricht
			tLuftfeuchtigkeitBad=createTimer(now.plusMinutes(60), [|        // wenn der der Timer abgelaufen ist:
                WAC350_Durchschnitt_Feuchte_Haus.postUpdate(ON)             // reaktiviere Feuchtesteuerung
                Lueftung_Bad.postUpdate(OFF)                                // Status Item Lüftung durch Nachlaufsteuerung AUS
                logInfo("Feuchtesteuerung mit Nachlauf:","Nachlauf 60min beendet!")
                sendBroadcastNotification("Feuchtesteuerung mit Nachlauf um " + now.toString("HH:mm") + " Uhr: 60min Timer beendet, Feuchtesteuerung reaktiviert")            //Pushnachricht
				tLuftfeuchtigkeitBad=null
			])
		}
	}
	else if (lfBad>55 && Lueftung_Bad.state==OFF && Lueftung_Temperatur.state==OFF && LuefterStufe.state==1) {
	
		// schalte die Lueftung auf Grund des Bades auf Stufe 2 (Mittel) und deaktiviere die Feuchtesteuerung
        Lueftung_Bad.postUpdate(ON)                                // Status Item Lüftung durch Nachlaufsteuerung EIN
		WAC350_Durchschnitt_Feuchte_Haus.postUpdate(OFF)           // Deaktiviere Feuchtesteuerung
        LuefterStufe.sendCommand(2)                                // Schalte Lüftung auf Stufe 2 (Mittel)
        logInfo("Feuchtesteuerung mit Nachlauf:","Lüftung Stufe 2 (Mittel)")
        sendBroadcastNotification("Feuchtesteuerung mit Nachlauf um " + now.toString("HH:mm") + " Uhr: Lüftung Stufe 2 (Mittel) durch: " + triggeringItem.name + " " + triggeringItem.state.toString)     //Pushnachricht
		// sollte noch der Timer laufen, der sie wieder ausschaltet, brich ihn ab, damit
		// erst dann mit Nachlauf ausgeschaltet wird, wenn der Luftfeuchtewert wieder gesunken ist
		if (tLuftfeuchtigkeitBad!==null) {
			tLuftfeuchtigkeitBad.cancel()
			tLuftfeuchtigkeitBad=null
		}
	}
end

rule "WACLuftfeuchtigkeit"
when
    Member of Durchschnitt_Feuchte_Haus changed // Durchschnitt Feuchte durch Lacrosse Sensoren
then
    if (WAC350_Durchschnitt_Feuchte_Haus.state==OFF) { // wenn Schalter aus, keine Aktionen
        logInfo("Feuchtesteuerung:","Steuerung deaktiviert!")
    return;
    }
    // bestimme die durchschnittliche und maximale Luftfeuchte
    var int lfSumme=0
    var int lfMax=0
    var int lfCnt=0
    for (item: Durchschnitt_Feuchte_Haus.members) {
        if (item.state instanceof Number) {
            lfCnt+=1
            val int lf=(item.state as Number).intValue()
            lfSumme+=lf
            if (lf>lfMax) lfMax=lf
        }
    }
    // nur bei mindestens 2 Werten ist es sinnvoll, die Werte zu betrachten
    var int lfAvg
    if (lfCnt>=2) {
        lfAvg=lfSumme/lfCnt
    }
    else {
        lfAvg=0
        lfMax=0
    }
    // Bei mehr als max 55% pro Sensor oder avg 50% Luftfeuchtigkeit, lüfte durch Lüftung Stufe 2 (Mittel)
    if ((lfMax>=55 || lfAvg>=50) && Lueftung_Feuchte.state==OFF && Lueftung_Bad.state==OFF) {   // Lüftung läuft auf Stufe 1 (Klein)
        LuefterStufe.sendCommand(2)                                    // Schalte Lüftung auf Stufe 2 (Mittel)
        Lueftung_Feuchte.postUpdate(ON)                                // Status Item Lüftung durch Feuchtesteuerung Stufe 2 (Mittel)
        logInfo("Feuchtesteuerung:","Lüftung Stufe 2 (Mittel)" + triggeringItem.name  + " " + triggeringItem.state.toString) 
        sendBroadcastNotification("Feuchtesteuerung um " + now.toString("HH:mm") + " Uhr: Lüftung Stufe 2 (Mittel) durch: " + triggeringItem.name + " " + triggeringItem.state.toString)     //Pushnachricht
    }
    else {
    // fällt die Luftfeuchte aller Sensoren unter 50%, wurde genug gelüftet, schalte Lüftung zurück auf Stufe 1 (Klein)
    if ((lfMax<50) && LuefterStufe.state==2) {                         // Lüftung läuft auf Stufe 2 (Mittel)
        LuefterStufe.sendCommand(1)                                    // Schalte Lüftung auf Stufe Klein
        Lueftung_Feuchte.postUpdate(OFF)                               // Status Item Lüftung durch Feuchtesteuerung Stufe Klein
        logInfo("Feuchtesteuerung:","Lüftung Stufe 1 (Klein)" + triggeringItem.name  + " " + triggeringItem.state.toString)
        sendBroadcastNotification("Feuchtesteuerung um " + now.toString("HH:mm") + " Uhr: Lüftung Stufe 1 (Klein) durch: " + triggeringItem.name + " " + triggeringItem.state.toString) //Pushnachricht
        }
    }
end

rule "Zwangslüftung EIN auf Stufe 2 (Mittel)"
when
    Time cron "0 30 11,18 * * ? *" // 11:30, 18:30 Uhr
then
    if (WAC350_Durchschnitt_Feuchte_Haus_Zwang.state==OFF) {          // wenn Schalter aus, keine Aktionen
        logInfo("Zwangslüftung:","Einschaltzeit erreicht aber Deaktiviert")
        sendBroadcastNotification("Zwangslüftung: Einschaltzeit " + now.toString("HH:mm") + " Uhr erreicht aber Deaktiviert") //Pushnachricht
    return;
    }
    if (LuefterStufe.state==2) {                                        // Lüftung läuft schon auf Stufe 2 (Mittel)
        WAC350_Durchschnitt_Feuchte_Haus.postUpdate(OFF)                // deaktiviere Feuchtesteuerung trotzdem
        Lueftung_Zeit.postUpdate(ON)                                    // Status Item Zwangslüftung ist EIN
        // läuft der Timer Nachlauf, brich ihn ab, damit die Zwangslüftung nicht vorzeitig beendet wird.
        if (tLuftfeuchtigkeitBad!==null) {
			tLuftfeuchtigkeitBad.cancel()
			tLuftfeuchtigkeitBad=null
            Lueftung_Bad.postUpdate(OFF)                                 // lösche Status Item Lüftung durch Nachlaufsteuerung
            logInfo("Zwangslüftung:","Beende Nachlauftimer 60min vorzeitig")
            sendBroadcastNotification("Zwangslüftung: Beende Nachlauftimer vorzeitig um: " + now.toString("HH:mm") + " Uhr") //Pushnachricht
		}
        logInfo("Zwangslüftung:","Einschaltzeit erreicht. Lüftung läuft schon auf Stufe 2 (Mittel)")
        sendBroadcastNotification("Zwangslüftung: Einschaltzeit " + now.toString("HH:mm") + " Uhr erreicht. Lüftung läuft schon auf Stufe 2 (Mittel)") //Pushnachricht
    return;
    }
    // wenn Uhrzeit erreicht ist und die Lüftung noch nicht auf Stufe 2 (Mittel) läuft deaktiviere Feuchtesteuerung und schalte Lüftung auf Stufe 2 (Mittel)
        WAC350_Durchschnitt_Feuchte_Haus.postUpdate(OFF)                 // deaktiviere Feuchtesteuerung
        LuefterStufe.sendCommand(2)                                      // Schalte Lüftung auf Stufe 2 (Mittel)
        Lueftung_Zeit.postUpdate(ON)                                     // Status Item Zwangslüftung ist EIN
        logInfo("Zwangslüftung:","Zwangslüftung gestartet") 
        sendBroadcastNotification("Zwangslüftung um " + now.toString("HH:mm") + " Uhr gestartet, Feuchtesteuerung deaktiviert, schalte Lüftung auf Stufe 2 (Mittel)")        //Pushnachricht
    
end

rule "Zwangslüftung AUS auf Stufe 1 (Klein)"
when
    Time cron "0 30 13,21 * * ? *" // 13:30, 21:30 Uhr
then
    if (WAC350_Durchschnitt_Feuchte_Haus_Zwang.state==OFF) {             // wenn Schalter aus, keine Aktionen
        logInfo("Zwangslüftung:","Ausschaltzeit erreicht aber Deaktiviert")
        sendBroadcastNotification("Zwangslüftung: Ausschaltzeit " + now.toString("HH:mm") + " Uhr erreicht aber Deaktiviert") //Pushnachricht
    return;
    }
    // wenn Uhrzeit erreicht ist und noch auf Stufe 1 (Klein) läuft reaktiviere nur Feuchte Steuerung
    if (LuefterStufe.state==1) {                                         // Lüftung läuft noch auf Stufe 1 (Klein)
        WAC350_Durchschnitt_Feuchte_Haus.postUpdate(ON)                  // reaktiviere Feuchtesteuerung
        Lueftung_Zeit.postUpdate(OFF)                                    // Status Item Zwangslüftung ist AUS
        logInfo("Zwangslüftung:","Ausschaltzeit erreicht. Lüftung läuft noch auf Stufe 1 (Klein)")
        sendBroadcastNotification("Zwangslüftung: Ausschaltzeit " + now.toString("HH:mm") + " Uhr erreicht. Lüftung läuft noch auf Stufe 1 (Klein)") //Pushnachricht
    return;
    }
    // wenn Uhrzeit erreicht ist und noch auf Stufe 2 (Mittel) läuft reaktiviere nur Feuchte Steuerung
        WAC350_Durchschnitt_Feuchte_Haus.postUpdate(ON)               // reaktiviere Feuchtesteuerung
        //LuefterStufe.sendCommand(1)                                   // Schalte Lüftung auf Stufe 1 (Klein)
        Lueftung_Zeit.postUpdate(OFF)                                 // Status Item Zwangslüftung ist AUS
        logInfo("Zwangslüftung:","Zwangslüftung beendet") 
        sendBroadcastNotification("Zwangslüftung um " + now.toString("HH:mm") + " Uhr beendet, Feuchtesteuerung reaktiviert")            //Pushnachricht
    
end

rule "Lüfterstufe nach Außentemperatur"
when
    Item AussenTemp changed // Lacrosse Sensor
then
    if(Lueftung_min_max_Temp.state==OFF) { // wenn Schalter aus, keine Aktionen
        //logInfo("Lüfterstufe nach Außentemperatur","Steuerung deaktiviert!")
        return;
    }
    if(!(AussenTemp.state instanceof Number)) {  // numerisch ?
        logWarn("Lüfterstufe nach Außentemperatur","AussenTemp.state not a Number: {}",AussenTemp.state)
        return;
   }
    if(!(AussenGrenzMax_Set.state instanceof Number)) {  // numerisch ?
        logWarn("Lüfterstufe nach Außentemperatur","AussenGrenzMax_Set.state not a Number: {}",AussenGrenzMax_Set.state)
        return;
   }
    if(!(AussenGrenzMin_Set.state instanceof Number)) {  // numerisch ?
        logWarn("Lüfterstufe nach Außentemperatur","AussenGrenzMin_Set.state not a Number: {}",AussenGrenzMin_Set.state)
        return;
   }
   if ((Aussenluft.state as Number) >= (AussenGrenzMax_Set.state as Number) && LuefterStufe.state==2) {        //Schalte Lüftung auf Stufe 1 wenn die angesaugte Außenluft den Sollwert erreicht hat
       LuefterStufe.sendCommand(1)
       WAC350_Durchschnitt_Feuchte_Haus_Zwang.postUpdate(OFF)                                                     // deaktiviere Zeitsteuerung
       WAC350_Durchschnitt_Feuchte_Haus.postUpdate(OFF)                                                           // deaktiviere Feuchtesteuerung
       Lueftung_Temperatur.postUpdate(ON)                                                                         // Status Item Lüftung auf Stufe 1 durch Außenlufttemperatur
       logInfo("Lüfterstufe nach Außentemperatur","LuefterStufe ist jetzt: {} Aussenluft {}",LuefterStufe.state,Aussenluft.state )
       sendBroadcastNotification("Lüftung durch Aussenluft: " +Aussenluft.state.toString+"°C, auf Stufe 1 (Klein) um " + now.toString("HH:mm") + " Uhr") //Pushnachricht
   } else if ((AussenTemp.state as Number) <= (AussenGrenzMin_Set.state as Number) && Lueftung_Temperatur.state==ON) { //Schalte Lüftung auf Stufe 2 wenn die Außentemperatur den Sollwert erreicht hat
       LuefterStufe.sendCommand(2)
       WAC350_Durchschnitt_Feuchte_Haus_Zwang.postUpdate(ON)                                                      // reaktiviere Zeitsteuerung
       WAC350_Durchschnitt_Feuchte_Haus.postUpdate(ON)                                                            // reaktiviere Feuchtesteuerung
       Lueftung_Temperatur.postUpdate(OFF)                                                                        // Status Item Lüftung auf Stufe 2 durch Außentemperatur
       logInfo("Lüfterstufe nach Außentemperatur","LuefterStufe ist jetzt: {} AussenTemp beträgt {}",LuefterStufe.state,AussenTemp.state)
       sendBroadcastNotification("Lüftung durch Aussentemp: " +AussenTemp.state.toString+"°C, auf Stufe 2 (Mittel) um " + now.toString("HH:mm") + " Uhr") //Pushnachricht
   }
end

rule "Lüfterstufe 1 (Klein) Zeitstempel"
when
    Item LuefterStufe changed from 2 to 1
then
    LuefterStufe.state == 1  // Lüftung ist auf Stufe 1 (Klein)
    Luefterstufe_1_last.postUpdate(now.toString())
end

rule "Lüfterstufe 2 (Mittel) Zeitstempel"
when
    Item LuefterStufe changed from 1 to 2
then
    LuefterStufe.state == 2  // Lüftung ist auf Srufe 2 (Mittel)
    Luefterstufe_2_last.postUpdate(now.toString())
end

rule "Lüftung Standby Zeitstempel AUS"
when
    Item FanStandby_Switch changed from OFF to ON
then
    FanStandby_Switch.state == ON  // Lüftung ist AUS
    Lueftungstandby_on_last.postUpdate(now.toString())
end

rule "Lüftung Standby Zeitstempel EIN"
when
    Item FanStandby_Switch changed from ON to OFF
then
    FanStandby_Switch.state == OFF  // Lüftung ist EIN
    Lueftungstandby_off_last.postUpdate(now.toString())
end

This is the place for connecting the to the ModBus


You might need also a kind of adaptor:
https://www.kab24.de/kommunikation/kab24-adapter-westernbuchse-rj45-auf-rj126p6crj11-stecker-02m.html?cache=1553093850

1 Like