Rule in openhab3 to control the intensity of the light based on the outside brightness

Hi, I wanted to write a rule in openhab3 to control the intensity of light based on the outside brightness. Is it possible in openhab3 to write a ‘RULE’ based on the outside brightness to control the light ? Say for example outside it is extremely sunny , then the light should be turned on to 10% intense, as the day progresses and comes for sunset then light should become 50% intense, as it gets darker it should keep increasing in steps as 60%, 70%, 80%, 90% intense of light. when it it totally dark the light should get turned to its full potential i.e. 100% intense. Is there any possibility to be done in openhab3, as stated above? Please help me!! Thanks in advance.

This would be possible, if you have an item, measuring the brightness. Then all you have to do is creating a rule to trigger on changes to this item.

And to create such an item, you need thing that can deliver such a measurement and is linked to the item.

Hope this helps for a first start.

Hi there,

I wrote a rules.dsl rule todo this a few years ago - I’ve been meaning to re-write it, and possibly using metadata instead of items for the various parameters (though items are useful for tuning the settings).

The theory is that between an upper and lower brightness setting (taken from a Lux sensor), I want the light to Dim between certain percentages. For example at a brightness reading of 100lux, I want the lights to come on at 25% increasing to 75% by the time the brightness reading drops to 50lux.

items:

Group gLoungeDimLight
Group gDarkThreshold
Group gDimThreshold
Group gMinBrightness
Group gMaxBrightness
Group gTargetBrightness

Number Lounge_DarkThreshold     "Lounge Dark Threshold [%d]"    (gDarkThreshold,gLoungeDimLight)
Number Lounge_DimThreshold      "Lounge Dim Threshold [%d]"     (gDimThreshold,gLoungeDimLight)
Number Lounge_MinBrightness     "Lounge Min Brightness [%d]"    (gMinBrightness,gLoungeDimLight)
Number Lounge_MaxBrightness     "Lounge Max Brightness [%d]"    (gMaxBrightness,gLoungeDimLight)
Number Lounge_TargetBrightness  "Lounge Target Brightness [%d]" (gTargetBrightness,gLoungeDimLight)

rule:

if ((vTimeOfDay.state.toString == "TWILIGHT") ||
        (vTimeOfDay.state.toString == "EVENING")){
  val StringBuilder sb = new StringBuilder
  sb.append ("Lux:" + FrontLux.state.toString)

  gLoungeDimLight.members.forEach[i | sb.append(" | " + i.state) ]
  logDebug(logName, sb.toString)

  if ( (FrontLux.state as Number) < (Lounge_DimThreshold.state as Number) && (FrontLux.state as Number) > (Lounge_DarkThreshold.state as Number) ){
    sb.setLength(0)
    var Number BrightnessRange = (Lounge_MaxBrightness.state as Number) - (Lounge_MinBrightness.state as Number)
    var Number LuxRange = (Lounge_DimThreshold.state as Number) - (Lounge_DarkThreshold.state as Number)
    sb.append("Bright Range: " + BrightnessRange + " Lux Range: " + LuxRange)

    if (LuxRange >= BrightnessRange) {
      var Number DimRatio = (BrightnessRange/LuxRange)
      var Number BrightConstant = (Lounge_DimThreshold.state as Number) - (FrontLux.state as Number)
      var Number TargetBrightness = (DimRatio * BrightConstant ).intValue()
      sb.append(" Dim Ratio: " + DimRatio )
      sb.append(" Bright Constant: " + BrightConstant )
      sb.append(" Target Brightness: " + TargetBrightness )
      logDebug(logName, sb.toString)
      LoungeLamps.sendCommand(TargetBrightness.toString);
    }
  }
}

Hope that helps as a starter.

Cheers
James

I did this :slight_smile:

Well, I did it in OH2.5 and then had to come up with a solution for OH3 because the variable types are still literally driving me insane now they don’t work anymore.

So I did it in Python. And I run that from a rule.

How you’d do that

In determining the current sunlight level, I took a bunch of factors into consideration:

First you need sunrise and sunset times. I got them from the Astro binding

Then cloud cover is a consideration. I get that from the OpenWeather binding.

Also I wanted to run it as a fade in and out, based on sunset and sunrise, and to run at maximum overnight, and minimum during my re defined sunlight hours.

