[SOLVED] Multiple 'when' statements, (AND/OR)

I have a set of rules that are working well… but… I would like to tweak them to do slightly different things, depending on the time of day.

rule "Initialise variables" 
when 
        System started 
then 
    	Z_LuxSetpoint_GF_Hallway.postUpdate(40)  
     	Z_LuxSetpoint_FF_Hallway.postUpdate(40) 
end 


rule "Enable Downstairs Hallway Light on Motion" 
when 
        Item Z_Motion_GF_Hallway changed
then 
        if ((Z_LUX_GF_Hallway.state as DecimalType) < ( 
                Z_LuxSetpoint_GF_Hallway.state as DecimalType )) 
        { 
                sendCommand(Hue_Hallway_Downstairs, ON) 
        } 
        if ((Z_LUX_GF_Hallway.state as DecimalType) > 
                ( Z_LuxSetpoint_GF_Hallway.state as DecimalType )) 
        { 
                sendCommand(Hue_Hallway_Downstairs, OFF) 
        } 
end 

Currently, the rule will check to see if the Lux is below 40, if so it detects motion, and turns on the Hue bulb referenced at 100% brightness. At 4am, this can be quite startling, so I would like to do something along the lines of

When motion is detected and;

Lux is <40, and the time is >8pm <10pm, the Hue is 100%
Lux is <40, and the time is >10pm <12am, the Hue is 30%
Lux is <40, and the time is >12am <6am, the Hue is 10%

I did have a ‘timed’ rule configured, prior to taking the Lux route, but this timed rule stopped working recently, hence the switch to Lux.

rule "Turn Hallway Light on to 100%, on Motion (8pm to 10pm)"
when  
Item sMotion1_PIR changed from CLOSED to OPEN
then  
if (now.getHourOfDay() >= 20 || now.getHourOfDay() < 22) {
	sendCommand(Toggle_1, ON)
        sendCommand(HueDim_1, 10)
        }
end

Any ideas of things I could do/try?

If I understand correctly, you want to turn the hallway light on at varying brightness depending on the time, but only when you detect that Lux is less than 40% in all cases?

You could try something along the lines of this (not tested) - I’ve assumed you detect lux level using a sensor (luxitem);

rule “Turn Hallway Light when Motion detected and Lux < 40%”
when
Item sMotion1_PIR changed from CLOSED to OPEN
then
if (now.getHourOfDay() >= 20 && now.getHourOfDay() < 22) && luxitem.state < 40 {
sendCommand(Toggle_1, ON)
sendCommand(HueDim_1, 100)
}
if (now.getHourOfDay() >= 22 && now.getHourOfDay() < 24) && luxitem.state < 40 {
sendCommand(Toggle_1, ON)
sendCommand(HueDim_1, 30)
}
if (now.getHourOfDay() >= 00 && now.getHourOfDay() < 06) && luxitem.state < 40 {
sendCommand(Toggle_1, ON)
sendCommand(HueDim_1, 10)
}
end

1 Like

What I do is have a few switches that represent time periods and set them to ON or OFF when it is that time. I’ll have rules that trigger at the start and top of the time periods that set the switches to ON or OFF. In my rules that care about what time period it is I check the state of the Switches.

For example, I’ll have a Day Switch that is ON from Sunrise until 90 minutes before sunset. Then one called Twilight that is on from 90 minutes from sunset to sunset. Then on called Night that is on from sunset to to sunrise. In my rules I’ll check:

if(Day.state == ON) {
    // do day stuff
}
else if(Twilight.state == ON) {
    // do twilight stuff
}
else if(Night.state == ON) {
    // do night stuff
}

I find this approach to be pretty clean and allows you to monkey with the time periods globally without needing to go into a bunch of different rules. It also lets you take good advantage of the Astro binding for sunrise and sunset rather than needing to keep up with the changing daylight times as the seasons change (particularly important the closer to the polls you get).

1 Like

Love both these answers/suggestions, thanks guys. Will give these both a go/play with.

