google-code-export / openhab

Automatically exported from code.google.com/p/openhab
GNU General Public License v3.0
0 stars 0 forks source link

Enhacements to 'time' expression in rules #71

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 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 reported on code.google.com by belovic...@gmail.com on 3 Feb 2012 at 7:41

GoogleCodeExporter commented 9 years ago
this lib looks promising http://www.jstott.me.uk/jsuntimes/

Original comment by teichsta on 4 Feb 2012 at 2:45

GoogleCodeExporter commented 9 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.

Original comment by vanhobo...@gmail.com on 9 Apr 2012 at 7:01

GoogleCodeExporter commented 9 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'

* Sun_Height
* Sun_Azimut

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?

Original comment by teichsta on 26 Jul 2012 at 1:06

GoogleCodeExporter commented 9 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

Original comment by kleina.i...@gmail.com on 5 Oct 2012 at 6:58

GoogleCodeExporter commented 9 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

Original comment by gzock...@gmail.com on 6 Jan 2013 at 8:50

GoogleCodeExporter commented 9 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((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", "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>   (Sun)
Contact Sun_Dawn_Nautical       "Zon dawn nautical [MAP(sunstates.map):%S]"     <sun> 
    (Sun)
Contact Sun_Dawn_Astronomical   "Zon dawn astronomical [MAP(sunstates.map):%S]" 
<sun>   (Sun)
Contact Sun_Dusk_Civil          "Zon dusk civil [MAP(sunstates.map):%S]"        <sun>   (Sun)
Contact Sun_Dusk_Nautical       "Zon dusk nautical [MAP(sunstates.map):%S]" 
    <sun>   (Sun)
Contact Sun_Dusk_Astronomical   "Zon dusk astronomical 
[MAP(sunstates.map):%S]"    <sun>   (Sun)
Contact Daytime                 "Daytime [MAP(test.map):%s]"                    <clock> (Sun)

Maybe this helps.
Regards,
Nico

Original comment by nicoat...@gmail.com on 7 Jan 2013 at 1:18

GoogleCodeExporter commented 9 years ago
Shame on me ... i mixed up the variables "lng" and "lat". :(

Original comment by gzock...@gmail.com on 7 Jan 2013 at 8:51

GoogleCodeExporter commented 9 years ago
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

Original comment by kristian...@gmail.com on 2 Apr 2013 at 11:28

GoogleCodeExporter commented 9 years ago
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.

Original comment by teichsta on 3 Apr 2013 at 8:21

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
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).

Original comment by Erland.L...@gmail.com on 28 Jun 2013 at 6:25

GoogleCodeExporter commented 9 years ago
Why not integrate this into the official code with the next release?

Original comment by evazzo...@gmail.com on 17 Jul 2013 at 10:27

GoogleCodeExporter commented 9 years ago
we could add the calculation rule to the demo package, yes

Original comment by teichsta on 18 Jul 2013 at 8:18

GoogleCodeExporter commented 9 years ago
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... 

Original comment by enrico.v...@archeometra.it on 24 Jul 2013 at 10:35

GoogleCodeExporter commented 9 years ago
I would agree with that, those functions should be nicely available.

Original comment by belovic...@gmail.com on 25 Jul 2013 at 5:01

GoogleCodeExporter commented 9 years ago
sounds like a good idea, yes.

Original comment by kai.openhab on 25 Jul 2013 at 7:29

GoogleCodeExporter commented 9 years ago
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.

Original comment by m...@subscribe.audumla.net on 16 Sep 2013 at 4:58

GoogleCodeExporter commented 9 years ago
For the Eclipse SmartHome project, I plan to further modularize the rule engine 
- triggers as addons definitely sound like a good idea.

Original comment by kai.openhab on 16 Sep 2013 at 4:35

GoogleCodeExporter commented 9 years ago

Original comment by kai.openhab on 5 Dec 2013 at 10:08

GoogleCodeExporter commented 9 years ago
has been migrated to https://github.com/openhab/openhab/issues/633

Original comment by teichsta on 10 Dec 2013 at 10:04