[SOLVED] Need help with lambda expression and type mismatch

Dear all,

I’m trying to get the Dewpoint controlled dehumidification/ventilation rules (from post #4) developed by @kisseler work - but stuck with 4 error messages in VSCode:

Line 81:
Cannot refer to the non-final variable ventilation_interval inside a lambda expression

Line 81:
Type mismatch: cannot convert from Number to int

Line 84:
Cannot refer to the non-final variable ventilation_interval inside a lambda expression

Line 86:
Cannot refer to the non-final variable ventilation_interval inside a lambda expression

My current rule is:

var Timer ventilation_timer = null
var Timer interval_timer = null


// this is to switch the dehumidificator when the inner humidity runs above a certain value
rule "Dehumidification"			
when
	Item KE_s_XiaomiTemperatursensorC_Humidity changed or
	Item KE_x_pow1_switch changed
then
	var Number basement_humidity = KE_s_XiaomiTemperatursensorC_Humidity.state as QuantityType<Number> * 100
	var Number dehumitification_bottom = Dehumitification_Bottom.state as DecimalType
	var Number dehumitification_top = Dehumitification_Top.state as DecimalType
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	
	if(basement_humidity >= dehumitification_top && dewpoint_gap <= dewpoint_min_gap) {
		sendCommand(KE_x_pow1_switch , ON)
		logInfo("KELLERLUEFTUNG","Entfeuchter AN (basement_humidity [" + basement_humidity + "]  >= dehumitification_bottom [" + dehumitification_top + "])" )
	}
	if(basement_humidity <= dehumitification_bottom) {
		sendCommand(KE_x_pow1_switch , OFF)
		logInfo("KELLERLUEFTUNG","Entfeuchter AUS (basement_humidity [" + basement_humidity + "]  <= dehumitification_bottom [" + dehumitification_bottom + "])" )
	}
end


// this is the core ventilation rule. I checks the surrounding parameters an starts the ventilators for 5 minutes and waits a certain time period 
// to let the "fresh" air collecting the humidity from the inside. 
rule "Ventilation"			
when
	Item Taupunkt_Differenz changed or
	Item Keller_Ventilation_Interval changed or
	Item Keller_Temperatur_Min changed or
	Item Keller_Ventilation_Steady changed or
	Item KE_x_pow2_switch changed
then
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	var Number outdoor_temperature = OUT_s_xtemp2_Temperature.state as QuantityType<Number>
	// var Number ventilation_interval = (Keller_Ventilation_Interval.state as DecimalType).intValue
	var Number ventilation_interval = Keller_Ventilation_Interval.state as DecimalType

	logInfo("KELLERLUEFTUNG","Außentaupunkt muss niedriger sein als Kellertaupunkt. Taupunktdifferenz (Keller minus Außen) ist " + dewpoint_gap + ". Mindestdifferenz ist " + dewpoint_min_gap + "." )

	// Wenn Lüftung ständig an sein soll, dann Steckdose an!
	if (Keller_Ventilation_Steady.state == ON) {
		sendCommand(KE_x_pow2_switch, ON)
		logInfo("KELLERLUEFTUNG","Lüftung an, da Dauerlüftung eingeschaltet.")
		ventilation_timer = null
		interval_timer = null
	}
	// Auskühlung des Kellers verhindern
	else if (outdoor_temperature <= 2) {
		sendCommand(KE_x_pow2_switch, OFF)
		logInfo("KELLERLUEFTUNG","Lüftung aus, da Außentemperatur niedriger als 2°C.")
		ventilation_timer = null
		interval_timer = null
	}
	// Wenn Taupunkt außen höher ist, als Taupunkt Keller, in jedem Fall ausschalten!
	else if (dewpoint_gap <= 0) {
		sendCommand(KE_x_pow2_switch, OFF)
		logInfo("KELLERLUEFTUNG","Lüftung aus, da Außentaupunkt höher als Kellertaupunkt.")
		ventilation_timer = null
		interval_timer = null
	}
	// Lüftungsprogramm abfragen
	else if (dewpoint_gap >= dewpoint_min_gap) {


		if (ventilation_interval > 0 && ventilation_timer === null && interval_timer === null) {

			if (KE_x_pow2_switch.state == OFF) sendCommand(KE_x_pow2_switch, ON)

			// nach 5 Minuten Lüften ausschalten
			ventilation_timer = createTimer(now.plusMinutes(5), [|
				sendCommand(KE_x_pow2_switch, OFF)
				ventilation_timer = null
				logInfo("KELLERLUEFTUNG","Lüftung aus, da 5 Minuten gelüftet.")
				// Pause gem. Keller_Ventilation_Interval
				interval_timer = createTimer(now.plusMinutes(ventilation_interval), [|
					sendCommand(KE_x_pow2_switch, ON)
					interval_timer = null
					logInfo("KELLERLUEFTUNG","Lüftung an, weil " + ventilation_interval + " Minuten Pausen-Intervall vorbei.")
				])
				logInfo("KELLERLUEFTUNG", ventilation_interval + "-Minuten-Timer für Pausen-Intervall gesetzt.")
			])
			logInfo("KELLERLUEFTUNG","5 Minuten-Timer für Lüftung gesetzt.")
		} else if (ventilation_interval == 0) {
			if (KE_x_pow2_switch.state == OFF) sendCommand(KE_x_pow2_switch, ON)
		}

	} else if (ventilation_timer === null && interval_timer === null) {

		if (KE_x_pow2_switch.state == ON) {
			if (KE_x_pow2_switch.state == ON) sendCommand(KE_x_pow2_switch, OFF)
			logInfo("KELLERLUEFTUNG","Lüftung pauschal aus.")
		}

	}
	
	if (KE_x_pow2_switch.state == ON) {
		logInfo("KELLERLUEFTUNG","Lüftung läuft.")
		    sendPushoverMessage(pushoverBuilder("Lüftung läuft").withSound("mechanical"))
    		Thread::sleep(100)
	}
	else logInfo("KELLERLUEFTUNG","Lüftung ist aus.")

end

The rule "Dehumidification" works fine but the rule "Ventilation" doesn’t …

It’s all related to the variable ventilation_interval.

The ventilation rule starts the 5-minute timer when dewpoint difference is triggered but after finishing the timer the rule starts again with the log error below:
(but actually it should wait X minutes - set with ventilation_interval - before starting again)

[ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.Timer 372 +01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {

  sendCommand(<XFeatureCallImplCustom>,<XFeatureCallImplCustom>)

  <null>.ventilation_timer = <XNullLiteralImplCustom>

  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)

  <null>.interval_timer = <XFeatureCallImplCustom>

  logInfo(<XStringLiteralImpl>,<XBinaryOperationImplCustom>)

} ] threw an unhandled Exception: 