Just out of curiosity @rlkoshak, how do you dictate the times set for “Day”, “Twilight” and “Night”, or are these built in variables…?

I use the Astro Binding. It is a binding so you do have to install and configure it. There is a gotcha though with Switches and the Astro Binding. When the celestial events occurs (the binding supports just about everything in the sky, not just the sun) a Switch set to that binding only momentarily switches to ON. So you have to have a second Switch that you use for the flag and use the Astro switch trigger a rule.

So to do Day, Twilight, and Night I would use:

Items:

Switch   Sunrise_Event                         { astro="planet=sun, type=rise, property=start" }
DateTime Sunrise_Time  "Sunrise [%1$tr]" <sun> { astro="planet=sun, type=rise, property=start" }
Switch   Day

Switch   Twilight_Event                        { astro="planet=sun, type=set, property=start, offset=-90" }
Switch   Twilight

Switch   Sunset_Event                          { astro="planet=sun, type=set, property=start" }
DateTime Sunset_Time  "Sunset [%1$tr]" <moon>  { astro="planet=sun, type=set, property=start" }
Switch   Night

Rules:

rule "Day"
when
    Item Sunrise_Event received update
then
    Night.sendCommand(OFF)
    Day.sendCommand(ON)
end

rule "Twilight"
when
    Item Twilight_Event received update
then
    Day.sendCommand(OFF)
    Twilight.sendCommand(ON)
end

rule "Night"
when
    Item Sunset_Event received update
then
    Twilight.sendCommand(OFF)
    Night.sendCommand(ON)
end
1 Like

Will this work?

Items.items

Switch	Day		{ astro="planet=sun, type=rise, property=start, offset=-45" }
Switch	Twilight	{ astro="planet=sun, type=set, property=end, offset=30" }
Switch 	Night		{ astro="planet=sun, type=set, property=end, offset=360" }

Rules.rules

rule "Enable Upstairs Hallway Light on Motion" 
when 
        Item Z_PIR_FF_Hallway changed
then 
	if(Day.state == ON) && ((Z_LUX_FF_Hallway.state as DecimalType) < ( 
                Z_LuxSetpoint_FF_Hallway.state as DecimalType )) 
	{
		sendCommand(Hue_Hallway_Upstairs, ON)
		sendCommand(Hue_Hallway_Upstairs, 100)
	}
	else if(Twilight.state == ON) && ((Z_LUX_FF_Hallway.state as DecimalType) < ( 
                Z_LuxSetpoint_FF_Hallway.state as DecimalType ))
        {
		sendCommand(Hue_Hallway_Upstairs, ON)
		sendCommand(Hue_Hallway_Upstairs, 50)
	}
	else if(Night.state == ON) && ((Z_LUX_FF_Hallway.state as DecimalType) < ( 
                Z_LuxSetpoint_FF_Hallway.state as DecimalType ))
        {
		sendCommand(Hue_Hallway_Upstairs, ON)
		sendCommand(Hue_Hallway_Upstairs, 10)
	}        
end

Thanks for that, applied your switches & rules, and configured my “light on when motion” rule as above… will see how this goes.

You have Day set to start 45 minutes before sunrise.

You have Twilight set to start 30 minutes after the sun has set.
You have Night set to start six hours after the sun has set.

I can’t say whether or not that makes sense or not, but I suspect if you really want Night to start six hours after sunset, you probably just want it to start at a specific time rather than be based on the sun. For example, in the summer where I am Night wouldn’t start until 2:30 am but in the winter it would start at midnight. I would personally just pick a time and stick to it:

rule "Night"
when
    Time cron "0 0 23 * * ? *" // 11 PM

The rule looks good.

I’m good with the minutes before & after sunset however, but I prefer your cron based “Night” setting!

The rule doesn’t appear to be working though, getting ambiguous methods… I am trying something else currently however.

