openhab / openhab1-addons

Add-ons for openHAB 1.x
Eclipse Public License 2.0
3.43k stars 1.69k forks source link

Enhacements to 'time' expression in rules #633

Closed openhab-bot closed 10 years ago

openhab-bot commented 10 years ago
  1. The existing time expression should be enhanced to support more "well-known" times and simple math. Right now there are only 2 well known times: Time is midnight and Time is noon. There should be sunrise and sunset times too. They can be calculated based on coordinates of location. Coordinates should be specified in openhab.cfg. There are existing java codes for that on the net. This valuation should support simple math like sunrise+30m or sunset-1h.
  2. Example Use Case is switching on and off lights based on sunrise and sunset times, it is also usefull for irrigation, shades, a lot of other hings. User installation can lack light sensor and this can be fixed by sunset and sunrise times. Simple math is needed so that you can switch lights before it becomes dark and switch them off after it is sunny enough.

Original issue: http://code.google.com/p/openhab/issues/detail?id=71

openhab-bot commented 10 years ago

this lib looks promising http://www.jstott.me.uk/jsuntimes/

openhab-bot commented 10 years ago

I use these fields in Misterhouse, would welcome them in Openhab: $Time_Sunset_Twilight, $Time_Sunrise_Twilight, $Dark: true between $Time_Sunset_Twilight and $Time_Sunrise_Twilight, false otherwise. These are more relevant than fixed offsets before sunrise and after sunset.

openhab-bot commented 10 years ago

This can be achieved by using the following (thanks RaK for providing the base script)

################################################# import org.openhab.core.library.types.* import java.lang.Math

// constants var Number K = 0.017453

// coordinates (see Google-Maps) var Number latitude = xx.xxxxxx var Number longitude = xx.xxxxxx

rule "Calculate Sunheight and Azimut" when Time cron "0 0/5 * * * ?" then var Number tageszahl var Number deklination var Number zeitgleichung var Number stundenwinkel var Number x var Number y var Number sonnenhoehe var Number azimut

var month  = now.getMonthOfYear
var day    = now.getDayOfMonth
var hour   = now.getHourOfDay
var minute = now.getMinuteOfHour

// Source: http://www.jgiesen.de/SME/tk/index.htm
tageszahl = (month - 1) * 30 + day + hour / 24 
deklination = -23.45 * Math::cos((K * 360 * (tageszahl + 10) / 365).doubleValue)
zeitgleichung = 60.0 * (-0.171 * Math::sin((0.0337*tageszahl+0.465).doubleValue) - 0.1299 * Math::sin((0.01787*tageszahl-0.168).doubleValue))
stundenwinkel = 15.0 * (hour.doubleValue + (minute.doubleValue/60.0) - (15.0-longitude)/15.0 - 12.0 + zeitgleichung/60.0)
x = Math::sin((K * latitude).doubleValue()) * Math::sin((K * deklination).doubleValue()) + Math::cos((K * latitude).doubleValue()) * Math::cos((K * deklination).doubleValue()) * Math::cos((K * stundenwinkel).doubleValue())
y = - (Math::sin((K*latitude).doubleValue) * x - Math::sin((K*deklination).doubleValue)) / (Math::cos((K*latitude).doubleValue) * Math::sin(Math::acos(x.doubleValue)))
sonnenhoehe = Math::asin(x.doubleValue) / K

var break = hour.doubleValue + (minute.doubleValue/60.0) <= 12.0 + (15.0-longitude)/15.0 - zeitgleichung/60.0 
if (break) {
    azimut = Math::acos(y.doubleValue) / K
} else {
    azimut = 360.0 - Math::acos(y.doubleValue) / K
}

logDebug("Sun.rules", "Calculated new SunHeight angle '" + sonnenhoehe + "°'")
logDebug("Sun.rules", "Calculated new Azimut angle '" + azimut + "°'")

// post new values to the bus
Sun_Height.postUpdate(sonnenhoehe)
Sun_Azimut.postUpdate(azimut)

end

#################################################

furthermore you need two new items of type 'Number'

In order to react properly please add other rules with an Item-Changed-Trigger in Sun_Height or Sun_Azimut which in turn can do all the time tweaks using joda-time

Does that suite your needs?