The sun doesn’t just pop on and off, it fades in and out.

So I needed to know how long to fade in the dark part of sunset/rise, and how long to fade in the bright part of sunset/sunrise. For this, I use 4 Dimmers. Bright Fade Hours, Bright Fade Minutes, Dark Fade Hours, Dark Fade Minutes.

(*Note: Its probably better to incorporate civil and nautical sunrise/sunset now that I’ve found out what they are, but this is where I’m at right now with it)

(I’ve also provisioned for shadows to be different between the morning and the afternoon. But then realised I could just work out equations to take the position of the sun in the sky and go from there. Long story short: Shadows are not yet implemented)

I’ve also set a minimum and maximum illumination for the light bulb. I’ve done this for 2 modes, Casual and Normal. They both cater to different lighting needs. Casual is for when I want some light in the room, but don’t need to see everything. Say if I’m watching Telly or something. And normal is for when I want to be able to actually see stuff. So I have Casual Max, Casual Min, Normal Max, Normal Min

I have other modes as well that don’t care about the sun: Intense, Freestyle, Wake Up & Sleep. Sleep begins a fade for a preset number of minutes, from where it is now, to zero. The others should be fairly self explanatory. They just have the 1 dimmer level each.

Method

1st: collect all your item states as appropriate variables.

Next, work out when the sunset and sunrise are in regard to your fade values.