java.lang.IllegalStateException: Could not invoke method: org.joda.time.DateTime.plusMinutes(int) on instance:

I would really appreciate when someone could help me.
Thanks in advance.

Variable ventilation_interval is declared in the rule.

By the time the timer runs later, the rule has exited and the variable was discarded long ago.

You can “capture” context from the rule for use in a Timer later, but you need to declare as val

In question of Type mismatch: plusMinutes() wants tpye int while ventilation_interval is of type Number.
Simple solution for all errors: just define ventilation_interval outside the rule as Integer:

var Integer ventilation_interval = 0 // initialize global var

rule ...
...
then
    ventilation_interval = 100 // set default value
    if(Keller_Ventilation_Interval.state instanceof Number)
        ventilation_interval = (Keller_Ventilation_Interval.state as Number).intValue
    ...

This way the var is guaranteed to hold a correct value.

2 Likes

I’m not sure that’s the case. Take this Rule I created awhile back to test this very thing out:

rule "Test string"
when
 Item Test received command
then
 logInfo("Test", "Test changed to \""+Test.state.toString+"\"")

 var count = 0
 timer = createTimer(now, [ |
     logInfo("test", "Looping timer with " + count)
     if(count < 10) {
       count = count + 1
       timer.reschedule(now.plusMillis(500))
     }
     else {
       timer = null
     }
 ])
end

It works, so I don’t think that the variable does get destroyed until the timer goes away, or more likely, the Timer get’s a copy of the variable to work with. It’s part of the context and the Timer gets a copy of the context when it’s created.

I do not remember if VSCode or OH complained about this Rule though. But it did work as of about nine months ago, give or take.

Thanks a lot - it works now!

Below you’ll find the final version of the rules … kudos to @Udo_Hartmann and @kisseler

var Timer ventilation_timer = null
var Timer interval_timer = null

var Integer ventilation_interval = 0


// this is to switch the dehumidificator when the inner humidity runs above a certain value
rule "Dehumidification"			
when
	Item KE_s_XiaomiTemperatursensorC_Humidity changed or
	Item KE_x_pow1_switch changed
then
	var Number basement_humidity = KE_s_XiaomiTemperatursensorC_Humidity.state as QuantityType<Number> * 100
	var Number dehumitification_bottom = Dehumitification_Bottom.state as DecimalType
	var Number dehumitification_top = Dehumitification_Top.state as DecimalType
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	
	if(basement_humidity >= dehumitification_top && dewpoint_gap <= dewpoint_min_gap) {
		sendCommand(KE_x_pow1_switch , ON)
		logInfo("KELLERLUEFTUNG","Entfeuchter AN (basement_humidity [" + basement_humidity + "]  >= dehumitification_bottom [" + dehumitification_top + "])" )
	}
	if(basement_humidity <= dehumitification_bottom) {
		sendCommand(KE_x_pow1_switch , OFF)
		logInfo("KELLERLUEFTUNG","Entfeuchter AUS (basement_humidity [" + basement_humidity + "]  <= dehumitification_bottom [" + dehumitification_bottom + "])" )
	}
