ComfoAir: Rule 'showMessage': Script 'comfoair_errorMsg' cannot be found

Anyone an idea? It seems a script in the ComfoAir Binding documentation is missing.

Version: OpenHAB 2.4
Configuration: https://www.openhab.org/addons/bindings/comfoair1/ → Exactly the same; copy&paste
Issue with: comfoair.rules
Error Message in openhab.log: Rule ‘showMessage’: Script ‘comfoair_errorMsg’ cannot be found.

Detailed Error Message:

2019-08-11 20:23:56.316 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘showMessage’: Script ‘comfoair_errorMsg’ cannot be found.

Detailed comfoair.rules (it’s copy pasted from https://www.openhab.org/addons/bindings/comfoair1/ ):

import java.lang.Math
import java.util.Date
import java.util.List
import java.util.ArrayList

// variable for auto_mode state
var Boolean isAutoMode = false

// based capacity for absent level
// absent level is used for supply/exhaust mode
var Number comfoairFanIn0BaseValue = 15
var Number comfoairFanOut0BaseValue = 15
var Number comfoairFanLevelLastValue
var Number comfoairFanIn0LastValue
var Number comfoairFanOut0LastValue


// System start can take some time
rule "delayForSystemStart"
when
	System started
then
	Delayed_Start.postUpdate( OFF )

	createTimer( now.plusSeconds( 120 )) [| Delayed_Start.sendCommand( ON )]
end


rule "setAutoModeForStart"
when
	Item Delayed_Start changed to ON
then
	if( comfoairControl.state == 1 ) {
		comfoairMode.postUpdate( 1 )
		isAutoMode = true
		comfoairFanMode.postUpdate( 0 )
	}
end


rule "setControlToCCEaseWhenSystemClose"
when
	System shuts down
then
	comfoairControl.sendCommand( 0 )
end


rule "messageWhenFrozen"
when
	Item Delayed_Start changed to ON
	or
	Item comfoairControl changed to 1
	or
	Item comfoairIncomingFan changed
	or
	Item comfoairOutgoingFan changed
	or
	Item weatherN_Temperature changed
then
	if( Delayed_Start.state == ON && comfoairControl.state == 1 ) {
		var String msg = ""

		if( comfoairIncomingFan.state instanceof DecimalType && comfoairOutgoingFan.state instanceof DecimalType && weatherN_Temperature.state instanceof DecimalType ) {
			
			if( weatherN_Temperature.state < 0 && ( comfoairIncomingFan.state == 0 || comfoairOutgoingFan.state == 0 ) ) {
				msg = "FROZEN"
			} else msg = "OK"
			
		} else msg = "communication failed"
		
		comfoairFrozenError.postUpdate( msg )
	}
end


rule "messageWhenInletPlugged"
when
	Item Delayed_Start changed to ON
	or
	Item comfoairControl changed to 1
	or
	Item comfoairOutdoorIncomingTemperature changed
	or
	Item weatherN_Temperature changed
	or
	Item comfoairFrozenError changed
then
	if( Delayed_Start.state == ON && comfoairControl.state == 1 ) {
		var String msg = ""

		if( comfoairOutdoorIncomingTemperature.state instanceof DecimalType && weatherN_Temperature.state instanceof DecimalType && comfoairFrozenError.state instanceof StringType ) {
			
			if( weatherN_Temperature.state as DecimalType + 10 < comfoairOutdoorIncomingTemperature.state as DecimalType && comfoairFrozenError.state == "OK" ) {
				msg = "PLUGGED"
			} else msg = "OK"
			
		} else msg = "communication failed"
		
		comfoairInletError.postUpdate( msg )
	}
end


rule "showMessage"
when
	Item Delayed_Start changed to ON
	or
	Item comfoairControl changed to 1
	or
	Item comfoairError changed
	or
	Item comfoairFilterErrorI changed
	or
	Item comfoairFilterErrorE changed
	or
	Item comfoairInletError changed
	or
	Item comfoairFreezeError changed
then
	if( Delayed_Start.state == ON && comfoairControl.state == 1 ) {
		var String msg = "" + callScript( "comfoair_errorMsg" )
		var String msgToSend = "" + callScript( "comfoair_errorMsgToSend" )
		
		comfoairError_Message.postUpdate( msg )
		
		if( comfoairError.state != "Ok" || comfoairFilterErrorI.state == 1 || comfoairFilterErrorE.state == 1 || comfoairInletError.state != "OK" || comfoairFrozenError.state != "OK" ) {
			sendMail( "xyz@gmail.com", "ComfoAir message", msgToSend )
		}
	}
end


rule "showFilterTime"
when
	Item comfoairFilterRuntime changed
	or
	Item comfoairFilterPeriod changed
	or
	Item Delayed_Start changed to ON
	or
	Item comfoairControl changed to 1
then
	if( comfoairFilterRuntime.state instanceof DecimalType && comfoairFilterPeriod.state instanceof DecimalType ) {
		var Number filterPeriodWeeks = comfoairFilterPeriod.state
		var Number filterPeriodHours = filterPeriodWeeks * 7 * 24
		
		if( comfoairFilterRuntime.state instanceof DecimalType ){
			var Number passedHours = comfoairFilterRuntime.state as DecimalType
			var Boolean isTimeExceeded

			if( filterPeriodHours - passedHours < 0) {
				isTimeExceeded = true
			} else isTimeExceeded = false

			var Number passedWeeks = Math::floor( ( passedHours / 168 ).doubleValue )
			var Number passedDays = Math::floor( ( ( passedHours - (passedWeeks * 168 ) ) / 24).doubleValue )
			var Number remainedHours = Math::abs( ( filterPeriodHours - passedHours ).intValue )
			var Number remainedWeeks = Math::floor( ( remainedHours / 168 ).doubleValue )
			var Number remainedDays = Math::floor( ( ( remainedHours - ( remainedWeeks * 168 ) ) / 24).doubleValue )
		
			var String msg = "Passed: "
		
			if( passedWeeks > 0 ) {

				if( passedWeeks == 1 ) msg = msg + "1 week"
				else msg = msg + passedWeeks.intValue + " weeks"
				
				if( passedDays > 0) msg = msg + " "
				else msg = msg + ","
			}
			if( passedDays > 0 ) {
				if( passedDays == 1 ) msg = msg + "1 day,"
				else msg = msg + passedDays.intValue + " days,"
			}
			if( passedWeeks == 0 && passedDays == 0 ) msg = msg + "0 days, "
			
			if( isTimeExceeded ) msg = msg + " Exceeded: "
			else msg = msg + " Remained: "
			
			if( remainedWeeks != 0 ) {

				if( remainedWeeks == 1 ) msg = msg + "1 week"
				else msg = msg + remainedWeeks.intValue + " weeks"

				if( remainedDays > 0 ) msg = msg + " "
			}
			if( remainedDays != 0 ) {
				if( remainedDays == 1 ) msg = msg + "1 day"
				else msg = msg + remainedDays.intValue + " days"
			}
			
			if( remainedWeeks == 0 && remainedDays == 0 ) msg = msg + "0 days"
			
			comfoairFilterRuntime_Message.postUpdate( msg )
			
		} else {
			comfoairFilterRuntime_Message.postUpdate( "communication failed" )
		}
	}
end


rule "errorReset"
when
	Item comfoairReset changed
then
	var String error
	
	if( comfoairControl.state == 1 ) {
		
		if( comfoairReset.state == "0" ) {
			comfoairFilterReset.sendCommand( 0 )
			logInfo( "ComfoAir", "Filter was reset !" )
			
		} else if( comfoairReset.state == "1" ) {
			comfoairErrorReset.sendCommand( 0 )
			logInfo( "ComfoAir", "Error was reset !" )
		}
		
		createTimer(now.plusSeconds( 5 ),  [ |
			error = "" + callScript( "comfoair_errorMsg" )
			comfoairError_Message.postUpdate( error )
		])
	} else {
		if( comfoairReset.state == "0" || comfoairReset.state == "1" ) {
			comfoairError_Message.postUpdate( "PC control is not active" )
			
			createTimer(now.plusSeconds( 10 ),  [ |
				error = "" + callScript( "comfoair_errorMsg" )
				comfoairError_Message.postUpdate( error )
			])
		}
	}
end


rule "calculateEfficiency"
when
	Item Delayed_Start changed to ON
	or
	Item comfoairOutdoorIncomingTemperature changed
	or
	Item comfoairIndoorOutgoingTemperature changed
	or
	Item comfoairIndoorIncomingTemperature changed
	or
	Item comfoairControl changed to 1
then
	var Number efficiency

	if( comfoairOutdoorIncomingTemperature.state instanceof DecimalType && comfoairIndoorOutgoingTemperature.state instanceof DecimalType && comfoairIndoorIncomingTemperature.state instanceof DecimalType && comfoairBypassMode.state == 0 ) {
		var Number tempOutIn = comfoairOutdoorIncomingTemperature.state as DecimalType
		var Number tempInOut = comfoairIndoorOutgoingTemperature.state as DecimalType
		var Number tempInIn = comfoairIndoorIncomingTemperature.state as DecimalType

		if( tempInOut != tempOutIn ) {
			efficiency = ( tempInIn - tempOutIn ) / ( tempInOut - tempOutIn ) * 100
			efficiency = Math::round( efficiency.doubleValue );
		} else efficiency = null
	} else efficiency = null

	comfoairEfficiency.postUpdate( efficiency )
end


rule "changeSupply-Exhaust"
when
	Item comfoairFanMode_Message changed
then
	var Number newFanMode = comfoairFanMode_Message.state as DecimalType
	
	if( comfoairControl.state == 1 ) {

		if( comfoairChimney.state == 0 ) {

			if( comfoairFanMode_Message.state != comfoairFanMode.state ) {
				comfoairFanMode.sendCommand( newFanMode )
			}

		} else if( comfoairFanMode_Message.state != comfoairFanMode.state ) {
			comfoairError_Message.postUpdate( "Fire programme is on" )

			createTimer(now.plusMillis( 200 ),  [ |
				comfoairFanMode_Message.postUpdate( comfoairFanMode.state )
			])
			
			createTimer(now.plusSeconds( 10 ),  [ |
				var String error = "" + callScript( "comfoair_errorMsg" )
				comfoairError_Message.postUpdate( error )
			])
		}
	} else if( comfoairFanMode_Message.state != comfoairFanMode.state ) {
		comfoairError_Message.postUpdate( "PC control is not active" )
		
		createTimer(now.plusMillis( 200 ),  [ |
			comfoairFanMode_Message.postUpdate( comfoairFanMode.state )
		])
		
		createTimer(now.plusSeconds( 10 ),  [ |
			var String error = "" + callScript( "comfoair_errorMsg" )
			comfoairError_Message.postUpdate( error )
		])
	}
end


rule "changeSupply-ExhaustSwitch"
when 
	Item Delayed_Start changed to ON
	or
	Item comfoairFanMode changed
then
	if( comfoairFanMode.state instanceof DecimalType ) {
		comfoairFanMode_Message.postUpdate( comfoairFanMode.state )
	}
end


rule "changeSupply-ExhaustMode"
when
	Item comfoairFanMode changed
then
	if( comfoairControl.state instanceof Number && comfoairChimney.state instanceof Number ) {

		if( comfoairFanMode.state instanceof Number ) {

			if( comfoairFanLevel.state > 1 ) {
				comfoairFanLevelLastValue = comfoairFanLevel.state

				Thread::sleep( 100 )
				comfoairFanIn0LastValue = comfoairIncomingFan.state
				comfoairFanOut0LastValue = comfoairOutgoingFan.state
			}

			// Supply + Exhaust
			if( comfoairFanMode.state == 0 ) {

				if( comfoairIncomingFan.state == 0 || comfoairOutgoingFan.state == 0 ) {
					comfoairFanIn0.sendCommand( comfoairFanIn0BaseValue )

					createTimer(now.plusSeconds( 1 ),  [ |
						comfoairFanOut0.sendCommand( comfoairFanOut0BaseValue )
					])

					if( comfoairFanLevel.state < 2 ) {
						comfoairFanLevel.sendCommand( comfoairFanLevelLastValue.toString )
					}
				}
			} else {
				comfoairFanLevel.sendCommand( "1" )

				// Supply
				if( comfoairFanMode.state == 1 ) {

					if( comfoairIncomingFan.state == 0 ) {
						comfoairFanIn0.sendCommand( comfoairFanIn0LastValue )
					} else {
						comfoairFanIn0.sendCommand( comfoairIncomingFan.state )
					}

					createTimer( now.plusSeconds( 1 ),  [ | comfoairFanOut0.sendCommand( 0 ) ])

					comfoairMode.postUpdate( 0 )
					isAutoMode = false
				}

				// Exhaust
				if( comfoairFanMode.state == 2 ) {

					comfoairFanIn0.sendCommand( 0 )

					createTimer(now.plusSeconds( 1 ),  [ |
						if( comfoairOutgoingFan.state == 0 ) {
							comfoairFanOut0.sendCommand( comfoairFanOut0LastValue )
						} else {
							comfoairFanOut0.sendCommand( comfoairOutgoingFan.state )
						}
					])

					comfoairMode.postUpdate( 0 )
					isAutoMode = false
				}
			}
		}
	}
end


rule "changeAuto-Manual"
when
	Item comfoairMode changed
then
	if( comfoairControl.state == 0 ) {
		comfoairError_Message.postUpdate( "PC control is not active" )
		
		createTimer(now.plusSeconds( 10 ),  [ |
			var String error = "" + callScript( "comfoair_errorMsg" )
			comfoairError_Message.postUpdate( error )
		])
	}
end


rule "changeVentilationLevel"
when
	Item comfoairFanLevel_Message changed
then
	var Number newLevel = comfoairFanLevel_Message.state as DecimalType
	
	if( comfoairControl.state == 1 && comfoairFanLevel_Message.state != comfoairFanLevel.state ) {
		comfoairFanLevel.sendCommand( newLevel )
		
		comfoairMode.postUpdate( 0 )
		isAutoMode = false

		if( newLevel > 1 ) {
			comfoairFanMode.postUpdate( 0 )
		}
	} else {

		if( comfoairFanLevel_Message.state != comfoairFanLevel.state ) {
			comfoairError_Message.postUpdate( "PC control is not active" )
			
			createTimer(now.plusMillis( 200 ),  [ |
				comfoairFanLevel_Message.postUpdate( comfoairFanLevel.state )
			])
			
			createTimer(now.plusSeconds( 10 ),  [ |
				var String error = "" + callScript( "comfoair_errorMsg" )
				comfoairError_Message.postUpdate( error )
			])
		}
	}
end


rule "changeVentilationLevelSwitch"
when 
	Item Delayed_Start changed to ON
	or
	Item comfoairFanLevel changed
then
	if( comfoairFanLevel.state instanceof DecimalType ) {
		comfoairFanLevel_Message.postUpdate( comfoairFanLevel.state )
	}
end


rule "changeComfortTemperature"
when
	Item comfoairTargetTemperature_Message changed
then
	var Number newTemp = comfoairTargetTemperature_Message.state as DecimalType
	
	if( comfoairControl.state == 1 ) {

		if( comfoairTargetTemperature_Message.state != comfoairTargetTemperature.state ) {
			comfoairTargetTemperature.sendCommand( newTemp )
		}
	} else {

		if( comfoairTargetTemperature_Message.state != comfoairTargetTemperature.state ) {
			comfoairError_Message.postUpdate( "PC control is not active" )
			
			createTimer( now.plusMillis( 200 ),  [ |
				comfoairTargetTemperature_Message.postUpdate( comfoairTargetTemperature.state )
			])
			
			createTimer( now.plusSeconds( 10 ),  [ |
				var String error = "" + callScript( "comfoair_errorMsg" )
				comfoairError_Message.postUpdate( error )
			])
	   }
	}
end


rule "changeComfortTemperatureSetpoint"
when 
	Item Delayed_Start changed to ON
	or
	Item comfoairTargetTemperature changed
then
	if( comfoairTargetTemperature.state instanceof DecimalType ) {
		comfoairTargetTemperature_Message.postUpdate( comfoairTargetTemperature.state )
	}
end


rule "AutoProgram"
when
	Item comfoairMode changed
	or
	Time cron "0 0/1 * * * ?"
then
	if( comfoairControl.state instanceof DecimalType && comfoairMode.state instanceof DecimalType && comfoairFanLevel.state instanceof DecimalType ) {
		var Number day    = now.getDayOfWeek
		var Number hour   = now.getHourOfDay
		var Number minute = now.getMinuteOfHour
	
		if( comfoairControl.state == 1 && comfoairMode.state == 1 ) {
			var Number currentLevel = comfoairFanLevel.state as DecimalType
			var Number newLevel
			
			// Summer energy time - from 1.04 to 30.09
			if( EnergySummerTime.state == ON ) {

				// First set level 1 and 2

				// All days a week
				// if you want Monday to Friday remove day==6 and day==7
				if( day == 1 || day == 2 || day == 3 || day == 4 || day == 5 || day == 6 || day == 7 ) {

					// Level 2 : 5:00 - 7:00, 16:00 - 23:00, level 1 : in the other time
					if( ( hour >= 5 && hour < 7 )
						||
						( hour >= 16 && hour < 23 )
					) {
						newLevel = 3
					} else newLevel = 2
				
				// Saterdey and Sunday
				} else if( day == 6 || day == 7 ) {

					if( ( hour >= 12 && hour < 14 )
					) {
						newLevel = 3
					} else newLevel = 2
				}

				// Now set level 3

				// All days a week
				if( day == 1 || day == 2 || day == 3 || day == 4 || day == 5 || day == 6 || day == 7 ) {

					// Level 3 : 13:00 - 13:30
					if( ( hour == 13 && minute < 30 )
					) {
						newLevel = 4
					}
				}
			
			// Winter energy time - from 1.10 to 31.03
			} else {

				// All days a week
				// podwyzszenie 0:00-2:00, 3:00-5:00, 7:00-8:00, 10:00-11:00, 13:00-15:00, 16:30- 18:30, 20:00-21:00, 22:00-23:00
				if( day == 1 || day == 2 || day == 3 || day == 4 || day == 5 || day == 6 || day == 7 ) {

					// Level 2 : 6:00 - 8:00, 16:00 - 21:00, level 1 : in the other time
					if( ( hour >= 6 && hour < 8 )
						||
						( hour >= 16 && hour < 21 )
					) {
						newLevel = 3
					} else newLevel = 2
				
				// Saterdey and Sunday
				} else if( day == 6 || day == 7 ) {
			        
					if( ( hour >= 12 && hour < 14 )
					) {
						newLevel = 3
					} else newLevel = 2
				}

				// Now set level 3

				// All days a week
				if( day == 1 || day == 2 || day == 3 || day == 4 || day == 5 || day == 6 || day == 7 ) {

					// Level 3 : 13:00 - 13:30
					if( ( hour == 13 && minute < 30 )
					) {
						newLevel = 4
					}
				}
			}

			if( newLevel != currentLevel ) {
				comfoairFanLevel.sendCommand( newLevel )
				isAutoMode = true
				comfoairFanMode.postUpdate( 0 )
			}

			logInfo( "comfoair", "ComfoAir - AUTO Mode" )

		} else logInfo( "comfoair", "ComfoAir - MANUAL Mode" )
	}
end

Your indentation is not the same as the original.
I know for some languages, indent spacing matters and must be consistent. I do not remember if that is the case here.

how you mean your intention is not the same?

indentation
This from the website:

rule "setAutoModeForStart"
when
	Item Delayed_Start changed to ON
then
	if( comfoairControl.state == 1 ) {
		comfoairMode.postUpdate( 1 )
		isAutoMode = true
		comfoairFanMode.postUpdate( 0 )
	}
end

is not the same as yours. The lines after when and then are not indented.

rule “setAutoModeForStart”

when

Item Delayed_Start changed to ON

then

if( comfoairControl.state == 1 ) {

    comfoairMode.postUpdate( 1 )

    isAutoMode = true

    comfoairFanMode.postUpdate( 0 )

}
end

Thanks Sir! I see the issue you mean. OK, i think these lines were included when copy & paste into the message here. In my rules file, there are no lines in between and it looks exactly as on the website

Then you are using the wrong editor (by using <CR><LF> instead of <LF>.
In fact, these control characters tend to be a problem.

Another thing: As you used quoting instead of code fences, the code is a mess (discourse may change some characters to others if code is not marked as code).

Please consider to edit your original posting. just delete all code completely, set code fences and paste the code (by copying it a gain from your file). If using Windows, please use an editor which is able to use linux-like code sequences.

I am using Visual Studio Code on a Mac, leveraging the functions as documented in the OpenHAB2 documentation. So far, I did not had any issue with code. However, How do I check / figure out in Visual Code this <CR><LF> instead of <LF>?

Good point. Done.

Hi,

Make sure your comfoair_errorMsg script is located in the $OPENHAB_CONF/scripts folder:

callScript(String scriptName) : Calls a script which must be located in the $OPENHAB_CONF/scripts folder.

Right, i think this is the issue. There is no script in my $OPENHAB_CONF/scripts folder.

Since I leverage copy&paste the example described on the OH2 documentation which is a very good and very good working example, I checked again whether the script is disclosed there. https://www.openhab.org/addons/bindings/comfoair1/

Unfortunately its not. I would assume that the next beginner (including me) will fail on the same and face the same issue I have. It would be really amazing that whomever has the correct script could post it here or much better in the OH2 documentation: https://www.openhab.org/addons/bindings/comfoair1/

1 Like

jfyi: VSCode can be switched if using LF or CRLF for line feed. Take a look at the bottom line (status line?) at the right side you will find some symbols as well as some text. one of the texts may be CRLF. left-click on the text and you will get a command line in the middle of the top of the window.There you can select between LF and CRLF. This option is per file :slight_smile:

Thanks. This was helpful. I checked this now for all my key files and they are all on LF. Whilst this seems fine, i think we have identified the issue of not having the relevant script in the $OPENHAB_CONF/scripts folder.

Since I have taken the example configuration from the OH2 description for this binding (https://www.openhab.org/addons/bindings/comfoair1/) I wonder whether maybe someone has this script and whether it can be added to the bindings description.

The example in the binding works perfectly fine, except that the script is missing and i could imagine that others fail on the same issue as I did.

Jepp, correct, the script is missing. I doubt that someone other than the rule author itself has the script… :wink:

In fact google can’t find anything with
comfoair_errorMsg or comfoair_errorMsgToSend other than this thread and the sources on openhab.org and github.

Both scripts will create a string, I think this will be a more or less long text, consisting of the error states of comfoair. As long as you don’t have this script, just replace the callScript() action with "error message", just to get the rules up and running.

I searched again, same as you, could not find anything. Here’s the ugly solution of this. I say ugly solution, because i do not know what the script was supposed to do, even I assume its for sure been a great script. So if the author of this script likes to share it, he could made one or the other very happy.

Lines 122 + 123 + 222 + 230 + 289 + 301 + 398 + 430 + 471

		var String msg = "Error" // + callScript( "comfoair_errorMsg" )
		var String msgToSend = "Errro" // + callScript( "comfoair_errorMsgToSend" )

While fixing the above i also deleted the below rules in the comfoair.rules as i understand its with OH 2.5 not required anymore.

import java.lang.Math
import java.util.Date
import java.util.List
import java.util.ArrayList