openhab-bot commented 10 years ago

may be this will also help:

import org.openhab.core.library.types. import org.openhab.model.script.actions.

import java.lang.Math import java.util.Date import java.util.Calendar import org.joda.time.DateTime

// Change this reflecting your destination var Number lat = x.xxxx var Number lng = y.yyyy

var Calendar sunrise = Calendar::getInstance() var Calendar sunset = Calendar::getInstance()

rule "Sun" when Time cron "0 0/30 * * * ?" then // http://www.suncalc.net var J1970 = 2440588 var J2000 = 2451545 var deg2rad = Math::PI / 180 var M0 = 357.5291 * deg2rad var M1 = 0.98560028 * deg2rad var J0 = 0.0009 var J1 = 0.0053 var J2 = -0.0069 var C1 = 1.9148 * deg2rad var C2 = 0.0200 * deg2rad var C3 = 0.0003 * deg2rad var P = 102.9372 * deg2rad var e = 23.45 * deg2rad var th0 = 280.1600 * deg2rad var th1 = 360.9856235 * deg2rad var h0 = -0.83 * deg2rad //sunset angle var d0 = 0.53 * deg2rad //sun diameter var h1 = -6 * deg2rad //nautical twilight angle var h2 = -12 * deg2rad //astronomical twilight angle var h3 = -18 * deg2rad //darkness angle var msInDay = 1000 * 60 * 60 * 24 var lw = -lng * deg2rad var phi = lat * deg2rad

var datum = new Date()

var J = datum.getTime() / msInDay - 0.5 + J1970
var n = Math::round( (J - J2000 - J0 - lw/(2 * Math::PI)).doubleValue)
var Js = (J2000 + J0 + (0 + lw)/(2 * Math::PI) + n)
var M = ( M0 + M1 * (Js - J2000))
var C = C1 * Math::sin(M.doubleValue) + C2 * Math::sin((2 * M).doubleValue) + C3 * Math::sin((3 * M).doubleValue)
var Lsun = M + P + C + Math::PI
var Jtransit = Js + (J1 * Math::sin(M.doubleValue)) + (J2 * Math::sin((2 * Lsun).doubleValue))
var d = Math::asin((Math::sin(Lsun.doubleValue) * Math::sin(e.doubleValue)).doubleValue)
var w0 = Math::acos(((Math::sin(h0.doubleValue) - Math::sin(phi.doubleValue) * Math::sin(d.doubleValue)) / (Math::cos(phi.doubleValue) * Math::cos(d.doubleValue))).doubleValue)
var w1 = Math::acos(((Math::sin((h0+d0).doubleValue) - Math::sin(phi.doubleValue) * Math::sin(d.doubleValue)) / (Math::cos(phi.doubleValue) * Math::cos(d.doubleValue))).doubleValue)

var Jset            = J2000 + J0 + (w0 + lw)/(2 * Math::PI) + n + (J1 * Math::sin(M.doubleValue)) + (J2 * Math::sin((2 * Lsun).doubleValue))
var Jsetstart       = J2000 + J0 + (w1 + lw)/(2 * Math::PI) + n + (J1 * Math::sin(M.doubleValue)) + (J2 * Math::sin((2 * Lsun).doubleValue))

var Jrise           = Jtransit - (Jset - Jtransit)
var Jriseend        = Jtransit - (Jsetstart - Jtransit)

var sunrise_ms_start = ( (Jrise + 0.5 - J1970) * msInDay).longValue
var sunrise_ms_end   = ( (Jriseend + 0.5 - J1970) * msInDay).longValue
var sunset_ms_start  = ((Jsetstart + 0.5 - J1970) * msInDay).longValue
var sunset_ms_end    = ((Jset + 0.5 - J1970) * msInDay).longValue

// Items:
// DateTime        SunRise "Sonnenaufgang [%1$tA, %1$td.%1$tm.%1$tY %1$tT]"                <calendar>
// DateTime        SunSet  "Sonnenuntergang [%1$tA, %1$td.%1$tm.%1$tY %1$tT]"         <calendar>

var Calendar sunrise_start = Calendar::getInstance()
sunrise_start.setTimeInMillis( sunrise_ms_start )
SunRise.postUpdate( new DateTimeType(sunrise_start))