end


// this is the core ventilation rule. It checks the surrounding parameters and starts the ventilators for 5 minutes and waits a certain time period 
// to let the "fresh" air collecting the humidity from the inside. 
rule "Ventilation"			
when
	Item Taupunkt_Differenz changed or
	Item Keller_Ventilation_Interval changed or
	Item Keller_Temperatur_Min changed or
	Item Keller_Ventilation_Steady changed or
	Item KE_x_pow2_switch changed
then
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	var Number outdoor_temperature = OUT_s_xtemp2_Temperature.state as QuantityType<Number>
	// var Number ventilation_interval = (Keller_Ventilation_Interval.state as DecimalType).intValue
	// var Number ventilation_interval = Keller_Ventilation_Interval.state as DecimalType

		// https://community.openhab.org/t/need-help-with-lambda-expression-and-type-mismatch/92029/3
	ventilation_interval = 20 // set default value
    if(Keller_Ventilation_Interval.state instanceof Number)
        ventilation_interval = (Keller_Ventilation_Interval.state as Number).intValue

	logInfo("KELLERLUEFTUNG","Taupunktdifferenz (Keller minus Außen) ist " + dewpoint_gap + ". Mindestdifferenz ist (custom)" + dewpoint_min_gap + ". Außentaupunkt muss niedriger sein als Kellertaupunkt." )

	// Wenn Lüftung ständig an sein soll, dann Steckdose an!
	if (Keller_Ventilation_Steady.state == ON) {
		sendCommand(KE_x_pow2_switch, ON)
		logInfo("KELLERLUEFTUNG","Lüftung an, da Dauerlüftung eingeschaltet.")
		ventilation_timer = null
		interval_timer = null
	}
	// Auskühlung des Kellers verhindern
	else if (outdoor_temperature <= 2) {
		sendCommand(KE_x_pow2_switch, OFF)
		logInfo("KELLERLUEFTUNG","Lüftung aus, da Außentemperatur niedriger als 2°C.")
		ventilation_timer = null
		interval_timer = null
	}
	// Wenn Taupunkt außen höher ist, als Taupunkt Keller, in jedem Fall ausschalten!
	else if (dewpoint_gap <= 0) {
		sendCommand(KE_x_pow2_switch, OFF)
		logInfo("KELLERLUEFTUNG","Lüftung aus, da Außentaupunkt höher als Kellertaupunkt.")
		ventilation_timer = null
		interval_timer = null
	}
	// Lüftungsprogramm abfragen
	else if (dewpoint_gap >= dewpoint_min_gap) {

		if (ventilation_interval > 0 && ventilation_timer === null && interval_timer === null) {

			if (KE_x_pow2_switch.state == OFF) sendCommand(KE_x_pow2_switch, ON)

			// nach 5 Minuten Lüften ausschalten
			ventilation_timer = createTimer(now.plusMinutes(5), [|
				sendCommand(KE_x_pow2_switch, OFF)
				ventilation_timer = null
				logInfo("KELLERLUEFTUNG","Lüftung aus, da 5 Minuten gelüftet.")
				// Pause gem. Keller_Ventilation_Interval
				interval_timer = createTimer(now.plusMinutes(ventilation_interval), [|
					sendCommand(KE_x_pow2_switch, ON)
					interval_timer = null
					logInfo("KELLERLUEFTUNG","Lüftung an, weil custom " + ventilation_interval + " Minuten Pausen-Intervall vorbei.")
				])
				logInfo("KELLERLUEFTUNG", ventilation_interval + "-Minuten-Timer (custom) für Pausen-Intervall gesetzt.")
			])
			logInfo("KELLERLUEFTUNG","Lüftung gestartet / 5 Minuten-Timer für Lüftung gesetzt.")
		} else if (ventilation_interval == 0) {
			if (KE_x_pow2_switch.state == OFF) sendCommand(KE_x_pow2_switch, ON)
		}

	} else if (ventilation_timer === null && interval_timer === null) {

		if (KE_x_pow2_switch.state == ON) {
			if (KE_x_pow2_switch.state == ON) sendCommand(KE_x_pow2_switch, OFF)
			logInfo("KELLERLUEFTUNG","Lüftung pauschal aus.")
		}

	}
	
	if (KE_x_pow2_switch.state == ON) {
		logInfo("KELLERLUEFTUNG","Lüftung läuft.")
		    // sendPushoverMessage(pushoverBuilder("Lüftung läuft").withSound("mechanical"))
    		// Thread::sleep(100)
	}
	else logInfo("KELLERLUEFTUNG","Lüftung ist aus.")

end