added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.astro/.classpath
Normal file
32
bundles/org.openhab.binding.astro/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.astro/.project
Normal file
23
bundles/org.openhab.binding.astro/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.astro</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.astro/NOTICE
Normal file
13
bundles/org.openhab.binding.astro/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
269
bundles/org.openhab.binding.astro/README.md
Normal file
269
bundles/org.openhab.binding.astro/README.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Astro Binding
|
||||
|
||||
The Astro binding is used for calculating
|
||||
|
||||
* many DateTime and positional values for sun and moon.
|
||||
* Radiation levels (direct, diffuse and total) of the sun during the day
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports two Things: Sun and Moon
|
||||
|
||||
## Discovery
|
||||
|
||||
If a system location is set, "Local Sun" and a "Local Moon" will be automatically discovered for this location.
|
||||
|
||||
If the system location is changed, the background discovery updates the configuration of "Local Sun" and "Local Moon" automatically.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
No binding configuration required.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
All Things require the parameter `geolocation` (as `<latitude>,<longitude>[,<altitude in m>]`) for which the calculation is done.
|
||||
The altitude segment is optional and sharpens results provided by the Radiation group.
|
||||
Optionally, a refresh `interval` (in seconds) can be defined to also calculate positional data like azimuth and elevation.
|
||||
|
||||
Season calculation can be switched from equinox based calculation to meteorological based (starting on the first day of the given month).
|
||||
This is done by setting `useMeteorologicalSeason` to true in the advanced setting of the sun.
|
||||
|
||||
## Channels
|
||||
|
||||
* **thing** `sun`
|
||||
* **group** `rise, set, noon, night, morningNight, astroDawn, nauticDawn, civilDawn, astroDusk, nauticDusk, civilDusk, eveningNight, daylight`
|
||||
* **channel**
|
||||
* `start, end` (DateTime)
|
||||
* `duration` (Number:Time)
|
||||
* **group** `position`
|
||||
* **channel**
|
||||
* `azimuth, elevation` (Number:Angle)
|
||||
* `shadeLength` (Number)
|
||||
* **group** `radiation`
|
||||
* **channel**
|
||||
* `direct, diffuse, total` (Number:Intensity)
|
||||
* **group** `zodiac`
|
||||
* **channel**
|
||||
* `start, end` (DateTime)
|
||||
* `sign` (String), values: `ARIES, TAURUS, GEMINI, CANCER, LEO, VIRGO, LIBRA, SCORPIO, SAGITTARIUS, CAPRICORN, AQUARIUS, PISCES`
|
||||
* **group** `season`
|
||||
* **channel**:
|
||||
* `spring, summer, autumn, winter` (DateTime)
|
||||
* `name`,`nextName` (String), values `SPRING, SUMMER, AUTUMN, WINTER`
|
||||
* `timeLeft` (Number:Time)
|
||||
* **group** `eclipse`
|
||||
* **channel**:
|
||||
* `total, partial, ring` (DateTime)
|
||||
* `totalElevation, partialElevation, ringElevation` (Number:Angle)
|
||||
* **group** `phase`
|
||||
* **channel**
|
||||
* `name` (String), values: `SUN_RISE, ASTRO_DAWN, NAUTIC_DAWN, CIVIL_DAWN, CIVIL_DUSK, NAUTIC_DUSK, ASTRO_DUSK, SUN_SET, DAYLIGHT, NIGHT`
|
||||
* **thing** `moon`
|
||||
* **group** `rise, set`
|
||||
* **channel**
|
||||
* `start, end` (DateTime)
|
||||
* **group** `phase`
|
||||
* **channel**:
|
||||
* `firstQuarter, thirdQuarter, full, new` (DateTime)
|
||||
* `age` (Number:Time)
|
||||
* `ageDegree` (Number:Angle)
|
||||
* `agePercent, illumination` (Number:Dimensionless)
|
||||
* `name` (String), values: `NEW, WAXING_CRESCENT, FIRST_QUARTER, WAXING_GIBBOUS, FULL, WANING_GIBBOUS, THIRD_QUARTER, WANING_CRESCENT`
|
||||
* **group** `eclipse`
|
||||
* **channel**:
|
||||
* `total, partial` (DateTime)
|
||||
* `totalElevation, partialElevation` (Number:Angle)
|
||||
* **group** `distance`
|
||||
* **channel**:
|
||||
* `date` (DateTime)
|
||||
* `distance` (Number:Length)
|
||||
* **group** `perigee`
|
||||
* **channel**:
|
||||
* `date` (DateTime),
|
||||
* `distance` (Number:Length)
|
||||
* **group** `apogee`
|
||||
* **channel**:
|
||||
* `date` (DateTime)
|
||||
* `distance` (Number:Length)
|
||||
* **group** `zodiac`
|
||||
* **channel**
|
||||
* `sign` (String), values: `ARIES, TAURUS, GEMINI, CANCER, LEO, VIRGO, LIBRA, SCORPIO, SAGITTARIUS, CAPRICORN, AQUARIUS, PISCES`
|
||||
* **group** `position`
|
||||
* **channel**
|
||||
* `azimuth, elevation` (Number:Angle)
|
||||
|
||||
### Trigger Channels
|
||||
|
||||
* **thing** `sun`
|
||||
* **group** `rise, set, noon, night, morningNight, astroDawn, nauticDawn, civilDawn, astroDusk, nauticDusk, civilDusk, eveningNight, daylight`
|
||||
* **event** `START, END`
|
||||
* **group** `eclipse`
|
||||
* **event**: `TOTAL, PARTIAL, RING`
|
||||
* **thing** `moon`
|
||||
* **group** `rise`
|
||||
* **event** `START`
|
||||
* **group** `set`
|
||||
* **event** `END`
|
||||
* **group** `phase`
|
||||
* **event**: `FIRST_QUARTER, THIRD_QUARTER, FULL, NEW`
|
||||
* **group** `eclipse`
|
||||
* **event**: `TOTAL, PARTIAL`
|
||||
* **group** `perigee`
|
||||
* **event**: `PERIGEE`
|
||||
* **group** `apogee`
|
||||
* **event**: `APOGEE`
|
||||
|
||||
### Channel config
|
||||
|
||||
**Offsets:** For each event group you can optionally configure an `offset` in minutes.
|
||||
The `offset` must be configured in the channel properties for the corresponding thing.
|
||||
|
||||
The minimum allowed offset is -1440 and the maximum allowed offset is 1440.
|
||||
|
||||
**Earliest/Latest:** For each trigger channel and `start`, `end` datetime value, you can optionally configure the `earliest` and `latest` time of the day.
|
||||
|
||||
e.g `sun#set earliest=18:00, latest=20:00`
|
||||
|
||||
sunset is 17:40, but `earliest` is set to 18:00 so the event/datetime value is moved to 18:00.
|
||||
|
||||
OR
|
||||
|
||||
sunset is 22:10 but `latest` is set to 20:00 so the event/datetime value is moved 20:00.
|
||||
|
||||
## Full Example
|
||||
|
||||
Things:
|
||||
|
||||
```
|
||||
astro:sun:home [ geolocation="52.5200066,13.4049540,100", interval=60 ]
|
||||
astro:moon:home [ geolocation="52.5200066,13.4049540", interval=60 ]
|
||||
```
|
||||
|
||||
or optionally with an event offset
|
||||
|
||||
```
|
||||
astro:sun:home [ geolocation="52.5200066,13.4049540,100", interval=60 ] {
|
||||
Channels:
|
||||
Type rangeEvent : rise#event [
|
||||
offset=-30
|
||||
]
|
||||
}
|
||||
astro:moon:home [ geolocation="52.5200066,13.4049540", interval=60 ]
|
||||
```
|
||||
|
||||
or a datetime offset
|
||||
|
||||
```
|
||||
astro:sun:home [ geolocation="52.5200066,13.4049540,100", interval=60 ] {
|
||||
Channels:
|
||||
Type start : rise#start [
|
||||
offset=5
|
||||
]
|
||||
Type end : rise#end [
|
||||
offset=5
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
or an offset and latest
|
||||
|
||||
```
|
||||
astro:sun:home [ geolocation="52.5200066,13.4049540,100", interval=60 ] {
|
||||
Channels:
|
||||
Type rangeEvent : rise#event [
|
||||
offset=-10,
|
||||
latest="08:00"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Items:
|
||||
|
||||
```
|
||||
DateTime Sunrise_Time "Sunrise [%1$tH:%1$tM]" { channel="astro:sun:home:rise#start" }
|
||||
DateTime Sunset_Time "Sunset [%1$tH:%1$tM]" { channel="astro:sun:home:set#start" }
|
||||
Number:Angle Azimuth "Azimuth" { channel="astro:sun:home:position#azimuth" }
|
||||
Number:Angle Elevation "Elevation" { channel="astro:sun:home:position#elevation" }
|
||||
String MoonPhase "MoonPhase" { channel="astro:moon:home:phase#name" }
|
||||
Number:Length MoonDistance "MoonDistance [%.1f %unit%]" { channel="astro:moon:home:distance#distance" }
|
||||
Number:Intensity Total_Radiation "Radiation [%.2f %unit%]" { channel="astro:sun:home:radiation#total" }
|
||||
Number:Intensity Diffuse_Radiation "Diffuse Radiation [%.2f %unit%]" { channel="astro:sun:home:radiation#diffuse" }
|
||||
```
|
||||
|
||||
Events:
|
||||
|
||||
```
|
||||
rule "example trigger rule"
|
||||
when
|
||||
Channel 'astro:sun:home:rise#event' triggered START
|
||||
then
|
||||
...
|
||||
end
|
||||
```
|
||||
|
||||
## Rule Actions
|
||||
|
||||
Multiple actions are supported by this binding. In classic rules these are accessible as shown in the example below:
|
||||
|
||||
Getting sunActions variable in scripts
|
||||
|
||||
```
|
||||
val sunActions = getActions("astro","astro:sun:local")
|
||||
if(null === sunActions) {
|
||||
logInfo("actions", "sunActions not found, check thing ID")
|
||||
return
|
||||
} else {
|
||||
// do something with sunActions
|
||||
}
|
||||
```
|
||||
|
||||
### getEventTime(phaseName, date, moment)
|
||||
|
||||
Retrieves date and time (ZonedDateTime) of the requested phase name.
|
||||
Thing method only applies to Sun thing type.
|
||||
|
||||
* `phaseName` (String), values: `SUN_RISE, ASTRO_DAWN, NAUTIC_DAWN, CIVIL_DAWN, CIVIL_DUSK, NAUTIC_DUSK, ASTRO_DUSK, SUN_SET, DAYLIGHT, NIGHT`. Mandatory.
|
||||
|
||||
* `date` (ZonedDateTime), only the date part of this parameter will be considered - defaulted to now() if null.
|
||||
|
||||
* `moment` (String), values: `START, END` - defaulted to `START` if null.
|
||||
|
||||
Example :
|
||||
|
||||
```
|
||||
val sunEvent = "SUN_SET"
|
||||
val today = ZonedDateTime.now;
|
||||
val sunEventTime = sunActions.getEventTime(sunEvent,today,"START")
|
||||
logInfo("AstroActions","{} will happen at : {}", sunEvent, sunEventTime.toString)
|
||||
```
|
||||
|
||||
### getElevation(timeStamp)
|
||||
|
||||
Retrieves the elevation (QuantityType<Angle>) of the sun at the requested instant.
|
||||
Thing method applies to Sun and Moon.
|
||||
|
||||
* `timeStamp` (ZonedDateTime) - defaulted to now() if null.
|
||||
|
||||
|
||||
### getAzimuth(timeStamp)
|
||||
|
||||
Retrieves the azimuth (QuantityType<Angle>) of the sun at the requested instant.
|
||||
Thing method applies to Sun and Moon.
|
||||
|
||||
* `timeStamp` (ZonedDateTime) - defaulted to now() if null.
|
||||
|
||||
Example :
|
||||
|
||||
```
|
||||
val azimuth = sunActions.getAzimuth(sunEventTime)
|
||||
val elevation = sunActions.getElevation(sunEventTime)
|
||||
logInfo("AstroActions", "{} will be positioned at elevation {} - azimuth {}",sunEvent, elevation.toString,azimuth.toString)
|
||||
```
|
||||
|
||||
|
||||
## Tips
|
||||
|
||||
Do not worry if for example the "astro dawn" is undefined at your location.
|
||||
The reason might be that you live in a northern country and it is summer, such that the sun is not 18 degrees below the horizon in the morning.
|
||||
For details see [this Wikipedia article](https://en.wikipedia.org/wiki/Dawn).
|
||||
The "civil dawn" event might often be the better choice.
|
||||
17
bundles/org.openhab.binding.astro/pom.xml
Normal file
17
bundles/org.openhab.binding.astro/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.astro</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Astro Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.astro-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-astro" description="Astro Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.astro/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link AstroBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Made non-Instantiable
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class AstroBindingConstants {
|
||||
|
||||
/** Constructor */
|
||||
private AstroBindingConstants() {
|
||||
throw new IllegalAccessError("Non-instantiable");
|
||||
}
|
||||
|
||||
public static final String BINDING_ID = "astro";
|
||||
|
||||
public static final String SUN = "sun";
|
||||
public static final String MOON = "moon";
|
||||
public static final String LOCAL = "local";
|
||||
|
||||
// things
|
||||
public static final ThingTypeUID THING_TYPE_SUN = new ThingTypeUID(BINDING_ID, SUN);
|
||||
public static final ThingTypeUID THING_TYPE_MOON = new ThingTypeUID(BINDING_ID, MOON);
|
||||
|
||||
// events
|
||||
public static final String EVENT_START = "START";
|
||||
public static final String EVENT_END = "END";
|
||||
|
||||
public static final String EVENT_PHASE_FIRST_QUARTER = "FIRST_QUARTER";
|
||||
public static final String EVENT_PHASE_THIRD_QUARTER = "THIRD_QUARTER";
|
||||
public static final String EVENT_PHASE_FULL = "FULL";
|
||||
public static final String EVENT_PHASE_NEW = "NEW";
|
||||
|
||||
public static final String EVENT_PERIGEE = "PERIGEE";
|
||||
public static final String EVENT_APOGEE = "APOGEE";
|
||||
|
||||
// event channelIds
|
||||
public static final String EVENT_CHANNEL_ID_MOON_PHASE = "phase#event";
|
||||
public static final String EVENT_CHANNEL_ID_ECLIPSE = "eclipse#event";
|
||||
public static final String EVENT_CHANNEL_ID_PERIGEE = "perigee#event";
|
||||
public static final String EVENT_CHANNEL_ID_APOGEE = "apogee#event";
|
||||
|
||||
public static final String EVENT_CHANNEL_ID_RISE = "rise#event";
|
||||
public static final String EVENT_CHANNEL_ID_SET = "set#event";
|
||||
public static final String EVENT_CHANNEL_ID_NOON = "noon#event";
|
||||
public static final String EVENT_CHANNEL_ID_NIGHT = "night#event";
|
||||
public static final String EVENT_CHANNEL_ID_MORNING_NIGHT = "morningNight#event";
|
||||
public static final String EVENT_CHANNEL_ID_ASTRO_DAWN = "astroDawn#event";
|
||||
public static final String EVENT_CHANNEL_ID_NAUTIC_DAWN = "nauticDawn#event";
|
||||
public static final String EVENT_CHANNEL_ID_CIVIL_DAWN = "civilDawn#event";
|
||||
public static final String EVENT_CHANNEL_ID_ASTRO_DUSK = "astroDusk#event";
|
||||
public static final String EVENT_CHANNEL_ID_NAUTIC_DUSK = "nauticDusk#event";
|
||||
public static final String EVENT_CHANNEL_ID_CIVIL_DUSK = "civilDusk#event";
|
||||
public static final String EVENT_CHANNEL_ID_EVENING_NIGHT = "eveningNight#event";
|
||||
public static final String EVENT_CHANNEL_ID_DAYLIGHT = "daylight#event";
|
||||
|
||||
public static final String CHANNEL_ID_SUN_PHASE_NAME = "phase#name";
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.binding.astro.internal.handler.MoonHandler;
|
||||
import org.openhab.binding.astro.internal.handler.SunHandler;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.scheduler.CronScheduler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link AstroHandlerFactory} is responsible for creating things and thing handlers.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.astro", service = ThingHandlerFactory.class)
|
||||
public class AstroHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream
|
||||
.concat(SunHandler.SUPPORTED_THING_TYPES.stream(), MoonHandler.SUPPORTED_THING_TYPES.stream())
|
||||
.collect(Collectors.toSet());
|
||||
private static final Map<String, AstroThingHandler> ASTRO_THING_HANDLERS = new HashMap<>();
|
||||
private final CronScheduler scheduler;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public AstroHandlerFactory(final @Reference CronScheduler scheduler,
|
||||
final @Reference TimeZoneProvider timeZoneProvider) {
|
||||
this.scheduler = scheduler;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
AstroThingHandler thingHandler = null;
|
||||
if (thingTypeUID.equals(THING_TYPE_SUN)) {
|
||||
thingHandler = new SunHandler(thing, scheduler, timeZoneProvider);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_MOON)) {
|
||||
thingHandler = new MoonHandler(thing, scheduler, timeZoneProvider);
|
||||
}
|
||||
if (thingHandler != null) {
|
||||
ASTRO_THING_HANDLERS.put(thing.getUID().toString(), thingHandler);
|
||||
}
|
||||
return thingHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterHandler(Thing thing) {
|
||||
super.unregisterHandler(thing);
|
||||
ASTRO_THING_HANDLERS.remove(thing.getUID().toString());
|
||||
}
|
||||
|
||||
public static @Nullable AstroThingHandler getHandler(String thingUid) {
|
||||
return ASTRO_THING_HANDLERS.get(thingUid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.action;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import javax.measure.quantity.Angle;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.AstroBindingConstants;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.binding.astro.internal.handler.SunHandler;
|
||||
import org.openhab.binding.astro.internal.model.SunPhaseName;
|
||||
import org.openhab.core.automation.annotation.ActionInput;
|
||||
import org.openhab.core.automation.annotation.ActionOutput;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {AstroActions } defines rule actions for the Astro binding.
|
||||
* <p>
|
||||
* <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
|
||||
* the test <i>actions instanceof AstroActions</i> fails. This test can fail
|
||||
* due to an issue in openHAB core v2.5.0 where the {@link AstroActions} class
|
||||
* can be loaded by a different classloader than the <i>actions</i> instance.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "astro")
|
||||
@NonNullByDefault
|
||||
public class AstroActions implements ThingActions, IAstroActions {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AstroActions.class);
|
||||
protected @Nullable AstroThingHandler handler;
|
||||
|
||||
public AstroActions() {
|
||||
logger.debug("Astro actions service instanciated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof AstroThingHandler) {
|
||||
this.handler = (AstroThingHandler) handler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RuleAction(label = "Astro : Get Azimuth", description = "Get the azimuth of the sun for a given time")
|
||||
public @Nullable @ActionOutput(name = "getAzimuth", label = "Azimuth", type = "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getAzimuth(
|
||||
@ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
|
||||
logger.debug("Astro action 'getAzimuth' called");
|
||||
AstroThingHandler theHandler = this.handler;
|
||||
if (theHandler != null) {
|
||||
return theHandler.getAzimuth(date != null ? date : ZonedDateTime.now());
|
||||
} else {
|
||||
logger.info("Astro Action service ThingHandler is null!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RuleAction(label = "Astro : Get Elevation", description = "Get the Elevation of the sun for a given time")
|
||||
public @Nullable @ActionOutput(name = "getElevation", label = "Elevation", type = "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getElevation(
|
||||
@ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
|
||||
logger.debug("Astro action 'getElevation' called");
|
||||
AstroThingHandler theHandler = this.handler;
|
||||
if (theHandler != null) {
|
||||
return theHandler.getElevation(date != null ? date : ZonedDateTime.now());
|
||||
} else {
|
||||
logger.info("Astro Action service ThingHandler is null!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RuleAction(label = "Sun : Get Event Time", description = "Get the date time of a given planet event")
|
||||
public @Nullable @ActionOutput(name = "getEventTime", type = "java.time.ZonedDateTime") ZonedDateTime getEventTime(
|
||||
@ActionInput(name = "phaseName", label = "Phase", required = true, description = "Requested phase") String phaseName,
|
||||
@ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date,
|
||||
@ActionInput(name = "moment", label = "Moment", required = false, defaultValue = "START", description = "Either START or END") @Nullable String moment) {
|
||||
logger.debug("Sun action 'getEventTime' called");
|
||||
try {
|
||||
AstroThingHandler theHandler = this.handler;
|
||||
if (theHandler != null) {
|
||||
if (theHandler instanceof SunHandler) {
|
||||
SunHandler handler = (SunHandler) theHandler;
|
||||
SunPhaseName phase = SunPhaseName.valueOf(phaseName.toUpperCase());
|
||||
return handler.getEventTime(phase, date != null ? date : ZonedDateTime.now(),
|
||||
moment == null || AstroBindingConstants.EVENT_START.equalsIgnoreCase(moment));
|
||||
} else {
|
||||
logger.info("Astro Action service ThingHandler is not a SunHandler!");
|
||||
}
|
||||
} else {
|
||||
logger.info("Astro Action service ThingHandler is null!");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.info("Parameter {} is not a valid phase name", phaseName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable QuantityType<Angle> getElevation(@Nullable ThingActions actions,
|
||||
@Nullable ZonedDateTime date) {
|
||||
return invokeMethodOf(actions).getElevation(date);
|
||||
}
|
||||
|
||||
public static @Nullable QuantityType<Angle> getAzimuth(@Nullable ThingActions actions,
|
||||
@Nullable ZonedDateTime date) {
|
||||
return invokeMethodOf(actions).getAzimuth(date);
|
||||
}
|
||||
|
||||
public static @Nullable ZonedDateTime getEventTime(@Nullable ThingActions actions, @Nullable String phaseName,
|
||||
@Nullable ZonedDateTime date, @Nullable String moment) {
|
||||
if (phaseName != null) {
|
||||
return invokeMethodOf(actions).getEventTime(phaseName, date, moment);
|
||||
} else {
|
||||
throw new IllegalArgumentException("phaseName can not be null");
|
||||
}
|
||||
}
|
||||
|
||||
private static IAstroActions invokeMethodOf(@Nullable ThingActions actions) {
|
||||
if (actions == null) {
|
||||
throw new IllegalArgumentException("actions cannot be null");
|
||||
}
|
||||
if (actions.getClass().getName().equals(AstroActions.class.getName())) {
|
||||
if (actions instanceof IAstroActions) {
|
||||
return (IAstroActions) actions;
|
||||
} else {
|
||||
return (IAstroActions) Proxy.newProxyInstance(IAstroActions.class.getClassLoader(),
|
||||
new Class[] { IAstroActions.class }, (Object proxy, Method method, Object[] args) -> {
|
||||
Method m = actions.getClass().getDeclaredMethod(method.getName(),
|
||||
method.getParameterTypes());
|
||||
return m.invoke(actions, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Actions is not an instance of AstroActions");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.action;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import javax.measure.quantity.Angle;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
|
||||
/**
|
||||
* The {@link IAstroActions} defines the interface for all thing actions supported by the binding.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface IAstroActions {
|
||||
public @Nullable ZonedDateTime getEventTime(String phaseName, @Nullable ZonedDateTime date,
|
||||
@Nullable String moment);
|
||||
|
||||
public @Nullable QuantityType<Angle> getAzimuth(@Nullable ZonedDateTime date);
|
||||
|
||||
public @Nullable QuantityType<Angle> getElevation(@Nullable ZonedDateTime date);
|
||||
}
|
||||
@@ -0,0 +1,857 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.openhab.binding.astro.internal.model.Eclipse;
|
||||
import org.openhab.binding.astro.internal.model.EclipseKind;
|
||||
import org.openhab.binding.astro.internal.model.EclipseType;
|
||||
import org.openhab.binding.astro.internal.model.Moon;
|
||||
import org.openhab.binding.astro.internal.model.MoonDistance;
|
||||
import org.openhab.binding.astro.internal.model.MoonPhase;
|
||||
import org.openhab.binding.astro.internal.model.MoonPhaseName;
|
||||
import org.openhab.binding.astro.internal.model.Position;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
import org.openhab.binding.astro.internal.model.Zodiac;
|
||||
import org.openhab.binding.astro.internal.model.ZodiacSign;
|
||||
import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
|
||||
/**
|
||||
* Calculates the phase, eclipse, rise, set, distance, illumination and age of
|
||||
* the moon.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
* @see based on the calculations of
|
||||
* http://www.computus.de/mondphase/mondphase.htm azimuth/elevation and
|
||||
* zodiac based on http://lexikon.astronomie.info/java/sunmoon/
|
||||
*/
|
||||
public class MoonCalc {
|
||||
private static final double NEW_MOON = 0;
|
||||
private static final double FULL_MOON = 0.5;
|
||||
private static final double FIRST_QUARTER = 0.25;
|
||||
private static final double LAST_QUARTER = 0.75;
|
||||
|
||||
/**
|
||||
* Calculates all moon data at the specified coordinates
|
||||
*/
|
||||
public Moon getMoonInfo(Calendar calendar, double latitude, double longitude) {
|
||||
Moon moon = new Moon();
|
||||
|
||||
double julianDate = DateTimeUtils.dateToJulianDate(calendar);
|
||||
double julianDateMidnight = DateTimeUtils.midnightDateToJulianDate(calendar);
|
||||
|
||||
double[] riseSet = getRiseSet(calendar, latitude, longitude);
|
||||
Calendar rise = DateTimeUtils.timeToCalendar(calendar, riseSet[0]);
|
||||
Calendar set = DateTimeUtils.timeToCalendar(calendar, riseSet[1]);
|
||||
|
||||
if (rise == null || set == null) {
|
||||
Calendar tomorrow = (Calendar) calendar.clone();
|
||||
tomorrow.add(Calendar.DAY_OF_MONTH, 1);
|
||||
|
||||
double[] riseSeTomorrow = getRiseSet(tomorrow, latitude, longitude);
|
||||
if (rise == null) {
|
||||
rise = DateTimeUtils.timeToCalendar(tomorrow, riseSeTomorrow[0]);
|
||||
}
|
||||
if (set == null) {
|
||||
set = DateTimeUtils.timeToCalendar(tomorrow, riseSeTomorrow[1]);
|
||||
}
|
||||
}
|
||||
|
||||
moon.setRise(new Range(rise, rise));
|
||||
moon.setSet(new Range(set, set));
|
||||
|
||||
MoonPhase phase = moon.getPhase();
|
||||
phase.setNew(DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, NEW_MOON)));
|
||||
phase.setFirstQuarter(DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, FIRST_QUARTER)));
|
||||
phase.setFull(DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, FULL_MOON)));
|
||||
phase.setThirdQuarter(DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, LAST_QUARTER)));
|
||||
|
||||
Eclipse eclipse = moon.getEclipse();
|
||||
eclipse.getKinds().forEach(eclipseKind -> {
|
||||
double jdate = getEclipse(calendar, EclipseType.MOON, julianDateMidnight, eclipseKind);
|
||||
eclipse.set(eclipseKind, DateTimeUtils.toCalendar(jdate), new Position());
|
||||
});
|
||||
|
||||
double decimalYear = DateTimeUtils.getDecimalYear(calendar);
|
||||
MoonDistance apogee = moon.getApogee();
|
||||
double apogeeJd = getApogee(julianDate, decimalYear);
|
||||
apogee.setDate(DateTimeUtils.toCalendar(apogeeJd));
|
||||
apogee.setDistance(getDistance(apogeeJd));
|
||||
|
||||
MoonDistance perigee = moon.getPerigee();
|
||||
double perigeeJd = getPerigee(julianDate, decimalYear);
|
||||
perigee.setDate(DateTimeUtils.toCalendar(perigeeJd));
|
||||
perigee.setDistance(getDistance(perigeeJd));
|
||||
|
||||
return moon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the moon illumination and distance.
|
||||
*/
|
||||
public void setPositionalInfo(Calendar calendar, double latitude, double longitude, Moon moon) {
|
||||
double julianDate = DateTimeUtils.dateToJulianDate(calendar);
|
||||
setMoonPhase(calendar, moon);
|
||||
setAzimuthElevationZodiac(julianDate, latitude, longitude, moon);
|
||||
|
||||
MoonDistance distance = moon.getDistance();
|
||||
distance.setDate(Calendar.getInstance());
|
||||
distance.setDistance(getDistance(julianDate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the age and the current phase.
|
||||
*/
|
||||
private void setMoonPhase(Calendar calendar, Moon moon) {
|
||||
MoonPhase phase = moon.getPhase();
|
||||
double julianDateEndOfDay = DateTimeUtils.endOfDayDateToJulianDate(calendar);
|
||||
double parentNewMoon = getPreviousPhase(calendar, julianDateEndOfDay, NEW_MOON);
|
||||
double age = Math.abs(parentNewMoon - julianDateEndOfDay);
|
||||
phase.setAge((int) age);
|
||||
|
||||
long parentNewMoonMillis = DateTimeUtils.toCalendar(parentNewMoon).getTimeInMillis();
|
||||
long ageRangeTimeMillis = phase.getNew().getTimeInMillis() - parentNewMoonMillis;
|
||||
long ageCurrentMillis = System.currentTimeMillis() - parentNewMoonMillis;
|
||||
double agePercent = ageRangeTimeMillis != 0 ? ageCurrentMillis * 100.0 / ageRangeTimeMillis : 0;
|
||||
phase.setAgePercent(agePercent);
|
||||
phase.setAgeDegree(3.6 * agePercent);
|
||||
double illumination = getIllumination(DateTimeUtils.dateToJulianDate(calendar));
|
||||
phase.setIllumination(illumination);
|
||||
boolean isWaxing = age < (29.530588853 / 2);
|
||||
if (DateTimeUtils.isSameDay(calendar, phase.getNew())) {
|
||||
phase.setName(MoonPhaseName.NEW);
|
||||
} else if (DateTimeUtils.isSameDay(calendar, phase.getFirstQuarter())) {
|
||||
phase.setName(MoonPhaseName.FIRST_QUARTER);
|
||||
} else if (DateTimeUtils.isSameDay(calendar, phase.getThirdQuarter())) {
|
||||
phase.setName(MoonPhaseName.THIRD_QUARTER);
|
||||
} else if (DateTimeUtils.isSameDay(calendar, phase.getFull())) {
|
||||
phase.setName(MoonPhaseName.FULL);
|
||||
} else if (illumination >= 0 && illumination < 50) {
|
||||
phase.setName(isWaxing ? MoonPhaseName.WAXING_CRESCENT : MoonPhaseName.WANING_CRESCENT);
|
||||
} else if (illumination >= 50 && illumination < 100) {
|
||||
phase.setName(isWaxing ? MoonPhaseName.WAXING_GIBBOUS : MoonPhaseName.WANING_GIBBOUS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates moonrise and moonset.
|
||||
*/
|
||||
private double[] getRiseSet(Calendar calendar, double latitude, double longitude) {
|
||||
double lambda = prepareCoordinate(longitude, 180);
|
||||
if (longitude > 0) {
|
||||
lambda *= -1;
|
||||
}
|
||||
double phi = prepareCoordinate(latitude, 90);
|
||||
if (latitude < 0) {
|
||||
phi *= -1;
|
||||
}
|
||||
|
||||
double moonJd = Math.floor(DateTimeUtils.midnightDateToJulianDate(calendar)) - 2400000.0;
|
||||
moonJd -= ((calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / 60000.0) / 1440.0;
|
||||
|
||||
double sphi = SN(phi);
|
||||
double cphi = CS(phi);
|
||||
double sinho = SN(8.0 / 60.0);
|
||||
|
||||
int hour = 1;
|
||||
double utrise = -1;
|
||||
double utset = -1;
|
||||
do {
|
||||
double yminus = SINALT(moonJd, hour - 1, lambda, cphi, sphi) - sinho;
|
||||
double yo = SINALT(moonJd, hour, lambda, cphi, sphi) - sinho;
|
||||
double yplus = SINALT(moonJd, hour + 1, lambda, cphi, sphi) - sinho;
|
||||
double[] quadRet = QUAD(yminus, yo, yplus);
|
||||
if (quadRet[3] == 1) {
|
||||
if (yminus < 0) {
|
||||
utrise = hour + quadRet[1];
|
||||
} else {
|
||||
utset = hour + quadRet[1];
|
||||
}
|
||||
}
|
||||
if (quadRet[3] == 2) {
|
||||
if (quadRet[0] < 0) {
|
||||
utrise = hour + quadRet[2];
|
||||
utset = hour + quadRet[1];
|
||||
} else {
|
||||
utrise = hour + quadRet[1];
|
||||
utset = hour + quadRet[2];
|
||||
}
|
||||
}
|
||||
yminus = yplus;
|
||||
hour += 2;
|
||||
} while (hour < 25 && (utrise == -1 || utset == -1));
|
||||
|
||||
double rise = prepareTime(utrise);
|
||||
double set = prepareTime(utset);
|
||||
|
||||
return new double[] { rise, set };
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the coordinate for moonrise and moonset calculation.
|
||||
*/
|
||||
private double prepareCoordinate(double coordinate, double system) {
|
||||
double c = Math.abs(coordinate);
|
||||
|
||||
if (c - Math.floor(c) >= .599) {
|
||||
c = Math.floor(c) + (c - Math.floor(c)) / 1 * .6;
|
||||
}
|
||||
if (c > system) {
|
||||
c = Math.floor(c) % system + (c - Math.floor(c));
|
||||
}
|
||||
return Math.round(c * 100.0) / 100.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a time value for converting to a calendar object.
|
||||
*/
|
||||
private double prepareTime(double riseSet) {
|
||||
if (riseSet == -1) {
|
||||
return riseSet;
|
||||
}
|
||||
double riseMinute = (riseSet - Math.floor(riseSet)) * 60.0 / 100.0;
|
||||
double rounded;
|
||||
if (riseMinute >= .595) {
|
||||
riseMinute = 0;
|
||||
rounded = riseSet + 1;
|
||||
} else {
|
||||
rounded = riseSet;
|
||||
}
|
||||
rounded = Math.floor(rounded) + riseMinute;
|
||||
|
||||
BigDecimal bd = new BigDecimal(Double.toString(rounded));
|
||||
bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
|
||||
return bd.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the moon phase.
|
||||
*/
|
||||
private double calcMoonPhase(double k, double mode) {
|
||||
double kMod = Math.floor(k) + mode;
|
||||
double t = kMod / 1236.85;
|
||||
double e = var_e(t);
|
||||
double m = var_m(kMod, t);
|
||||
double m1 = var_m1(kMod, t);
|
||||
double f = var_f(kMod, t);
|
||||
double o = var_o(kMod, t);
|
||||
double jd = var_jde(kMod, t);
|
||||
if (mode == NEW_MOON) {
|
||||
jd += -.4072 * SN(m1) + .17241 * e * SN(m) + .01608 * SN(2 * m1) + .01039 * SN(2 * f)
|
||||
+ .00739 * e * SN(m1 - m) - .00514 * e * SN(m1 + m) + .00208 * e * e * SN(2 * m)
|
||||
- .00111 * SN(m1 - 2 * f) - .00057 * SN(m1 + 2 * f);
|
||||
jd += .00056 * e * SN(2 * m1 + m) - .00042 * SN(3 * m1) + .00042 * e * SN(m + 2 * f)
|
||||
+ .00038 * e * SN(m - 2 * f) - .00024 * e * SN(2 * m1 - m) - .00017 * SN(o)
|
||||
- .00007 * SN(m1 + 2 * m) + .00004 * SN(2 * m1 - 2 * f);
|
||||
jd += .00004 * SN(3 * m) + .00003 * SN(m1 + m - 2 * f) + .00003 * SN(2 * m1 + 2 * f)
|
||||
- .00003 * SN(m1 + m + 2 * f) + .00003 * SN(m1 - m + 2 * f) - .00002 * SN(m1 - m - 2 * f)
|
||||
- .00002 * SN(3 * m1 + m);
|
||||
jd += .00002 * SN(4 * m1);
|
||||
} else if (mode == FULL_MOON) {
|
||||
jd += -.40614 * SN(m1) + .17302 * e * SN(m) + .01614 * SN(2 * m1) + .01043 * SN(2 * f)
|
||||
+ .00734 * e * SN(m1 - m) - .00515 * e * SN(m1 + m) + .00209 * e * e * SN(2 * m)
|
||||
- .00111 * SN(m1 - 2 * f) - .00057 * SN(m1 + 2 * f);
|
||||
jd += .00056 * e * SN(2 * m1 + m) - .00042 * SN(3 * m1) + .00042 * e * SN(m + 2 * f)
|
||||
+ .00038 * e * SN(m - 2 * f) - .00024 * e * SN(2 * m1 - m) - .00017 * SN(o)
|
||||
- .00007 * SN(m1 + 2 * m) + .00004 * SN(2 * m1 - 2 * f);
|
||||
jd += .00004 * SN(3 * m) + .00003 * SN(m1 + m - 2 * f) + .00003 * SN(2 * m1 + 2 * f)
|
||||
- .00003 * SN(m1 + m + 2 * f) + .00003 * SN(m1 - m + 2 * f) - .00002 * SN(m1 - m - 2 * f)
|
||||
- .00002 * SN(3 * m1 + m);
|
||||
jd += .00002 * SN(4 * m1);
|
||||
} else {
|
||||
jd += -.62801 * SN(m1) + .17172 * e * SN(m) - .01183 * e * SN(m1 + m) + .00862 * SN(2 * m1)
|
||||
+ .00804 * SN(2 * f) + .00454 * e * SN(m1 - m) + .00204 * e * e * SN(2 * m) - .0018 * SN(m1 - 2 * f)
|
||||
- .0007 * SN(m1 + 2 * f);
|
||||
jd += -.0004 * SN(3 * m1) - .00034 * e * SN(2 * m1 - m) + .00032 * e * SN(m + 2 * f)
|
||||
+ .00032 * e * SN(m - 2 * f) - .00028 * e * e * SN(m1 + 2 * m) + .00027 * e * SN(2 * m1 + m)
|
||||
- .00017 * SN(o);
|
||||
jd += -.00005 * SN(m1 - m - 2 * f) + .00004 * SN(2 * m1 + 2 * f) - .00004 * SN(m1 + m + 2 * f)
|
||||
+ .00004 * SN(m1 - 2 * m) + .00003 * SN(m1 + m - 2 * f) + .00003 * SN(3 * m)
|
||||
+ .00002 * SN(2 * m1 - 2 * f);
|
||||
jd += .00002 * SN(m1 - m + 2 * f) - .00002 * SN(3 * m1 + m);
|
||||
double w = .00306 - .00038 * e * CS(m) + .00026 * CS(m1) - .00002 * CS(m1 - m) + .00002 * CS(m1 + m)
|
||||
+ .00002 * CS(2 * f);
|
||||
jd += (mode == FIRST_QUARTER) ? w : -w;
|
||||
}
|
||||
return moonCorrection(jd, t, kMod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the eclipse.
|
||||
*/
|
||||
private double getEclipse(double k, EclipseType typ, EclipseKind eclipse) {
|
||||
double kMod = Math.floor(k) + ((typ == EclipseType.SUN) ? 0 : 0.5);
|
||||
double t = kMod / 1236.85;
|
||||
double f = var_f(kMod, t);
|
||||
double jd = 0;
|
||||
double ringTest = 0;
|
||||
if (SN(Math.abs(f)) <= .36) {
|
||||
double o = var_o(kMod, t);
|
||||
double f1 = f - .02665 * SN(o);
|
||||
double a1 = 299.77 + .107408 * kMod - .009173 * t * t;
|
||||
double e = var_e(t);
|
||||
double m = var_m(kMod, t);
|
||||
double m1 = var_m1(kMod, t);
|
||||
double p = .207 * e * SN(m) + .0024 * e * SN(2 * m) - .0392 * SN(m1) + .0116 * SN(2 * m1)
|
||||
- .0073 * e * SN(m1 + m) + .0067 * e * SN(m1 - m) + .0118 * SN(2 * f1);
|
||||
double q = 5.2207 - .0048 * e * CS(m) + .002 * e * CS(2 * m) - .3299 * CS(m1) - .006 * e * CS(m1 + m)
|
||||
+ .0041 * e * CS(m1 - m);
|
||||
double g = (p * CS(f1) + q * SN(f1)) * (1 - .0048 * CS(Math.abs(f1)));
|
||||
double u = .0059 + .0046 * e * CS(m) - .0182 * CS(m1) + .0004 * CS(2 * m1) - .0005 * CS(m + m1);
|
||||
jd = var_jde(kMod, t);
|
||||
jd += (typ == EclipseType.MOON) ? -.4065 * SN(m1) + .1727 * e * SN(m) : -.4075 * SN(m1) + .1721 * e * SN(m);
|
||||
|
||||
jd += .0161 * SN(2 * m1) - .0097 * SN(2 * f1) + .0073 * e * SN(m1 - m) - .005 * e * SN(m1 + m)
|
||||
- .0023 * SN(m1 - 2 * f1) + .0021 * e * SN(2 * m);
|
||||
jd += .0012 * SN(m1 + 2 * f1) + .0006 * e * SN(2 * m1 + m) - .0004 * SN(3 * m1) - .0003 * e * SN(m + 2 * f1)
|
||||
+ .0003 * SN(a1) - .0002 * e * SN(m - 2 * f1) - .0002 * e * SN(2 * m1 - m) - .0002 * SN(o);
|
||||
switch (typ) {
|
||||
case MOON:
|
||||
if ((1.0248 - u - Math.abs(g)) / .545 <= 0) {
|
||||
jd = 0; // no moon eclipse
|
||||
}
|
||||
if (eclipse == EclipseKind.PARTIAL && (1.0128 - u - Math.abs(g)) / .545 > 0
|
||||
&& (.4678 - u) * (.4678 - u) - g * g > 0) {
|
||||
jd = 0; // no partial moon eclipse
|
||||
}
|
||||
if (eclipse == EclipseKind.TOTAL
|
||||
&& ((1.0128 - u - Math.abs(g)) / .545 <= 0 != (.4678 - u) * (.4678 - u) - g * g <= 0)) {
|
||||
jd = 0; // no total moon eclipse
|
||||
}
|
||||
break;
|
||||
case SUN:
|
||||
if (Math.abs(g) > 1.5433 + u) {
|
||||
jd = 0; // no sun eclipse
|
||||
}
|
||||
if (eclipse == EclipseKind.PARTIAL && ((g >= -.9972 && g <= .9972)
|
||||
|| (Math.abs(g) >= .9972 && Math.abs(g) < .9972 + Math.abs(u)))) {
|
||||
jd = 0; // no partial sun eclipse
|
||||
}
|
||||
if (eclipse != EclipseKind.PARTIAL) {
|
||||
if ((g < -.9972 || g > .9972) || (Math.abs(g) < .9972 && Math.abs(g) > .9972 + Math.abs(u))) {
|
||||
jd = 0; // no ring or total sun eclipse
|
||||
}
|
||||
if (u > .0047 || u >= .00464 * Math.sqrt(1 - g * g)) {
|
||||
ringTest = 1; // no total sun eclipse
|
||||
}
|
||||
if (ringTest == 1 && eclipse == EclipseKind.TOTAL) {
|
||||
jd = 0;
|
||||
}
|
||||
if (ringTest == 0 && eclipse == EclipseKind.RING) {
|
||||
jd = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return jd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the illumination.
|
||||
*/
|
||||
private double getIllumination(double jd) {
|
||||
double t = (jd - 2451545) / 36525;
|
||||
double d = 297.8502042 + 445267.11151686 * t - .00163 * t * t + t * t * t / 545868 - t * t * t * t / 113065000;
|
||||
double m = 357.5291092 + 35999.0502909 * t - .0001536 * t * t + t * t * t / 24490000;
|
||||
double m1 = 134.9634114 + 477198.8676313 * t + .008997 * t * t + t * t * t / 69699 - t * t * t * t / 14712000;
|
||||
double i = 180 - d - 6.289 * SN(m1) + 2.1 * SN(m) - 1.274 * SN(2 * d - m1) - .658 * SN(2 * d)
|
||||
- .241 * SN(2 * m1) - .110 * SN(d);
|
||||
return (1 + CS(i)) / 2 * 100.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next moon phase.
|
||||
*/
|
||||
private double getNextPhase(Calendar cal, double midnightJd, double mode) {
|
||||
double tz = 0;
|
||||
double phaseJd = 0;
|
||||
do {
|
||||
double k = var_k(cal, tz);
|
||||
tz += 1;
|
||||
phaseJd = calcMoonPhase(k, mode);
|
||||
} while (phaseJd <= midnightJd);
|
||||
return phaseJd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the previous moon phase.
|
||||
*/
|
||||
public double getPreviousPhase(Calendar cal, double jd, double mode) {
|
||||
double tz = 0;
|
||||
double phaseJd = 0;
|
||||
do {
|
||||
double k = var_k(cal, tz);
|
||||
tz -= 1;
|
||||
phaseJd = calcMoonPhase(k, mode);
|
||||
} while (phaseJd > jd);
|
||||
return phaseJd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next eclipse.
|
||||
*/
|
||||
protected double getEclipse(Calendar cal, EclipseType type, double midnightJd, EclipseKind eclipse) {
|
||||
double tz = 0;
|
||||
double eclipseJd = 0;
|
||||
do {
|
||||
double k = var_k(cal, tz);
|
||||
tz += 1;
|
||||
eclipseJd = getEclipse(k, type, eclipse);
|
||||
} while (eclipseJd <= midnightJd);
|
||||
return eclipseJd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the date, where the moon is furthest away from the earth.
|
||||
*/
|
||||
private double getApogee(double julianDate, double decimalYear) {
|
||||
double k = Math.floor((decimalYear - 1999.97) * 13.2555) + .5;
|
||||
double jd = 0;
|
||||
do {
|
||||
double t = k / 1325.55;
|
||||
double d = 171.9179 + 335.9106046 * k - .010025 * t * t - .00001156 * t * t * t
|
||||
+ .000000055 * t * t * t * t;
|
||||
double m = 347.3477 + 27.1577721 * k - .0008323 * t * t - .000001 * t * t * t;
|
||||
double f = 316.6109 + 364.5287911 * k - .0125131 * t * t - .0000148 * t * t * t;
|
||||
jd = 2451534.6698 + 27.55454988 * k - .0006886 * t * t - .000001098 * t * t * t + .0000000052 * t * t
|
||||
+ .4392 * SN(2 * d) + .0684 * SN(4 * d) + (.0456 - .00011 * t) * SN(m)
|
||||
+ (.0426 - .00011 * t) * SN(2 * d - m) + .0212 * SN(2 * f);
|
||||
jd += -.0189 * SN(d) + .0144 * SN(6 * d) + .0113 * SN(4 * d - m) + .0047 * SN(2 * d + 2 * f)
|
||||
+ .0036 * SN(d + m) + .0035 * SN(8 * d) + .0034 * SN(6 * d - m) - .0034 * SN(2 * d - 2 * f)
|
||||
+ .0022 * SN(2 * d - 2 * m) - .0017 * SN(3 * d);
|
||||
jd += .0013 * SN(4 * d + 2 * f) + .0011 * SN(8 * d - m) + .001 * SN(4 * d - 2 * m) + .0009 * SN(10 * d)
|
||||
+ .0007 * SN(3 * d + m) + .0006 * SN(2 * m) + .0005 * SN(2 * d + m) + .0005 * SN(2 * d + 2 * m)
|
||||
+ .0004 * SN(6 * d + 2 * f);
|
||||
jd += .0004 * SN(6 * d - 2 * m) + .0004 * SN(10 * d - m) - .0004 * SN(5 * d) - .0004 * SN(4 * d - 2 * f)
|
||||
+ .0003 * SN(2 * f + m) + .0003 * SN(12 * d) + .0003 * SN(2 * d + 2 * f - m) - .0003 * SN(d - m);
|
||||
k += 1;
|
||||
} while (jd < julianDate);
|
||||
return jd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the date, where the moon is closest to the earth.
|
||||
*/
|
||||
private double getPerigee(double julianDate, double decimalYear) {
|
||||
double k = Math.floor((decimalYear - 1999.97) * 13.2555);
|
||||
double jd = 0;
|
||||
do {
|
||||
double t = k / 1325.55;
|
||||
double d = 171.9179 + 335.9106046 * k - .010025 * t * t - .00001156 * t * t * t
|
||||
+ .000000055 * t * t * t * t;
|
||||
double m = 347.3477 + 27.1577721 * k - .0008323 * t * t - .000001 * t * t * t;
|
||||
double f = 316.6109 + 364.5287911 * k - .0125131 * t * t - .0000148 * t * t * t;
|
||||
jd = 2451534.6698 + 27.55454988 * k - .0006886 * t * t - .000001098 * t * t * t + .0000000052 * t * t
|
||||
- 1.6769 * SN(2 * d) + .4589 * SN(4 * d) - .1856 * SN(6 * d) + .0883 * SN(8 * d);
|
||||
jd += -(.0773 + .00019 * t) * SN(2 * d - m) + (.0502 - .00013 * t) * SN(m) - .046 * SN(10 * d)
|
||||
+ (.0422 - .00011 * t) * SN(4 * d - m) - .0256 * SN(6 * d - m) + .0253 * SN(12 * d) + .0237 * SN(d);
|
||||
jd += .0162 * SN(8 * d - m) - .0145 * SN(14 * d) + .0129 * SN(2 * f) - .0112 * SN(3 * d)
|
||||
- .0104 * SN(10 * d - m) + .0086 * SN(16 * d) + .0069 * SN(12 * d - m) + .0066 * SN(5 * d)
|
||||
- .0053 * SN(2 * d + 2 * f);
|
||||
jd += -.0052 * SN(18 * d) - .0046 * SN(14 * d - m) - .0041 * SN(7 * d) + .004 * SN(2 * d + m)
|
||||
+ .0032 * SN(20 * d) - .0032 * SN(d + m) + .0031 * SN(16 * d - m);
|
||||
jd += -.0029 * SN(4 * d + m) - .0027 * SN(2 * d - 2 * m) + .0024 * SN(4 * d - 2 * m)
|
||||
- .0021 * SN(6 * d - 2 * m) - .0021 * SN(22 * d) - .0021 * SN(18 * d - m);
|
||||
jd += .0019 * SN(6 * d + m) - .0018 * SN(11 * d) - .0014 * SN(8 * d + m) - .0014 * SN(4 * d - 2 * f)
|
||||
- .0014 * SN(6 * d - 2 * f) + .0014 * SN(3 * d + m) - .0014 * SN(5 * d + m) + .0013 * SN(13 * d);
|
||||
jd += .0013 * SN(20 * d - m) + .0011 * SN(3 * d + 2 * m) - .0011 * SN(4 * d + 2 * f - 2 * m)
|
||||
- .001 * SN(d + 2 * m) - .0009 * SN(22 * d - m) - .0008 * SN(4 * f) + .0008 * SN(6 * d - 2 * f)
|
||||
+ .0008 * SN(2 * d - 2 * f + m);
|
||||
jd += .0007 * SN(2 * m) + .0007 * SN(2 * f - m) + .0007 * SN(2 * d + 4 * f) - .0006 * SN(2 * f - 2 * m)
|
||||
- .0006 * SN(2 * d - 2 * f + 2 * m) + .0006 * SN(24 * d) + .0005 * SN(4 * d - 4 * f)
|
||||
+ .0005 * SN(2 * d + 2 * m) - .0004 * SN(d - m) + .0027 * SN(9 * d) + .0027 * SN(4 * d + 2 * f);
|
||||
k += 1;
|
||||
} while (jd < julianDate);
|
||||
return jd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the distance from the moon to earth.
|
||||
*/
|
||||
private double getDistance(double jd) {
|
||||
double t = (jd - 2451545) / 36525;
|
||||
double d = 297.8502042 + 445267.11151686 * t - .00163 * t * t + t * t * t / 545868 - t * t * t * t / 113065000;
|
||||
double m = 357.5291092 + 35999.0502909 * t - .0001536 * t * t + t * t * t / 24490000;
|
||||
double m1 = 134.9634114 + 477198.8676313 * t + .008997 * t * t + t * t * t / 69699 - t * t * t * t / 14712000;
|
||||
double f = 93.27209929999999 + 483202.0175273 * t - .0034029 * t * t - t * t * t / 3526000
|
||||
+ t * t * t * t / 863310000;
|
||||
double sr = 385000.56 + getCoefficient(d, m, m1, f) / 1000;
|
||||
return sr;
|
||||
}
|
||||
|
||||
public double[] calcMoon(double t) {
|
||||
double p2 = 6.283185307;
|
||||
double arc = 206264.8062;
|
||||
double coseps = .91748;
|
||||
double sineps = .39778;
|
||||
double lo = FRAK(.606433 + 1336.855225 * t);
|
||||
double l = p2 * FRAK(.374897 + 1325.55241 * t);
|
||||
double ls = p2 * FRAK(.993133 + 99.997361 * t);
|
||||
double d = p2 * FRAK(.827361 + 1236.853086 * t);
|
||||
double f = p2 * FRAK(.259086 + 1342.227825 * t);
|
||||
double dl = 22640 * Math.sin(l) - 4586 * Math.sin(l - 2 * d) + 2370 * Math.sin(2 * d) + 769 * Math.sin(2 * l)
|
||||
- 668 * Math.sin(ls) - 412 * Math.sin(2 * f) - 212 * Math.sin(2 * l - 2 * d)
|
||||
- 206 * Math.sin(l + ls - 2 * d) + 192 * Math.sin(l + 2 * d) - 165 * Math.sin(ls - 2 * d)
|
||||
- 125 * Math.sin(d) - 110 * Math.sin(l + ls) + 148 * Math.sin(l - ls) - 55 * Math.sin(2 * f - 2 * d);
|
||||
double s = f + (dl + 412 * Math.sin(2 * f) + 541 * Math.sin(ls)) / arc;
|
||||
double h = f - 2 * d;
|
||||
double n = -526 * Math.sin(h) + 44 * Math.sin(l + h) - 31 * Math.sin(-l + h) - 23 * Math.sin(ls + h)
|
||||
+ 11 * Math.sin(-ls + h) - 25 * Math.sin(-2 * l + f) + 21 * Math.sin(-l + f);
|
||||
double lmoon = p2 * FRAK(lo + dl / 1296000);
|
||||
double bmoon = (18520 * Math.sin(s) + n) / arc;
|
||||
double cb = Math.cos(bmoon);
|
||||
double x = cb * Math.cos(lmoon);
|
||||
double v = cb * Math.sin(lmoon);
|
||||
double w = Math.sin(bmoon);
|
||||
double y = coseps * v - sineps * w;
|
||||
double z = sineps * v + coseps * w;
|
||||
double rho = Math.sqrt(1 - z * z);
|
||||
double dec = (360 / p2) * Math.atan(z / rho);
|
||||
double ra = (48 / p2) * Math.atan(y / (x + rho));
|
||||
if (ra < 0) {
|
||||
ra += 24;
|
||||
}
|
||||
return new double[] { dec, ra };
|
||||
}
|
||||
|
||||
private double CS(double x) {
|
||||
return Math.cos(x * SunCalc.DEG2RAD);
|
||||
}
|
||||
|
||||
private double SN(double x) {
|
||||
return Math.sin(x * SunCalc.DEG2RAD);
|
||||
}
|
||||
|
||||
private double SINALT(double moonJd, int hour, double lambda, double cphi, double sphi) {
|
||||
double jdo = moonJd + hour / 24.0;
|
||||
double t = (jdo - 51544.5) / 36525.0;
|
||||
double decra[] = calcMoon(t);
|
||||
double tau = 15.0 * (LMST(jdo, lambda) - decra[1]);
|
||||
return sphi * SN(decra[0]) + cphi * CS(decra[0]) * CS(tau);
|
||||
}
|
||||
|
||||
private double LMST(double moonJd, double lambda) {
|
||||
double moonJdo = Math.floor(moonJd);
|
||||
double ut = (moonJd - moonJdo) * 24.0;
|
||||
double t = (moonJdo - 51544.5) / 36525.0;
|
||||
double gmst = 6.697374558 + 1.0027379093 * ut + (8640184.812866 + (.093104 - .0000062 * t) * t) * t / 3600.0;
|
||||
return 24.0 * FRAK((gmst - lambda / 15.0) / 24.0);
|
||||
}
|
||||
|
||||
private double FRAK(double x) {
|
||||
double ret = x - (int) (x);
|
||||
if (ret < 0) {
|
||||
ret += 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private double[] QUAD(double yminus, double yo, double yplus) {
|
||||
double nz = 0;
|
||||
double a = .5 * (yminus + yplus) - yo;
|
||||
double b = .5 * (yplus - yminus);
|
||||
double c = yo;
|
||||
double xe = -b / (2 * a);
|
||||
double ye = (a * xe + b) * xe + c;
|
||||
double dis = b * b - 4 * a * c;
|
||||
double zero1 = 0;
|
||||
double zero2 = 0;
|
||||
if (dis >= 0) {
|
||||
double dx = .5 * Math.sqrt(dis) / Math.abs(a);
|
||||
zero1 = xe - dx;
|
||||
zero2 = xe + dx;
|
||||
if (Math.abs(zero1) <= 1) {
|
||||
nz += 1;
|
||||
}
|
||||
if (Math.abs(zero2) <= 1) {
|
||||
nz += 1;
|
||||
}
|
||||
if (zero1 < -1) {
|
||||
zero1 = zero2;
|
||||
}
|
||||
}
|
||||
return new double[] { ye, zero1, zero2, nz };
|
||||
}
|
||||
|
||||
private double var_o(double k, double t) {
|
||||
return 124.7746 - 1.5637558 * k + .0020691 * t * t + .00000215 * t * t * t;
|
||||
}
|
||||
|
||||
private double var_f(double k, double t) {
|
||||
return 160.7108 + 390.67050274 * k - .0016341 * t * t - .00000227 * t * t * t + .000000011 * t * t * t * t;
|
||||
}
|
||||
|
||||
private double var_m1(double k, double t) {
|
||||
return 201.5643 + 385.81693528 * k + .1017438 * t * t + .00001239 * t * t * t - .000000058 * t * t * t * t;
|
||||
}
|
||||
|
||||
private double var_m(double k, double t) {
|
||||
return 2.5534 + 29.10535669 * k - .0000218 * t * t - .00000011 * t * t * t;
|
||||
}
|
||||
|
||||
private double var_e(double t) {
|
||||
return 1 - .002516 * t - .0000074 * t * t;
|
||||
}
|
||||
|
||||
private double var_jde(double k, double t) {
|
||||
return 2451550.09765 + 29.530588853 * k + .0001337 * t * t - .00000015 * t * t * t
|
||||
+ .00000000073 * t * t * t * t;
|
||||
}
|
||||
|
||||
private double var_k(Calendar cal, double tz) {
|
||||
return (cal.get(Calendar.YEAR) + (cal.get(Calendar.DAY_OF_YEAR) + tz) / 365 - 2000) * 12.3685;
|
||||
}
|
||||
|
||||
private double moonCorrection(double jd, double t, double k) {
|
||||
double ret = jd;
|
||||
ret += .000325 * SN(299.77 + .107408 * k - .009173 * t * t) + .000165 * SN(251.88 + .016321 * k)
|
||||
+ .000164 * SN(251.83 + 26.651886 * k) + .000126 * SN(349.42 + 36.412478 * k)
|
||||
+ .00011 * SN(84.66 + 18.206239 * k);
|
||||
ret += .000062 * SN(141.74 + 53.303771 * k) + .00006 * SN(207.14 + 2.453732 * k)
|
||||
+ .000056 * SN(154.84 + 7.30686 * k) + .000047 * SN(34.52 + 27.261239 * k)
|
||||
+ .000042 * SN(207.19 + .121824 * k) + .00004 * SN(291.34 + 1.844379 * k);
|
||||
ret += .000037 * SN(161.72 + 24.198154 * k) + .000035 * SN(239.56 + 25.513099 * k)
|
||||
+ .000023 * SN(331.55 + 3.592518 * k);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private double getCoefficient(double d, double m, double m1, double f) {
|
||||
int[] kd = new int[] { 0, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 1, 0, 2, 0, 0, 4, 0, 4, 2, 2, 1, 1, 2, 2, 4, 2, 0, 2, 2,
|
||||
1, 2, 0, 0, 2, 2, 2, 4, 0, 3, 2, 4, 0, 2, 2, 2, 4, 0, 4, 1, 2, 0, 1, 3, 4, 2, 0, 1, 2, 2 };
|
||||
int[] km = new int[] { 0, 0, 0, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, -1, 0, 0, 0, 1, 0,
|
||||
-1, 0, -2, 1, 2, -2, 0, 0, -1, 0, 0, 1, -1, 2, 2, 1, -1, 0, 0, -1, 0, 1, 0, 1, 0, 0, -1, 2, 1, 0, 0 };
|
||||
int[] km1 = new int[] { 1, -1, 0, 2, 0, 0, -2, -1, 1, 0, -1, 0, 1, 0, 1, 1, -1, 3, -2, -1, 0, -1, 0, 1, 2, 0,
|
||||
-3, -2, -1, -2, 1, 0, 2, 0, -1, 1, 0, -1, 2, -1, 1, -2, -1, -1, -2, 0, 1, 4, 0, -2, 0, 2, 1, -2, -3, 2,
|
||||
1, -1, 3, -1 };
|
||||
int[] kf = new int[] { 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
|
||||
0, 0, 0, 0, 0, 0, -2, 2, 0, 2, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, -2 };
|
||||
int[] kr = new int[] { -20905355, -3699111, -2955968, -569925, 48888, -3149, 246158, -152138, -170733, -204586,
|
||||
-129620, 108743, 104755, 10321, 0, 79661, -34782, -23210, -21636, 24208, 30824, -8379, -16675, -12831,
|
||||
-10445, -11650, 14403, -7003, 0, 10056, 6322, -9884, 5751, 0, -4950, 4130, 0, -3958, 0, 3258, 2616,
|
||||
-1897, -2117, 2354, 0, 0, -1423, -1117, -1571, -1739, 0, -4421, 0, 0, 0, 0, 1165, 0, 0, 8752 };
|
||||
double sr = 0;
|
||||
for (int t = 0; t < 60; t++) {
|
||||
sr += kr[t] * CS(kd[t] * d + km[t] * m + km1[t] * m1 + kf[t] * f);
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the azimuth, elevation and zodiac in the moon object.
|
||||
*/
|
||||
private void setAzimuthElevationZodiac(double julianDate, double latitude, double longitude, Moon moon) {
|
||||
double lat = latitude * SunCalc.DEG2RAD;
|
||||
double lon = longitude * SunCalc.DEG2RAD;
|
||||
|
||||
double gmst = toGMST(julianDate);
|
||||
double lmst = toLMST(gmst, lon) * 15. * SunCalc.DEG2RAD;
|
||||
|
||||
double d = julianDate - 2447891.5;
|
||||
double anomalyMean = 360 * SunCalc.DEG2RAD / 365.242191 * d + 4.87650757829735 - 4.935239984568769;
|
||||
double nu = anomalyMean + 360.0 * SunCalc.DEG2RAD / Math.PI * 0.016713 * Math.sin(anomalyMean);
|
||||
double sunLon = mod2Pi(nu + 4.935239984568769);
|
||||
|
||||
double l0 = 318.351648 * SunCalc.DEG2RAD;
|
||||
double p0 = 36.340410 * SunCalc.DEG2RAD;
|
||||
double n0 = 318.510107 * SunCalc.DEG2RAD;
|
||||
double i = 5.145396 * SunCalc.DEG2RAD;
|
||||
double l = 13.1763966 * SunCalc.DEG2RAD * d + l0;
|
||||
double mMoon = l - 0.1114041 * SunCalc.DEG2RAD * d - p0;
|
||||
double n = n0 - 0.0529539 * SunCalc.DEG2RAD * d;
|
||||
double c = l - sunLon;
|
||||
double ev = 1.2739 * SunCalc.DEG2RAD * Math.sin(2 * c - mMoon);
|
||||
double ae = 0.1858 * SunCalc.DEG2RAD * Math.sin(anomalyMean);
|
||||
double a3 = 0.37 * SunCalc.DEG2RAD * Math.sin(anomalyMean);
|
||||
double mMoon2 = mMoon + ev - ae - a3;
|
||||
double ec = 6.2886 * SunCalc.DEG2RAD * Math.sin(mMoon2);
|
||||
double a4 = 0.214 * SunCalc.DEG2RAD * Math.sin(2 * mMoon2);
|
||||
double l2 = l + ev + ec - ae + a4;
|
||||
double v = 0.6583 * SunCalc.DEG2RAD * Math.sin(2 * (l2 - sunLon));
|
||||
double l3 = l2 + v;
|
||||
double n2 = n - 0.16 * SunCalc.DEG2RAD * Math.sin(anomalyMean);
|
||||
|
||||
double moonLon = mod2Pi(n2 + Math.atan2(Math.sin(l3 - n2) * Math.cos(i), Math.cos(l3 - n2)));
|
||||
double moonLat = Math.asin(Math.sin(l3 - n2) * Math.sin(i));
|
||||
|
||||
double raDec[] = ecl2Equ(moonLat, moonLon, julianDate);
|
||||
|
||||
double distance = (1 - 0.00301401) / (1 + 0.054900 * Math.cos(mMoon2 + ec)) * 384401;
|
||||
|
||||
double raDecTopo[] = geoEqu2TopoEqu(raDec, distance, lat, lmst);
|
||||
double azAlt[] = equ2AzAlt(raDecTopo[0], raDecTopo[1], lat, lmst);
|
||||
|
||||
Position position = moon.getPosition();
|
||||
position.setAzimuth(azAlt[0] * SunCalc.RAD2DEG);
|
||||
position.setElevation(azAlt[1] * SunCalc.RAD2DEG + refraction(azAlt[1]));
|
||||
|
||||
// zodiac
|
||||
double idxd = Math.floor(moonLon * SunCalc.RAD2DEG / 30);
|
||||
int idx = 0;
|
||||
if (idxd < 0) {
|
||||
idx = (int) (Math.ceil(idxd));
|
||||
} else {
|
||||
idx = (int) (Math.floor(idxd));
|
||||
}
|
||||
|
||||
if (idx >= 0 || idx <= ZodiacSign.values().length) {
|
||||
moon.setZodiac(new Zodiac(ZodiacSign.values()[idx]));
|
||||
}
|
||||
}
|
||||
|
||||
private double mod2Pi(double x) {
|
||||
return (mod(x, 2. * Math.PI));
|
||||
}
|
||||
|
||||
private double mod(double a, double b) {
|
||||
return (a - Math.floor(a / b) * b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform equatorial coordinates (ra/dec) to horizonal coordinates
|
||||
* (azimuth/altitude).
|
||||
*/
|
||||
private double[] equ2AzAlt(double ra, double dec, double geolat, double lmst) {
|
||||
double cosdec = Math.cos(dec);
|
||||
double sindec = Math.sin(dec);
|
||||
double lha = lmst - ra;
|
||||
double coslha = Math.cos(lha);
|
||||
double sinlha = Math.sin(lha);
|
||||
double coslat = Math.cos(geolat);
|
||||
double sinlat = Math.sin(geolat);
|
||||
|
||||
double n = -cosdec * sinlha;
|
||||
double d = sindec * coslat - cosdec * coslha * sinlat;
|
||||
double az = mod2Pi(Math.atan2(n, d));
|
||||
double alt = Math.asin(sindec * sinlat + cosdec * coslha * coslat);
|
||||
|
||||
return new double[] { az, alt };
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform ecliptical coordinates (lon/lat) to equatorial coordinates
|
||||
* (ra/dec)
|
||||
*/
|
||||
private double[] ecl2Equ(double lat, double lon, double jd) {
|
||||
double t = (jd - 2451545.0) / 36525.0;
|
||||
double eps = (23. + (26 + 21.45 / 60.) / 60. + t * (-46.815 + t * (-0.0006 + t * 0.00181)) / 3600.)
|
||||
* SunCalc.DEG2RAD;
|
||||
double coseps = Math.cos(eps);
|
||||
double sineps = Math.sin(eps);
|
||||
|
||||
double sinlon = Math.sin(lon);
|
||||
double ra = mod2Pi(Math.atan2((sinlon * coseps - Math.tan(lat) * sineps), Math.cos(lon)));
|
||||
double dec = Math.asin(Math.sin(lat) * coseps + Math.cos(lat) * sineps * sinlon);
|
||||
|
||||
return new double[] { ra, dec };
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform geocentric equatorial coordinates (rA/dec) to topocentric
|
||||
* equatorial coordinates.
|
||||
*/
|
||||
private double[] geoEqu2TopoEqu(double[] raDec, double distance, double observerLat, double lmst) {
|
||||
double cosdec = Math.cos(raDec[1]);
|
||||
double sindec = Math.sin(raDec[1]);
|
||||
double coslst = Math.cos(lmst);
|
||||
double sinlst = Math.sin(lmst);
|
||||
double coslat = Math.cos(observerLat);
|
||||
double sinlat = Math.sin(observerLat);
|
||||
double rho = getCenterDistance(observerLat);
|
||||
|
||||
double x = distance * cosdec * Math.cos(raDec[0]) - rho * coslat * coslst;
|
||||
double y = distance * cosdec * Math.sin(raDec[0]) - rho * coslat * sinlst;
|
||||
double z = distance * sindec - rho * sinlat;
|
||||
|
||||
double distanceTopocentric = Math.sqrt(x * x + y * y + z * z);
|
||||
double raTopo = mod2Pi(Math.atan2(y, x));
|
||||
double decTopo = Math.asin(z / distanceTopocentric);
|
||||
|
||||
return new double[] { raTopo, decTopo };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert julian date to greenwich mean sidereal time.
|
||||
*/
|
||||
private double toGMST(double jd) {
|
||||
double ut = (jd - 0.5 - Math.floor(jd - 0.5)) * 24.;
|
||||
double jdMod = Math.floor(jd - 0.5) + 0.5;
|
||||
double t = (jdMod - 2451545.0) / 36525.0;
|
||||
double t0 = 6.697374558 + t * (2400.051336 + t * 0.000025862);
|
||||
return (mod(t0 + ut * 1.002737909, 24.));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert greenwich mean sidereal time to local mean sidereal time.
|
||||
*/
|
||||
private double toLMST(double gmst, double lon) {
|
||||
return mod(gmst + SunCalc.RAD2DEG * lon / 15., 24.);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns geocentric distance from earth center.
|
||||
*/
|
||||
private double getCenterDistance(double lat) {
|
||||
double co = Math.cos(lat);
|
||||
double si = Math.sin(lat);
|
||||
double fl = 1.0 - 1.0 / 298.257223563;
|
||||
fl = fl * fl;
|
||||
si = si * si;
|
||||
double u = 1.0 / Math.sqrt(co * co + fl * si);
|
||||
double a = 6378.137 * u;
|
||||
double b = 6378.137 * fl * u;
|
||||
return Math.sqrt(a * a * co * co + b * b * si);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns altitude increase in altitude in degrees. Rough refraction
|
||||
* formula using standard atmosphere: 1015 mbar and 10°C.
|
||||
*/
|
||||
private double refraction(double alt) {
|
||||
int pressure = 1015;
|
||||
int temperature = 10;
|
||||
double altdeg = alt * SunCalc.RAD2DEG;
|
||||
|
||||
if (altdeg < -2 || altdeg >= 90) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (altdeg > 15) {
|
||||
return 0.00452 * pressure / ((273 + temperature) * Math.tan(alt));
|
||||
}
|
||||
|
||||
double y = alt;
|
||||
double d = 0.0;
|
||||
double p = (pressure - 80.0) / 930.0;
|
||||
double q = 0.0048 * (temperature - 10.0);
|
||||
double y0 = y;
|
||||
double d0 = d;
|
||||
double n = 0.0;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
n = y + (7.31 / (y + 4.4));
|
||||
n = 1.0 / Math.tan(n * SunCalc.DEG2RAD);
|
||||
d = n * p / (60.0 + q * (n + 39.0));
|
||||
n = y - y0;
|
||||
y0 = d - d0 - n;
|
||||
n = ((n != 0.0) && (y0 != 0.0)) ? y - n * (alt + d - y) / y0 : alt + d;
|
||||
y0 = y;
|
||||
d0 = d;
|
||||
y = n;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.openhab.binding.astro.internal.model.Season;
|
||||
import org.openhab.binding.astro.internal.model.SeasonName;
|
||||
import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
|
||||
/**
|
||||
* Calculates the seasons of the year.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @see based on the calculations of http://stellafane.org/misc/equinox.html
|
||||
*/
|
||||
public class SeasonCalc {
|
||||
private int currentYear;
|
||||
private Season currentSeason;
|
||||
|
||||
/**
|
||||
* Returns the seasons of the year of the specified calendar.
|
||||
*/
|
||||
public Season getSeason(Calendar calendar, double latitude, boolean useMeteorologicalSeason) {
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
boolean isSouthernHemisphere = latitude < 0.0;
|
||||
Season season = currentSeason;
|
||||
if (currentYear != year) {
|
||||
season = new Season();
|
||||
if (!isSouthernHemisphere) {
|
||||
season.setSpring(calcEquiSol(0, year));
|
||||
season.setSummer(calcEquiSol(1, year));
|
||||
season.setAutumn(calcEquiSol(2, year));
|
||||
season.setWinter(calcEquiSol(3, year));
|
||||
} else {
|
||||
season.setSpring(calcEquiSol(2, year));
|
||||
season.setSummer(calcEquiSol(3, year));
|
||||
season.setAutumn(calcEquiSol(0, year));
|
||||
season.setWinter(calcEquiSol(1, year));
|
||||
}
|
||||
currentSeason = season;
|
||||
currentYear = year;
|
||||
}
|
||||
|
||||
if (useMeteorologicalSeason) {
|
||||
atMidnightOfFirstMonthDay(season.getSpring());
|
||||
atMidnightOfFirstMonthDay(season.getSummer());
|
||||
atMidnightOfFirstMonthDay(season.getAutumn());
|
||||
atMidnightOfFirstMonthDay(season.getWinter());
|
||||
}
|
||||
|
||||
season.setName(!isSouthernHemisphere ? getCurrentSeasonNameNorthern(calendar)
|
||||
: getCurrentSeasonNameSouthern(calendar));
|
||||
return season;
|
||||
}
|
||||
|
||||
private void atMidnightOfFirstMonthDay(Calendar calendar) {
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 1);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current season name for the northern hemisphere.
|
||||
*/
|
||||
private SeasonName getCurrentSeasonNameNorthern(Calendar calendar) {
|
||||
long currentMillis = calendar.getTimeInMillis();
|
||||
if (currentMillis < currentSeason.getSpring().getTimeInMillis()
|
||||
|| currentMillis >= currentSeason.getWinter().getTimeInMillis()) {
|
||||
return SeasonName.WINTER;
|
||||
} else if (currentMillis >= currentSeason.getSpring().getTimeInMillis()
|
||||
&& currentMillis < currentSeason.getSummer().getTimeInMillis()) {
|
||||
return SeasonName.SPRING;
|
||||
} else if (currentMillis >= currentSeason.getSummer().getTimeInMillis()
|
||||
&& currentMillis < currentSeason.getAutumn().getTimeInMillis()) {
|
||||
return SeasonName.SUMMER;
|
||||
} else if (currentMillis >= currentSeason.getAutumn().getTimeInMillis()
|
||||
&& currentMillis < currentSeason.getWinter().getTimeInMillis()) {
|
||||
return SeasonName.AUTUMN;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current season name for the southern hemisphere.
|
||||
*/
|
||||
private SeasonName getCurrentSeasonNameSouthern(Calendar calendar) {
|
||||
long currentMillis = calendar.getTimeInMillis();
|
||||
if (currentMillis < currentSeason.getAutumn().getTimeInMillis()
|
||||
|| currentMillis >= currentSeason.getSummer().getTimeInMillis()) {
|
||||
return SeasonName.SUMMER;
|
||||
} else if (currentMillis >= currentSeason.getAutumn().getTimeInMillis()
|
||||
&& currentMillis < currentSeason.getWinter().getTimeInMillis()) {
|
||||
return SeasonName.AUTUMN;
|
||||
} else if (currentMillis >= currentSeason.getWinter().getTimeInMillis()
|
||||
&& currentMillis < currentSeason.getSpring().getTimeInMillis()) {
|
||||
return SeasonName.WINTER;
|
||||
} else if (currentMillis >= currentSeason.getSpring().getTimeInMillis()
|
||||
&& currentMillis < currentSeason.getSummer().getTimeInMillis()) {
|
||||
return SeasonName.SPRING;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the date of the season.
|
||||
*/
|
||||
private Calendar calcEquiSol(int season, int year) {
|
||||
double estimate = calcInitial(season, year);
|
||||
double t = (estimate - 2451545.0) / 36525;
|
||||
double w = 35999.373 * t - 2.47;
|
||||
double dl = 1 + 0.0334 * cosDeg(w) + 0.0007 * cosDeg(2 * w);
|
||||
double s = periodic24(t);
|
||||
double julianDate = estimate + ((0.00001 * s) / dl);
|
||||
return DateTimeUtils.toCalendar(julianDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an initial guess of the Equinox or Solstice of a given year.
|
||||
*/
|
||||
private double calcInitial(int season, int year) {
|
||||
double y = (year - 2000) / 1000d;
|
||||
switch (season) {
|
||||
case 0:
|
||||
return 2451623.80984 + 365242.37404 * y + 0.05169 * Math.pow(y, 2) - 0.00411 * Math.pow(y, 3)
|
||||
- 0.00057 * Math.pow(y, 4);
|
||||
case 1:
|
||||
return 2451716.56767 + 365241.62603 * y + 0.00325 * Math.pow(y, 2) + 0.00888 * Math.pow(y, 3)
|
||||
- 0.00030 * Math.pow(y, 4);
|
||||
case 2:
|
||||
return 2451810.21715 + 365242.01767 * y - 0.11575 * Math.pow(y, 2) + 0.00337 * Math.pow(y, 3)
|
||||
+ 0.00078 * Math.pow(y, 4);
|
||||
case 3:
|
||||
return 2451900.05952 + 365242.74049 * y - 0.06223 * Math.pow(y, 2) - 0.00823 * Math.pow(y, 3)
|
||||
+ 0.00032 * Math.pow(y, 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate 24 periodic terms
|
||||
*/
|
||||
private double periodic24(double T) {
|
||||
int[] a = new int[] { 485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, 44, 29, 18, 17, 16, 14, 12, 12,
|
||||
12, 9, 8 };
|
||||
double[] b = new double[] { 324.96, 337.23, 342.08, 27.85, 73.14, 171.52, 222.54, 296.72, 243.58, 119.81,
|
||||
297.17, 21.02, 247.54, 325.15, 60.93, 155.12, 288.79, 198.04, 199.76, 95.39, 287.11, 320.81, 227.73,
|
||||
15.45 };
|
||||
double[] c = new double[] { 1934.136, 32964.467, 20.186, 445267.112, 45036.886, 22518.443, 65928.934, 3034.906,
|
||||
9037.513, 33718.147, 150.678, 2281.226, 29929.562, 31555.956, 4443.417, 67555.328, 4562.452, 62894.029,
|
||||
31436.921, 14577.848, 31931.756, 34777.259, 1222.114, 16859.074 };
|
||||
|
||||
double result = 0;
|
||||
for (int i = 0; i < 24; i++) {
|
||||
result += a[i] * cosDeg(b[i] + (c[i] * T));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cosinus of a degree value.
|
||||
*/
|
||||
private double cosDeg(double deg) {
|
||||
return Math.cos(deg * Math.PI / 180);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.openhab.binding.astro.internal.model.Eclipse;
|
||||
import org.openhab.binding.astro.internal.model.EclipseType;
|
||||
import org.openhab.binding.astro.internal.model.Position;
|
||||
import org.openhab.binding.astro.internal.model.Radiation;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
import org.openhab.binding.astro.internal.model.Sun;
|
||||
import org.openhab.binding.astro.internal.model.SunPhaseName;
|
||||
import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
|
||||
/**
|
||||
* Calculates the SunPosition (azimuth, elevation) and Sun data.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
* @see based on the calculations of http://www.suncalc.net
|
||||
*/
|
||||
public class SunCalc {
|
||||
private static final double J2000 = 2451545.0;
|
||||
private static final double SC = 1367; // Solar constant in W/m²
|
||||
public static final double DEG2RAD = Math.PI / 180;
|
||||
public static final double RAD2DEG = 180. / Math.PI;
|
||||
|
||||
private static final double M0 = 357.5291 * DEG2RAD;
|
||||
private static final double M1 = 0.98560028 * DEG2RAD;
|
||||
private static final double J0 = 0.0009;
|
||||
private static final double J1 = 0.0053;
|
||||
private static final double J2 = -0.0069;
|
||||
private static final double C1 = 1.9148 * DEG2RAD;
|
||||
private static final double C2 = 0.0200 * DEG2RAD;
|
||||
private static final double C3 = 0.0003 * DEG2RAD;
|
||||
private static final double P = 102.9372 * DEG2RAD;
|
||||
private static final double E = 23.45 * DEG2RAD;
|
||||
private static final double TH0 = 280.1600 * DEG2RAD;
|
||||
private static final double TH1 = 360.9856235 * DEG2RAD;
|
||||
private static final double SUN_ANGLE = -0.83;
|
||||
private static final double SUN_DIAMETER = 0.53 * DEG2RAD; // sun diameter
|
||||
private static final double H0 = SUN_ANGLE * DEG2RAD;
|
||||
private static final double H1 = -6.0 * DEG2RAD; // nautical twilight angle
|
||||
private static final double H2 = -12.0 * DEG2RAD; // astronomical twilight
|
||||
// angle
|
||||
private static final double H3 = -18.0 * DEG2RAD; // darkness angle
|
||||
private static final double MINUTES_PER_DAY = 60 * 24;
|
||||
private static final int CURVE_TIME_INTERVAL = 20; // 20 minutes
|
||||
private static final double JD_ONE_MINUTE_FRACTION = 1.0 / 60 / 24;
|
||||
|
||||
/**
|
||||
* Calculates the sun position (azimuth and elevation).
|
||||
*/
|
||||
public void setPositionalInfo(Calendar calendar, double latitude, double longitude, Double altitude, Sun sun) {
|
||||
double lw = -longitude * DEG2RAD;
|
||||
double phi = latitude * DEG2RAD;
|
||||
|
||||
double j = DateTimeUtils.dateToJulianDate(calendar);
|
||||
double m = getSolarMeanAnomaly(j);
|
||||
double c = getEquationOfCenter(m);
|
||||
double lsun = getEclipticLongitude(m, c);
|
||||
double d = getSunDeclination(lsun);
|
||||
double a = getRightAscension(lsun);
|
||||
double th = getSiderealTime(j, lw);
|
||||
|
||||
double azimuth = getAzimuth(th, a, phi, d) / DEG2RAD;
|
||||
double elevation = getElevation(th, a, phi, d) / DEG2RAD;
|
||||
double shadeLength = getShadeLength(elevation);
|
||||
|
||||
Position position = sun.getPosition();
|
||||
position.setAzimuth(azimuth + 180);
|
||||
position.setElevation(elevation);
|
||||
position.setShadeLength(shadeLength);
|
||||
|
||||
setRadiationInfo(calendar, elevation, altitude, sun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates sun radiation data.
|
||||
*/
|
||||
public void setRadiationInfo(Calendar calendar, double elevation, Double altitude, Sun sun) {
|
||||
double sinAlpha = Math.sin(DEG2RAD * elevation);
|
||||
|
||||
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
|
||||
int daysInYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
|
||||
|
||||
// Direct Solar Radiation (in W/m²) at the atmosphere entry
|
||||
// At sunrise/sunset - calculations limits are reached
|
||||
double rOut = (elevation > 3) ? SC * (0.034 * Math.cos(DEG2RAD * (360 * dayOfYear / daysInYear)) + 1) : 0;
|
||||
double altitudeRatio = (altitude != null) ? 1 / Math.pow((1 - (6.5 / 288) * (altitude / 1000.0)), 5.256) : 1;
|
||||
double m = (Math.sqrt(1229 + Math.pow(614 * sinAlpha, 2)) - 614 * sinAlpha) * altitudeRatio;
|
||||
|
||||
// Direct radiation after atmospheric layer
|
||||
// 0.6 = Coefficient de transmissivité
|
||||
double rDir = rOut * Math.pow(0.6, m) * sinAlpha;
|
||||
|
||||
// Diffuse Radiation
|
||||
double rDiff = rOut * (0.271 - 0.294 * Math.pow(0.6, m)) * sinAlpha;
|
||||
double rTot = rDir + rDiff;
|
||||
|
||||
Radiation radiation = sun.getRadiation();
|
||||
radiation.setDirect(rDir);
|
||||
radiation.setDiffuse(rDiff);
|
||||
radiation.setTotal(rTot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the sun is up all day (no rise and set).
|
||||
*/
|
||||
private boolean isSunUpAllDay(Calendar calendar, double latitude, double longitude, Double altitude) {
|
||||
Calendar cal = DateTimeUtils.truncateToMidnight(calendar);
|
||||
Sun sun = new Sun();
|
||||
for (int minutes = 0; minutes <= MINUTES_PER_DAY; minutes += CURVE_TIME_INTERVAL) {
|
||||
setPositionalInfo(cal, latitude, longitude, altitude, sun);
|
||||
if (sun.getPosition().getElevationAsDouble() < SUN_ANGLE) {
|
||||
return false;
|
||||
}
|
||||
cal.add(Calendar.MINUTE, CURVE_TIME_INTERVAL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates all sun rise and sets at the specified coordinates.
|
||||
*/
|
||||
public Sun getSunInfo(Calendar calendar, double latitude, double longitude, Double altitude,
|
||||
boolean useMeteorologicalSeason) {
|
||||
return getSunInfo(calendar, latitude, longitude, altitude, false, useMeteorologicalSeason);
|
||||
}
|
||||
|
||||
private Sun getSunInfo(Calendar calendar, double latitude, double longitude, Double altitude, boolean onlyAstro,
|
||||
boolean useMeteorologicalSeason) {
|
||||
double lw = -longitude * DEG2RAD;
|
||||
double phi = latitude * DEG2RAD;
|
||||
double j = DateTimeUtils.midnightDateToJulianDate(calendar) + 0.5;
|
||||
double n = getJulianCycle(j, lw);
|
||||
double js = getApproxSolarTransit(0, lw, n);
|
||||
double m = getSolarMeanAnomaly(js);
|
||||
double c = getEquationOfCenter(m);
|
||||
double lsun = getEclipticLongitude(m, c);
|
||||
double d = getSunDeclination(lsun);
|
||||
double jtransit = getSolarTransit(js, m, lsun);
|
||||
double w0 = getHourAngle(H0, phi, d);
|
||||
double w1 = getHourAngle(H0 + SUN_DIAMETER, phi, d);
|
||||
double jset = getSunsetJulianDate(w0, m, lsun, lw, n);
|
||||
double jsetstart = getSunsetJulianDate(w1, m, lsun, lw, n);
|
||||
double jrise = getSunriseJulianDate(jtransit, jset);
|
||||
double jriseend = getSunriseJulianDate(jtransit, jsetstart);
|
||||
double w2 = getHourAngle(H1, phi, d);
|
||||
double jnau = getSunsetJulianDate(w2, m, lsun, lw, n);
|
||||
double jciv2 = getSunriseJulianDate(jtransit, jnau);
|
||||
|
||||
double w3 = getHourAngle(H2, phi, d);
|
||||
double w4 = getHourAngle(H3, phi, d);
|
||||
double jastro = getSunsetJulianDate(w3, m, lsun, lw, n);
|
||||
double jdark = getSunsetJulianDate(w4, m, lsun, lw, n);
|
||||
double jnau2 = getSunriseJulianDate(jtransit, jastro);
|
||||
double jastro2 = getSunriseJulianDate(jtransit, jdark);
|
||||
|
||||
Sun sun = new Sun();
|
||||
sun.setAstroDawn(new Range(DateTimeUtils.toCalendar(jastro2), DateTimeUtils.toCalendar(jnau2)));
|
||||
sun.setAstroDusk(new Range(DateTimeUtils.toCalendar(jastro), DateTimeUtils.toCalendar(jdark)));
|
||||
|
||||
if (onlyAstro) {
|
||||
return sun;
|
||||
}
|
||||
|
||||
sun.setNoon(new Range(DateTimeUtils.toCalendar(jtransit),
|
||||
DateTimeUtils.toCalendar(jtransit + JD_ONE_MINUTE_FRACTION)));
|
||||
sun.setRise(new Range(DateTimeUtils.toCalendar(jrise), DateTimeUtils.toCalendar(jriseend)));
|
||||
sun.setSet(new Range(DateTimeUtils.toCalendar(jsetstart), DateTimeUtils.toCalendar(jset)));
|
||||
|
||||
sun.setCivilDawn(new Range(DateTimeUtils.toCalendar(jciv2), DateTimeUtils.toCalendar(jrise)));
|
||||
sun.setCivilDusk(new Range(DateTimeUtils.toCalendar(jset), DateTimeUtils.toCalendar(jnau)));
|
||||
|
||||
sun.setNauticDawn(new Range(DateTimeUtils.toCalendar(jnau2), DateTimeUtils.toCalendar(jciv2)));
|
||||
sun.setNauticDusk(new Range(DateTimeUtils.toCalendar(jnau), DateTimeUtils.toCalendar(jastro)));
|
||||
|
||||
boolean isSunUpAllDay = isSunUpAllDay(calendar, latitude, longitude, altitude);
|
||||
|
||||
// daylight
|
||||
Range daylightRange = new Range();
|
||||
if (sun.getRise().getStart() == null && sun.getRise().getEnd() == null) {
|
||||
if (isSunUpAllDay) {
|
||||
daylightRange = new Range(DateTimeUtils.truncateToMidnight(calendar),
|
||||
DateTimeUtils.truncateToMidnight(addDays(calendar, 1)));
|
||||
}
|
||||
} else {
|
||||
daylightRange = new Range(sun.getRise().getEnd(), sun.getSet().getStart());
|
||||
}
|
||||
sun.setDaylight(daylightRange);
|
||||
|
||||
// morning night
|
||||
Sun sunYesterday = getSunInfo(addDays(calendar, -1), latitude, longitude, altitude, true,
|
||||
useMeteorologicalSeason);
|
||||
Range morningNightRange = null;
|
||||
if (sunYesterday.getAstroDusk().getEnd() != null
|
||||
&& DateTimeUtils.isSameDay(sunYesterday.getAstroDusk().getEnd(), calendar)) {
|
||||
morningNightRange = new Range(sunYesterday.getAstroDusk().getEnd(), sun.getAstroDawn().getStart());
|
||||
} else if (isSunUpAllDay || sun.getAstroDawn().getStart() == null) {
|
||||
morningNightRange = new Range();
|
||||
} else {
|
||||
morningNightRange = new Range(DateTimeUtils.truncateToMidnight(calendar), sun.getAstroDawn().getStart());
|
||||
}
|
||||
sun.setMorningNight(morningNightRange);
|
||||
|
||||
// evening night
|
||||
Range eveningNightRange = null;
|
||||
if (sun.getAstroDusk().getEnd() != null && DateTimeUtils.isSameDay(sun.getAstroDusk().getEnd(), calendar)) {
|
||||
eveningNightRange = new Range(sun.getAstroDusk().getEnd(),
|
||||
DateTimeUtils.truncateToMidnight(addDays(calendar, 1)));
|
||||
} else {
|
||||
eveningNightRange = new Range();
|
||||
}
|
||||
sun.setEveningNight(eveningNightRange);
|
||||
|
||||
// night
|
||||
if (isSunUpAllDay) {
|
||||
sun.setNight(new Range());
|
||||
} else {
|
||||
Sun sunTomorrow = getSunInfo(addDays(calendar, 1), latitude, longitude, altitude, true,
|
||||
useMeteorologicalSeason);
|
||||
sun.setNight(new Range(sun.getAstroDusk().getEnd(), sunTomorrow.getAstroDawn().getStart()));
|
||||
}
|
||||
|
||||
// eclipse
|
||||
Eclipse eclipse = sun.getEclipse();
|
||||
MoonCalc mc = new MoonCalc();
|
||||
|
||||
eclipse.getKinds().forEach(eclipseKind -> {
|
||||
double jdate = mc.getEclipse(calendar, EclipseType.SUN, j, eclipseKind);
|
||||
eclipse.set(eclipseKind, DateTimeUtils.toCalendar(jdate), new Position());
|
||||
});
|
||||
|
||||
SunZodiacCalc zodiacCalc = new SunZodiacCalc();
|
||||
zodiacCalc.getZodiac(calendar).ifPresent(z -> sun.setZodiac(z));
|
||||
|
||||
SeasonCalc seasonCalc = new SeasonCalc();
|
||||
sun.setSeason(seasonCalc.getSeason(calendar, latitude, useMeteorologicalSeason));
|
||||
|
||||
// phase
|
||||
for (Entry<SunPhaseName, Range> rangeEntry : sun.getAllRanges().entrySet()) {
|
||||
SunPhaseName entryPhase = rangeEntry.getKey();
|
||||
if (rangeEntry.getValue().matches(Calendar.getInstance())) {
|
||||
if (entryPhase == SunPhaseName.MORNING_NIGHT || entryPhase == SunPhaseName.EVENING_NIGHT) {
|
||||
sun.getPhase().setName(SunPhaseName.NIGHT);
|
||||
} else {
|
||||
sun.getPhase().setName(entryPhase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified days to the calendar.
|
||||
*/
|
||||
private Calendar addDays(Calendar calendar, int days) {
|
||||
Calendar cal = (Calendar) calendar.clone();
|
||||
cal.add(Calendar.DAY_OF_MONTH, days);
|
||||
return cal;
|
||||
}
|
||||
|
||||
// all the following methods are translated to java based on the javascript
|
||||
// calculations of http://www.suncalc.net
|
||||
private double getJulianCycle(double j, double lw) {
|
||||
return Math.round(j - J2000 - J0 - lw / (2 * Math.PI));
|
||||
}
|
||||
|
||||
private double getApproxSolarTransit(double ht, double lw, double n) {
|
||||
return J2000 + J0 + (ht + lw) / (2 * Math.PI) + n;
|
||||
}
|
||||
|
||||
private double getSolarMeanAnomaly(double js) {
|
||||
return M0 + M1 * (js - J2000);
|
||||
}
|
||||
|
||||
private double getEquationOfCenter(double m) {
|
||||
return C1 * Math.sin(m) + C2 * Math.sin(2 * m) + C3 * Math.sin(3 * m);
|
||||
}
|
||||
|
||||
private double getEclipticLongitude(double m, double c) {
|
||||
return m + P + c + Math.PI;
|
||||
}
|
||||
|
||||
private double getSolarTransit(double js, double m, double lsun) {
|
||||
return js + (J1 * Math.sin(m)) + (J2 * Math.sin(2 * lsun));
|
||||
}
|
||||
|
||||
private double getSunDeclination(double lsun) {
|
||||
return Math.asin(Math.sin(lsun) * Math.sin(E));
|
||||
}
|
||||
|
||||
private double getRightAscension(double lsun) {
|
||||
return Math.atan2(Math.sin(lsun) * Math.cos(E), Math.cos(lsun));
|
||||
}
|
||||
|
||||
private double getSiderealTime(double j, double lw) {
|
||||
return TH0 + TH1 * (j - J2000) - lw;
|
||||
}
|
||||
|
||||
private double getAzimuth(double th, double a, double phi, double d) {
|
||||
double h = th - a;
|
||||
return Math.atan2(Math.sin(h), Math.cos(h) * Math.sin(phi) - Math.tan(d) * Math.cos(phi));
|
||||
}
|
||||
|
||||
private double getElevation(double th, double a, double phi, double d) {
|
||||
return Math.asin(Math.sin(phi) * Math.sin(d) + Math.cos(phi) * Math.cos(d) * Math.cos(th - a));
|
||||
}
|
||||
|
||||
private double getShadeLength(double elevation) {
|
||||
return 1 / Math.tan(elevation * DEG2RAD);
|
||||
}
|
||||
|
||||
private double getHourAngle(double h, double phi, double d) {
|
||||
return Math.acos((Math.sin(h) - Math.sin(phi) * Math.sin(d)) / (Math.cos(phi) * Math.cos(d)));
|
||||
}
|
||||
|
||||
private double getSunsetJulianDate(double w0, double m, double Lsun, double lw, double n) {
|
||||
return getSolarTransit(getApproxSolarTransit(w0, lw, n), m, Lsun);
|
||||
}
|
||||
|
||||
private double getSunriseJulianDate(double jtransit, double jset) {
|
||||
return jtransit - (jset - jtransit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.model.SunZodiac;
|
||||
import org.openhab.binding.astro.internal.model.ZodiacSign;
|
||||
import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
|
||||
/**
|
||||
* Calculates the sign and range of the current zodiac.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SunZodiacCalc {
|
||||
private Map<Integer, List<SunZodiac>> zodiacsByYear = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Returns the zodiac for the specified calendar.
|
||||
*/
|
||||
public Optional<SunZodiac> getZodiac(Calendar calendar) {
|
||||
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
List<SunZodiac> zodiacs;
|
||||
|
||||
if (zodiacsByYear.containsKey(year)) {
|
||||
zodiacs = zodiacsByYear.get(year);
|
||||
} else {
|
||||
zodiacs = calculateZodiacs(year);
|
||||
zodiacsByYear.clear();
|
||||
zodiacsByYear.put(year, zodiacs);
|
||||
}
|
||||
|
||||
return zodiacs.stream().filter(z -> z.isValid(calendar)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the zodiacs for the current year.
|
||||
*/
|
||||
private List<SunZodiac> calculateZodiacs(int year) {
|
||||
List<SunZodiac> zodiacs = new ArrayList<>();
|
||||
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.ARIES,
|
||||
DateTimeUtils.getRange(year, Calendar.MARCH, 21, year, Calendar.APRIL, 19)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.TAURUS,
|
||||
DateTimeUtils.getRange(year, Calendar.APRIL, 20, year, Calendar.MAY, 20)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.GEMINI,
|
||||
DateTimeUtils.getRange(year, Calendar.MAY, 21, year, Calendar.JUNE, 20)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.CANCER,
|
||||
DateTimeUtils.getRange(year, Calendar.JUNE, 21, year, Calendar.JULY, 22)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.LEO,
|
||||
DateTimeUtils.getRange(year, Calendar.JULY, 23, year, Calendar.AUGUST, 22)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.VIRGO,
|
||||
DateTimeUtils.getRange(year, Calendar.AUGUST, 23, year, Calendar.SEPTEMBER, 22)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.LIBRA,
|
||||
DateTimeUtils.getRange(year, Calendar.SEPTEMBER, 23, year, Calendar.OCTOBER, 22)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.SCORPIO,
|
||||
DateTimeUtils.getRange(year, Calendar.OCTOBER, 23, year, Calendar.NOVEMBER, 21)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.SAGITTARIUS,
|
||||
DateTimeUtils.getRange(year, Calendar.NOVEMBER, 22, year, Calendar.DECEMBER, 21)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.CAPRICORN,
|
||||
DateTimeUtils.getRange(year, Calendar.DECEMBER, 22, year + 1, Calendar.JANUARY, 19)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.CAPRICORN,
|
||||
DateTimeUtils.getRange(year - 1, Calendar.DECEMBER, 22, year, Calendar.JANUARY, 19)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.AQUARIUS,
|
||||
DateTimeUtils.getRange(year, Calendar.JANUARY, 20, year, Calendar.FEBRUARY, 18)));
|
||||
zodiacs.add(new SunZodiac(ZodiacSign.PISCES,
|
||||
DateTimeUtils.getRange(year, Calendar.FEBRUARY, 19, year, Calendar.MARCH, 20)));
|
||||
|
||||
return zodiacs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Channel configuration from Eclipse SmartHome.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AstroChannelConfig {
|
||||
public int offset = 0;
|
||||
public @Nullable String earliest;
|
||||
public @Nullable String latest;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Thing configuration from Eclipse SmartHome.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AstroThingConfig {
|
||||
public @Nullable String geolocation;
|
||||
public @Nullable Double altitude;
|
||||
public @Nullable Double latitude;
|
||||
public @Nullable Double longitude;
|
||||
public boolean useMeteorologicalSeason;
|
||||
public int interval = 300;
|
||||
|
||||
/**
|
||||
* Splits the geolocation into latitude and longitude.
|
||||
*/
|
||||
public void parseGeoLocation() {
|
||||
if (geolocation != null) {
|
||||
String[] geoParts = geolocation.split(",");
|
||||
if (geoParts.length == 2) {
|
||||
latitude = toDouble(geoParts[0]);
|
||||
longitude = toDouble(geoParts[1]);
|
||||
} else if (geoParts.length == 3) {
|
||||
latitude = toDouble(geoParts[0]);
|
||||
longitude = toDouble(geoParts[1]);
|
||||
altitude = toDouble(geoParts[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Double toDouble(String value) {
|
||||
try {
|
||||
return Double.parseDouble(value.trim());
|
||||
} catch (NumberFormatException ex) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.LocationProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AstroDiscoveryService} creates things based on the configured location.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial Contribution
|
||||
* @author Stefan Triller - Use configured location
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.astro")
|
||||
public class AstroDiscoveryService extends AbstractDiscoveryService {
|
||||
private static final int DISCOVER_TIMEOUT_SECONDS = 2;
|
||||
private static final int LOCATION_CHANGED_CHECK_INTERVAL = 60;
|
||||
private static final Set<String> METEO_BASED_COUNTRIES = new HashSet<>(Arrays.asList("NZ", "AU"));
|
||||
private static final ThingUID SUN_THING = new ThingUID(THING_TYPE_SUN, LOCAL);
|
||||
private static final ThingUID MOON_THING = new ThingUID(THING_TYPE_MOON, LOCAL);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AstroDiscoveryService.class);
|
||||
|
||||
private final LocationProvider locationProvider;
|
||||
|
||||
private @Nullable ScheduledFuture<?> astroDiscoveryJob;
|
||||
private @Nullable PointType previousLocation;
|
||||
|
||||
@Activate
|
||||
public AstroDiscoveryService(final @Reference LocationProvider locationProvider,
|
||||
final @Reference LocaleProvider localeProvider, final @Reference TranslationProvider i18nProvider,
|
||||
@Nullable Map<String, @Nullable Object> configProperties) {
|
||||
super(new HashSet<>(Arrays.asList(new ThingTypeUID(BINDING_ID, "-"))), DISCOVER_TIMEOUT_SECONDS, true);
|
||||
this.locationProvider = locationProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
this.i18nProvider = i18nProvider;
|
||||
activate(configProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Starting Astro discovery scan");
|
||||
PointType location = locationProvider.getLocation();
|
||||
if (location == null) {
|
||||
logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
|
||||
return;
|
||||
}
|
||||
createResults(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
if (astroDiscoveryJob == null) {
|
||||
astroDiscoveryJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
PointType currentLocation = locationProvider.getLocation();
|
||||
if (currentLocation != null && !Objects.equals(currentLocation, previousLocation)) {
|
||||
logger.debug("Location has been changed from {} to {}: Creating new discovery results",
|
||||
previousLocation, currentLocation);
|
||||
createResults(currentLocation);
|
||||
previousLocation = currentLocation;
|
||||
}
|
||||
}, 0, LOCATION_CHANGED_CHECK_INTERVAL, TimeUnit.SECONDS);
|
||||
logger.debug("Scheduled astro location-changed job every {} seconds", LOCATION_CHANGED_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stopping Astro device background discovery");
|
||||
ScheduledFuture<?> discoveryJob = astroDiscoveryJob;
|
||||
if (discoveryJob != null) {
|
||||
discoveryJob.cancel(true);
|
||||
}
|
||||
astroDiscoveryJob = null;
|
||||
}
|
||||
|
||||
public void createResults(PointType location) {
|
||||
String propGeolocation;
|
||||
String country = localeProvider.getLocale().getCountry();
|
||||
propGeolocation = String.format("%s,%s,%s", location.getLatitude(), location.getLongitude(),
|
||||
location.getAltitude());
|
||||
thingDiscovered(DiscoveryResultBuilder.create(SUN_THING).withLabel("Local Sun")
|
||||
.withProperty("geolocation", propGeolocation)
|
||||
.withProperty("useMeteorologicalSeason", METEO_BASED_COUNTRIES.contains(country))
|
||||
.withRepresentationProperty("geolocation").build());
|
||||
thingDiscovered(DiscoveryResultBuilder.create(MOON_THING).withLabel("Local Moon")
|
||||
.withProperty("geolocation", propGeolocation).withRepresentationProperty("geolocation").build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.handler;
|
||||
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
import static org.openhab.core.thing.type.ChannelKind.TRIGGER;
|
||||
import static org.openhab.core.types.RefreshType.REFRESH;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.measure.quantity.Angle;
|
||||
|
||||
import org.apache.commons.lang.time.DateFormatUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.action.AstroActions;
|
||||
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
|
||||
import org.openhab.binding.astro.internal.config.AstroThingConfig;
|
||||
import org.openhab.binding.astro.internal.job.Job;
|
||||
import org.openhab.binding.astro.internal.job.PositionalJob;
|
||||
import org.openhab.binding.astro.internal.model.Planet;
|
||||
import org.openhab.binding.astro.internal.model.Position;
|
||||
import org.openhab.binding.astro.internal.util.PropertyUtils;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.scheduler.CronScheduler;
|
||||
import org.openhab.core.scheduler.ScheduledCompletableFuture;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Base ThingHandler for all Astro handlers.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AstroThingHandler extends BaseThingHandler {
|
||||
|
||||
private static final String DAILY_MIDNIGHT = "30 0 0 * * ? *";
|
||||
|
||||
/** Logger Instance */
|
||||
protected final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
/** Scheduler to schedule jobs */
|
||||
private final CronScheduler cronScheduler;
|
||||
|
||||
protected final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
private final Lock monitor = new ReentrantLock();
|
||||
|
||||
private final Set<ScheduledFuture<?>> scheduledFutures = new HashSet<>();
|
||||
|
||||
private int linkedPositionalChannels = 0;
|
||||
|
||||
protected AstroThingConfig thingConfig = new AstroThingConfig();
|
||||
|
||||
private @Nullable ScheduledCompletableFuture<?> dailyJob;
|
||||
|
||||
public AstroThingHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.cronScheduler = scheduler;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing thing {}", getThing().getUID());
|
||||
String thingUid = getThing().getUID().toString();
|
||||
thingConfig = getConfigAs(AstroThingConfig.class);
|
||||
boolean validConfig = true;
|
||||
String geoLocation = thingConfig.geolocation;
|
||||
if (geoLocation == null || geoLocation.trim().isEmpty()) {
|
||||
logger.error("Astro parameter geolocation is mandatory and must be configured, disabling thing '{}'",
|
||||
thingUid);
|
||||
validConfig = false;
|
||||
} else {
|
||||
thingConfig.parseGeoLocation();
|
||||
}
|
||||
|
||||
if (thingConfig.latitude == null || thingConfig.longitude == null) {
|
||||
logger.error(
|
||||
"Astro parameters geolocation could not be split into latitude and longitude, disabling thing '{}'",
|
||||
thingUid);
|
||||
validConfig = false;
|
||||
}
|
||||
if (thingConfig.interval < 1 || thingConfig.interval > 86400) {
|
||||
logger.error("Astro parameter interval must be in the range of 1-86400, disabling thing '{}'", thingUid);
|
||||
validConfig = false;
|
||||
}
|
||||
|
||||
if (validConfig) {
|
||||
logger.debug("{}", thingConfig);
|
||||
updateStatus(ONLINE);
|
||||
restartJobs();
|
||||
} else {
|
||||
updateStatus(OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||
}
|
||||
logger.debug("Thing {} initialized {}", getThing().getUID(), getThing().getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing thing {}", getThing().getUID());
|
||||
stopJobs();
|
||||
logger.debug("Thing {} disposed", getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (REFRESH == command) {
|
||||
logger.debug("Refreshing {}", channelUID);
|
||||
publishChannelIfLinked(channelUID);
|
||||
} else {
|
||||
logger.warn("The Astro-Binding is a read-only binding and can not handle commands");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates all channels of the thing and updates their states.
|
||||
*/
|
||||
public void publishPlanet() {
|
||||
Planet planet = getPlanet();
|
||||
if (planet == null) {
|
||||
return;
|
||||
}
|
||||
logger.debug("Publishing planet {} for thing {}", planet.getClass().getSimpleName(), getThing().getUID());
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
if (channel.getKind() != TRIGGER) {
|
||||
publishChannelIfLinked(channel.getUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the channel with data if it's linked.
|
||||
*/
|
||||
public void publishChannelIfLinked(ChannelUID channelUID) {
|
||||
Planet planet = getPlanet();
|
||||
if (isLinked(channelUID.getId()) && planet != null) {
|
||||
final Channel channel = getThing().getChannel(channelUID.getId());
|
||||
if (channel == null) {
|
||||
logger.error("Cannot find channel for {}", channelUID);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
|
||||
updateState(channelUID,
|
||||
PropertyUtils.getState(channelUID, config, planet, timeZoneProvider.getTimeZone()));
|
||||
} catch (Exception ex) {
|
||||
logger.error("Can't update state for channel {} : {}", channelUID, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a positional and a daily job at midnight for Astro calculation and starts it immediately too. Removes
|
||||
* already scheduled jobs first.
|
||||
*/
|
||||
private void restartJobs() {
|
||||
logger.debug("Restarting jobs for thing {}", getThing().getUID());
|
||||
monitor.lock();
|
||||
try {
|
||||
stopJobs();
|
||||
if (getThing().getStatus() == ONLINE) {
|
||||
String thingUID = getThing().getUID().toString();
|
||||
// Daily Job
|
||||
Job runnable = getDailyJob();
|
||||
dailyJob = cronScheduler.schedule(runnable, DAILY_MIDNIGHT);
|
||||
logger.debug("Scheduled {} at midnight", dailyJob);
|
||||
// Execute daily startup job immediately
|
||||
runnable.run();
|
||||
|
||||
// Repeat positional job every configured seconds
|
||||
// Use scheduleAtFixedRate to avoid time drift associated with scheduleWithFixedDelay
|
||||
if (isPositionalChannelLinked()) {
|
||||
Job positionalJob = new PositionalJob(thingUID);
|
||||
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(positionalJob, 0, thingConfig.interval,
|
||||
TimeUnit.SECONDS);
|
||||
scheduledFutures.add(future);
|
||||
logger.info("Scheduled {} every {} seconds", positionalJob, thingConfig.interval);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
monitor.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all jobs for this thing.
|
||||
*/
|
||||
private void stopJobs() {
|
||||
logger.debug("Stopping scheduled jobs for thing {}", getThing().getUID());
|
||||
monitor.lock();
|
||||
try {
|
||||
ScheduledCompletableFuture<?> job = dailyJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
}
|
||||
dailyJob = null;
|
||||
for (ScheduledFuture<?> future : scheduledFutures) {
|
||||
if (!future.isDone()) {
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
scheduledFutures.clear();
|
||||
} catch (Exception ex) {
|
||||
logger.error("{}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
monitor.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelLinked(ChannelUID channelUID) {
|
||||
linkedChannelChange(channelUID, 1);
|
||||
publishChannelIfLinked(channelUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnlinked(ChannelUID channelUID) {
|
||||
linkedChannelChange(channelUID, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts positional channels and restarts Astro jobs.
|
||||
*/
|
||||
private void linkedChannelChange(ChannelUID channelUID, int step) {
|
||||
if (Arrays.asList(getPositionalChannelIds()).contains(channelUID.getId())) {
|
||||
int oldValue = linkedPositionalChannels;
|
||||
linkedPositionalChannels += step;
|
||||
if (oldValue == 0 && linkedPositionalChannels > 0 || oldValue > 0 && linkedPositionalChannels == 0) {
|
||||
restartJobs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true}, if at least one positional channel is linked.
|
||||
*/
|
||||
private boolean isPositionalChannelLinked() {
|
||||
List<String> positionalChannels = Arrays.asList(getPositionalChannelIds());
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
String id = channel.getUID().getId();
|
||||
if (isLinked(id) && positionalChannels.contains(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event for the given channel.
|
||||
*/
|
||||
public void triggerEvent(String channelId, String event) {
|
||||
final Channel channel = getThing().getChannel(channelId);
|
||||
if (channel == null) {
|
||||
logger.warn("Event {} in thing {} does not exist, please recreate the thing", event, getThing().getUID());
|
||||
return;
|
||||
}
|
||||
triggerChannel(channel.getUID(), event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided {@link Job} to the queue (cannot be {@code null})
|
||||
*
|
||||
* @return {@code true} if the {@code job} is added to the queue, otherwise {@code false}
|
||||
*/
|
||||
public void schedule(Job job, Calendar eventAt) {
|
||||
long sleepTime;
|
||||
monitor.lock();
|
||||
try {
|
||||
tidyScheduledFutures();
|
||||
sleepTime = eventAt.getTimeInMillis() - new Date().getTime();
|
||||
ScheduledFuture<?> future = scheduler.schedule(job, sleepTime, TimeUnit.MILLISECONDS);
|
||||
scheduledFutures.add(future);
|
||||
} finally {
|
||||
monitor.unlock();
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
String formattedDate = DateFormatUtils.ISO_DATETIME_FORMAT.format(eventAt);
|
||||
logger.debug("Scheduled {} in {}ms (at {})", job, sleepTime, formattedDate);
|
||||
}
|
||||
}
|
||||
|
||||
private void tidyScheduledFutures() {
|
||||
for (Iterator<ScheduledFuture<?>> iterator = scheduledFutures.iterator(); iterator.hasNext();) {
|
||||
ScheduledFuture<?> future = iterator.next();
|
||||
if (future.isDone()) {
|
||||
logger.trace("Tidying up done future {}", future);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and publishes the daily Astro data.
|
||||
*/
|
||||
public void publishDailyInfo() {
|
||||
publishPositionalInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and publishes the interval Astro data.
|
||||
*/
|
||||
public abstract void publishPositionalInfo();
|
||||
|
||||
/**
|
||||
* Returns the {@link Planet} instance (cannot be {@code null})
|
||||
*/
|
||||
public abstract @Nullable Planet getPlanet();
|
||||
|
||||
/**
|
||||
* Returns the channelIds for positional calculation (cannot be {@code null})
|
||||
*/
|
||||
protected abstract String[] getPositionalChannelIds();
|
||||
|
||||
/**
|
||||
* Returns the daily calculation {@link Job} (cannot be {@code null})
|
||||
*/
|
||||
protected abstract Job getDailyJob();
|
||||
|
||||
public abstract @Nullable Position getPositionAt(ZonedDateTime date);
|
||||
|
||||
public @Nullable QuantityType<Angle> getAzimuth(ZonedDateTime date) {
|
||||
Position position = getPositionAt(date);
|
||||
return position != null ? position.getAzimuth() : null;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Angle> getElevation(ZonedDateTime date) {
|
||||
Position position = getPositionAt(date);
|
||||
return position != null ? position.getElevation() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singletonList(AstroActions.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.handler;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_MOON;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.calc.MoonCalc;
|
||||
import org.openhab.binding.astro.internal.job.DailyJobMoon;
|
||||
import org.openhab.binding.astro.internal.job.Job;
|
||||
import org.openhab.binding.astro.internal.model.Moon;
|
||||
import org.openhab.binding.astro.internal.model.Planet;
|
||||
import org.openhab.binding.astro.internal.model.Position;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.scheduler.CronScheduler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The MoonHandler is responsible for updating calculated moon data.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MoonHandler extends AstroThingHandler {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(Arrays.asList(THING_TYPE_MOON));
|
||||
|
||||
private final String[] positionalChannelIds = new String[] { "phase#name", "phase#age", "phase#agePercent",
|
||||
"phase#ageDegree", "phase#illumination", "position#azimuth", "position#elevation", "zodiac#sign" };
|
||||
private final MoonCalc moonCalc = new MoonCalc();
|
||||
private @NonNullByDefault({}) Moon moon;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public MoonHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider) {
|
||||
super(thing, scheduler, timeZoneProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishPositionalInfo() {
|
||||
moon = getMoonAt(ZonedDateTime.now());
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
moonCalc.setPositionalInfo(Calendar.getInstance(), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0, moon);
|
||||
|
||||
moon.getEclipse().setElevations(this, timeZoneProvider);
|
||||
|
||||
publishPlanet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Planet getPlanet() {
|
||||
return moon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
moon = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getPositionalChannelIds() {
|
||||
return positionalChannelIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Job getDailyJob() {
|
||||
return new DailyJobMoon(thing.getUID().getAsString(), this);
|
||||
}
|
||||
|
||||
private Moon getMoonAt(ZonedDateTime date) {
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
return moonCalc.getMoonInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Position getPositionAt(ZonedDateTime date) {
|
||||
Moon localMoon = getMoonAt(date);
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
moonCalc.setPositionalInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0, localMoon);
|
||||
return localMoon.getPosition();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.handler;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_SUN;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.calc.SunCalc;
|
||||
import org.openhab.binding.astro.internal.job.DailyJobSun;
|
||||
import org.openhab.binding.astro.internal.job.Job;
|
||||
import org.openhab.binding.astro.internal.model.Planet;
|
||||
import org.openhab.binding.astro.internal.model.Position;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
import org.openhab.binding.astro.internal.model.Sun;
|
||||
import org.openhab.binding.astro.internal.model.SunPhaseName;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.scheduler.CronScheduler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The SunHandler is responsible for updating calculated sun data.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SunHandler extends AstroThingHandler {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(Arrays.asList(THING_TYPE_SUN));
|
||||
|
||||
private final String[] positionalChannelIds = new String[] { "position#azimuth", "position#elevation",
|
||||
"radiation#direct", "radiation#diffuse", "radiation#total" };
|
||||
private final SunCalc sunCalc = new SunCalc();
|
||||
private @NonNullByDefault({}) Sun sun;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SunHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider) {
|
||||
super(thing, scheduler, timeZoneProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishPositionalInfo() {
|
||||
sun = getSunAt(ZonedDateTime.now());
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
Double altitude = thingConfig.altitude;
|
||||
sunCalc.setPositionalInfo(Calendar.getInstance(), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0, altitude != null ? altitude : 0, sun);
|
||||
|
||||
sun.getEclipse().setElevations(this, timeZoneProvider);
|
||||
|
||||
publishPlanet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Planet getPlanet() {
|
||||
return sun;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
sun = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getPositionalChannelIds() {
|
||||
return positionalChannelIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Job getDailyJob() {
|
||||
return new DailyJobSun(thing.getUID().getAsString(), this);
|
||||
}
|
||||
|
||||
private Sun getSunAt(ZonedDateTime date) {
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
Double altitude = thingConfig.altitude;
|
||||
return sunCalc.getSunInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0, altitude != null ? altitude : 0,
|
||||
thingConfig.useMeteorologicalSeason);
|
||||
}
|
||||
|
||||
public @Nullable ZonedDateTime getEventTime(SunPhaseName sunPhase, ZonedDateTime date, boolean begin) {
|
||||
Range eventRange = getSunAt(date).getAllRanges().get(sunPhase);
|
||||
Calendar cal = begin ? eventRange.getStart() : eventRange.getEnd();
|
||||
return ZonedDateTime.ofInstant(cal.toInstant(), date.getZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Position getPositionAt(ZonedDateTime date) {
|
||||
Sun localSun = getSunAt(date);
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
Double altitude = thingConfig.altitude;
|
||||
sunCalc.setPositionalInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0, altitude != null ? altitude : 0, localSun);
|
||||
return localSun.getPosition();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* This class contains the default methods required for different jobs
|
||||
*
|
||||
* @author Amit Kumar Mondal - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractJob implements Job {
|
||||
|
||||
private final String thingUID;
|
||||
|
||||
public AbstractJob(String thingUID) {
|
||||
this.thingUID = thingUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThingUID() {
|
||||
return thingUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the truth of an expression involving one or more parameters to the
|
||||
* calling method.
|
||||
*
|
||||
* @param expression a boolean expression
|
||||
* @param errorMessage the exception message to use if the check fails
|
||||
* @throws IllegalArgumentException if {@code expression} is false
|
||||
*/
|
||||
public static void checkArgument(boolean expression, String errorMessage) {
|
||||
if (!expression) {
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* {@link CompositeJob} comprises multiple {@link Job}s to be executed in order
|
||||
*
|
||||
* @author Markus Rathgeb - Initial contribution
|
||||
* @author Amit Kumar Mondal - Minor modifications
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class CompositeJob extends AbstractJob {
|
||||
|
||||
private final List<Job> jobs;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID thing UID
|
||||
* @param jobs the jobs to execute
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code jobs} is {@code null} or empty
|
||||
*/
|
||||
public CompositeJob(String thingUID, List<Job> jobs) {
|
||||
super(thingUID);
|
||||
|
||||
this.jobs = jobs;
|
||||
|
||||
boolean notMatched = jobs.stream().anyMatch(j -> !j.getThingUID().equals(thingUID));
|
||||
checkArgument(!notMatched, "The jobs must associate the same thing UID");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jobs.forEach(j -> {
|
||||
try {
|
||||
j.run();
|
||||
} catch (RuntimeException ex) {
|
||||
LOGGER.warn("Job execution failed.", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return jobs.stream().map(j -> j.toString()).collect(Collectors.joining(" + "));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
|
||||
import static org.openhab.binding.astro.internal.job.Job.scheduleEvent;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.binding.astro.internal.model.Eclipse;
|
||||
import org.openhab.binding.astro.internal.model.Moon;
|
||||
import org.openhab.binding.astro.internal.model.MoonPhase;
|
||||
import org.openhab.binding.astro.internal.model.Planet;
|
||||
|
||||
/**
|
||||
* Daily scheduled jobs for Moon planet
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class DailyJobMoon extends AbstractJob {
|
||||
|
||||
private final AstroThingHandler handler;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param handler the {@link AstroThingHandler} instance
|
||||
* @throws IllegalArgumentException if {@code thingUID} or {@code handler} is {@code null}
|
||||
*/
|
||||
public DailyJobMoon(String thingUID, AstroThingHandler handler) {
|
||||
super(thingUID);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.publishDailyInfo();
|
||||
String thingUID = getThingUID();
|
||||
LOGGER.debug("Scheduled Astro event-jobs for thing {}", thingUID);
|
||||
|
||||
Planet planet = handler.getPlanet();
|
||||
if (planet == null) {
|
||||
LOGGER.error("Planet not instantiated");
|
||||
return;
|
||||
}
|
||||
Moon moon = (Moon) planet;
|
||||
scheduleEvent(thingUID, handler, moon.getRise().getStart(), EVENT_START, EVENT_CHANNEL_ID_RISE, false);
|
||||
scheduleEvent(thingUID, handler, moon.getSet().getEnd(), EVENT_END, EVENT_CHANNEL_ID_SET, false);
|
||||
|
||||
MoonPhase moonPhase = moon.getPhase();
|
||||
scheduleEvent(thingUID, handler, moonPhase.getFirstQuarter(), EVENT_PHASE_FIRST_QUARTER,
|
||||
EVENT_CHANNEL_ID_MOON_PHASE, false);
|
||||
scheduleEvent(thingUID, handler, moonPhase.getThirdQuarter(), EVENT_PHASE_THIRD_QUARTER,
|
||||
EVENT_CHANNEL_ID_MOON_PHASE, false);
|
||||
scheduleEvent(thingUID, handler, moonPhase.getFull(), EVENT_PHASE_FULL, EVENT_CHANNEL_ID_MOON_PHASE, false);
|
||||
scheduleEvent(thingUID, handler, moonPhase.getNew(), EVENT_PHASE_NEW, EVENT_CHANNEL_ID_MOON_PHASE, false);
|
||||
|
||||
Eclipse eclipse = moon.getEclipse();
|
||||
eclipse.getKinds().forEach(eclipseKind -> {
|
||||
Calendar eclipseDate = eclipse.getDate(eclipseKind);
|
||||
if (eclipseDate != null) {
|
||||
scheduleEvent(thingUID, handler, eclipseDate, eclipseKind.toString(), EVENT_CHANNEL_ID_ECLIPSE, false);
|
||||
}
|
||||
});
|
||||
|
||||
scheduleEvent(thingUID, handler, moon.getPerigee().getDate(), EVENT_PERIGEE, EVENT_CHANNEL_ID_PERIGEE, false);
|
||||
scheduleEvent(thingUID, handler, moon.getApogee().getDate(), EVENT_APOGEE, EVENT_CHANNEL_ID_APOGEE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Daily job moon " + getThingUID();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
|
||||
import static org.openhab.binding.astro.internal.job.Job.*;
|
||||
import static org.openhab.binding.astro.internal.model.SunPhaseName.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.binding.astro.internal.model.Eclipse;
|
||||
import org.openhab.binding.astro.internal.model.Planet;
|
||||
import org.openhab.binding.astro.internal.model.Sun;
|
||||
|
||||
/**
|
||||
* Daily scheduled jobs For Sun planet
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class DailyJobSun extends AbstractJob {
|
||||
|
||||
private final AstroThingHandler handler;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param handler the {@link AstroThingHandler} instance
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code thingUID} or {@code handler} is {@code null}
|
||||
*/
|
||||
public DailyJobSun(String thingUID, AstroThingHandler handler) {
|
||||
super(thingUID);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.publishDailyInfo();
|
||||
String thingUID = getThingUID();
|
||||
LOGGER.debug("Scheduled Astro event-jobs for thing {}", thingUID);
|
||||
|
||||
Planet planet = handler.getPlanet();
|
||||
if (planet == null) {
|
||||
LOGGER.error("Planet not instantiated");
|
||||
return;
|
||||
}
|
||||
Sun sun = (Sun) planet;
|
||||
scheduleRange(thingUID, handler, sun.getRise(), EVENT_CHANNEL_ID_RISE);
|
||||
scheduleRange(thingUID, handler, sun.getSet(), EVENT_CHANNEL_ID_SET);
|
||||
scheduleRange(thingUID, handler, sun.getNoon(), EVENT_CHANNEL_ID_NOON);
|
||||
scheduleRange(thingUID, handler, sun.getNight(), EVENT_CHANNEL_ID_NIGHT);
|
||||
scheduleRange(thingUID, handler, sun.getMorningNight(), EVENT_CHANNEL_ID_MORNING_NIGHT);
|
||||
scheduleRange(thingUID, handler, sun.getAstroDawn(), EVENT_CHANNEL_ID_ASTRO_DAWN);
|
||||
scheduleRange(thingUID, handler, sun.getNauticDawn(), EVENT_CHANNEL_ID_NAUTIC_DAWN);
|
||||
scheduleRange(thingUID, handler, sun.getCivilDawn(), EVENT_CHANNEL_ID_CIVIL_DAWN);
|
||||
scheduleRange(thingUID, handler, sun.getAstroDusk(), EVENT_CHANNEL_ID_ASTRO_DUSK);
|
||||
scheduleRange(thingUID, handler, sun.getNauticDusk(), EVENT_CHANNEL_ID_NAUTIC_DUSK);
|
||||
scheduleRange(thingUID, handler, sun.getCivilDusk(), EVENT_CHANNEL_ID_CIVIL_DUSK);
|
||||
scheduleRange(thingUID, handler, sun.getEveningNight(), EVENT_CHANNEL_ID_EVENING_NIGHT);
|
||||
scheduleRange(thingUID, handler, sun.getDaylight(), EVENT_CHANNEL_ID_DAYLIGHT);
|
||||
|
||||
Eclipse eclipse = sun.getEclipse();
|
||||
eclipse.getKinds().forEach(eclipseKind -> {
|
||||
Calendar eclipseDate = eclipse.getDate(eclipseKind);
|
||||
if (eclipseDate != null) {
|
||||
scheduleEvent(thingUID, handler, eclipseDate, eclipseKind.toString(), EVENT_CHANNEL_ID_ECLIPSE, false);
|
||||
}
|
||||
});
|
||||
|
||||
// schedule republish jobs
|
||||
schedulePublishPlanet(thingUID, handler, sun.getZodiac().getEnd());
|
||||
schedulePublishPlanet(thingUID, handler, sun.getSeason().getNextSeason());
|
||||
|
||||
// schedule phase jobs
|
||||
scheduleSunPhase(thingUID, handler, SUN_RISE, sun.getRise().getStart());
|
||||
scheduleSunPhase(thingUID, handler, SUN_SET, sun.getSet().getStart());
|
||||
scheduleSunPhase(thingUID, handler, NIGHT, sun.getNight().getStart());
|
||||
scheduleSunPhase(thingUID, handler, DAYLIGHT, sun.getDaylight().getStart());
|
||||
scheduleSunPhase(thingUID, handler, ASTRO_DAWN, sun.getAstroDawn().getStart());
|
||||
scheduleSunPhase(thingUID, handler, NAUTIC_DAWN, sun.getNauticDawn().getStart());
|
||||
scheduleSunPhase(thingUID, handler, CIVIL_DAWN, sun.getCivilDawn().getStart());
|
||||
scheduleSunPhase(thingUID, handler, ASTRO_DUSK, sun.getAstroDusk().getStart());
|
||||
scheduleSunPhase(thingUID, handler, NAUTIC_DUSK, sun.getNauticDusk().getStart());
|
||||
scheduleSunPhase(thingUID, handler, CIVIL_DUSK, sun.getCivilDusk().getStart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Daily job sun " + getThingUID();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.AstroHandlerFactory;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
|
||||
/**
|
||||
* Scheduled job to trigger events
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class EventJob extends AbstractJob {
|
||||
|
||||
private final String channelID;
|
||||
private final String event;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID thing UID
|
||||
* @param channelID channel ID
|
||||
* @param event Event name
|
||||
* @throws IllegalArgumentException
|
||||
* if any of the arguments is {@code null}
|
||||
*/
|
||||
public EventJob(String thingUID, String channelID, String event) {
|
||||
super(thingUID);
|
||||
this.channelID = channelID;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
AstroThingHandler astroHandler = AstroHandlerFactory.getHandler(getThingUID());
|
||||
if (astroHandler != null) {
|
||||
astroHandler.triggerEvent(channelID, event);
|
||||
} else {
|
||||
LOGGER.trace("AstroThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event job " + getThingUID() + "/" + channelID + "/" + event;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Calendar.SECOND;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.apache.commons.lang.time.DateUtils.truncatedEquals;
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
|
||||
import static org.openhab.binding.astro.internal.util.DateTimeUtils.*;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
import org.openhab.binding.astro.internal.model.SunPhaseName;
|
||||
import org.openhab.core.scheduler.SchedulerRunnable;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The interface to be implemented by classes which represent a 'job' to be performed
|
||||
*
|
||||
* @author Amit Kumar Mondal - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface Job extends SchedulerRunnable, Runnable {
|
||||
|
||||
/** Logger Instance */
|
||||
public final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
/**
|
||||
* Schedules the provided {@link Job} instance
|
||||
*
|
||||
* @param thingUID the UID of the Thing instance
|
||||
* @param astroHandler the {@link AstroThingHandler} instance
|
||||
* @param job the {@link Job} instance to schedule
|
||||
* @param eventAt the {@link Calendar} instance denoting scheduled instant
|
||||
*/
|
||||
public static void schedule(String thingUID, AstroThingHandler astroHandler, Job job, Calendar eventAt) {
|
||||
try {
|
||||
Calendar today = Calendar.getInstance();
|
||||
if (isSameDay(eventAt, today) && isTimeGreaterEquals(eventAt, today)) {
|
||||
astroHandler.schedule(job, eventAt);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOGGER.error("{}", ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules an {@link EventJob} instance
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param astroHandler the {@link AstroThingHandler} instance
|
||||
* @param eventAt the {@link Calendar} instance denoting scheduled instant
|
||||
* @param event the event ID
|
||||
* @param channelId the channel ID
|
||||
*/
|
||||
public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler, Calendar eventAt, String event,
|
||||
String channelId, boolean configAlreadyApplied) {
|
||||
scheduleEvent(thingUID, astroHandler, eventAt, singletonList(event), channelId, configAlreadyApplied);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules an {@link EventJob} instance
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param astroHandler the {@link AstroThingHandler} instance
|
||||
* @param eventAt the {@link Calendar} instance denoting scheduled instant
|
||||
* @param events the event IDs to schedule
|
||||
* @param channelId the channel ID
|
||||
*/
|
||||
public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler, Calendar eventAt,
|
||||
List<String> events, String channelId, boolean configAlreadyApplied) {
|
||||
|
||||
if (events.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final Calendar instant;
|
||||
if (!configAlreadyApplied) {
|
||||
final Channel channel = astroHandler.getThing().getChannel(channelId);
|
||||
if (channel == null) {
|
||||
LOGGER.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
|
||||
return;
|
||||
}
|
||||
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
|
||||
instant = applyConfig(eventAt, config);
|
||||
} else {
|
||||
instant = eventAt;
|
||||
}
|
||||
List<Job> jobs = events.stream().map(e -> new EventJob(thingUID, channelId, e)).collect(toList());
|
||||
schedule(thingUID, astroHandler, new CompositeJob(thingUID, jobs), instant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules {@link Channel} events
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param astroHandler the {@link AstroThingHandler} instance
|
||||
* @param range the {@link Range} instance
|
||||
* @param channelId the channel ID
|
||||
*/
|
||||
public static void scheduleRange(String thingUID, AstroThingHandler astroHandler, Range range, String channelId) {
|
||||
Calendar start = range.getStart();
|
||||
Calendar end = range.getEnd();
|
||||
|
||||
// depending on the location you might not have a valid range for day/night, so skip the events:
|
||||
if (start == null || end == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Channel channel = astroHandler.getThing().getChannel(channelId);
|
||||
if (channel == null) {
|
||||
LOGGER.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
|
||||
return;
|
||||
}
|
||||
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
|
||||
Calendar configStart = applyConfig(start, config);
|
||||
Calendar configEnd = applyConfig(end, config);
|
||||
|
||||
if (truncatedEquals(configStart, configEnd, SECOND)) {
|
||||
scheduleEvent(thingUID, astroHandler, configStart, asList(EVENT_START, EVENT_END), channelId, true);
|
||||
} else {
|
||||
scheduleEvent(thingUID, astroHandler, configStart, EVENT_START, channelId, true);
|
||||
scheduleEvent(thingUID, astroHandler, configEnd, EVENT_END, channelId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules Planet events
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param astroHandler the {@link AstroThingHandler} instance
|
||||
* @param eventAt the {@link Calendar} instance denoting scheduled instant
|
||||
*/
|
||||
public static void schedulePublishPlanet(String thingUID, AstroThingHandler astroHandler, Calendar eventAt) {
|
||||
Job publishJob = new PublishPlanetJob(thingUID);
|
||||
schedule(thingUID, astroHandler, publishJob, eventAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules {@link SunPhaseJob}
|
||||
*
|
||||
* @param thingUID the Thing UID
|
||||
* @param astroHandler the {@link AstroThingHandler} instance
|
||||
* @param sunPhaseName {@link SunPhaseName} instance
|
||||
* @param eventAt the {@link Calendar} instance denoting scheduled instant
|
||||
*/
|
||||
public static void scheduleSunPhase(String thingUID, AstroThingHandler astroHandler, SunPhaseName sunPhaseName,
|
||||
Calendar eventAt) {
|
||||
Job sunPhaseJob = new SunPhaseJob(thingUID, sunPhaseName);
|
||||
schedule(thingUID, astroHandler, sunPhaseJob, eventAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thing UID that is associated with this {@link Job} (cannot be {@code null})
|
||||
*/
|
||||
public String getThingUID();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.AstroHandlerFactory;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
|
||||
/**
|
||||
* Scheduled job for planet positions
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class PositionalJob extends AbstractJob {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID thing UID
|
||||
* @throws IllegalArgumentException
|
||||
* if the provided argument is {@code null}
|
||||
*/
|
||||
public PositionalJob(String thingUID) {
|
||||
super(thingUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
AstroThingHandler astroHandler = AstroHandlerFactory.getHandler(getThingUID());
|
||||
if (astroHandler != null) {
|
||||
astroHandler.publishPositionalInfo();
|
||||
} else {
|
||||
LOGGER.trace("AstroThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Positional job " + getThingUID();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.AstroHandlerFactory;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
|
||||
/**
|
||||
* Scheduled job for planets
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class PublishPlanetJob extends AbstractJob {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID thing UID
|
||||
* @throws IllegalArgumentException
|
||||
* if the provided argument is {@code null}
|
||||
*/
|
||||
public PublishPlanetJob(String thingUID) {
|
||||
super(thingUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
AstroThingHandler astroHandler = AstroHandlerFactory.getHandler(getThingUID());
|
||||
if (astroHandler != null) {
|
||||
astroHandler.publishDailyInfo();
|
||||
} else {
|
||||
LOGGER.trace("AstroThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Publish planet job " + getThingUID();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.job;
|
||||
|
||||
import static org.openhab.binding.astro.internal.AstroBindingConstants.CHANNEL_ID_SUN_PHASE_NAME;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.astro.internal.AstroHandlerFactory;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.binding.astro.internal.model.Planet;
|
||||
import org.openhab.binding.astro.internal.model.Sun;
|
||||
import org.openhab.binding.astro.internal.model.SunPhaseName;
|
||||
import org.openhab.core.thing.Channel;
|
||||
|
||||
/**
|
||||
* Scheduled job for Sun phase change
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Amit Kumar Mondal - Implementation to be compliant with ESH Scheduler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class SunPhaseJob extends AbstractJob {
|
||||
|
||||
private final SunPhaseName sunPhaseName;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param thingUID thing UID
|
||||
* @param sunPhaseName {@link SunPhaseName} name
|
||||
* @throws IllegalArgumentException
|
||||
* if any of the arguments is {@code null}
|
||||
*/
|
||||
public SunPhaseJob(String thingUID, SunPhaseName sunPhaseName) {
|
||||
super(thingUID);
|
||||
this.sunPhaseName = sunPhaseName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
AstroThingHandler astroHandler = AstroHandlerFactory.getHandler(getThingUID());
|
||||
if (astroHandler != null) {
|
||||
Channel phaseNameChannel = astroHandler.getThing().getChannel(CHANNEL_ID_SUN_PHASE_NAME);
|
||||
if (phaseNameChannel != null) {
|
||||
Planet planet = astroHandler.getPlanet();
|
||||
if (planet != null && planet instanceof Sun) {
|
||||
final Sun typedSun = (Sun) planet;
|
||||
typedSun.getPhase().setName(sunPhaseName);
|
||||
astroHandler.publishChannelIfLinked(phaseNameChannel.getUID());
|
||||
}
|
||||
} else {
|
||||
LOGGER.trace("{}", "Phase Name Channel is null");
|
||||
}
|
||||
} else {
|
||||
LOGGER.trace("AstroThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Sun phase job " + getThingUID() + "/" + sunPhaseName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
|
||||
/**
|
||||
* Holds eclipse informations.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Eclipse {
|
||||
private final Map<EclipseKind, @Nullable Entry<Calendar, @Nullable Double>> entries = new HashMap<>();
|
||||
|
||||
public Eclipse(EclipseKind... eclipses) {
|
||||
for (EclipseKind eclipseKind : eclipses) {
|
||||
entries.put(eclipseKind, null);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<EclipseKind> getKinds() {
|
||||
return entries.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the next total eclipse.
|
||||
*/
|
||||
public @Nullable Calendar getTotal() {
|
||||
return getDate(EclipseKind.TOTAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the next partial eclipse.
|
||||
*/
|
||||
public @Nullable Calendar getPartial() {
|
||||
return getDate(EclipseKind.PARTIAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the next ring eclipse.
|
||||
*/
|
||||
public @Nullable Calendar getRing() {
|
||||
return getDate(EclipseKind.RING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elevation of the next total eclipse.
|
||||
*/
|
||||
public @Nullable Double getTotalElevation() {
|
||||
return getElevation(EclipseKind.TOTAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elevation of the next partial eclipse.
|
||||
*/
|
||||
public @Nullable Double getPartialElevation() {
|
||||
return getElevation(EclipseKind.PARTIAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elevation of the next ring eclipse.
|
||||
*/
|
||||
public @Nullable Double getRingElevation() {
|
||||
return getElevation(EclipseKind.RING);
|
||||
}
|
||||
|
||||
public @Nullable Calendar getDate(EclipseKind eclipseKind) {
|
||||
Entry<Calendar, @Nullable Double> entry = entries.get(eclipseKind);
|
||||
return entry != null ? entry.getKey() : null;
|
||||
}
|
||||
|
||||
private @Nullable Double getElevation(EclipseKind eclipseKind) {
|
||||
Entry<Calendar, @Nullable Double> entry = entries.get(eclipseKind);
|
||||
return entry != null ? entry.getValue() : null;
|
||||
}
|
||||
|
||||
public void set(EclipseKind eclipseKind, Calendar eclipseDate, @Nullable Position position) {
|
||||
entries.put(eclipseKind,
|
||||
new SimpleEntry<>(eclipseDate, position != null ? position.getElevationAsDouble() : null));
|
||||
}
|
||||
|
||||
public void setElevations(AstroThingHandler astroHandler, TimeZoneProvider timeZoneProvider) {
|
||||
getKinds().forEach(eclipseKind -> {
|
||||
Calendar eclipseDate = getDate(eclipseKind);
|
||||
if (eclipseDate != null) {
|
||||
set(eclipseKind, eclipseDate,
|
||||
astroHandler.getPositionAt(eclipseDate.toInstant().atZone(timeZoneProvider.getTimeZone())));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* All kind of eclipses.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EclipseKind {
|
||||
PARTIAL,
|
||||
TOTAL,
|
||||
RING;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Astro objects susceptible of being eclipsed.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EclipseType {
|
||||
SUN,
|
||||
MOON;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* Holds the calculated moon data.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class Moon extends RiseSet implements Planet {
|
||||
private MoonPhase phase = new MoonPhase();
|
||||
private MoonDistance apogee = new MoonDistance();
|
||||
private MoonDistance perigee = new MoonDistance();
|
||||
private MoonDistance distance = new MoonDistance();
|
||||
private Eclipse eclipse = new Eclipse(EclipseKind.PARTIAL, EclipseKind.TOTAL);
|
||||
private Position position = new Position();
|
||||
private Zodiac zodiac = new Zodiac(null);
|
||||
|
||||
/**
|
||||
* Returns the moon phase.
|
||||
*/
|
||||
public MoonPhase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the moon phase.
|
||||
*/
|
||||
public void setPhase(MoonPhase phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the apogee.
|
||||
*/
|
||||
public MoonDistance getApogee() {
|
||||
return apogee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the apogee.
|
||||
*/
|
||||
public void setApogee(MoonDistance apogee) {
|
||||
this.apogee = apogee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the perigee.
|
||||
*/
|
||||
public MoonDistance getPerigee() {
|
||||
return perigee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the perigee.
|
||||
*/
|
||||
public void setPerigee(MoonDistance perigee) {
|
||||
this.perigee = perigee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the eclipses.
|
||||
*/
|
||||
public Eclipse getEclipse() {
|
||||
return eclipse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the eclipses.
|
||||
*/
|
||||
public void setEclipse(Eclipse eclipse) {
|
||||
this.eclipse = eclipse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current distance.
|
||||
*/
|
||||
public MoonDistance getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current distance.
|
||||
*/
|
||||
public void setDistance(MoonDistance distance) {
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position.
|
||||
*/
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position.
|
||||
*/
|
||||
public void setPosition(Position position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the zodiac.
|
||||
*/
|
||||
public Zodiac getZodiac() {
|
||||
return zodiac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the zodiac.
|
||||
*/
|
||||
public void setZodiac(Zodiac zodiac) {
|
||||
this.zodiac = zodiac;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import static org.openhab.core.library.unit.MetricPrefix.KILO;
|
||||
import static org.openhab.core.library.unit.SIUnits.METRE;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.measure.quantity.Length;
|
||||
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
|
||||
/**
|
||||
* Holds a distance informations.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
*/
|
||||
public class MoonDistance {
|
||||
|
||||
private Calendar date;
|
||||
private double distance;
|
||||
|
||||
/**
|
||||
* Returns the date of the calculated distance.
|
||||
*/
|
||||
public Calendar getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the calculated distance.
|
||||
*/
|
||||
public void setDate(Calendar date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance in kilometers.
|
||||
*/
|
||||
public QuantityType<Length> getDistance() {
|
||||
return new QuantityType<>(distance, KILO(METRE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the distance in kilometers.
|
||||
*/
|
||||
public void setDistance(double kilometer) {
|
||||
this.distance = kilometer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.measure.quantity.Angle;
|
||||
import javax.measure.quantity.Dimensionless;
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* Holds the calculates moon phase informations.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
*/
|
||||
public class MoonPhase {
|
||||
private Calendar firstQuarter;
|
||||
private Calendar full;
|
||||
private Calendar thirdQuarter;
|
||||
private Calendar _new;
|
||||
private int age;
|
||||
private double illumination;
|
||||
private double agePercent;
|
||||
private double ageDegree;
|
||||
|
||||
private MoonPhaseName name;
|
||||
|
||||
/**
|
||||
* Returns the date at which the moon is in the first quarter.
|
||||
*/
|
||||
public Calendar getFirstQuarter() {
|
||||
return firstQuarter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date at which the moon is in the first quarter.
|
||||
*/
|
||||
public void setFirstQuarter(Calendar firstQuarter) {
|
||||
this.firstQuarter = firstQuarter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the full moon.
|
||||
*/
|
||||
public Calendar getFull() {
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the full moon.
|
||||
*/
|
||||
public void setFull(Calendar full) {
|
||||
this.full = full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date at which the moon is in the third quarter.
|
||||
*/
|
||||
public Calendar getThirdQuarter() {
|
||||
return thirdQuarter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date at which the moon is in the third quarter.
|
||||
*/
|
||||
public void setThirdQuarter(Calendar thirdQuarter) {
|
||||
this.thirdQuarter = thirdQuarter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the new moon.
|
||||
*/
|
||||
public Calendar getNew() {
|
||||
return _new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the new moon.
|
||||
*/
|
||||
public void setNew(Calendar _new) {
|
||||
this._new = _new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the age in days.
|
||||
*/
|
||||
public QuantityType<Time> getAge() {
|
||||
return new QuantityType<>(age, SmartHomeUnits.DAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the age in days.
|
||||
*/
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the illumination.
|
||||
*/
|
||||
public QuantityType<Dimensionless> getIllumination() {
|
||||
return new QuantityType<>(illumination, SmartHomeUnits.PERCENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the illumination.
|
||||
*/
|
||||
public void setIllumination(double illumination) {
|
||||
this.illumination = illumination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the phase name.
|
||||
*/
|
||||
public MoonPhaseName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the phase name.
|
||||
*/
|
||||
public void setName(MoonPhaseName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the age in degree.
|
||||
*/
|
||||
public QuantityType<Angle> getAgeDegree() {
|
||||
return new QuantityType<>(ageDegree, SmartHomeUnits.DEGREE_ANGLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the age in degree.
|
||||
*/
|
||||
public void setAgeDegree(double ageDegree) {
|
||||
this.ageDegree = ageDegree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the age in percent.
|
||||
*/
|
||||
public QuantityType<Dimensionless> getAgePercent() {
|
||||
return new QuantityType<>(agePercent, SmartHomeUnits.PERCENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the age in percent.
|
||||
*/
|
||||
public void setAgePercent(double agePercent) {
|
||||
this.agePercent = agePercent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* All moon phases.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public enum MoonPhaseName {
|
||||
NEW,
|
||||
WAXING_CRESCENT,
|
||||
FIRST_QUARTER,
|
||||
WAXING_GIBBOUS,
|
||||
FULL,
|
||||
WANING_GIBBOUS,
|
||||
THIRD_QUARTER,
|
||||
WANING_CRESCENT
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* Marker interface for all planets.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public interface Planet {
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import javax.measure.quantity.Angle;
|
||||
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* Holds the calculated azimuth and elevation.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Gaël L'hopital - Added shade length
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
*/
|
||||
public class Position {
|
||||
|
||||
private double azimuth;
|
||||
private double elevation;
|
||||
private double shadeLength;
|
||||
|
||||
public Position() {
|
||||
}
|
||||
|
||||
public Position(double azimuth, double elevation, double shadeLength) {
|
||||
this.azimuth = azimuth;
|
||||
this.elevation = elevation;
|
||||
this.shadeLength = shadeLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the azimuth.
|
||||
*/
|
||||
public QuantityType<Angle> getAzimuth() {
|
||||
return new QuantityType<>(azimuth, SmartHomeUnits.DEGREE_ANGLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the azimuth.
|
||||
*/
|
||||
public void setAzimuth(double azimuth) {
|
||||
this.azimuth = azimuth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elevation.
|
||||
*/
|
||||
public QuantityType<Angle> getElevation() {
|
||||
return new QuantityType<>(elevation, SmartHomeUnits.DEGREE_ANGLE);
|
||||
}
|
||||
|
||||
public double getElevationAsDouble() {
|
||||
return elevation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the elevation.
|
||||
*/
|
||||
public void setElevation(double elevation) {
|
||||
this.elevation = elevation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shade length.
|
||||
*/
|
||||
public double getShadeLength() {
|
||||
return shadeLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shade length.
|
||||
*/
|
||||
public void setShadeLength(double shadeLength) {
|
||||
this.shadeLength = shadeLength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import org.openhab.core.library.dimension.Intensity;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* Holds the calculated direct, diffuse and total
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
*/
|
||||
public class Radiation {
|
||||
|
||||
private double direct;
|
||||
private double diffuse;
|
||||
private double total;
|
||||
|
||||
public Radiation() {
|
||||
}
|
||||
|
||||
public Radiation(double direct, double diffuse, double total) {
|
||||
this.direct = direct;
|
||||
this.diffuse = diffuse;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the direct radiation.
|
||||
*/
|
||||
public void setDirect(double direct) {
|
||||
this.direct = direct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the diffuse radiation.
|
||||
*/
|
||||
public void setDiffuse(double diffuse) {
|
||||
this.diffuse = diffuse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total radiation.
|
||||
*/
|
||||
public void setTotal(double total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total radiation.
|
||||
*/
|
||||
public QuantityType<Intensity> getTotal() {
|
||||
return new QuantityType<>(total, SmartHomeUnits.IRRADIANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the direct radiation.
|
||||
*/
|
||||
public QuantityType<Intensity> getDirect() {
|
||||
return new QuantityType<>(direct, SmartHomeUnits.IRRADIANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the diffuse radiation.
|
||||
*/
|
||||
public QuantityType<Intensity> getDiffuse() {
|
||||
return new QuantityType<>(diffuse, SmartHomeUnits.IRRADIANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import static org.openhab.core.library.unit.MetricPrefix.MILLI;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* Range class which holds a start and a end calendar object.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
*/
|
||||
public class Range {
|
||||
|
||||
private Calendar start;
|
||||
private Calendar end;
|
||||
|
||||
public Range() {
|
||||
}
|
||||
|
||||
public Range(Calendar start, Calendar end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start of the range.
|
||||
*/
|
||||
public Calendar getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end of the range.
|
||||
*/
|
||||
public Calendar getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration in minutes.
|
||||
*/
|
||||
public QuantityType<Time> getDuration() {
|
||||
if (start == null || end == null) {
|
||||
return null;
|
||||
}
|
||||
if (start.after(end)) {
|
||||
return new QuantityType<>(0, SmartHomeUnits.MINUTE);
|
||||
}
|
||||
return new QuantityType<>(end.getTimeInMillis() - start.getTimeInMillis(), MILLI(SmartHomeUnits.SECOND))
|
||||
.toUnit(SmartHomeUnits.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the given calendar matches into the range.
|
||||
*/
|
||||
public boolean matches(Calendar cal) {
|
||||
if (start == null && end == null) {
|
||||
return false;
|
||||
}
|
||||
long matchStart = start != null ? start.getTimeInMillis()
|
||||
: DateTimeUtils.truncateToMidnight(cal).getTimeInMillis();
|
||||
long matchEnd = end != null ? end.getTimeInMillis() : DateTimeUtils.endOfDayDate(cal).getTimeInMillis();
|
||||
return cal.getTimeInMillis() >= matchStart && cal.getTimeInMillis() < matchEnd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Base class for the rise and set ranges.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class RiseSet {
|
||||
private Range rise = new Range();
|
||||
private Range set = new Range();
|
||||
|
||||
/**
|
||||
* Returns the rise range.
|
||||
*/
|
||||
public Range getRise() {
|
||||
return rise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rise range.
|
||||
*/
|
||||
public void setRise(Range rise) {
|
||||
this.rise = rise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set range.
|
||||
*/
|
||||
public Range getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the set range.
|
||||
*/
|
||||
public void setSet(Range set) {
|
||||
this.set = set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import static org.openhab.core.library.unit.MetricPrefix.MILLI;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* Holds the season dates of the year and the current name.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class Season {
|
||||
private Calendar spring;
|
||||
private Calendar summer;
|
||||
private Calendar autumn;
|
||||
private Calendar winter;
|
||||
|
||||
private SeasonName name;
|
||||
|
||||
/**
|
||||
* Returns the date of the beginning of spring.
|
||||
*/
|
||||
public Calendar getSpring() {
|
||||
return spring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the beginning of spring.
|
||||
*/
|
||||
public void setSpring(Calendar spring) {
|
||||
this.spring = spring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the beginning of summer.
|
||||
*/
|
||||
public Calendar getSummer() {
|
||||
return summer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the beginning of summer.
|
||||
*/
|
||||
public void setSummer(Calendar summer) {
|
||||
this.summer = summer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the beginning of autumn.
|
||||
*/
|
||||
public Calendar getAutumn() {
|
||||
return autumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the beginning of autumn.
|
||||
*/
|
||||
public void setAutumn(Calendar autumn) {
|
||||
this.autumn = autumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the beginning of winter.
|
||||
*/
|
||||
public Calendar getWinter() {
|
||||
return winter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the beginning of winter.
|
||||
*/
|
||||
public void setWinter(Calendar winter) {
|
||||
this.winter = winter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current season name.
|
||||
*/
|
||||
public SeasonName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current season name.
|
||||
*/
|
||||
public void setName(SeasonName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next season.
|
||||
*/
|
||||
public Calendar getNextSeason() {
|
||||
return DateTimeUtils.getNext(spring, summer, autumn, winter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next season name.
|
||||
*/
|
||||
public SeasonName getNextName() {
|
||||
int ordinal = name.ordinal() + 1;
|
||||
if (ordinal > 3) {
|
||||
ordinal = 0;
|
||||
}
|
||||
return SeasonName.values()[ordinal];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time left for current season
|
||||
*/
|
||||
public QuantityType<Time> getTimeLeft() {
|
||||
Calendar now = Calendar.getInstance();
|
||||
Calendar next = getNextSeason();
|
||||
return new QuantityType<>(next.getTimeInMillis() - now.getTimeInMillis(), MILLI(SmartHomeUnits.SECOND))
|
||||
.toUnit(SmartHomeUnits.DAY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* All season names.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public enum SeasonName {
|
||||
SPRING,
|
||||
SUMMER,
|
||||
AUTUMN,
|
||||
WINTER
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds the calculated sun data.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class Sun extends RiseSet implements Planet {
|
||||
|
||||
private Map<SunPhaseName, Range> ranges = new HashMap<>();
|
||||
|
||||
private Position position = new Position();
|
||||
|
||||
private SunZodiac zodiac = new SunZodiac(null, null);
|
||||
|
||||
private Season season = new Season();
|
||||
|
||||
private Eclipse eclipse = new Eclipse(EclipseKind.PARTIAL, EclipseKind.TOTAL, EclipseKind.RING);
|
||||
|
||||
private Radiation radiation = new Radiation();
|
||||
|
||||
private SunPhase phase = new SunPhase();
|
||||
|
||||
/**
|
||||
* Returns the astro dawn range.
|
||||
*/
|
||||
public Range getAstroDawn() {
|
||||
return ranges.get(SunPhaseName.ASTRO_DAWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the astro dawn range.
|
||||
*/
|
||||
public void setAstroDawn(Range astroDawn) {
|
||||
ranges.put(SunPhaseName.ASTRO_DAWN, astroDawn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nautic dawn range.
|
||||
*/
|
||||
public Range getNauticDawn() {
|
||||
return ranges.get(SunPhaseName.NAUTIC_DAWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nautic dawn range.
|
||||
*/
|
||||
public void setNauticDawn(Range nauticDawn) {
|
||||
ranges.put(SunPhaseName.NAUTIC_DAWN, nauticDawn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the civil dawn range.
|
||||
*/
|
||||
public Range getCivilDawn() {
|
||||
return ranges.get(SunPhaseName.CIVIL_DAWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the civil dawn range.
|
||||
*/
|
||||
public void setCivilDawn(Range civilDawn) {
|
||||
ranges.put(SunPhaseName.CIVIL_DAWN, civilDawn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the civil dusk range.
|
||||
*/
|
||||
public Range getCivilDusk() {
|
||||
return ranges.get(SunPhaseName.CIVIL_DUSK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the civil dusk range.
|
||||
*/
|
||||
public void setCivilDusk(Range civilDusk) {
|
||||
ranges.put(SunPhaseName.CIVIL_DUSK, civilDusk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nautic dusk range.
|
||||
*/
|
||||
public Range getNauticDusk() {
|
||||
return ranges.get(SunPhaseName.NAUTIC_DUSK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nautic dusk range.
|
||||
*/
|
||||
public void setNauticDusk(Range nauticDusk) {
|
||||
ranges.put(SunPhaseName.NAUTIC_DUSK, nauticDusk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the astro dusk range.
|
||||
*/
|
||||
public Range getAstroDusk() {
|
||||
return ranges.get(SunPhaseName.ASTRO_DUSK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the astro dusk range.
|
||||
*/
|
||||
public void setAstroDusk(Range astroDusk) {
|
||||
ranges.put(SunPhaseName.ASTRO_DUSK, astroDusk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the noon range, start and end is always equal.
|
||||
*/
|
||||
public Range getNoon() {
|
||||
return ranges.get(SunPhaseName.NOON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the noon range.
|
||||
*/
|
||||
public void setNoon(Range noon) {
|
||||
ranges.put(SunPhaseName.NOON, noon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the daylight range.
|
||||
*/
|
||||
public Range getDaylight() {
|
||||
return ranges.get(SunPhaseName.DAYLIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the daylight range.
|
||||
*/
|
||||
public void setDaylight(Range daylight) {
|
||||
ranges.put(SunPhaseName.DAYLIGHT, daylight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the morning night range.
|
||||
*/
|
||||
public Range getMorningNight() {
|
||||
return ranges.get(SunPhaseName.MORNING_NIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the morning night range.
|
||||
*/
|
||||
public void setMorningNight(Range morningNight) {
|
||||
ranges.put(SunPhaseName.MORNING_NIGHT, morningNight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the evening night range.
|
||||
*/
|
||||
public Range getEveningNight() {
|
||||
return ranges.get(SunPhaseName.EVENING_NIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the evening night range.
|
||||
*/
|
||||
public void setEveningNight(Range eveningNight) {
|
||||
ranges.put(SunPhaseName.EVENING_NIGHT, eveningNight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the night range.
|
||||
*/
|
||||
public Range getNight() {
|
||||
return ranges.get(SunPhaseName.NIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the night range.
|
||||
*/
|
||||
public void setNight(Range night) {
|
||||
ranges.put(SunPhaseName.NIGHT, night);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rise range.
|
||||
*/
|
||||
@Override
|
||||
public void setRise(Range rise) {
|
||||
super.setRise(rise);
|
||||
ranges.put(SunPhaseName.SUN_RISE, rise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the set range.
|
||||
*/
|
||||
@Override
|
||||
public void setSet(Range set) {
|
||||
super.setSet(set);
|
||||
ranges.put(SunPhaseName.SUN_SET, set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sun position.
|
||||
*/
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sun radiation
|
||||
*/
|
||||
public Radiation getRadiation() {
|
||||
return radiation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sun position.
|
||||
*/
|
||||
public void setPosition(Position position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the zodiac.
|
||||
*/
|
||||
public SunZodiac getZodiac() {
|
||||
return zodiac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the zodiac.
|
||||
*/
|
||||
public void setZodiac(SunZodiac zodiac) {
|
||||
this.zodiac = zodiac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the seasons.
|
||||
*/
|
||||
public Season getSeason() {
|
||||
return season;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the seasons.
|
||||
*/
|
||||
public void setSeason(Season season) {
|
||||
this.season = season;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the eclipses.
|
||||
*/
|
||||
public Eclipse getEclipse() {
|
||||
return eclipse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the eclipses.
|
||||
*/
|
||||
public void setEclipse(Eclipse eclipse) {
|
||||
this.eclipse = eclipse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sun phase.
|
||||
*/
|
||||
public SunPhase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sun phase.
|
||||
*/
|
||||
public void setPhase(SunPhase phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all ranges of the sun.
|
||||
*/
|
||||
public Map<SunPhaseName, Range> getAllRanges() {
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* Holds the calculated sun phase informations.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class SunPhase {
|
||||
private SunPhaseName name;
|
||||
|
||||
/**
|
||||
* Returns the sun phase.
|
||||
*/
|
||||
public SunPhaseName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sun phase.
|
||||
*/
|
||||
public void setName(SunPhaseName name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* All sun phases.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public enum SunPhaseName {
|
||||
SUN_RISE,
|
||||
ASTRO_DAWN,
|
||||
NAUTIC_DAWN,
|
||||
CIVIL_DAWN,
|
||||
CIVIL_DUSK,
|
||||
NAUTIC_DUSK,
|
||||
ASTRO_DUSK,
|
||||
SUN_SET,
|
||||
DAYLIGHT,
|
||||
NOON,
|
||||
NIGHT,
|
||||
MORNING_NIGHT,
|
||||
EVENING_NIGHT
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Extends the zodiac with a date range.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class SunZodiac extends Zodiac {
|
||||
private Range range;
|
||||
|
||||
/**
|
||||
* Creates a Zodiac with a sign and a range.
|
||||
*/
|
||||
public SunZodiac(ZodiacSign sign, Range range) {
|
||||
super(sign);
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns she start of the zodiac.
|
||||
*/
|
||||
public Calendar getStart() {
|
||||
return range == null ? null : range.getStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end of the zodiac.
|
||||
*/
|
||||
public Calendar getEnd() {
|
||||
return range == null ? null : range.getEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the zodiac is valid on the specified calendar object.
|
||||
*/
|
||||
public boolean isValid(Calendar calendar) {
|
||||
if (range == null || range.getStart() == null || range.getEnd() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return range.getStart().getTimeInMillis() <= calendar.getTimeInMillis()
|
||||
&& range.getEnd().getTimeInMillis() >= calendar.getTimeInMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* Holds the sign of the zodiac.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class Zodiac {
|
||||
private ZodiacSign sign;
|
||||
|
||||
public Zodiac(ZodiacSign sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sign of the zodiac.
|
||||
*/
|
||||
public ZodiacSign getSign() {
|
||||
return sign;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
/**
|
||||
* All zodiac signs.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public enum ZodiacSign {
|
||||
ARIES,
|
||||
TAURUS,
|
||||
GEMINI,
|
||||
CANCER,
|
||||
LEO,
|
||||
VIRGO,
|
||||
LIBRA,
|
||||
SCORPIO,
|
||||
SAGITTARIUS,
|
||||
CAPRICORN,
|
||||
AQUARIUS,
|
||||
PISCES
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Common used DateTime functions.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
*/
|
||||
public class DateTimeUtils {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeUtils.class);
|
||||
private static final Pattern HHMM_PATTERN = Pattern.compile("^([0-1][0-9]|2[0-3])(:[0-5][0-9])$");
|
||||
|
||||
public static final double J1970 = 2440588.0;
|
||||
public static final double MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
|
||||
|
||||
/** Constructor */
|
||||
private DateTimeUtils() {
|
||||
throw new IllegalAccessError("Non-instantiable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the time from the calendar object.
|
||||
*/
|
||||
public static Calendar truncateToMidnight(Calendar calendar) {
|
||||
return DateUtils.truncate(calendar, Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Range object within the specified months and days. The start
|
||||
* time is midnight, the end time is end of the day.
|
||||
*/
|
||||
public static Range getRange(int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay) {
|
||||
Calendar start = Calendar.getInstance();
|
||||
start.set(Calendar.YEAR, startYear);
|
||||
start.set(Calendar.MONTH, startMonth);
|
||||
start.set(Calendar.DAY_OF_MONTH, startDay);
|
||||
start = truncateToMidnight(start);
|
||||
|
||||
Calendar end = Calendar.getInstance();
|
||||
end.set(Calendar.YEAR, endYear);
|
||||
end.set(Calendar.MONTH, endMonth);
|
||||
end.set(Calendar.DAY_OF_MONTH, endDay);
|
||||
end.set(Calendar.HOUR_OF_DAY, 23);
|
||||
end.set(Calendar.MINUTE, 59);
|
||||
end.set(Calendar.SECOND, 59);
|
||||
end.set(Calendar.MILLISECOND, 999);
|
||||
|
||||
return new Range(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a calendar object from a julian date.
|
||||
*/
|
||||
public static Calendar toCalendar(double julianDate) {
|
||||
if (Double.compare(julianDate, Double.NaN) == 0 || julianDate == 0) {
|
||||
return null;
|
||||
}
|
||||
long millis = (long) ((julianDate + 0.5 - J1970) * MILLISECONDS_PER_DAY);
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(millis);
|
||||
return DateUtils.round(cal, Calendar.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the julian date from the calendar object.
|
||||
*/
|
||||
public static double dateToJulianDate(Calendar calendar) {
|
||||
return calendar.getTimeInMillis() / MILLISECONDS_PER_DAY - 0.5 + J1970;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the midnight julian date from the calendar object.
|
||||
*/
|
||||
public static double midnightDateToJulianDate(Calendar calendar) {
|
||||
return dateToJulianDate(truncateToMidnight(calendar));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end of day from the calendar object.
|
||||
*/
|
||||
public static Calendar endOfDayDate(Calendar calendar) {
|
||||
Calendar cal = (Calendar) calendar.clone();
|
||||
cal = DateUtils.ceiling(cal, Calendar.DATE);
|
||||
cal.add(Calendar.MILLISECOND, -1);
|
||||
return cal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end of day julian date from the calendar object.
|
||||
*/
|
||||
public static double endOfDayDateToJulianDate(Calendar calendar) {
|
||||
return dateToJulianDate(endOfDayDate(calendar));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the year of the calendar object as a decimal value.
|
||||
*/
|
||||
public static double getDecimalYear(Calendar calendar) {
|
||||
return calendar.get(Calendar.YEAR)
|
||||
+ (double) calendar.get(Calendar.DAY_OF_YEAR) / calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the time (hour.minute) to a calendar object.
|
||||
*/
|
||||
public static Calendar timeToCalendar(Calendar calendar, double time) {
|
||||
if (time < 0.0) {
|
||||
return null;
|
||||
}
|
||||
Calendar cal = (Calendar) calendar.clone();
|
||||
int hour = 0;
|
||||
int minute = 0;
|
||||
if (time == 24.0) {
|
||||
cal.add(Calendar.DAY_OF_MONTH, 1);
|
||||
} else {
|
||||
hour = (int) time;
|
||||
minute = (int) ((time * 100) - (hour * 100));
|
||||
}
|
||||
cal.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal.set(Calendar.MINUTE, minute);
|
||||
return DateUtils.truncate(cal, Calendar.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if two calendar objects are on the same day ignoring time.
|
||||
*/
|
||||
public static boolean isSameDay(Calendar cal1, Calendar cal2) {
|
||||
return cal1 != null && cal2 != null && DateUtils.isSameDay(cal1, cal2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date object from a calendar.
|
||||
*/
|
||||
public static Date getDate(Calendar calendar) {
|
||||
return calendar == null ? null : calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next Calendar from today.
|
||||
*/
|
||||
public static Calendar getNext(Calendar... calendars) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
Calendar next = null;
|
||||
for (Calendar calendar : calendars) {
|
||||
if (calendar.after(now) && (next == null || calendar.before(next))) {
|
||||
next = calendar;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if cal1 is greater or equal than cal2, ignoring seconds.
|
||||
*/
|
||||
public static boolean isTimeGreaterEquals(Calendar cal1, Calendar cal2) {
|
||||
Calendar truncCal1 = DateUtils.truncate(cal1, Calendar.MINUTE);
|
||||
Calendar truncCal2 = DateUtils.truncate(cal2, Calendar.MINUTE);
|
||||
return truncCal1.getTimeInMillis() >= truncCal2.getTimeInMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the config to the given calendar.
|
||||
*/
|
||||
public static Calendar applyConfig(Calendar cal, AstroChannelConfig config) {
|
||||
Calendar cCal = cal;
|
||||
if (config.offset != 0) {
|
||||
Calendar cOffset = Calendar.getInstance();
|
||||
cOffset.setTime(cCal.getTime());
|
||||
cOffset.add(Calendar.MINUTE, config.offset);
|
||||
cCal = cOffset;
|
||||
}
|
||||
|
||||
Calendar cEarliest = adjustTime(cCal, getMinutesFromTime(config.earliest));
|
||||
if (cCal.before(cEarliest)) {
|
||||
return cEarliest;
|
||||
}
|
||||
Calendar cLatest = adjustTime(cCal, getMinutesFromTime(config.latest));
|
||||
if (cCal.after(cLatest)) {
|
||||
return cLatest;
|
||||
}
|
||||
|
||||
return cCal;
|
||||
}
|
||||
|
||||
private static Calendar adjustTime(Calendar cal, int minutes) {
|
||||
if (minutes > 0) {
|
||||
Calendar cTime = Calendar.getInstance();
|
||||
cTime = DateUtils.truncate(cal, Calendar.DAY_OF_MONTH);
|
||||
cTime.add(Calendar.MINUTE, minutes);
|
||||
return cTime;
|
||||
}
|
||||
return cal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a HH:MM string and returns the minutes.
|
||||
*/
|
||||
private static int getMinutesFromTime(String configTime) {
|
||||
if (configTime != null) {
|
||||
String time = configTime.trim();
|
||||
if (!time.isEmpty()) {
|
||||
try {
|
||||
if (!HHMM_PATTERN.matcher(time).matches()) {
|
||||
throw new NumberFormatException();
|
||||
} else {
|
||||
String[] elements = time.split(":");
|
||||
int hour = Integer.parseInt(elements[0]);
|
||||
int minutes = Integer.parseInt(elements[1]);
|
||||
return (hour * 60) + minutes;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
LOGGER.warn(
|
||||
"Can not parse astro channel configuration '{}' to hour and minutes, use pattern hh:mm, ignoring!",
|
||||
time);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* Methods to get the value from a property of an object.
|
||||
*
|
||||
* @author Gerhard Riegler - Initial contribution
|
||||
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
|
||||
* @author Christoph Weitkamp - Introduced UoM
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PropertyUtils {
|
||||
|
||||
/** Constructor */
|
||||
private PropertyUtils() {
|
||||
throw new IllegalAccessError("Non-instantiable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the channel.
|
||||
*/
|
||||
public static State getState(ChannelUID channelUID, AstroChannelConfig config, Object instance, ZoneId zoneId)
|
||||
throws Exception {
|
||||
Object value = getPropertyValue(channelUID, instance);
|
||||
if (value == null) {
|
||||
return UnDefType.UNDEF;
|
||||
} else if (value instanceof State) {
|
||||
return (State) value;
|
||||
} else if (value instanceof Calendar) {
|
||||
Calendar cal = (Calendar) value;
|
||||
GregorianCalendar gregorianCal = (GregorianCalendar) DateTimeUtils.applyConfig(cal, config);
|
||||
cal.setTimeZone(TimeZone.getTimeZone(zoneId));
|
||||
ZonedDateTime zoned = gregorianCal.toZonedDateTime().withFixedOffsetZone();
|
||||
return new DateTimeType(zoned);
|
||||
} else if (value instanceof Number) {
|
||||
BigDecimal decimalValue = new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_UP);
|
||||
return new DecimalType(decimalValue);
|
||||
} else if (value instanceof String || value instanceof Enum) {
|
||||
return new StringType(value.toString());
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported value type " + value.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property value from the object instance, nested properties are possible. If the propertyName is for
|
||||
* example rise.start, the methods getRise().getStart() are called.
|
||||
*/
|
||||
public static @Nullable Object getPropertyValue(ChannelUID channelUID, Object instance) throws Exception {
|
||||
String[] properties = channelUID.getId().split("#");
|
||||
return getPropertyValue(instance, properties, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the nested properties and returns the getter value.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private static @Nullable Object getPropertyValue(Object instance, String[] properties, int nestedIndex)
|
||||
throws Exception {
|
||||
String propertyName = properties[nestedIndex];
|
||||
Method m = instance.getClass().getMethod(toGetterString(propertyName), null);
|
||||
Object result = m.invoke(instance, (Object[]) null);
|
||||
if (nestedIndex + 1 < properties.length) {
|
||||
return getPropertyValue(result, properties, nestedIndex + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the string to a getter property.
|
||||
*/
|
||||
private static String toGetterString(String str) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("get");
|
||||
sb.append(Character.toTitleCase(str.charAt(0)));
|
||||
sb.append(str.substring(1));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="astro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Astro Binding</name>
|
||||
<description>The Astro binding calculates astronomical data from sun and moon.</description>
|
||||
<author>Gerhard Riegler</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:astro:moonconfig">
|
||||
<parameter name="geolocation" type="text" required="true">
|
||||
<context>location</context>
|
||||
<label>Location</label>
|
||||
<description>The latitude, longitude and altitude separated with a comma (lat,long,[alt]).</description>
|
||||
</parameter>
|
||||
<parameter name="interval" type="integer" min="1" max="86400" unit="s">
|
||||
<label>Interval</label>
|
||||
<description>Refresh interval for positional data calculation in seconds.</description>
|
||||
<default>300</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:astro:sunconfig">
|
||||
<parameter name="geolocation" type="text" required="true">
|
||||
<context>location</context>
|
||||
<label>Location</label>
|
||||
<description>The latitude, longitude and altitude separated with a comma (lat,long,[alt]).</description>
|
||||
</parameter>
|
||||
<parameter name="useMeteorologicalSeason" type="boolean" required="true">
|
||||
<label>Meteorological Season</label>
|
||||
<description>Uses meteorological season calculation method instead of equinox method.</description>
|
||||
<advanced>true</advanced>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="interval" type="integer" min="1" max="86400" unit="s">
|
||||
<label>Interval</label>
|
||||
<description>Refresh interval for positional data calculation in seconds.</description>
|
||||
<default>300</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="channel-type:astro:config">
|
||||
<parameter name="offset" type="integer" min="-1440" max="1440" unit="min">
|
||||
<label>Offset</label>
|
||||
<description>Moves an event or datetime value forward or backward (in minutes).</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
<parameter name="earliest" type="text" pattern="^([0-1][0-9]|2[0-3])(:[0-5][0-9])$">
|
||||
<label>Earliest</label>
|
||||
<description>The earliest time of the day for the event or the datetime value (hh:mm).</description>
|
||||
</parameter>
|
||||
<parameter name="latest" type="text" pattern="^([0-1][0-9]|2[0-3])(:[0-5][0-9])$">
|
||||
<label>Latest</label>
|
||||
<description>The latest time of the day for the event or the datetime value (hh:mm).</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,3 @@
|
||||
# Discovery result
|
||||
discovery.astro.sun.local.label = Local Sun
|
||||
discovery.astro.moon.local.label = Local Moon
|
||||
@@ -0,0 +1,188 @@
|
||||
# binding
|
||||
binding.astro.name = Astro Binding
|
||||
binding.astro.description = Das Astro Binding berechnet astronomische Daten von Sonne und Mond.
|
||||
|
||||
# thing types
|
||||
thing-type.astro.sun.label = Astronomische Sonnendaten
|
||||
thing-type.astro.sun.description = Stellt astronomische Sonnendaten zur Verfügung
|
||||
thing-type.astro.sun.group.rise.label = Sonnenaufgang
|
||||
thing-type.astro.sun.group.set.label = Sonnenuntergang
|
||||
thing-type.astro.sun.group.noon.label = Mittag
|
||||
thing-type.astro.sun.group.night.label = Nacht
|
||||
thing-type.astro.sun.group.morningNight.label = Morgendliche Nacht
|
||||
thing-type.astro.sun.group.astroDawn.label = Astronomische Morgendämmerung
|
||||
thing-type.astro.sun.group.nauticDawn.label = Nautische Morgendämmerung
|
||||
thing-type.astro.sun.group.civilDawn.label = Bürgerliche Morgendämmerung
|
||||
thing-type.astro.sun.group.astroDusk.label = Astronomische Abenddämmerung
|
||||
thing-type.astro.sun.group.nauticDusk.label = Nautische Abenddämmerung
|
||||
thing-type.astro.sun.group.civilDusk.label = Bürgerliche Abenddämmerung
|
||||
thing-type.astro.sun.group.eveningNight.label = Abendliche Nacht
|
||||
thing-type.astro.sun.group.daylight.label = Tageslicht
|
||||
thing-type.astro.sun.group.position.description = Position der Sonne
|
||||
|
||||
thing-type.astro.moon.label = Astronomische Monddaten
|
||||
thing-type.astro.moon.description = Stellt astronomische Monddaten zur Verfügung
|
||||
thing-type.astro.moon.group.rise.label = Mondaufgang
|
||||
thing-type.astro.moon.group.set.label = Monduntergang
|
||||
thing-type.astro.moon.group.distance.description = Die Entfernung zum Mond
|
||||
thing-type.astro.moon.group.perigee.label = Perigäum
|
||||
thing-type.astro.moon.group.perigee.description = Das Perigäum des Mondes
|
||||
thing-type.astro.moon.group.apogee.label = Apogäum
|
||||
thing-type.astro.moon.group.apogee.description = Das Apogäum des Mondes
|
||||
thing-type.astro.moon.group.position.description = Die Position des Mondes
|
||||
|
||||
# thing types config
|
||||
thing-type.config.astro.sunconfig.geolocation.label = Ort der Sonnendaten
|
||||
thing-type.config.astro.sunconfig.geolocation.description = Ort der Sonnendaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
|
||||
|
||||
thing-type.config.astro.sunconfig.useMeteorologicalSeason.label = Meteorologische Jahreszeit
|
||||
thing-type.config.astro.sunconfig.useMeteorologicalSeason.description = Verwendung der meteorologischen Berechnungsmethode für die Jahreszeiten anstelle der astronomischen.
|
||||
|
||||
thing-type.config.astro.sunconfig.interval.label = Aktualisierungsintervall
|
||||
thing-type.config.astro.sunconfig.interval.description = Intervall zur Aktualisierung der Positionsdaten (in s).
|
||||
|
||||
thing-type.config.astro.moonconfig.geolocation.label = Ort der Monddaten
|
||||
thing-type.config.astro.moonconfig.geolocation.description = Ort der Monddaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
|
||||
|
||||
thing-type.config.astro.moonconfig.interval.label = Aktualisierungsintervall
|
||||
thing-type.config.astro.moonconfig.interval.description = Intervall zur Aktualisierung der Positionsdaten (in s).
|
||||
|
||||
# channel group types
|
||||
channel-group-type.astro.position.label = Position
|
||||
channel-group-type.astro.position.description = Die Position des Himmelskörpers
|
||||
channel-group-type.astro.radiation.label = Strahlung
|
||||
channel-group-type.astro.radiation.description = Das Strahlungslevel der Sonne
|
||||
channel-group-type.astro.sunRange.label = Spannweite
|
||||
channel-group-type.astro.moonRange.label = Spannweite
|
||||
channel-group-type.astro.sunZodiac.label = Sternzeichen
|
||||
channel-group-type.astro.sunZodiac.description = Das Sternzeichen der Sonne
|
||||
channel-group-type.astro.season.label = Jahreszeit
|
||||
channel-group-type.astro.season.description = Die Jahreszeiten in diesem Jahr
|
||||
channel-group-type.astro.sunEclipse.label = Finsternisse
|
||||
channel-group-type.astro.sunEclipse.description = Zeitpunkt der nächsten Sonnenfinsternis
|
||||
channel-group-type.astro.sunPhase.label = Sonnenphasen
|
||||
channel-group-type.astro.sunPhase.description = Zeigt die Details der aktuellen Sonnenphase an
|
||||
channel-group-type.astro.moonPhase.label = Mondphasen
|
||||
channel-group-type.astro.moonPhase.description = Zeigt die Details der aktuellen und der nächsten Mondphasen an
|
||||
channel-group-type.astro.moonEclipse.label = Finsternisse
|
||||
channel-group-type.astro.moonEclipse.description = Zeitpunkt der nächsten Mondfinsternis
|
||||
channel-group-type.astro.distance.label = Entfernung
|
||||
channel-group-type.astro.distance.description = Entfernungsdaten
|
||||
channel-group-type.astro.moonZodiac.label = Sternzeichen
|
||||
channel-group-type.astro.moonZodiac.description = Das Sternzeichen des Mondes
|
||||
|
||||
channel-group-type.astro.moonEclipse.channel.total.label = Totale Mondfinsternis
|
||||
channel-group-type.astro.moonEclipse.channel.total.description = Zeitpunkt der nächsten totalen Mondfinsternis
|
||||
channel-group-type.astro.moonEclipse.channel.partial.label = Partielle Mondfinsternis
|
||||
channel-group-type.astro.moonEclipse.channel.partial.description = Zeitpunkt der nächsten partiellen Mondfinsternis
|
||||
|
||||
# channel types
|
||||
channel-type.astro.azimuth.label = Azimut
|
||||
channel-type.astro.azimuth.description = Das Azimut des Himmelskörpers
|
||||
channel-type.astro.elevation.label = Höhenwinkel
|
||||
channel-type.astro.elevation.description = Der Höhenwinkel des Himmelskörpers
|
||||
channel-type.astro.shadeLength.label = Schattenlängenverhältnis
|
||||
channel-type.astro.shadeLength.description = Projiziertes Schattenlängenverhältnis (Abgeleitet vom Höhenwinkel)
|
||||
channel-type.astro.directRadiation.label = Direkte Strahlung
|
||||
channel-type.astro.directRadiation.description = Höhe der Strahlung nach Eindringen in die atmosphärische Schicht
|
||||
channel-type.astro.diffuseRadiation.label = Diffuse Strahlung
|
||||
channel-type.astro.diffuseRadiation.description = Höhe der Strahlung, nach Beugung durch Wolken und Atmosphäre
|
||||
channel-type.astro.totalRadiation.label = Gesamtstrahlung
|
||||
channel-type.astro.totalRadiation.description = Gesamtmenge der Strahlung auf dem Boden
|
||||
channel-type.astro.start.label = Startzeit
|
||||
channel-type.astro.start.description = Die Startzeit des Ereignisses
|
||||
channel-type.astro.end.label = Endzeit
|
||||
channel-type.astro.end.description = Die Endzeit des Ereignisses
|
||||
channel-type.astro.duration.label = Dauer
|
||||
channel-type.astro.duration.description = Die Dauer des Ereignisses
|
||||
channel-type.astro.sign.label = Sternzeichen
|
||||
channel-type.astro.sign.description = Das Sternzeichen
|
||||
channel-type.astro.sign.state.option.ARIES = Widder
|
||||
channel-type.astro.sign.state.option.TAURUS = Stier
|
||||
channel-type.astro.sign.state.option.GEMINI = Zwilling
|
||||
channel-type.astro.sign.state.option.CANCER = Krebs
|
||||
channel-type.astro.sign.state.option.LEO = Löwe
|
||||
channel-type.astro.sign.state.option.VIRGO = Jungfrau
|
||||
channel-type.astro.sign.state.option.LIBRA = Waage
|
||||
channel-type.astro.sign.state.option.SCORPIO = Skorpion
|
||||
channel-type.astro.sign.state.option.SAGITTARIUS = Schütze
|
||||
channel-type.astro.sign.state.option.CAPRICORN = Steinbock
|
||||
channel-type.astro.sign.state.option.AQUARIUS = Wassermann
|
||||
channel-type.astro.sign.state.option.PISCES = Fische
|
||||
channel-type.astro.seasonName.label = Jahreszeit
|
||||
channel-type.astro.seasonName.description = Der Name der aktuellen Jahreszeit
|
||||
channel-type.astro.seasonName.state.option.SPRING = Frühling
|
||||
channel-type.astro.seasonName.state.option.SUMMER = Sommer
|
||||
channel-type.astro.seasonName.state.option.AUTUMN = Herbst
|
||||
channel-type.astro.seasonName.state.option.WINTER = Winter
|
||||
channel-type.astro.spring.label = Frühling
|
||||
channel-type.astro.spring.description = Frühlingsanfang
|
||||
channel-type.astro.summer.label = Sommer
|
||||
channel-type.astro.summer.description = Sommeranfang
|
||||
channel-type.astro.autumn.label = Herbst
|
||||
channel-type.astro.autumn.description = Herbstanfang
|
||||
channel-type.astro.winter.label = Winter
|
||||
channel-type.astro.winter.description = Winteranfang
|
||||
channel-type.astro.total.label = Totale Sonnenfinsternis
|
||||
channel-type.astro.total.description = Zeitpunkt der nächsten totalen Sonnenfinsternis
|
||||
channel-type.astro.partial.label = Partielle Sonnenfinsternis
|
||||
channel-type.astro.partial.description = Zeitpunkt der nächsten partiellen Sonnenfinsternis
|
||||
channel-type.astro.ring.label = Ringförmige Sonnenfinsternis
|
||||
channel-type.astro.ring.description = Zeitpunkt der nächsten ringförmigen Sonnenfinsternis
|
||||
channel-type.astro.sunPhaseName.label = Sonnenphase
|
||||
channel-type.astro.sunPhaseName.description = Der Name der aktuellen Sonnenphase
|
||||
channel-type.astro.sunPhaseName.state.option.SUN_RISE = Sonnenaufgang
|
||||
channel-type.astro.sunPhaseName.state.option.ASTRO_DAWN = Astronomische Morgendämmerung
|
||||
channel-type.astro.sunPhaseName.state.option.NAUTIC_DAWN = Nautische Dämmerung
|
||||
channel-type.astro.sunPhaseName.state.option.CIVIL_DAWN = Bürgerliche Morgendämmerung
|
||||
channel-type.astro.sunPhaseName.state.option.CIVIL_DUSK = Bürgerliche Abenddämmerung
|
||||
channel-type.astro.sunPhaseName.state.option.NAUTIC_DUSK = Nautische Abenddämmerung
|
||||
channel-type.astro.sunPhaseName.state.option.ASTRO_DUSK = Astronomische Abenddämmerung
|
||||
channel-type.astro.sunPhaseName.state.option.SUN_SET = Sonnenuntergang
|
||||
channel-type.astro.sunPhaseName.state.option.DAYLIGHT = Tageslicht
|
||||
channel-type.astro.sunPhaseName.state.option.NOON = Mittag
|
||||
channel-type.astro.sunPhaseName.state.option.NIGHT = Nacht
|
||||
channel-type.astro.firstQuarter.label = Erstes Viertel
|
||||
channel-type.astro.firstQuarter.description = Zeitpunkt des nächsten erstes Viertels
|
||||
channel-type.astro.thirdQuarter.label = Drittes Viertel
|
||||
channel-type.astro.thirdQuarter.description = Zeitpunkt des nächsten dritten Viertels
|
||||
channel-type.astro.fullMoon.label = Vollmond
|
||||
channel-type.astro.fullMoon.description = Zeitpunkt des nächsten Vollmondes
|
||||
channel-type.astro.newMoon.label = Neumond
|
||||
channel-type.astro.newMoon.description = Zeitpunkt des nächsten Neumondes
|
||||
channel-type.astro.age.label = Mondalter
|
||||
channel-type.astro.age.description = Das Alter des Mondes in Tagen
|
||||
channel-type.astro.ageDegree.label = Mondalter
|
||||
channel-type.astro.ageDegree.description = Das Alter des Mondes
|
||||
channel-type.astro.agePercent.label = Mondalter
|
||||
channel-type.astro.agePercent.description = Das Alter des Mondes
|
||||
channel-type.astro.illumination.label = Beleuchtungsstärke des Mondes
|
||||
channel-type.astro.illumination.description = Die Beleuchtungsstärke des Mondes
|
||||
channel-type.astro.phaseName.label = Mondphase
|
||||
channel-type.astro.phaseName.description = Der Name der aktuellen Mondphase
|
||||
channel-type.astro.phaseName.state.option.NEW = Neumond
|
||||
channel-type.astro.phaseName.state.option.WAXING_CRESCENT = Zunehmende Sichel
|
||||
channel-type.astro.phaseName.state.option.FIRST_QUARTER = Erstes Viertel
|
||||
channel-type.astro.phaseName.state.option.WAXING_GIBBOUS = Zunehmender Halbmond
|
||||
channel-type.astro.phaseName.state.option.FULL = Vollmond
|
||||
channel-type.astro.phaseName.state.option.WANING_GIBBOUS = Abnehmender Halbmond
|
||||
channel-type.astro.phaseName.state.option.THIRD_QUARTER = Drittes Viertel
|
||||
channel-type.astro.phaseName.state.option.WANING_CRESCENT = Abnehmende Sichel
|
||||
channel-type.astro.distanceDate.label = Datum
|
||||
channel-type.astro.distanceDate.description = Zeitpunkt wenn der Abstand erreicht ist
|
||||
channel-type.astro.distance.label = Entfernung
|
||||
channel-type.astro.distance.description = Die Entfernung des Objekts
|
||||
|
||||
# channel types config
|
||||
channel-type.config.astro.config.offset.label = Verschiebung
|
||||
channel-type.config.astro.config.offset.description = Verschiebt den Zeitpunkt eines Triggers vor oder zurück (in Minuten).
|
||||
|
||||
channel-type.config.astro.config.earliest.label = Frühester Zeitpunkt
|
||||
channel-type.config.astro.config.earliest.description = Frühester Zeitpunkt eines Triggers am aktuellen Tag (in hh:mm).
|
||||
|
||||
channel-type.config.astro.config.latest.label = Spätester Zeitpunkt
|
||||
channel-type.config.astro.config.latest.description = Spätester Zeitpunkt eines Triggers am aktuellen Tag (in hh:mm).
|
||||
|
||||
# Discovery result
|
||||
discovery.astro.sun.local.label = Lokale Sonnendaten
|
||||
discovery.astro.moon.local.label = Lokale Monddaten
|
||||
@@ -0,0 +1,162 @@
|
||||
# binding
|
||||
binding.astro.name = Astro ba\u011flant\u0131s\u0131
|
||||
binding.astro.description = Astro ba\u011flant\u0131s\u0131 ay ve güne\u015f bilgilerini hesaplamaya yarar.
|
||||
|
||||
# thing types
|
||||
thing-type.astro.sun.label = Astronomik güne\u015f verileri
|
||||
thing-type.astro.sun.description = Astronomik güne\u015f verileri sunar
|
||||
thing-type.astro.sun.group.rise.label = Gündo\u011fumu
|
||||
thing-type.astro.sun.group.set.label = Günbat\u0131\u015f\u0131
|
||||
thing-type.astro.sun.group.noon.label = Ö\u011fle
|
||||
thing-type.astro.sun.group.night.label = Gece
|
||||
thing-type.astro.sun.group.morningNight.label = Sabah gecesi
|
||||
thing-type.astro.sun.group.astroDawn.label = Astronomik \u015fafak
|
||||
thing-type.astro.sun.group.nauticDawn.label = Denizcilik \u015fafa\u011f\u0131
|
||||
thing-type.astro.sun.group.civilDawn.label = Medeni \u015fafak
|
||||
thing-type.astro.sun.group.astroDusk.label = Astronomik alacakaranl\u0131k
|
||||
thing-type.astro.sun.group.nauticDusk.label = Deizcilik alacakaranl\u0131\u011f\u0131
|
||||
thing-type.astro.sun.group.civilDusk.label = Medeni alacakaranl\u0131k
|
||||
thing-type.astro.sun.group.eveningNight.label = Ak\u015fam gecesi
|
||||
thing-type.astro.sun.group.daylight.label = Gün \u0131\u015f\u0131\u011f\u0131
|
||||
thing-type.astro.sun.group.position.description = Güne\u015fin konumu
|
||||
|
||||
thing-type.astro.moon.label = Astronomik ay verileri
|
||||
thing-type.astro.moon.description = Astronomik ay verileri sa\u011flar
|
||||
thing-type.astro.moon.group.rise.label = Ay do\u011fu\u015fu
|
||||
thing-type.astro.moon.group.set.label = Ay bat\u0131\u015f\u0131
|
||||
thing-type.astro.moon.group.distance.description = Aya olan mesafe
|
||||
thing-type.astro.moon.group.perigee.label = Yerberi
|
||||
thing-type.astro.moon.group.perigee.description = Ay\u0131n yerberi
|
||||
thing-type.astro.moon.group.apogee.label = Yeröte
|
||||
thing-type.astro.moon.group.apogee.description = Ay\u0131n yerötesi
|
||||
thing-type.astro.moon.group.position.description = Ay\u0131n konumu
|
||||
|
||||
# channel group types
|
||||
channel-group-type.astro.position.label = Konum
|
||||
channel-group-type.astro.position.description = Göksel bedenin konumu
|
||||
channel-group-type.astro.radiation.label = I\u015f\u0131n\u0131m
|
||||
channel-group-type.astro.radiation.description = Güne\u015fin \u0131\u015f\u0131n\u0131m seviyesi
|
||||
channel-group-type.astro.sunRange.label = Aç\u0131kl\u0131k
|
||||
channel-group-type.astro.moonRange.label = Aç\u0131kl\u0131k
|
||||
channel-group-type.astro.sunZodiac.label = Zodyak
|
||||
channel-group-type.astro.sunZodiac.description = Güne\u015fin zodyak i\u015fareti
|
||||
channel-group-type.astro.season.label = Mevsim
|
||||
channel-group-type.astro.season.description = Bu y\u0131l mevsimler
|
||||
channel-group-type.astro.sunEclipse.label = Tutulmalar
|
||||
channel-group-type.astro.sunEclipse.description = Bir sonraki güne\u015f tutulmas\u0131 zaman\u0131
|
||||
channel-group-type.astro.sunPhase.label = Güne\u015f evreleri
|
||||
channel-group-type.astro.sunPhase.description = Mevcut güne\u015f evresinin ayr\u0131nt\u0131lar\u0131n\u0131 görüntüler
|
||||
channel-group-type.astro.moonPhase.label = Ay evreleri
|
||||
channel-group-type.astro.moonPhase.description = Mevcut ve sonraki ay evreleri ayr\u0131nt\u0131lar\u0131n\u0131 görüntüler.
|
||||
channel-group-type.astro.moonEclipse.label = Tutulmalar
|
||||
channel-group-type.astro.moonEclipse.description = Bir sonraki ay tutulmas\u0131 zaman\u0131
|
||||
channel-group-type.astro.distance.label = Mesafe
|
||||
channel-group-type.astro.distance.description = Mesafe verileri
|
||||
channel-group-type.astro.moonZodiac.label = Zodyak
|
||||
channel-group-type.astro.moonZodiac.description = Ay\u0131n Zodyak i\u015fareti
|
||||
|
||||
channel-group-type.astro.moonEclipse.channel.total.label = Toplam ay tutulmas\u0131
|
||||
channel-group-type.astro.moonEclipse.channel.total.description = Bir sonraki toplam ay tutulmas\u0131 zaman\u0131
|
||||
channel-group-type.astro.moonEclipse.channel.partial.label = K\u0131smi ay tutulmas\u0131
|
||||
channel-group-type.astro.moonEclipse.channel.partial.description = Bir sonraki k\u0131smi ay tutulmas\u0131 zaman\u0131
|
||||
|
||||
# channel types
|
||||
channel-type.astro.azimuth.label = Aç\u0131sal konum
|
||||
channel-type.astro.azimuth.description = Gök cisminin aç\u0131sal konumu
|
||||
channel-type.astro.elevation.label = Yükseklik aç\u0131s\u0131
|
||||
channel-type.astro.elevation.description = Gök cisminin yükseklik aç\u0131s\u0131
|
||||
channel-type.astro.shadeLength.label = Gölge uzunluk oran\u0131
|
||||
channel-type.astro.shadeLength.description = Yans\u0131t\u0131lan gölge uzunluk oran\u0131 (yükseklik aç\u0131s\u0131ndan türetilmi\u015ftir)
|
||||
channel-type.astro.directRadiation.label = Do\u011frudan \u0131\u015f\u0131n\u0131m
|
||||
channel-type.astro.directRadiation.description = Atmosferik katmana nüfuz ettikten sonra \u0131\u015f\u0131n\u0131m derecesi
|
||||
channel-type.astro.diffuseRadiation.label = Da\u011f\u0131n\u0131k \u0131\u015f\u0131n\u0131m
|
||||
channel-type.astro.diffuseRadiation.description = Bulutlar\u0131n ve atmosferin da\u011f\u0131tmas\u0131ndan sonra \u0131\u015f\u0131n\u0131m derecesi
|
||||
channel-type.astro.totalRadiation.label = Toplam \u0131\u015f\u0131n\u0131m
|
||||
channel-type.astro.totalRadiation.description = Yere ula\u015fan toplam \u0131\u015f\u0131n\u0131m miktar\u0131
|
||||
channel-type.astro.start.label = Baslang\u0131ç zaman\u0131
|
||||
channel-type.astro.start.description = Etkinli\u011fin ba\u015flang\u0131ç zaman\u0131
|
||||
channel-type.astro.end.label = Biti\u015f zaman\u0131
|
||||
channel-type.astro.end.description = Etkinli\u011fin biti\u015f zaman\u0131
|
||||
channel-type.astro.duration.label = Süre
|
||||
channel-type.astro.duration.description = Etkinli\u011fin süresi
|
||||
channel-type.astro.sign.label = Burç
|
||||
channel-type.astro.sign.description = Burç
|
||||
channel-type.astro.sign.state.option.ARIES = Koç
|
||||
channel-type.astro.sign.state.option.TAURUS = Bo\u011fa
|
||||
channel-type.astro.sign.state.option.GEMINI = \u0130kizler
|
||||
channel-type.astro.sign.state.option.CANCER = Yengeç
|
||||
channel-type.astro.sign.state.option.LEO = Aslan
|
||||
channel-type.astro.sign.state.option.VIRGO = Ba\u015fak
|
||||
channel-type.astro.sign.state.option.LIBRA = Terazi
|
||||
channel-type.astro.sign.state.option.SCORPIO = Akrep
|
||||
channel-type.astro.sign.state.option.SAGITTARIUS = Yay
|
||||
channel-type.astro.sign.state.option.CAPRICORN = O\u011flak
|
||||
channel-type.astro.sign.state.option.AQUARIUS = Kova
|
||||
channel-type.astro.sign.state.option.PISCES = Bal\u0131k
|
||||
channel-type.astro.seasonName.label = Mevsim
|
||||
channel-type.astro.seasonName.description = \u015eu anki mevsimin ad\u0131
|
||||
channel-type.astro.seasonName.state.option.SPRING = \u0130lkbahar
|
||||
channel-type.astro.seasonName.state.option.SUMMER = Yaz
|
||||
channel-type.astro.seasonName.state.option.AUTUMN = Sonbahar
|
||||
channel-type.astro.seasonName.state.option.WINTER = K\u0131\u015f
|
||||
channel-type.astro.spring.label = \u0130lkbahar
|
||||
channel-type.astro.spring.description = \u0130lkbahar ba\u015flang\u0131c\u0131
|
||||
channel-type.astro.summer.label = Yaz
|
||||
channel-type.astro.summer.description = Yaz ba\u015flang\u0131c\u0131
|
||||
channel-type.astro.autumn.label = Sonbahar
|
||||
channel-type.astro.autumn.description = Sonbahar ba\u015flang\u0131c\u0131
|
||||
channel-type.astro.winter.label = K\u0131\u015f
|
||||
channel-type.astro.winter.description = K\u0131\u015f ba\u015flang\u0131c\u0131
|
||||
channel-type.astro.total.label = Toplu güne\u015f tutulmas\u0131
|
||||
channel-type.astro.total.description = Bir sonraki toplu güne\u015f tutulmas\u0131 zaman\u0131
|
||||
channel-type.astro.partial.label = K\u0131smi güne\u015f tutulmas\u0131
|
||||
channel-type.astro.partial.description = Bir sonraki k\u0131smi güne\u015f tutulmas\u0131 zaman\u0131
|
||||
channel-type.astro.ring.label = Halka \u015feklindeki güne\u015f tutulmas\u0131
|
||||
channel-type.astro.ring.description = Bir sonraki halka \u015feklindeki güne\u015f tutulmas\u0131 zaman\u0131
|
||||
channel-type.astro.sunPhaseName.label = Güne\u015f evresi
|
||||
channel-type.astro.sunPhaseName.description = Güncel güne\u015f evresinin ad\u0131
|
||||
channel-type.astro.sunPhaseName.state.option.SUN_RISE = Gün do\u011fumu
|
||||
channel-type.astro.sunPhaseName.state.option.ASTRO_DAWN = Astronomik \u015fafak
|
||||
channel-type.astro.sunPhaseName.state.option.NAUTIC_DAWN = Denizcilik alacakaranl\u0131\u011f\u0131
|
||||
channel-type.astro.sunPhaseName.state.option.CIVIL_DAWN = Medeni \u015fafak
|
||||
channel-type.astro.sunPhaseName.state.option.CIVIL_DUSK = Medeni alacakaranl\u0131k
|
||||
channel-type.astro.sunPhaseName.state.option.NAUTIC_DUSK = Denizcilik alacakaranl\u0131\u011f\u0131
|
||||
channel-type.astro.sunPhaseName.state.option.ASTRO_DUSK = Denizcilik alacakaranl\u0131\u011f\u0131
|
||||
channel-type.astro.sunPhaseName.state.option.SUN_SET = Gün bat\u0131m\u0131
|
||||
channel-type.astro.sunPhaseName.state.option.DAYLIGHT = Gün \u0131\u015f\u0131\u011f\u0131
|
||||
channel-type.astro.sunPhaseName.state.option.NOON = Ö\u011fle vakti
|
||||
channel-type.astro.sunPhaseName.state.option.NIGHT = Gece
|
||||
channel-type.astro.firstQuarter.label = \u0130lk çeyrek
|
||||
channel-type.astro.firstQuarter.description = Bir sonraki ilk çeyre\u011fin zaman\u0131
|
||||
channel-type.astro.thirdQuarter.label = Üçüncü çeyrek
|
||||
channel-type.astro.thirdQuarter.description = Bir sonraki üçüncü çeyre\u011fin zaman\u0131
|
||||
channel-type.astro.fullMoon.label = Dolunay
|
||||
channel-type.astro.fullMoon.description = Bir sonraki dolunay\u0131n zaman\u0131
|
||||
channel-type.astro.newMoon.label = Yeni ay
|
||||
channel-type.astro.newMoon.description = Bir sonraki yeni ay\u0131n zaman\u0131
|
||||
channel-type.astro.age.label = Ay ya\u015f\u0131
|
||||
channel-type.astro.age.description = Gün olarak ay ya\u015f\u0131
|
||||
channel-type.astro.ageDegree.label = Ay ya\u015f\u0131
|
||||
channel-type.astro.ageDegree.description = Ay ya\u015f\u0131
|
||||
channel-type.astro.agePercent.label = Ay ya\u015f\u0131
|
||||
channel-type.astro.agePercent.description = Ay ya\u015f\u0131
|
||||
channel-type.astro.illumination.label = Ay\u0131n ayd\u0131nl\u0131\u011f\u0131
|
||||
channel-type.astro.illumination.description = Ay\u0131n ayd\u0131nl\u0131\u011f\u0131
|
||||
channel-type.astro.phaseName.label = Ay evresi
|
||||
channel-type.astro.phaseName.description = Mevcut ay evresinin ad\u0131
|
||||
channel-type.astro.phaseName.state.option.NEW = Yeni ay
|
||||
channel-type.astro.phaseName.state.option.WAXING_CRESCENT = Artan orak
|
||||
channel-type.astro.phaseName.state.option.FIRST_QUARTER = \u0130lk dördün
|
||||
channel-type.astro.phaseName.state.option.WAXING_GIBBOUS = \u015ei\u015fkin ay
|
||||
channel-type.astro.phaseName.state.option.FULL = Dolunay
|
||||
channel-type.astro.phaseName.state.option.WANING_GIBBOUS = Azalan Hilal
|
||||
channel-type.astro.phaseName.state.option.THIRD_QUARTER = Üçüncü çeyrek
|
||||
channel-type.astro.phaseName.state.option.WANING_CRESCENT = Azalan orak
|
||||
channel-type.astro.distanceDate.label = Tarih
|
||||
channel-type.astro.distanceDate.description = Mesafeye ula\u015f\u0131ld\u0131\u011f\u0131 zaman
|
||||
channel-type.astro.distance.label = Mesafe
|
||||
channel-type.astro.distance.description = Nesnenin uzakl\u0131\u011f\u0131
|
||||
|
||||
# Discovery result
|
||||
discovery.astro.sun.local.label = Yerel güne\u015f bilgileri
|
||||
discovery.astro.moon.local.label = Yerel ay bilgileri
|
||||
@@ -0,0 +1,485 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="astro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- position -->
|
||||
<channel-group-type id="position">
|
||||
<label>Position</label>
|
||||
<description>The position of the planet</description>
|
||||
<channels>
|
||||
<channel id="azimuth" typeId="azimuth"/>
|
||||
<channel id="elevation" typeId="elevation"/>
|
||||
<channel id="shadeLength" typeId="shadeLength"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="radiation">
|
||||
<label>Radiation</label>
|
||||
<description>The radiation level of the sun</description>
|
||||
<channels>
|
||||
<channel id="direct" typeId="directRadiation"/>
|
||||
<channel id="diffuse" typeId="diffuseRadiation"/>
|
||||
<channel id="total" typeId="totalRadiation"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="azimuth">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Azimuth</label>
|
||||
<description>The azimuth of the planet</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="elevation">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Elevation</label>
|
||||
<description>The elevation of the planet</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="shadeLength">
|
||||
<item-type>Number</item-type>
|
||||
<label>Shade Length Ratio</label>
|
||||
<description>Projected shade length ratio (derived from Elevation)</description>
|
||||
<state readOnly="true" pattern="%.2f"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="directRadiation">
|
||||
<item-type>Number:Intensity</item-type>
|
||||
<label>Direct Radiation</label>
|
||||
<description>Level of radiation after penetration of the atmospheric layer</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="diffuseRadiation">
|
||||
<item-type>Number:Intensity</item-type>
|
||||
<label>Diffuse Radiation</label>
|
||||
<description>Level of radiation diffracted by clouds and atmosphere</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="totalRadiation">
|
||||
<item-type>Number:Intensity</item-type>
|
||||
<label>Total Radiation</label>
|
||||
<description>Total quantity of radiation on the ground</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- sun range -->
|
||||
<channel-group-type id="sunRange">
|
||||
<label>Range</label>
|
||||
<description>Range for a sun event</description>
|
||||
<channels>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="end" typeId="end"/>
|
||||
<channel id="duration" typeId="duration"/>
|
||||
<channel id="event" typeId="rangeEvent"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="moonRange">
|
||||
<label>Range</label>
|
||||
<description>Range for a moon event</description>
|
||||
<channels>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="end" typeId="end"/>
|
||||
<channel id="event" typeId="rangeEvent"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="start">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Start Time</label>
|
||||
<description>The start time of the event</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="end">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>End Time</label>
|
||||
<description>The end time of the event</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="duration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Duration</label>
|
||||
<description>The duration of the event</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rangeEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>Range Event</label>
|
||||
<description>Range event</description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="START">start</option>
|
||||
<option value="END">end</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- sunZodiac -->
|
||||
<channel-group-type id="sunZodiac">
|
||||
<label>Zodiac</label>
|
||||
<description>The zodiac of the sun</description>
|
||||
<channels>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="end" typeId="end"/>
|
||||
<channel id="sign" typeId="sign"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="sign">
|
||||
<item-type>String</item-type>
|
||||
<label>Sign</label>
|
||||
<description>The sign of the zodiac</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="ARIES">Aries</option>
|
||||
<option value="TAURUS">Taurus</option>
|
||||
<option value="GEMINI">Gemini</option>
|
||||
<option value="CANCER">Cancer</option>
|
||||
<option value="LEO">Leo</option>
|
||||
<option value="VIRGO">Virgo</option>
|
||||
<option value="LIBRA">Libra</option>
|
||||
<option value="SCORPIO">Scorpio</option>
|
||||
<option value="SAGITTARIUS">Sagittarius</option>
|
||||
<option value="CAPRICORN">Capricorn</option>
|
||||
<option value="AQUARIUS">Aquarius</option>
|
||||
<option value="PISCES">Pisces</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<!-- season -->
|
||||
<channel-group-type id="season">
|
||||
<label>Season</label>
|
||||
<description>The seasons this year</description>
|
||||
<channels>
|
||||
<channel id="name" typeId="seasonName"/>
|
||||
<channel id="spring" typeId="spring"/>
|
||||
<channel id="summer" typeId="summer"/>
|
||||
<channel id="autumn" typeId="autumn"/>
|
||||
<channel id="winter" typeId="winter"/>
|
||||
<channel id="nextName" typeId="seasonName">
|
||||
<label>Next Season</label>
|
||||
<description>The name of the next season</description>
|
||||
</channel>
|
||||
<channel id="timeLeft" typeId="duration">
|
||||
<label>Time Left</label>
|
||||
<description>The time remaining before season change</description>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="seasonName">
|
||||
<item-type>String</item-type>
|
||||
<label>Season Name</label>
|
||||
<description>The name of the current season</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="SPRING">Spring</option>
|
||||
<option value="SUMMER">Summer</option>
|
||||
<option value="AUTUMN">Autumn</option>
|
||||
<option value="WINTER">Winter</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="spring">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Spring</label>
|
||||
<description>The beginning of spring</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="summer">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Summer</label>
|
||||
<description>The beginning of summer</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="autumn">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Autumn</label>
|
||||
<description>The beginning of autumn</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="winter">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Winter</label>
|
||||
<description>The beginning of winter</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- sun eclipse -->
|
||||
<channel-group-type id="sunEclipse">
|
||||
<label>Eclipses</label>
|
||||
<description>The DateTime of the next sun eclipses</description>
|
||||
<channels>
|
||||
<channel id="total" typeId="total"/>
|
||||
<channel id="totalElevation" typeId="elevation"/>
|
||||
<channel id="partial" typeId="partial"/>
|
||||
<channel id="partialElevation" typeId="elevation"/>
|
||||
<channel id="ring" typeId="ring"/>
|
||||
<channel id="ringElevation" typeId="elevation"/>
|
||||
<channel id="event" typeId="sunEclipseEvent"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="total">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Total Eclipse</label>
|
||||
<description>The DateTime of the next total eclipse</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="partial">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Partial Eclipse</label>
|
||||
<description>The DateTime of the next partial eclipse</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ring">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Ring Eclipse</label>
|
||||
<description>The DateTime of the next ring eclipse</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sunEclipseEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>Sun Eclipse Event</label>
|
||||
<description>Sun eclipse event</description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="TOTAL">total</option>
|
||||
<option value="PARTIAL">partial</option>
|
||||
<option value="RING">ring</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- sunphase -->
|
||||
<channel-group-type id="sunPhase">
|
||||
<label>Sun Phase</label>
|
||||
<description>The details of the current sun phase</description>
|
||||
<channels>
|
||||
<channel id="name" typeId="sunPhaseName"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="sunPhaseName">
|
||||
<item-type>String</item-type>
|
||||
<label>Sun Phase Name</label>
|
||||
<description>The name of the current sun phase</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="SUN_RISE">Sunrise</option>
|
||||
<option value="ASTRO_DAWN">Astro dawn</option>
|
||||
<option value="NAUTIC_DAWN">Nautic dawn</option>
|
||||
<option value="CIVIL_DAWN">Civil dawn</option>
|
||||
<option value="CIVIL_DUSK">Civil dusk</option>
|
||||
<option value="NAUTIC_DUSK">Nautic dusk</option>
|
||||
<option value="ASTRO_DUSK">Astro dusk</option>
|
||||
<option value="SUN_SET">Sunset</option>
|
||||
<option value="DAYLIGHT">Daylight</option>
|
||||
<option value="NOON">Noon</option>
|
||||
<option value="NIGHT">Night</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<!-- moonphase -->
|
||||
<channel-group-type id="moonPhase">
|
||||
<label>Moon Phase</label>
|
||||
<description>The details of the current and next moon phases</description>
|
||||
<channels>
|
||||
<channel id="firstQuarter" typeId="firstQuarter"/>
|
||||
<channel id="thirdQuarter" typeId="thirdQuarter"/>
|
||||
<channel id="full" typeId="fullMoon"/>
|
||||
<channel id="new" typeId="newMoon"/>
|
||||
<channel id="age" typeId="age"/>
|
||||
<channel id="ageDegree" typeId="ageDegree"/>
|
||||
<channel id="agePercent" typeId="agePercent"/>
|
||||
<channel id="illumination" typeId="illumination"/>
|
||||
<channel id="name" typeId="phaseName"/>
|
||||
<channel id="event" typeId="phaseEvent"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="firstQuarter">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>First Quarter</label>
|
||||
<description>The DateTime the moon is in the first quarter</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="thirdQuarter">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Third Quarter</label>
|
||||
<description>The DateTime the moon is in the third quarter</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fullMoon">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Full Moon</label>
|
||||
<description>The DateTime for full moon</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="newMoon">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>New Moon</label>
|
||||
<description>The DateTime for new moon</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="age">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Moon Age</label>
|
||||
<description>The age of the moon in days</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ageDegree">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Moon Age</label>
|
||||
<description>The age of the moon</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="agePercent">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Moon Age</label>
|
||||
<description>The age of the moon</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="illumination">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Moon Illumination</label>
|
||||
<description>The illumination of the moon</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="phaseName">
|
||||
<item-type>String</item-type>
|
||||
<label>Moon Phase Name</label>
|
||||
<description>The name of the current moon phase</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="NEW">New moon</option>
|
||||
<option value="WAXING_CRESCENT">Waxing crescent</option>
|
||||
<option value="FIRST_QUARTER">First quarter</option>
|
||||
<option value="WAXING_GIBBOUS">Waxing gibbous</option>
|
||||
<option value="FULL">Full moon</option>
|
||||
<option value="WANING_GIBBOUS">Waning gibbous</option>
|
||||
<option value="THIRD_QUARTER">Third quarter</option>
|
||||
<option value="WANING_CRESCENT">Waning crescent</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="phaseEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>Moon Phase Event</label>
|
||||
<description>Moon phase event</description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="FIRST_QUARTER">first quarter</option>
|
||||
<option value="THIRD_QUARTER">third quarter</option>
|
||||
<option value="FULL">full</option>
|
||||
<option value="NEW">new</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
|
||||
<!-- moon eclipse -->
|
||||
<channel-group-type id="moonEclipse">
|
||||
<label>Eclipses</label>
|
||||
<description>The DateTime of the next moon eclipses</description>
|
||||
<channels>
|
||||
<channel id="total" typeId="total"/>
|
||||
<channel id="totalElevation" typeId="elevation"/>
|
||||
<channel id="partial" typeId="partial"/>
|
||||
<channel id="partialElevation" typeId="elevation"/>
|
||||
<channel id="event" typeId="moonEclipseEvent"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="moonEclipseEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>Moon Eclipse Event</label>
|
||||
<description>Moon eclipse event</description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="TOTAL">total</option>
|
||||
<option value="PARTIAL">partial</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- distance -->
|
||||
<channel-group-type id="distance">
|
||||
<label>Distance</label>
|
||||
<description>Distance data</description>
|
||||
<channels>
|
||||
<channel id="date" typeId="distanceDate"/>
|
||||
<channel id="distance" typeId="distance"/>
|
||||
<channel id="event" typeId="distanceEvent"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="distanceDate">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Date</label>
|
||||
<description>The DateTime when the distance is reached</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="distance">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Distance</label>
|
||||
<description>The distance of the object</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="distanceEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>Moon Distance Event</label>
|
||||
<description>Moon distance event</description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="PERIGEE">perigee</option>
|
||||
<option value="APOGEE">apogee</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description-ref uri="channel-type:astro:config"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- moonZodiac -->
|
||||
<channel-group-type id="moonZodiac">
|
||||
<label>Zodiac</label>
|
||||
<description>The zodiac of the moon</description>
|
||||
<channels>
|
||||
<channel id="sign" typeId="sign"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="astro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="moon">
|
||||
<label>Astro Moon Data</label>
|
||||
<description>Provides astronomical data from the moon</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="rise" typeId="moonRange">
|
||||
<label>Moonrise</label>
|
||||
<description>The moonrise event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="set" typeId="moonRange">
|
||||
<label>Moonset</label>
|
||||
<description>The moonset event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="phase" typeId="moonPhase"/>
|
||||
<channel-group id="eclipse" typeId="moonEclipse"/>
|
||||
<channel-group id="distance" typeId="distance">
|
||||
<description>The distance to the moon</description>
|
||||
</channel-group>
|
||||
<channel-group id="perigee" typeId="distance">
|
||||
<label>Perigee</label>
|
||||
<description>The perigee of the moon</description>
|
||||
</channel-group>
|
||||
<channel-group id="apogee" typeId="distance">
|
||||
<label>Apogee</label>
|
||||
<description>The apogee of the moon</description>
|
||||
</channel-group>
|
||||
<channel-group id="position" typeId="position">
|
||||
<description>The position of the moon</description>
|
||||
</channel-group>
|
||||
<channel-group id="zodiac" typeId="moonZodiac"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>geolocation</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:astro:moonconfig"/>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="astro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="sun">
|
||||
<label>Astro Sun Data</label>
|
||||
<description>Provides astronomical data from the sun</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="rise" typeId="sunRange">
|
||||
<label>Sunrise</label>
|
||||
<description>The sunrise event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="set" typeId="sunRange">
|
||||
<label>Sunset</label>
|
||||
<description>The sunset event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="noon" typeId="sunRange">
|
||||
<label>Noon</label>
|
||||
<description>The noon event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="night" typeId="sunRange">
|
||||
<label>Night</label>
|
||||
<description>The night event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="morningNight" typeId="sunRange">
|
||||
<label>Morning Night</label>
|
||||
<description>The morning night event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="astroDawn" typeId="sunRange">
|
||||
<label>Astro Dawn</label>
|
||||
<description>The astro dawn event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="nauticDawn" typeId="sunRange">
|
||||
<label>Nautic Dawn</label>
|
||||
<description>The nautic dawn event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="civilDawn" typeId="sunRange">
|
||||
<label>Civil Dawn</label>
|
||||
<description>The civil dawn event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="astroDusk" typeId="sunRange">
|
||||
<label>Astro Dusk</label>
|
||||
<description>The astro dusk event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="nauticDusk" typeId="sunRange">
|
||||
<label>Nautic Dusk</label>
|
||||
<description>The nautic dusk event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="civilDusk" typeId="sunRange">
|
||||
<label>Civil Dusk</label>
|
||||
<description>The civil dusk event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="eveningNight" typeId="sunRange">
|
||||
<label>Evening Night</label>
|
||||
<description>The evening night event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="daylight" typeId="sunRange">
|
||||
<label>Daylight</label>
|
||||
<description>The daylight event range</description>
|
||||
</channel-group>
|
||||
<channel-group id="position" typeId="position">
|
||||
<description>The position of the sun</description>
|
||||
</channel-group>
|
||||
<channel-group id="radiation" typeId="radiation"/>
|
||||
<channel-group id="zodiac" typeId="sunZodiac"/>
|
||||
<channel-group id="season" typeId="season"/>
|
||||
<channel-group id="eclipse" typeId="sunEclipse"/>
|
||||
<channel-group id="phase" typeId="sunPhase"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>geolocation</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:astro:sunconfig"/>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.openhab.binding.astro.internal.model.Sun;
|
||||
import org.openhab.binding.astro.internal.model.SunPhaseName;
|
||||
|
||||
/***
|
||||
* Specific unit tests to check if {@link SunCalc} generates correct data for
|
||||
* Amsterdam city on 27 February 2019. In particular the following cases are
|
||||
* covered:
|
||||
* <ul>
|
||||
* <li>checks if generated data are the same (with some accuracy) as produced by
|
||||
* haevens-above.com</li>
|
||||
* <li>checks if the generated {@link Sun#getAllRanges()} are consistent with
|
||||
* each other</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Witold Markowski - Initial contribution
|
||||
* @see <a href="https://github.com/openhab/openhab-addons/issues/5006">[astro]
|
||||
* Sun Phase returns UNDEF</a>
|
||||
* @see <a href="https://www.heavens-above.com/sun.aspx">Heavens Above Sun</a>
|
||||
*/
|
||||
public class SunCalcTest {
|
||||
|
||||
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Europe/Amsterdam");
|
||||
private static final Calendar FEB_27_2019 = SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 1, 0, TIME_ZONE);
|
||||
private static final double AMSTERDAM_LATITUDE = 52.367607;
|
||||
private static final double AMSTERDAM_LONGITUDE = 4.8978293;
|
||||
private static final double AMSTERDAM_ALTITUDE = 0.0;
|
||||
private static final int ACCURACY_IN_MILLIS = 3 * 60 * 1000;
|
||||
|
||||
private SunCalc sunCalc;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
sunCalc = new SunCalc();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForOldDate() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertNotNull(sun.getNight());
|
||||
|
||||
assertNotNull(sun.getAstroDawn());
|
||||
assertNotNull(sun.getNauticDawn());
|
||||
assertNotNull(sun.getCivilDawn());
|
||||
|
||||
assertNotNull(sun.getRise());
|
||||
|
||||
assertNotNull(sun.getDaylight());
|
||||
assertNotNull(sun.getNoon());
|
||||
assertNotNull(sun.getSet());
|
||||
|
||||
assertNotNull(sun.getCivilDusk());
|
||||
assertNotNull(sun.getNauticDusk());
|
||||
assertNotNull(sun.getAstroDusk());
|
||||
assertNotNull(sun.getNight());
|
||||
|
||||
assertNotNull(sun.getMorningNight());
|
||||
assertNotNull(sun.getEveningNight());
|
||||
|
||||
// for an old date the phase is always null
|
||||
assertNull(sun.getPhase().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForAstronomicalDawnAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 05:39 till 06:18
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 5, 39, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getAstroDawn().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getAstroDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForNauticDawnAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 06:18 till 06:58
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getNauticDawn().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getNauticDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForCivilDawnAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 06:58 till 07:32
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getCivilDawn().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getCivilDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForRiseAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 07:32
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getRise().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForSunNoonAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 12:54
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 12, 54, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getNoon().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForSetAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 18:15
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 15, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getSet().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForCivilDuskAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 18:15 till 18:50
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 15, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getCivilDusk().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getCivilDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForNauticDuskAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 18:50 till 19:29
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getNauticDusk().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getNauticDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForAstronomicalDuskAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
// expected result from haevens-above.com is 27 Feb 2019 19:29 till 20:09
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getAstroDusk().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 20, 9, TIME_ZONE).getTimeInMillis(),
|
||||
sun.getAstroDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testRangesForCoherenceBetweenNightEndAndAstroDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.NIGHT).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenMorningNightEndAndAstroDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.MORNING_NIGHT).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenAstroDownEndAndNauticDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenNauticDawnEndAndCivilDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenCivilDawnEndAndSunRiseStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.SUN_RISE).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenSunRiseEndAndDaylightStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.SUN_RISE).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.DAYLIGHT).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenDaylightEndAndSunSetStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.DAYLIGHT).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.SUN_SET).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenSunSetEndAndCivilDuskStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.SUN_SET).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenCivilDuskEndAndNauticDuskStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenNauticDuskEndAndAstroDuskStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenAstroDuskEndAndNightStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.NIGHT).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenAstroDuskEndAndEveningNightStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
|
||||
|
||||
assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getEnd(),
|
||||
sun.getAllRanges().get(SunPhaseName.EVENING_NIGHT).getStart());
|
||||
}
|
||||
|
||||
/***
|
||||
* Constructs a <code>GregorianCalendar</code> with the given date and time set
|
||||
* for the provided time zone.
|
||||
*
|
||||
* @param year
|
||||
* the value used to set the <code>YEAR</code> calendar field in the
|
||||
* calendar.
|
||||
* @param month
|
||||
* the value used to set the <code>MONTH</code> calendar field in the
|
||||
* calendar. Month value is 0-based. e.g., 0 for January.
|
||||
* @param dayOfMonth
|
||||
* the value used to set the <code>DAY_OF_MONTH</code> calendar field
|
||||
* in the calendar.
|
||||
* @param hourOfDay
|
||||
* the value used to set the <code>HOUR_OF_DAY</code> calendar field
|
||||
* in the calendar.
|
||||
* @param minute
|
||||
* the value used to set the <code>MINUTE</code> calendar field in
|
||||
* the calendar.
|
||||
* @param zone
|
||||
* the given time zone.
|
||||
* @return
|
||||
*/
|
||||
private static Calendar newCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, TimeZone zone) {
|
||||
Calendar result = new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute);
|
||||
result.setTimeZone(zone);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAstroAndMeteoSeasons() {
|
||||
Sun meteoSun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE,
|
||||
true);
|
||||
Sun equiSun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE,
|
||||
false);
|
||||
|
||||
assertEquals(meteoSun.getSeason().getSpring().get(Calendar.MONTH),
|
||||
equiSun.getSeason().getSpring().get(Calendar.MONTH));
|
||||
assertEquals(meteoSun.getSeason().getSpring().get(Calendar.YEAR),
|
||||
equiSun.getSeason().getSpring().get(Calendar.YEAR));
|
||||
assertEquals(1, meteoSun.getSeason().getSpring().get(Calendar.DAY_OF_MONTH));
|
||||
assertFalse(meteoSun.getSeason().getSpring().get(Calendar.DAY_OF_MONTH) == equiSun.getSeason().getSpring()
|
||||
.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
|
||||
import org.openhab.binding.astro.internal.util.PropertyUtils;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/***
|
||||
* A set of standard unit test of {@link Sun} class. In particular it checks if
|
||||
* {@link Sun#getAllRanges()} contains a correct {@link SunPhaseName}.
|
||||
*
|
||||
* @author Witold Markowski - Initial contribution
|
||||
* @see <a href="https://github.com/openhab/openhab-addons/issues/5006">[astro]
|
||||
* Sun Phase returns UNDEF</a>
|
||||
*/
|
||||
public class SunTest {
|
||||
|
||||
private Sun sun;
|
||||
private AstroChannelConfig config;
|
||||
|
||||
private static ZoneId ZONE = ZoneId.systemDefault();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
sun = new Sun();
|
||||
config = new AstroChannelConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructor() throws Exception {
|
||||
assertNotNull(sun.getPhase());
|
||||
assertEquals(UnDefType.UNDEF,
|
||||
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStateWhenNullPhaseName() throws Exception {
|
||||
sun.getPhase().setName(null);
|
||||
|
||||
assertEquals(UnDefType.UNDEF,
|
||||
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStateWhenNotNullPhaseName() throws Exception {
|
||||
sun.getPhase().setName(SunPhaseName.DAYLIGHT);
|
||||
|
||||
assertEquals(new StringType("DAYLIGHT"),
|
||||
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testGetStateWhenNullPhase() throws Exception {
|
||||
sun.setPhase(null);
|
||||
|
||||
assertNull(sun.getPhase());
|
||||
assertEquals(UnDefType.UNDEF,
|
||||
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForNight() {
|
||||
sun.setNight(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NIGHT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForMorningNight() {
|
||||
sun.setMorningNight(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.MORNING_NIGHT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForAstroDawn() {
|
||||
sun.setAstroDawn(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.ASTRO_DAWN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForNauticDawn() {
|
||||
sun.setNauticDawn(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NAUTIC_DAWN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForCivilDawn() {
|
||||
sun.setCivilDawn(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.CIVIL_DAWN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForRise() {
|
||||
sun.setRise(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.SUN_RISE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForDaylight() {
|
||||
sun.setDaylight(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.DAYLIGHT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForNoon() {
|
||||
sun.setNoon(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NOON));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForSet() {
|
||||
sun.setSet(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.SUN_SET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForCivilDusk() {
|
||||
sun.setCivilDusk(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.CIVIL_DUSK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForNauticDusk() {
|
||||
sun.setNauticDusk(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NAUTIC_DUSK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForAstroDusk() {
|
||||
sun.setAstroDusk(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.ASTRO_DUSK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllRangesForEveningNight() {
|
||||
sun.setEveningNight(new Range());
|
||||
|
||||
assertTrue(sun.getAllRanges().containsKey(SunPhaseName.EVENING_NIGHT));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user