var  Calendar sunset_start = Calendar::getInstance()
sunset_start.setTimeInMillis( sunset_ms_start )
SunSet.postUpdate( new DateTimeType(sunset_start))

end

openhab-bot commented 10 years ago

Can someon confirm that comment #4 give valid results? I tried it for my self (northern germany) and got

SunRise 3:44 seems to be wrong SunSet 15:16 seens to be UTC, so this is correct

while Google gives me

SunRise 8:34 local SunSet 16:17 local

openhab-bot commented 10 years ago

This code works for me : import org.openhab.core.library.types.* import java.lang.Math

// Constants var Number K = 0.017453

// Change this reflecting your destination var Number latitude = 50.892 var Number longitude = 4.733

rule "Set Sun and Dawn States" when Time cron "0 0/5 * * * ?" then var Number tageszahl var Number deklination var Number zeitgleichung var Number stundenwinkel var Number x var Number y var Number sonnenhoehe var Number azimut

var month  = now.getMonthOfYear
var day    = now.getDayOfMonth
var hour   = now.getHourOfDay
var minute = now.getMinuteOfHour

// Source: http://www.jgiesen.de/SME/tk/index.htm
tageszahl = (month - 1) * 30 + day + hour / 24 
deklination = -23.45 * Math::cos((K * 360 * (tageszahl + 10) / 365).doubleValue)
zeitgleichung = 60.0 * (-0.171 * Math::sin((0.0337*tageszahl+0.465).doubleValue) - 0.1299 * Math::sin((0.01787*tageszahl-0.168).doubleValue))
stundenwinkel = 15.0 * (hour.doubleValue + (minute.doubleValue/60.0) - (15.0-longitude)/15.0 - 12.0 + zeitgleichung/60.0)
x = Math::sin((K * latitude).doubleValue()) * Math::sin((K * deklination).doubleValue()) + Math::cos((K * latitude).doubleValue()) * Math::cos((K * deklination).doubleValue()) * Math::cos((K * st

undenwinkel).doubleValue()) y = - (Math::sin((K_latitude).doubleValue) * x - Math::sin((K_deklination).doubleValue)) / (Math::cos((Klatitude).doubleValue) \ Math::sin(Math::acos(x.doubleValue))) sonnenhoehe = Math::asin(x.doubleValue) / K

var break = hour.doubleValue + (minute.doubleValue/60.0) <= 12.0 + (15.0-longitude)/15.0 - zeitgleichung/60.0 
if (break) {
    azimut = Math::acos(y.doubleValue) / K
} else {
    azimut = 360.0 - Math::acos(y.doubleValue) / K
}

logDebug("Sun.rules", "month: " + month)
logDebug("Sun.rules", "day: " + day)
logDebug("Sun.rules", "hour: " + hour)
logDebug("Sun.rules", "minute: " + minute)
logDebug("Sun.rules", "tageszahl: " + tageszahl)
logDebug("Sun.rules", "deklination: " + deklination)
logDebug("Sun.rules", "zeitgleichung: " + zeitgleichung)
logDebug("Sun.rules", "stundenwinkel: " + stundenwinkel)
logDebug("Sun.rules", "x: " + x)
logDebug("Sun.rules", "y: " + y)
logDebug("Sun.rules", "sonnenhoehe: " + sonnenhoehe)
logDebug("Sun.rules", "azimut: " + azimut)

logDebug("Sun.rules", "Calculated new SunHeight angle '" + sonnenhoehe + "°'")
logDebug("Sun.rules", "Calculated new Azimut angle '" + sonnenhoehe + "°'")

// Send all calculations to the event bus

Sun_Height.postUpdate(sonnenhoehe)
Sun_Azimut.postUpdate(azimut)
Daytime.postUpdate( if (sonnenhoehe > 0) OPEN else CLOSED )
if (hour > 3 && hour < 10){
Sun_Dawn_Civil.postUpdate( if (sonnenhoehe > -6 && sonnenhoehe < 0 ) OPEN else CLOSED )
Sun_Dawn_Nautical.postUpdate( if (sonnenhoehe > -12 && sonnenhoehe < -6 ) OPEN else CLOSED )
Sun_Dawn_Astronomical.postUpdate( if (sonnenhoehe > -18 && sonnenhoehe < -12) OPEN else CLOSED )
} if (hour > 15 && hour < 21) {
Sun_Dusk_Civil.postUpdate( if (sonnenhoehe > -6 && sonnenhoehe < 0 ) OPEN else CLOSED )
Sun_Dusk_Nautical.postUpdate( if (sonnenhoehe > -12 && sonnenhoehe < -6 ) OPEN else CLOSED )
Sun_Dusk_Astronomical.postUpdate( if (sonnenhoehe > -18 && sonnenhoehe < -12) OPEN else CLOSED )
}