You evaluate:

  • SunriseStart = LocalSunriseStart - minutes(((DarkFadeHours * 60) + DarkFadeMinutes)
  • SunriseEnd = LocalSunriseEnd + minutes(((LightFadeHours * 60) + LightFadeMinutes)
  • SunsetStart = LocalSunsetStart - minutes(((LightFadeHours * 60) + LightFadeMinutes)
  • SunsetEnd = LocalSunsetEnd + minutes(((DarkFadeHours * 60) + DarkFadeMinutes)

For me I work it out as Dark = 1 hour while Light = 30 mins. That way, an hour before the sun pokes its head above the horizon, my light starts very slowly fading to take into account the slowly brightening sky. And half an hour after the sun has poked its head up, and the sky is as bright as its going to be, the light will be at its (theoretical) minimum.

So now we work out our sunlight strength

If you look at the time now, you know if you’re before sunrise or after sunset. In either case, your sunlight strength is 0.

If you’re in the sunrise or sunset fade periods, we need to work out what percentage of the way through we are. That’s done by working out the length of time of the fade period, then getting the percentage of the way we are through it:

  • (20 mins into 100 mins fade (20 / 100) = 0.2, or 20%.
    Your sunlight strength is 20 if this is sunrise, and (100 - 20 = 80) for sunset. At sunset our progress through gets us from 100% sunlight strength to 0% sunlight strength while sunrise takes us from 0% sunlight to 100% sunlight. Thats why we take the 20 from 100 at sunset.

We now have a sunlight strength in relation to day/night.

So then cloudiness is a factor.

For cloudiness, I devised a cloudOpacity factor and set it to 80%. Essentially if I have 100% cloud cover, it is blocking out 80% of the sun.

Apply cloudiness by working out (cloudOpacity / 100) * sunlightStrength

That way if its night and there’s 100% cloud, we’re working out 20% of nothing and coming up with nothing, while at midday with 100% sun and 50% cloud, the sunlightStrenth is 60%

Now we have an empiricle sunlightSrength based on the sun’s aspect to your position on the planet, and the amount of cloud between you and it.

So we use that on the Light Mode maximum and minimum power values.

If we’re in normal mode, work out the normal mode range: Normal Max - Normal Min

Then apply sunlight strength, but reversed, to the range. ((100 - sunlightStrength) / 100) * range

Then we add that modified range value to the minimum: Evaluated brightness = modifiedRange

Phew. So that’s how it’s done once. Apply it to the bulb.

But you don’t want to have to keep manually triggering, so you use a timer.

Everything I said above: Consider that a solid function. You hit “TellMe!” and it returns a power level.

So run it once.

Then start a timer. I set mine for 5 minutes.

Once elapsed, it checks that the light mode is still the casual or normal you began running with, if not, stop running. If so, rerun the function, restart the timer and go again.

You said something about Python?

Yes, I run the program in the regular rules, but then I had to hand off to python to do all the calculations. I couldn’t make it work in Java and although I’m sure I eventually could, why? Why? Well external python is using the REST API and so there’s a delay of a few seconds before the light changes. Also I think the Exe binding might be slowing things down. I’m not sure

So I hand off to python at this point by sending a command to an Exe binding item and having that trigger the script.
Here’s the settings for it

  • UID: exec:command:setLightForSun
  • label: Set Light For Sun
  • thingTypeUID: exec:command
  • configuration:
    • transform: REGEX((.*))
    • interval: 0
    • autorun: false
    • command: /usr/bin/python3.7 /srv/openhab-conf/python-scripts/setLightForSun.py
    • timeout: 15
  • location: Runs a python script to set the LED dimmer in response to current sunlight

So, in a nutshell, and if you ignore the numerous ifs and buts, there it is. I’ll post the source below.

lightTricks.items

String LightModeSceneNmItem "Light Mode: Scene Name ( normal | casual | intense | wakeup | sleep | freestyle )"
Dimmer LightModeHouBrihItem "Light Mode: Bright Fade Hours (0 - 3)"
Dimmer LightModeMinBrihItem "Light Mode: Bright Fade Minutes (0 - 59)"
Dimmer LightModeHouDarkItem "Light Mode: Dark Fade Hours (0 - 3)"
Dimmer LightModeMinDarkItem "Light Mode: Dark Fade Minutes (0 - 59)"
Dimmer LightModeNormMaxItem "Light Mode: Normal mode Maximum power (0 - 100)"
Dimmer LightModeNormMinItem "Light Mode: Normal mode Minimum power (0 - 100)"
Dimmer LightModeCasuMaxItem "Light Mode: Casual mode Maximum power (0 - 100)"
Dimmer LightModeCasuMinItem "Light Mode: Casual mode Minimum power (0 - 100)"

// Also:

/*
Dimmer LightModeIntenseItem "Light Mode: Intense power (0 - 100)"
Dimmer LightModeWakeUupItem "Light Mode: Wake Up power (0 - 100)"
Dimmer LightModeAShadowItem "Light Mode: Morning Shadow (0 - 5) (Not implimented)"
Dimmer LightModePShadowItem "Light Mode: Afternoon Shadow (0 - 5) (Not implimented)"

*/

// And from existing binding channels, you'll need:

/*

// From Astro Sun
DateTime LocalSunrise_StartTime
DateTime LocalSunrise_EndTime
DateTime LocalSunset_StartTime
DateTime LocalSunset_EndTime

// From OpenWeather
Number Localweatherandforecast_Cloudiness

// From Exe
Switch SetLightForSun_Running


*/

lightTricks.rules

import org.openhab.core.model.script.ScriptServiceUtil

var Timer sunTimer = null
var Timer fadeTimer = null

rule "Activate Light Mode Scene Command"
when
	Item LightModeSceneNmItem received command
then
 
	logInfo("MyLog", "Running Lighting Scene command: " 
		+ LightModeSceneNmItem.state)
		
	sunTimer?.cancel
	fadeTimer?.cancel		
	
	val dimmer_name	= "LED1Control_Dimmer"
	val intMax_name	= "LightModeIntenseItem"
	val wakMax_name	= "LightModeWakeUupItem"

	switch (LightModeSceneNmItem.state) {
		case "sleep": {
/*
	// Yeah, this is a whole different script thatfades the light over a period	
			autodimmer.sendCommand("LED1Control_Dimmer," + (
				(Sleep_Fade_Minutes.state as Number) 
				+ ((Sleep_Fade_Hours.state as Number) * 60)))
*/
		}
		case "intense": {
			ScriptServiceUtil.getItemRegistry.getItem(dimmer_name).sendCommand(
				ScriptServiceUtil.getItemRegistry.getItem(intMax_name).state as Number)
		}
		case "wakeup": {
			ScriptServiceUtil.getItemRegistry.getItem(dimmer_name).sendCommand(
				ScriptServiceUtil.getItemRegistry.getItem(wakMax_name).state as Number)
		}
		case "normal", case "casual": {
			val int sunTimerMinutes = 5
			// Call the setLightForSun.py script
			SetLightForSun_Running.sendCommand("ON")
			sunTimer = createTimer(now.plusMinutes(sunTimerMinutes)) [|
				logInfo("MyLog", "ReRunning Lighting Scene command: " 
					+ CallLightingSceneItem.state)
				SetLightForSun_Running.sendCommand("ON")
		 		if ((CallLightingSceneItem.state == "normal") || (CallLightingSceneItem.state == "casual")) {
					sunTimer.reschedule(now.plusMinutes(sunTimerMinutes))
				}
			]
		}
		case "freestyle": {
		}
	}

end

setLightForSun.py

from datetime import datetime, time, timedelta
from openhab import OpenHAB

username = 'shhhh!'
password = 'iDontHaveOne(IDoReallyButForThePurposesOf...)'
base_url = 'http://192.168.[subnet].[addr]:8080/rest'

#oh = OpenHAB(base_url,username,password)
oh = OpenHAB(base_url)

# fetch all items
otherItem = oh.get_item('LED1Control_Dimmer')
sunriseStart = datetime.strptime((oh.get_item('LocalSunrise_StartTime').state - timedelta(minutes=oh.get_item('Sun_Fade_Dark_Minutes').state) - timedelta(hours=oh.get_item('Sun_Fade_Dark_Hours').state)).strftime('%Y %d %m %H %M %S'),'%Y %d %m %H %M %S')
sunriseEnd = datetime.strptime((oh.get_item('LocalSunrise_EndTime').state + timedelta(minutes=oh.get_item('Sun_Fade_Light_Minutes').state) + timedelta(hours=oh.get_item('Sun_Fade_Light_Hours').state)).strftime('%Y %d %m %H %M %S'),'%Y %d %m %H %M %S')
sunsetStart = datetime.strptime((oh.get_item('LocalSunset_StartTime').state - timedelta(minutes=oh.get_item('Sun_Fade_Light_Minutes').state) - timedelta(hours=oh.get_item('Sun_Fade_Light_Hours').state)).strftime('%Y %d %m %H %M %S'),'%Y %d %m %H %M %S')
sunsetEnd = datetime.strptime((oh.get_item('LocalSunset_EndTime').state + timedelta(minutes=oh.get_item('Sun_Fade_Dark_Minutes').state) + timedelta(hours=oh.get_item('Sun_Fade_Dark_Hours').state)).strftime('%Y %d %m %H %M %S'),'%Y %d %m %H %M %S')
duringSR = datetime.strptime((datetime.now() + timedelta(hours=0)).strftime('%Y %d %m %H %M %S'),'%Y %d %m %H %M %S')
cloudPerc = oh.get_item('Localweatherandforecast_Cloudiness').state
sunStrength = 0

if duringSR < sunriseStart:
	sunStrength = 0
elif duringSR < sunriseEnd:
	sunriseLength = (sunriseEnd - sunriseStart) / 1000
	sunriseProgress = (duringSR - sunriseStart) / 1000
	sunrisePercent = ((sunriseProgress / sunriseLength) * 100)
	sunStrength = sunrisePercent
elif duringSR < sunsetStart:
	sunStrength = 100
elif duringSR < sunsetEnd:
	sunsetLength = (sunsetEnd - sunsetStart) / 1000
	sunsetProgress = (duringSR - sunsetStart) / 1000
	sunsetPercent = ((sunsetProgress / sunsetLength) * 100)
	sunStrength = 100 - sunsetPercent
else:
	sunStrength = 0 

cloudOpacity = 80;
cloudFactor = (1 - (((cloudPerc) * (cloudOpacity)) / 10000))
sunStrength = round( sunStrength * cloudFactor)

minLevel = 0
maxLevel = 100
cLLS = oh.get_item('CallLightingSceneItem').state

if cLLS == 'normal':
	minLevel = oh.get_item('LED_Normal_Min').state
	maxLevel = oh.get_item('LED_Normal_Max').state
elif cLLS == 'casual':
	minLevel = oh.get_item('LED_Casual_Min').state
	maxLevel = oh.get_item('LED_Casual_Max').state

diff = maxLevel - minLevel
subt = round((sunStrength / 100) * diff)
mybrightness =  maxLevel - subt
otherItem = oh.get_item('LED1Control_Dimmer')
otherItem.state = mybrightness
otherItem.command(otherItem.state)
print(str(datetime.now()) + ": Set " + str(cLLS) + " light level to " + str(mybrightness) + ".")
1 Like