2015-11-03 08:45:39.834 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule ‘Enable Upstairs Hallway Light on Motion’: Ambiguous methods [protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateTryCatchFinallyExpression(org.eclipse.xtext.xbase.XTryCatchFinallyExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateConstructorCall(org.eclipse.xtext.xbase.XConstructorCall,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateForLoopExpression(org.eclipse.xtext.xbase.XForLoopExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(org.eclipse.xtext.xbase.XAbstractFeatureCall,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected org.eclipse.xtext.xbase.interpreter.IEvaluationResult org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateDoWhileExpression(org.eclipse.xtext.xbase.XDoWhileExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateWhileExpression(org.eclipse.xtext.xbase.XWhileExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateSwitchExpression(org.eclipse.xtext.xbase.XSwitchExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateInstanceOf(org.eclipse.xtext.xbase.XInstanceOfExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateMemberFeatureCall(org.eclipse.xtext.xbase.XMemberFeatureCall,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateIfExpression(org.eclipse.xtext.xbase.XIfExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateNumberLiteral(org.eclipse.xtext.xbase.XNumberLiteral,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAssignment(org.eclipse.xtext.xbase.XAssignment,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateReturnExpression(org.eclipse.xtext.xbase.XReturnExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateBlockExpression(org.eclipse.xtext.xbase.XBlockExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateNullLiteral(org.eclipse.xtext.xbase.XNullLiteral,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateThrowExpression(org.eclipse.xtext.xbase.XThrowExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateStringLiteral(org.eclipse.xtext.xbase.XStringLiteral,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateCastedExpression(org.eclipse.xtext.xbase.XCastedExpression,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateVariableDeclaration(org.eclipse.xtext.xbase.XVariableDeclaration,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateBooleanLiteral(org.eclipse.xtext.xbase.XBooleanLiteral,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateTypeLiteral(org.eclipse.xtext.xbase.XTypeLiteral,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator), protected java.lang.Object org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateClosure(org.eclipse.xtext.xbase.XClosure,org.eclipse.xtext.xbase.interpreter.IEvaluationContext,org.eclipse.xtext.util.CancelIndicator)] for params [null, org.eclipse.xtext.xbase.interpreter.impl.DefaultEvaluationContext@77310729, org.eclipse.xtext.util.CancelIndicator$1@d768f31]

That’s an new one for me. I’ve never seen that error.

Here is how I implemented my “Night” switch as a cron instead of using the Astro binding:

Items:

Switch Night

Rule:

rule "Night Started"
when
    Time cron "0 0 23 * * ? *"
then
    logInfo("Weather", "It's Night!")
    Morning.sendCommand(OFF)
    Day.sendCommand(OFF)
    Twilight.sendCommand(OFF)
    Night.sendCommand(ON)
end

I’ve also a rule that runs at startup to calculate the current day time period so everything works even on a restart.

rule "Get time period for right now"
when
    System started
then
    val morning = now.withTimeAtStartOfDay.plusHours(6).millis // 6 AM
    val sunrise = new DateTime((Sunrise_Time.state as DateTimeType).calendar.timeInMillis)
    val twilight = new DateTime((Twilight_Time.state as DateTimeType).calendar.timeInMillis)
    val night = now.withTimeAtStartOfDay.plusHours(23).millis // 11 PM

    if(now.isAfter(morning) && now.isBefore(sunrise)) {
        Morning.sendCommand(ON)
        Day.sendCommand(OFF)
        Twilight.sendCommand(OFF)
        Night.sendCommand(OFF)
    }
    else if(now.isAfter...
1 Like

Not sure the previous rule liked the “Lux” calculation? Something wasn’t right, anyway, I was only using the Lux calculation as a method of detecting when it got dark to the point of needing the light on. I have managed to get this working adequately using the “morning, day, twilight, night” switches, by doing the following;

items.items

Switch   Morning_Event                         { astro="planet=sun, type=rise, property=start, offset=-60" }
Switch   Morning

Switch   Sunrise_Event                         { astro="planet=sun, type=rise, property=start" }
DateTime Sunrise_Time  "Sunrise [%1$tr]" <sun> { astro="planet=sun, type=rise, property=start" }
Switch   Day

Switch   Twilight_Event                        { astro="planet=sun, type=set, property=start, offset=-60" }
Switch   Twilight

Switch   Sunset_Event                          { astro="planet=sun, type=set, property=start" }
DateTime Sunset_Time  "Sunset [%1$tr]" <moon>  { astro="planet=sun, type=set, property=start" }

Switch   Night

rules.rules

    rule "Get time period for right now"
    when
        System started
    then
        val morning = now.withTimeAtStartOfDay.plusHours(6).millis // 6 AM
        val sunrise = new DateTime((Sunrise_Time.state as DateTimeType).calendar.timeInMillis)
        val twilight = new DateTime((Twilight_Time.state as DateTimeType).calendar.timeInMillis)
        val night = now.withTimeAtStartOfDay.plusHours(23).millis // 11 PM
    
        if(now.isAfter(morning) && now.isBefore(sunrise)) {
            Morning.sendCommand(ON)
            Day.sendCommand(OFF)
            Twilight.sendCommand(OFF)
            Night.sendCommand(OFF)
        }
        else if(now.isAfter(sunrise) && now.isBefore(twilight)) {
            Morning.sendCommand(OFF)
            Day.sendCommand(ON)
            Twilight.sendCommand(OFF)
            Night.sendCommand(OFF)
        }
        else if(now.isAfter(twilight) && now.isBefore(night)) {
            Morning.sendCommand(OFF)
            Day.sendCommand(OFF)
            Twilight.sendCommand(ON)
            Night.sendCommand(OFF)
        }
        else if(now.isAfter(night) && now.isBefore(morning)) {
            Morning.sendCommand(OFF)
            Day.sendCommand(OFF)
            Twilight.sendCommand(OFF)
            Night.sendCommand(ON)
        }
    end
    
    rule "Morning Started"
    when
        Time cron "0 0 6 * * ? *"
    then
        logInfo("Weather", "It's Morning!")
        Morning.sendCommand(ON)
        Day.sendCommand(OFF)
        Twilight.sendCommand(OFF)
        Night.sendCommand(OFF)
    end
    
    rule "Day"
    when
        Item Sunrise_Event received update
    then
        pushNotification("Weather", "It's Daytime!")
        Night.sendCommand(OFF)
        Twilight.sendCommand(OFF)
        Morning.sendCommand(OFF)
        Day.sendCommand(ON)
    end
    
    rule "Twilight"
    when
        Item Twilight_Event received update
    then
        pushNotification("Weather", "It's Twilight!")
        Night.sendCommand(OFF)
        Morning.sendCommand(OFF)
        Day.sendCommand(OFF)
        Twilight.sendCommand(ON)
    
    end
    
    rule "Night Started"
    when
        Time cron "0 0 23 * * ? *"
    then
        pushNotification("Weather", "It's Night!")
        Morning.sendCommand(OFF)
        Day.sendCommand(OFF)
        Twilight.sendCommand(OFF)
        Night.sendCommand(ON)
    end
    
    rule "Enable Upstairs Hallway Light on Motion" 
    when 
            Item Z_PIR_FF_Hallway changed
    then
    	if(Twilight.state == ON)
            {
    		sendCommand(Hue_Hallway_Upstairs, ON)
    		sendCommand(Hue_Hallway_UpstairsDim, 100)
    	}
    	else if(Night.state == ON)
            {
    		sendCommand(Hue_Hallway_Upstairs, ON)
    		sendCommand(Hue_Hallway_UpstairsDim, 10)
    	}        
    end 

rule "Turn Upstairs Hallway Light off after 5 minutes"
when
	Item Hue_Hallway_Upstairs received command
then
	if(receivedCommand==ON) {
		if(tUpstairsLights==null) {
			// first ON command, so create a timer to turn the light off again
			tUpstairsLights = createTimer(now.plusSeconds(300)) [|
				sendCommand(Hue_Hallway_Upstairs, OFF)
			]
		} else {
			// subsequent ON command, so reschedule the existing timer
			tUpstairsLights.reschedule(now.plusSeconds(300))
		}
	} else if(receivedCommand==OFF) {
		// remove any previously scheduled timer
		if(tUpstairsLights!=null) {
			tUpstairsLights.cancel
			tUpstairsLights = null
		}	
	}
end

Really appreciate your help @rlkoshak, a few pointers in the right direction and my setup is working much better now! Thank you!

1 Like

I just want to point out that you set up a Morning_Event Item that triggers at an hour before sunrise but in your rules you activate the Morning switch at 6 AM making Morning_Event never used.

It isn’t a problem but I personally try to not have “dead code” when I can help it.

Otherwise, I’m glad you got it to work!

1 Like

Good eyes! Thanks!

Sorry to revisit this, @rlkoshak, my rule for “Get time period for right now” doesn’t seem to work.

Am I missing/needing to import something to get the current period?

2015-11-07 21:18:48.907 [ERROR] [m.r.internal.engine.RuleEngine] - Error during the execution of startup rule 'Get time period for right now': Cannot cast org.openhab.core.types.UnDefType to org.openhab.core.library.types.DateTimeType
2015-11-07 21:18:49.025 [WARN ] [.o.c.p.e.PersistenceExtensions] - There is no queryable persistence service registered with the name 'rrd4j'

I currently import

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.java.math.*

Does the problem occur at openHAB startup or all the time?

UnDefType indicates that an Item you are referring to is not defined. Make sure that there isn’t a typo in your rules (e.g. it is “Morning” not “Mroning”). Using Designer is an excellent way to find these sorts of errors.

Make sure that your Items and Persistence are loaded before your Rules during startup. You can do this by changing the polling periods on openhab.cfg so that the rules value is larger than the rest.

Occurs at openHab launch. Once the “time” has passed to trigger the next state (morning, day, twilight, night) it gets in sync and all is well.

Will have a peek at your suggestions.

All my imported bundles are at the top of the rules.

Just to re-re-revisit this, I’m still getting the Cannot cast org.openhab.core.types.UnDefType to org.openhab.core.library.types.DateTimeType error.

This occurs frequently and is NOT just at startup. It seems to be when it’s trying to recalc time related ‘things’. Designer shows no conflicts…

My rules are below;

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.java.math.* 

var Timer tDownstairsLights
var Timer tUpstairsLights
var Timer tLivingRoomLight
var Timer tOnkyoPower
var Timer tSamsungPower

rule "Get time period for right now"
when
    System started
then
    val morning = now.withTimeAtStartOfDay.plusHours(5).millis // 5 AM
    val sunrise = new DateTime((Sunrise_Time.state as DateTimeType).calendar.timeInMillis)
    val twilight = new DateTime((Twilight_Event.state as DateTimeType).calendar.timeInMillis)
    val night = now.withTimeAtStartOfDay.plusHours(23).millis // 11 PM

    if(now.isAfter(morning) && now.isBefore(sunrise)) {
        Morning.sendCommand(ON)
        Day.sendCommand(OFF)
        Twilight.sendCommand(OFF)
        Night.sendCommand(OFF)
    }
    else if(now.isAfter(sunrise) && now.isBefore(twilight)) {
        Morning.sendCommand(OFF)
        Day.sendCommand(ON)
        Twilight.sendCommand(OFF)
        Night.sendCommand(OFF)
    }
    else if(now.isAfter(twilight) && now.isBefore(night)) {
        Morning.sendCommand(OFF)
        Day.sendCommand(OFF)
        Twilight.sendCommand(ON)
        Night.sendCommand(OFF)
    }
    else if(now.isAfter(night) && now.isBefore(morning)) {
        Morning.sendCommand(OFF)
        Day.sendCommand(OFF)
        Twilight.sendCommand(OFF)
        Night.sendCommand(ON)
    }
end

rule "Morning Started"
when
    Time cron "0 0 5 * * ? *"
then
    Morning.sendCommand(ON)
    Day.sendCommand(OFF)
    Twilight.sendCommand(OFF)
    Night.sendCommand(OFF)
end

rule "Day"
when
    Item Sunrise_Event received update
then
    Night.sendCommand(OFF)
    Twilight.sendCommand(OFF)
    Morning.sendCommand(OFF)
    Day.sendCommand(ON)
end

rule "Twilight"
when
    Item Twilight_Event received update
then
    Night.sendCommand(OFF)
    Morning.sendCommand(OFF)
    Day.sendCommand(OFF)
    Twilight.sendCommand(ON)

end

rule "Night Started"
when
    Time cron "0 0 23 * * ? *"
then
    Morning.sendCommand(OFF)
    Day.sendCommand(OFF)
    Twilight.sendCommand(OFF)
    Night.sendCommand(ON)
end

rule "Enable Downstairs Hallway Light on Motion" 
when 
        Item Z_Motion_GF_Hallway changed
then
	if(Morning.state == ON)
        {
		sendCommand(Hue_Hallway_Downstairs, ON)
                sendCommand(Hue_Hallway_DownstairsDim, 5)
	}
	else if(Twilight.state == ON)
        {
		sendCommand(Hue_Hallway_Downstairs, ON)
                sendCommand(Hue_Hallway_DownstairsDim, 100)
	}
	else if(Night.state == ON)
        {
		sendCommand(Hue_Hallway_Downstairs, ON)
		sendCommand(Hue_Hallway_DownstairsDim, 10)
	}        
end 



rule "Enable Upstairs Hallway Light on Motion" 
when 
        Item Z_PIR_FF_Hallway changed
then
	if(Morning.state == ON)
        {
		sendCommand(Hue_Hallway_Upstairs, ON)
                sendCommand(Hue_Hallway_UpstairsDim, 5)
	}
	else if(Twilight.state == ON)
        {
		sendCommand(Hue_Hallway_Upstairs, ON)
		sendCommand(Hue_Hallway_UpstairsDim, 100)
	}
	else if(Night.state == ON)
        {
		sendCommand(Hue_Hallway_Upstairs, ON)
		sendCommand(Hue_Hallway_UpstairsDim, 10)
	}        
end 




rule "Turn Bathroom Speakers on at 6pm"
when 
       Time cron "0 0 18 * * ?"
then
	sendCommand(Z_Socket_FF_BathroomSpeakers, ON)
end


rule "Turn Kitchen Speakers on at 6pm"
when 
       Time cron "0 0 13 * * ?"
then
	sendCommand(Z_Socket_GF_KitchenSpeakers, ON)
end


rule "Turn Bedroom TV Off at Half past Midnight on a School night"
when 
       Time cron "0 30 0 ? * MON,TUE,WED,THU,SUN *"   // School nights at 00:30 hours
then
	sendCommand(LgTvPower, ON)
end



rule "Turn Bedroom TV Off at Half past Midnight at the weekend"
when 
	Time cron "0 0 1 ? * FRI,SAT"   // Weekend 01:30 hours
then
	sendCommand(LgTvPower, ON)
end



rule "Turn Bedroom TV down at 11pm"
when 
        Time cron "0 0 23 * * ?"   // Every day at 23:00, turn volume down further
then
	sendCommand(LgTvVolume, DECREASE)
	sendCommand(LgTvVolume, DECREASE)
end


rule "Turn Bedroom TV down at Midnight"
when 
	    Time is midnight   // Every day Midnight, turn volume down even further
then
	sendCommand(LgTvVolume, DECREASE)
	sendCommand(LgTvVolume, DECREASE)
end


rule "Update max and min temperatures"
when
	Item Weather_Temperature changed or
	Time cron "0 0 0 * * ?" or
	System started
then	
	postUpdate(Weather_Temp_Max, Weather_Temperature.maximumSince(now.toDateMidnight).state)
	postUpdate(Weather_Temp_Min, Weather_Temperature.minimumSince(now.toDateMidnight).state)
	logInfo("Weather", "Temperature evolved of " + Weather_Temperature.deltaSince(now.minusMinutes(2)) + " degrees.")
end


rule "Turn living room lights on when it gets dark" 
when 
        Item Twilight_Event received update ON
then
	sendCommand(Hue_LivingRoom_TallLamp, ON)
	sendCommand(Hue_LivingRoom_TallLampDim, 50)
	sendCommand(Hue_LivingRoom_Lamp, ON)
	sendCommand(Hue_LivingRoom_LampDim, 50)
	sendCommand(Hue_LivingRoom_MainLight, ON)
	sendCommand(Hue_LivingRoom_MainLightDim, 50)
end 

rule "Turn living room lights up when it gets darker" 
when 
        Item Sunset_Event received update ON
then
	sendCommand(Hue_LivingRoom_TallLamp, ON)
	sendCommand(Hue_LivingRoom_TallLampDim, 100)
	sendCommand(Hue_LivingRoom_Lamp, ON)
	sendCommand(Hue_LivingRoom_LampDim, 100)
	sendCommand(Hue_LivingRoom_MainLight, ON)
	sendCommand(Hue_LivingRoom_MainLightDim, 100)
end 


rule "Enable Living Room Main Light on Motion (cat feed)" 
when 
        Item Z_Motion_GF_Hallway changed
then
	if(Morning.state == ON)
        {
					sendCommand(Hue_LivingRoom_MainLightMorning, ON)
			    sendCommand(Hue_LivingRoom_MainLightMorningDim, 30)
				}     
end 


rule "Turn Living Room Main Light off after time in morning"
when
	Item Hue_LivingRoom_MainLightMorning received command 
then
	if(Twilight.state == ON)
		{
		if(receivedCommand==ON) {
			if(tLivingRoomLight==null) {
				// first ON command, so create a timer to turn the light off again
				tLivingRoomLight = createTimer(now.plusSeconds(300)) [|
					sendCommand(Hue_LivingRoom_MainLightMorning, OFF)
					sendMail("email@address.com", "Living Room Light is off (Morning)", "Cat feed light")												
				]
			} else {
				// subsequent ON command, so reschedule the existing timer
				tLivingRoomLight.reschedule(now.plusSeconds(300))
			}
		} else if(receivedCommand==OFF) {
			// remove any previously scheduled timer
			if(tLivingRoomLight!=null) {
				tLivingRoomLight.cancel
				tLivingRoomLight = null
			}	
		}
	}	
end



rule "Turn Downstairs Hallway Light off after 5 minutes"
when
	Item Hue_Hallway_Downstairs received command 
then
	if(receivedCommand==ON) {
		if(tDownstairsLights==null) {
			// first ON command, so create a timer to turn the light off again
			tDownstairsLights = createTimer(now.plusSeconds(300)) [|
				sendCommand(Hue_Hallway_Downstairs, OFF)
			]
		} else {
			// subsequent ON command, so reschedule the existing timer
			tDownstairsLights.reschedule(now.plusSeconds(300))
		}
	} else if(receivedCommand==OFF) {
		// remove any previously scheduled timer
		if(tDownstairsLights!=null) {
			tDownstairsLights.cancel
			tDownstairsLights = null
		}	
	}
end



rule "Turn Upstairs Hallway Light off after 5 minutes"
when
	Item Hue_Hallway_Upstairs received command
then
	if(receivedCommand==ON) {
		if(tUpstairsLights==null) {
			// first ON command, so create a timer to turn the light off again
			tUpstairsLights = createTimer(now.plusSeconds(300)) [|
				sendCommand(Hue_Hallway_Upstairs, OFF)
			]
		} else {
			// subsequent ON command, so reschedule the existing timer
			tUpstairsLights.reschedule(now.plusSeconds(300))
		}
	} else if(receivedCommand==OFF) {
		// remove any previously scheduled timer
		if(tUpstairsLights!=null) {
			tUpstairsLights.cancel
			tUpstairsLights = null
		}	
	}
end


rule "Turn things off at 10:30pm daily"
when 
       Time cron "0 30 22 * * ?"
then
	sendCommand(Z_Socket_GF_KitchenSpeakers, OFF)
	sendCommand(Z_Socket_FF_BathroomSpeakers, OFF)
end


rule "Turn everything off at Midnight"
when 
        Time is midnight
then
	sendCommand(Z_Socket_GF_KitchenSpeakers, OFF)
	sendCommand(Z_Socket_FF_BathroomSpeakers, OFF)
	sendCommand(Hue_LivingRoom_Lamp, OFF)
	sendCommand(Hue_LivingRoom_TallLamp, OFF)	
	sendCommand(Hue_LivingRoom_MainLight, OFF)	
	sendCommand(Z_Socket_FrontBedroom_DAB, OFF)
	sendMail("email@address.com", "It's midnight!", "Everything is now off.")		
end

rule "Turn Downstairs Lights off 5 minutes after TV goes off"
when
	Item SamsungTV received command 
then
	if(receivedCommand==ON) {
		if(tSamsungPower==null) {
			tSamsungPower = createTimer(now.plusSeconds(300)) [|
				sendCommand(Hue_LivingRoom_MainLight, OFF)
				sendCommand(Hue_LivingRoom_Lamp, OFF)
				sendCommand(Hue_LivingRoom_TallLamp, OFF)
				sendMail("email@address.com", "TV off, Lights off! (1)", "TV off, Lights off! (1)")						
			]
		} else {
			tSamsungPower.reschedule(now.plusSeconds(300))
		}
	} else if(receivedCommand==OFF) {
		if(tSamsungPower==null) {
			tSamsungPower = createTimer(now.plusSeconds(300)) [|
				sendCommand(Hue_LivingRoom_MainLight, OFF)
				sendCommand(Hue_LivingRoom_Lamp, OFF)
				sendCommand(Hue_LivingRoom_TallLamp, OFF)
				sendMail("email@address.com", "TV off, Lights off! (2)", "TV off, Lights off! (2)")										
			]
		} else {
			tSamsungPower.reschedule(now.plusSeconds(300))
		}	
	}
end


rule "Turn Front Bedroom Toys on"
when
	Item Z_Light_FF_FrontBedroomSwitch received command ON
then
	sendCommand(Z_Socket_FrontBedroom_DAB, ON)
	sendCommand(Z_Socket_FrontBedroom_Morning, ON)
end

rule "Turn Front Bedroom Toys off"
when
	Item Z_Light_FF_FrontBedroomSwitch received command OFF
then
	sendCommand(Z_Socket_FrontBedroom_DAB, OFF)
	sendCommand(Z_Socket_FrontBedroom_Morning, OFF)
	sendMail("email@address.com", "Front Bedroom is OFF!", "Radio is off.")							
end

Which rule or rules throw the error?

Sorry, it’s the rule “Get time period for right now” rule

With the trigger “System started” this rule will only run when openHAB is starting and when the .rule file’s timestamp is updated (e.g. file edited or touched). This why your statement the it occurs frequently and NOT just at startup is confusing. Are you editing the files at these times or are you or something else touching the files so the timestamp gets updated?

I do notice that you are getting the DateTime of Sunrise_Time but Twilight_Event. Shouldn’t that be Twilight_Time? Twilight_Event is a Switch, not a DateTime type. I don’t think Twilight_Time was part of my original example above so you may need to add it.

I quickly looked back and I didn’t see that I’ve made this suggestion yet. If I missed it, sorry for the duplicate.

Have you set up persistence and are you using restoreOnStartup for your Sunrise and Twilight times? That should populate them with the last known values at startup and avoid the UnDef error.

Maybe add a Thread::sleep(msec) where msec is long enough for the Astro binding to load and recalculate the Sunrise and Twilight times.