end

in items Contact Sun_Dawn_Civil "Zon dawn civil [MAP(sunstates.map):%S]" (Sun) Contact Sun_Dawn_Nautical "Zon dawn nautical [MAP(sunstates.map):%S]" (Sun) Contact Sun_Dawn_Astronomical "Zon dawn astronomical [MAP(sunstates.map):%S]" (Sun) Contact Sun_Dusk_Civil "Zon dusk civil [MAP(sunstates.map):%S]" (Sun) Contact Sun_Dusk_Nautical "Zon dusk nautical [MAP(sunstates.map):%S]" (Sun) Contact Sun_Dusk_Astronomical "Zon dusk astronomical [MAP(sunstates.map):%S]" (Sun) Contact Daytime "Daytime [MAP(test.map):%s]" (Sun)

Maybe this helps. Regards, Nico

openhab-bot commented 10 years ago

7 gzockoll

Shame on me ... i mixed up the variables "lng" and "lat". :(

Apr 2, 2013 Delete comment #8 kristian.backstrom It seems to me, that #4 gives good results for sunrise / sunset.

It also seems to me that #6 gives me negative sun height values quite too early in the evenings. My coordinates are roughly 25,66E and 60,39N (if you like to test).

Have anyone written a rule for calculating the solar beam hitting angle against flat solar collectors for a given time? The additional parameters would naturally be panel tilt angle and panel rotation angle.

I would like to forecast their power, by also using weather forecasts for estimating the cloud cover.

Regards, Kristian

Apr 3, 2013 Delete comment Project Member #9 teichsta good question ...

I would propose to ask the question on the google group since there are more observers than on this issue.

Regards,

Thomas E.-E.

Jun 28, 2013 Delete comment #11 Erland.Lestander For sake of completness, two booleans should be made available as well: MidnightSun and MidWinterDarkness, or something similiar in name. Those should indicate when we are outside of the valid region for the equation, i.e. when the sun doesn't set resp. rise. Then the equation work for everybody (on this planet).

Jul 17, 2013 Delete comment #12 evazzoler Why not integrate this into the official code with the next release?

Jul 18, 2013 Delete comment Project Member #13 teichsta we could add the calculation rule to the demo package, yes

Jul 24, 2013 Delete comment #14 enrico.vazzoler@archeometra.it No, I meant in the code giving the possibility to use a short syntax in a rule:

"Time is sunset" or "Time is sunrise.plusHours(2)"

Pay attention on this: there is no home automation where no astronomical timer is provided...

Jul 24, 2013 Delete comment Project Member #15 belovictor I would agree with that, those functions should be nicely available.

Jul 25, 2013 Delete comment Project Member #16 kai.openhab sounds like a good idea, yes.

Sep 15, 2013 Delete comment #17 mg@subscribe.audumla.net This is something that I needed for my own Apache Karaf project. I'm in the process of converting it from Apache Karaf to OpenHab but will have difficulty converting the sunrise/sunset functionality as OpenHab is quite restricted regarding extension of the trigger mechanism. This component really should allow an extensible mechanism of allowing custom triggers to be written and deployed as bundles. In the meantime I will try to package up my Quartz Astrological Scheduler and make it available. As OpenHab uses Quartz it would fit straight in if the createTimer method was changed to switch schedulers instead of statically using the cron scheduler.

Sep 16, 2013 Delete comment Project Member #18 kai.openhab For the Eclipse SmartHome project, I plan to further modularize the rule engine - triggers as addons definitely sound like a good idea.

teichsta commented 10 years ago

meanwhile the Astro-Binding (see https://github.com/openhab/openhab/wiki/Astro-binding) has been implemented which should solve the problem