[astro] Various refactoring/fixes (#19310)

* Fix DateTimeUtilsTest default Locale dependency

Signed-off-by: Ravi Nadahar <nadahar@rediffmail.com>
This commit is contained in:
Nadahar
2025-09-19 11:10:54 +02:00
committed by GitHub
parent cbe003ac59
commit e79d3a6a76
42 changed files with 1094 additions and 571 deletions
@@ -14,8 +14,6 @@ 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 org.eclipse.jdt.annotation.NonNullByDefault;
@@ -23,6 +21,7 @@ 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.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Thing;
@@ -44,15 +43,16 @@ import org.osgi.service.component.annotations.Reference;
public class AstroHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_SUN, THING_TYPE_MOON);
private static final Map<String, AstroThingHandler> ASTRO_THING_HANDLERS = new HashMap<>();
private final CronScheduler scheduler;
private final TimeZoneProvider timeZoneProvider;
private final LocaleProvider localeProvider;
@Activate
public AstroHandlerFactory(final @Reference CronScheduler scheduler,
final @Reference TimeZoneProvider timeZoneProvider) {
final @Reference TimeZoneProvider timeZoneProvider, @Reference LocaleProvider localeProvider) {
this.scheduler = scheduler;
this.timeZoneProvider = timeZoneProvider;
this.localeProvider = localeProvider;
}
@Override
@@ -65,23 +65,10 @@ public class AstroHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
AstroThingHandler thingHandler = null;
if (thingTypeUID.equals(THING_TYPE_SUN)) {
thingHandler = new SunHandler(thing, scheduler, timeZoneProvider);
thingHandler = new SunHandler(thing, scheduler, timeZoneProvider, localeProvider);
} else if (thingTypeUID.equals(THING_TYPE_MOON)) {
thingHandler = new MoonHandler(thing, scheduler, timeZoneProvider);
}
if (thingHandler != null) {
ASTRO_THING_HANDLERS.put(thing.getUID().toString(), thingHandler);
thingHandler = new MoonHandler(thing, scheduler, timeZoneProvider, localeProvider);
}
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);
}
}
@@ -99,7 +99,7 @@ public class AstroActions implements ThingActions {
if (theHandler != null) {
if (theHandler instanceof SunHandler sunHandler) {
Radiation radiation = sunHandler.getRadiationAt(date != null ? date : ZonedDateTime.now());
return radiation.getTotal();
return radiation == null ? null : radiation.getTotal();
} else {
logger.info("Astro Action service ThingHandler is not a SunHandler!");
}
@@ -15,7 +15,10 @@ package org.openhab.binding.astro.internal.calc;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.astro.internal.model.Eclipse;
import org.openhab.binding.astro.internal.model.EclipseKind;
import org.openhab.binding.astro.internal.model.EclipseType;
@@ -39,6 +42,7 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
* http://www.computus.de/mondphase/mondphase.htm azimuth/elevation and
* zodiac based on http://lexikon.astronomie.info/java/sunmoon/
*/
@NonNullByDefault
public class MoonCalc {
private static final double NEW_MOON = 0;
private static final double FULL_MOON = 0.5;
@@ -48,7 +52,7 @@ public class MoonCalc {
/**
* Calculates all moon data at the specified coordinates
*/
public Moon getMoonInfo(Calendar calendar, double latitude, double longitude) {
public Moon getMoonInfo(Calendar calendar, double latitude, double longitude, TimeZone zone, Locale locale) {
Moon moon = new Moon();
double julianDate = DateTimeUtils.dateToJulianDate(calendar);
@@ -75,26 +79,31 @@ public class MoonCalc {
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)));
phase.setNew(DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, NEW_MOON), zone, locale));
phase.setFirstQuarter(
DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, FIRST_QUARTER), zone, locale));
phase.setFull(DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, FULL_MOON), zone, locale));
phase.setThirdQuarter(
DateTimeUtils.toCalendar(getNextPhase(calendar, julianDateMidnight, LAST_QUARTER), zone, locale));
Eclipse eclipse = moon.getEclipse();
eclipse.getKinds().forEach(eclipseKind -> {
double jdate = getEclipse(calendar, EclipseType.MOON, julianDateMidnight, eclipseKind);
eclipse.set(eclipseKind, DateTimeUtils.toCalendar(jdate), new Position());
Calendar eclipseDate = DateTimeUtils.toCalendar(jdate, zone, locale);
if (eclipseDate != null) {
eclipse.set(eclipseKind, eclipseDate, new Position());
}
});
double decimalYear = DateTimeUtils.getDecimalYear(calendar);
MoonDistance apogee = moon.getApogee();
double apogeeJd = getApogee(julianDate, decimalYear);
apogee.setDate(DateTimeUtils.toCalendar(apogeeJd));
apogee.setDate(DateTimeUtils.toCalendar(apogeeJd, zone, locale));
apogee.setDistance(getDistance(apogeeJd));
MoonDistance perigee = moon.getPerigee();
double perigeeJd = getPerigee(julianDate, decimalYear);
perigee.setDate(DateTimeUtils.toCalendar(perigeeJd));
perigee.setDate(DateTimeUtils.toCalendar(perigeeJd, zone, locale));
perigee.setDistance(getDistance(perigeeJd));
return moon;
@@ -103,28 +112,37 @@ public class MoonCalc {
/**
* Calculates the moon illumination and distance.
*/
public void setPositionalInfo(Calendar calendar, double latitude, double longitude, Moon moon) {
public void setPositionalInfo(Calendar calendar, double latitude, double longitude, Moon moon, TimeZone zone,
Locale locale) {
double julianDate = DateTimeUtils.dateToJulianDate(calendar);
setMoonPhase(calendar, moon);
setMoonPhase(calendar, moon, zone, locale);
setAzimuthElevationZodiac(julianDate, latitude, longitude, moon);
MoonDistance distance = moon.getDistance();
distance.setDate(Calendar.getInstance());
distance.setDate(calendar);
distance.setDistance(getDistance(julianDate));
}
/**
* Calculates the age and the current phase.
*/
private void setMoonPhase(Calendar calendar, Moon moon) {
private void setMoonPhase(Calendar calendar, Moon moon, TimeZone zone, Locale locale) {
MoonPhase phase = moon.getPhase();
double julianDate = DateTimeUtils.dateToJulianDate(calendar);
double parentNewMoon = getPreviousPhase(calendar, julianDate, NEW_MOON);
double age = Math.abs(parentNewMoon - julianDate);
Calendar parentNewMoonCal = DateTimeUtils.toCalendar(parentNewMoon, zone, locale);
if (parentNewMoonCal == null) {
return;
}
phase.setAge(age);
long parentNewMoonMillis = DateTimeUtils.toCalendar(parentNewMoon).getTimeInMillis();
long ageRangeTimeMillis = phase.getNew().getTimeInMillis() - parentNewMoonMillis;
long parentNewMoonMillis = parentNewMoonCal.getTimeInMillis();
Calendar cal = phase.getNew();
if (cal == null) {
return;
}
long ageRangeTimeMillis = cal.getTimeInMillis() - parentNewMoonMillis;
long ageCurrentMillis = System.currentTimeMillis() - parentNewMoonMillis;
double agePercent = ageRangeTimeMillis != 0 ? ageCurrentMillis * 100.0 / ageRangeTimeMillis : 0;
phase.setAgePercent(agePercent);
@@ -13,7 +13,11 @@
package org.openhab.binding.astro.internal.calc;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.model.Season;
import org.openhab.binding.astro.internal.model.SeasonName;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
@@ -24,44 +28,61 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
* @author Gerhard Riegler - Initial contribution
* @implNote based on the calculations of http://stellafane.org/misc/equinox.html
*/
@NonNullByDefault
public class SeasonCalc {
private int currentYear;
private Season currentSeason;
private @Nullable Season currentSeason;
/**
* Returns the seasons of the year of the specified calendar.
*/
public Season getSeason(Calendar calendar, double latitude, boolean useMeteorologicalSeason) {
public Season getSeason(Calendar calendar, double latitude, boolean useMeteorologicalSeason, TimeZone zone,
Locale locale) {
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));
season.setSpring(calcEquiSol(0, year, zone, locale));
season.setSummer(calcEquiSol(1, year, zone, locale));
season.setAutumn(calcEquiSol(2, year, zone, locale));
season.setWinter(calcEquiSol(3, year, zone, locale));
} else {
season.setSpring(calcEquiSol(2, year));
season.setSummer(calcEquiSol(3, year));
season.setAutumn(calcEquiSol(0, year));
season.setWinter(calcEquiSol(1, year));
season.setSpring(calcEquiSol(2, year, zone, locale));
season.setSummer(calcEquiSol(3, year, zone, locale));
season.setAutumn(calcEquiSol(0, year, zone, locale));
season.setWinter(calcEquiSol(1, year, zone, locale));
}
currentSeason = season;
currentYear = year;
}
if (useMeteorologicalSeason) {
atMidnightOfFirstMonthDay(season.getSpring());
atMidnightOfFirstMonthDay(season.getSummer());
atMidnightOfFirstMonthDay(season.getAutumn());
atMidnightOfFirstMonthDay(season.getWinter());
if (useMeteorologicalSeason && season != null) {
Calendar cal = season.getSpring();
if (cal != null) {
atMidnightOfFirstMonthDay(cal);
}
cal = season.getSummer();
if (cal != null) {
atMidnightOfFirstMonthDay(cal);
}
cal = season.getAutumn();
if (cal != null) {
atMidnightOfFirstMonthDay(cal);
}
cal = season.getWinter();
if (cal != null) {
atMidnightOfFirstMonthDay(cal);
}
}
season.setName(!isSouthernHemisphere ? getCurrentSeasonNameNorthern(calendar)
: getCurrentSeasonNameSouthern(calendar));
return season;
if (season != null) {
season.setName(!isSouthernHemisphere ? getCurrentSeasonNameNorthern(calendar)
: getCurrentSeasonNameSouthern(calendar));
return season;
}
return new Season();
}
private void atMidnightOfFirstMonthDay(Calendar calendar) {
@@ -75,19 +96,28 @@ public class SeasonCalc {
/**
* Returns the current season name for the northern hemisphere.
*/
@Nullable
private SeasonName getCurrentSeasonNameNorthern(Calendar calendar) {
Season currentSeason = this.currentSeason;
if (currentSeason == null) {
return null;
}
long currentMillis = calendar.getTimeInMillis();
if (currentMillis < currentSeason.getSpring().getTimeInMillis()
|| currentMillis >= currentSeason.getWinter().getTimeInMillis()) {
Calendar spring = currentSeason.getSpring();
Calendar summer = currentSeason.getSummer();
Calendar autumn = currentSeason.getAutumn();
Calendar winter = currentSeason.getWinter();
if ((spring != null && currentMillis < spring.getTimeInMillis())
|| (winter != null && currentMillis >= winter.getTimeInMillis())) {
return SeasonName.WINTER;
} else if (currentMillis >= currentSeason.getSpring().getTimeInMillis()
&& currentMillis < currentSeason.getSummer().getTimeInMillis()) {
} else if (spring != null && summer != null && currentMillis >= spring.getTimeInMillis()
&& currentMillis < summer.getTimeInMillis()) {
return SeasonName.SPRING;
} else if (currentMillis >= currentSeason.getSummer().getTimeInMillis()
&& currentMillis < currentSeason.getAutumn().getTimeInMillis()) {
} else if (summer != null && autumn != null && currentMillis >= summer.getTimeInMillis()
&& currentMillis < autumn.getTimeInMillis()) {
return SeasonName.SUMMER;
} else if (currentMillis >= currentSeason.getAutumn().getTimeInMillis()
&& currentMillis < currentSeason.getWinter().getTimeInMillis()) {
} else if (autumn != null && winter != null && currentMillis >= autumn.getTimeInMillis()
&& currentMillis < winter.getTimeInMillis()) {
return SeasonName.AUTUMN;
}
return null;
@@ -96,19 +126,28 @@ public class SeasonCalc {
/**
* Returns the current season name for the southern hemisphere.
*/
@Nullable
private SeasonName getCurrentSeasonNameSouthern(Calendar calendar) {
Season currentSeason = this.currentSeason;
if (currentSeason == null) {
return null;
}
long currentMillis = calendar.getTimeInMillis();
if (currentMillis < currentSeason.getAutumn().getTimeInMillis()
|| currentMillis >= currentSeason.getSummer().getTimeInMillis()) {
Calendar spring = currentSeason.getSpring();
Calendar summer = currentSeason.getSummer();
Calendar autumn = currentSeason.getAutumn();
Calendar winter = currentSeason.getWinter();
if ((autumn != null && currentMillis < autumn.getTimeInMillis())
|| (summer != null && currentMillis >= summer.getTimeInMillis())) {
return SeasonName.SUMMER;
} else if (currentMillis >= currentSeason.getAutumn().getTimeInMillis()
&& currentMillis < currentSeason.getWinter().getTimeInMillis()) {
} else if (autumn != null && winter != null && currentMillis >= autumn.getTimeInMillis()
&& currentMillis < winter.getTimeInMillis()) {
return SeasonName.AUTUMN;
} else if (currentMillis >= currentSeason.getWinter().getTimeInMillis()
&& currentMillis < currentSeason.getSpring().getTimeInMillis()) {
} else if (winter != null && spring != null && currentMillis >= winter.getTimeInMillis()
&& currentMillis < spring.getTimeInMillis()) {
return SeasonName.WINTER;
} else if (currentMillis >= currentSeason.getSpring().getTimeInMillis()
&& currentMillis < currentSeason.getSummer().getTimeInMillis()) {
} else if (spring != null && summer != null && currentMillis >= spring.getTimeInMillis()
&& currentMillis < summer.getTimeInMillis()) {
return SeasonName.SPRING;
}
return null;
@@ -117,14 +156,15 @@ public class SeasonCalc {
/**
* Calculates the date of the season.
*/
private Calendar calcEquiSol(int season, int year) {
@Nullable
private Calendar calcEquiSol(int season, int year, TimeZone zone, Locale locale) {
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);
return DateTimeUtils.toCalendar(julianDate, zone, locale);
}
/**
@@ -18,9 +18,13 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.model.Eclipse;
import org.openhab.binding.astro.internal.model.EclipseType;
import org.openhab.binding.astro.internal.model.Position;
@@ -37,6 +41,7 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
* @author Christoph Weitkamp - Introduced UoM
* @implNote based on the calculations of http://www.suncalc.net
*/
@NonNullByDefault
public class SunCalc {
private static final double J2000 = 2451545.0;
private static final double SC = 1367; // Solar constant in W/m²
@@ -69,7 +74,8 @@ public class SunCalc {
/**
* Calculates the sun position (azimuth and elevation).
*/
public void setPositionalInfo(Calendar calendar, double latitude, double longitude, Double altitude, Sun sun) {
public void setPositionalInfo(Calendar calendar, double latitude, double longitude, @Nullable Double altitude,
Sun sun) {
double lw = -longitude * DEG2RAD;
double phi = latitude * DEG2RAD;
@@ -96,7 +102,7 @@ public class SunCalc {
/**
* Calculates sun radiation data.
*/
private void setRadiationInfo(Calendar calendar, double elevation, Double altitude, Sun sun) {
private void setRadiationInfo(Calendar calendar, double elevation, @Nullable Double altitude, Sun sun) {
double sinAlpha = Math.sin(DEG2RAD * elevation);
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
@@ -125,7 +131,7 @@ public class SunCalc {
/**
* Returns true, if the sun is up all day (no rise and set).
*/
private boolean isSunUpAllDay(Calendar calendar, double latitude, double longitude, Double altitude) {
private boolean isSunUpAllDay(Calendar calendar, double latitude, double longitude, @Nullable Double altitude) {
Calendar cal = DateTimeUtils.truncateToMidnight(calendar);
Sun sun = new Sun();
for (int minutes = 0; minutes <= MINUTES_PER_DAY; minutes += CURVE_TIME_INTERVAL) {
@@ -141,13 +147,13 @@ public class SunCalc {
/**
* 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);
public Sun getSunInfo(Calendar calendar, double latitude, double longitude, @Nullable Double altitude,
boolean useMeteorologicalSeason, TimeZone zone, Locale locale) {
return getSunInfo(calendar, latitude, longitude, altitude, false, useMeteorologicalSeason, zone, locale);
}
private Sun getSunInfo(Calendar calendar, double latitude, double longitude, Double altitude, boolean onlyAstro,
boolean useMeteorologicalSeason) {
private Sun getSunInfo(Calendar calendar, double latitude, double longitude, @Nullable Double altitude,
boolean onlyAstro, boolean useMeteorologicalSeason, TimeZone zone, Locale locale) {
double lw = -longitude * DEG2RAD;
double phi = latitude * DEG2RAD;
double j = DateTimeUtils.midnightDateToJulianDate(calendar) + 0.5;
@@ -176,23 +182,31 @@ public class SunCalc {
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)));
sun.setAstroDawn(new Range(DateTimeUtils.toCalendar(jastro2, zone, locale),
DateTimeUtils.toCalendar(jnau2, zone, locale)));
sun.setAstroDusk(new Range(DateTimeUtils.toCalendar(jastro, zone, locale),
DateTimeUtils.toCalendar(jdark, zone, locale)));
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.setNoon(new Range(DateTimeUtils.toCalendar(jtransit, zone, locale),
DateTimeUtils.toCalendar(jtransit + JD_ONE_MINUTE_FRACTION, zone, locale)));
sun.setRise(new Range(DateTimeUtils.toCalendar(jrise, zone, locale),
DateTimeUtils.toCalendar(jriseend, zone, locale)));
sun.setSet(new Range(DateTimeUtils.toCalendar(jsetstart, zone, locale),
DateTimeUtils.toCalendar(jset, zone, locale)));
sun.setCivilDawn(new Range(DateTimeUtils.toCalendar(jciv2), DateTimeUtils.toCalendar(jrise)));
sun.setCivilDusk(new Range(DateTimeUtils.toCalendar(jset), DateTimeUtils.toCalendar(jnau)));
sun.setCivilDawn(new Range(DateTimeUtils.toCalendar(jciv2, zone, locale),
DateTimeUtils.toCalendar(jrise, zone, locale)));
sun.setCivilDusk(
new Range(DateTimeUtils.toCalendar(jset, zone, locale), DateTimeUtils.toCalendar(jnau, zone, locale)));
sun.setNauticDawn(new Range(DateTimeUtils.toCalendar(jnau2), DateTimeUtils.toCalendar(jciv2)));
sun.setNauticDusk(new Range(DateTimeUtils.toCalendar(jnau), DateTimeUtils.toCalendar(jastro)));
sun.setNauticDawn(new Range(DateTimeUtils.toCalendar(jnau2, zone, locale),
DateTimeUtils.toCalendar(jciv2, zone, locale)));
sun.setNauticDusk(new Range(DateTimeUtils.toCalendar(jnau, zone, locale),
DateTimeUtils.toCalendar(jastro, zone, locale)));
boolean isSunUpAllDay = isSunUpAllDay(calendar, latitude, longitude, altitude);
@@ -210,23 +224,26 @@ public class SunCalc {
// morning night
Sun sunYesterday = getSunInfo(addDays(calendar, -1), latitude, longitude, altitude, true,
useMeteorologicalSeason);
useMeteorologicalSeason, zone, locale);
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) {
Range range, range2;
if ((range = sunYesterday.getAstroDusk()) != null && range.getEnd() != null
&& DateTimeUtils.isSameDay(range.getEnd(), calendar)) {
morningNightRange = new Range(range.getEnd(),
(range2 = sun.getAstroDawn()) == null ? null : range2.getStart());
} else if (isSunUpAllDay || (range2 = sun.getAstroDawn()) == null || range2.getStart() == null) {
morningNightRange = new Range();
} else {
morningNightRange = new Range(DateTimeUtils.truncateToMidnight(calendar), sun.getAstroDawn().getStart());
morningNightRange = new Range(DateTimeUtils.truncateToMidnight(calendar),
(range2 = sun.getAstroDawn()) == null ? null : range2.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)));
if ((range = sun.getAstroDusk()) != null && range.getEnd() != null
&& DateTimeUtils.isSameDay(range.getEnd(), calendar)) {
eveningNightRange = new Range(range.getEnd(), DateTimeUtils.truncateToMidnight(addDays(calendar, 1)));
} else {
eveningNightRange = new Range();
}
@@ -237,8 +254,9 @@ public class SunCalc {
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()));
useMeteorologicalSeason, zone, locale);
sun.setNight(new Range((range = sun.getAstroDusk()) == null ? null : range.getEnd(),
(range2 = sunTomorrow.getAstroDawn()) == null ? null : range2.getStart()));
}
// eclipse
@@ -247,14 +265,17 @@ public class SunCalc {
eclipse.getKinds().forEach(eclipseKind -> {
double jdate = mc.getEclipse(calendar, EclipseType.SUN, j, eclipseKind);
eclipse.set(eclipseKind, DateTimeUtils.toCalendar(jdate), new Position());
Calendar eclipseDate = DateTimeUtils.toCalendar(jdate, zone, locale);
if (eclipseDate != null) {
eclipse.set(eclipseKind, eclipseDate, new Position());
}
});
SunZodiacCalc zodiacCalc = new SunZodiacCalc();
SunZodiacCalc zodiacCalc = new SunZodiacCalc(zone, locale);
zodiacCalc.getZodiac(calendar).ifPresent(z -> sun.setZodiac(z));
SeasonCalc seasonCalc = new SeasonCalc();
sun.setSeason(seasonCalc.getSeason(calendar, latitude, useMeteorologicalSeason));
sun.setSeason(seasonCalc.getSeason(calendar, latitude, useMeteorologicalSeason, zone, locale));
// phase
for (Entry<SunPhaseName, Range> rangeEntry : sortByValue(sun.getAllRanges()).entrySet()) {
@@ -16,8 +16,10 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.astro.internal.model.SunZodiac;
@@ -33,6 +35,14 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
public class SunZodiacCalc {
private Map<Integer, List<SunZodiac>> zodiacsByYear = new HashMap<>();
private final TimeZone zone;
private final Locale locale;
public SunZodiacCalc(TimeZone zone, Locale locale) {
this.zone = zone;
this.locale = locale;
}
/**
* Returns the zodiac for the specified calendar.
*/
@@ -58,31 +68,31 @@ public class SunZodiacCalc {
List<SunZodiac> zodiacs = new ArrayList<>();
zodiacs.add(new SunZodiac(ZodiacSign.ARIES,
DateTimeUtils.getRange(year, Calendar.MARCH, 21, year, Calendar.APRIL, 19)));
DateTimeUtils.getRange(year, Calendar.MARCH, 21, year, Calendar.APRIL, 19, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.TAURUS,
DateTimeUtils.getRange(year, Calendar.APRIL, 20, year, Calendar.MAY, 20)));
DateTimeUtils.getRange(year, Calendar.APRIL, 20, year, Calendar.MAY, 20, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.GEMINI,
DateTimeUtils.getRange(year, Calendar.MAY, 21, year, Calendar.JUNE, 20)));
DateTimeUtils.getRange(year, Calendar.MAY, 21, year, Calendar.JUNE, 20, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.CANCER,
DateTimeUtils.getRange(year, Calendar.JUNE, 21, year, Calendar.JULY, 22)));
DateTimeUtils.getRange(year, Calendar.JUNE, 21, year, Calendar.JULY, 22, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.LEO,
DateTimeUtils.getRange(year, Calendar.JULY, 23, year, Calendar.AUGUST, 22)));
DateTimeUtils.getRange(year, Calendar.JULY, 23, year, Calendar.AUGUST, 22, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.VIRGO,
DateTimeUtils.getRange(year, Calendar.AUGUST, 23, year, Calendar.SEPTEMBER, 22)));
DateTimeUtils.getRange(year, Calendar.AUGUST, 23, year, Calendar.SEPTEMBER, 22, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.LIBRA,
DateTimeUtils.getRange(year, Calendar.SEPTEMBER, 23, year, Calendar.OCTOBER, 22)));
DateTimeUtils.getRange(year, Calendar.SEPTEMBER, 23, year, Calendar.OCTOBER, 22, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.SCORPIO,
DateTimeUtils.getRange(year, Calendar.OCTOBER, 23, year, Calendar.NOVEMBER, 21)));
DateTimeUtils.getRange(year, Calendar.OCTOBER, 23, year, Calendar.NOVEMBER, 21, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.SAGITTARIUS,
DateTimeUtils.getRange(year, Calendar.NOVEMBER, 22, year, Calendar.DECEMBER, 21)));
DateTimeUtils.getRange(year, Calendar.NOVEMBER, 22, year, Calendar.DECEMBER, 21, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.CAPRICORN,
DateTimeUtils.getRange(year, Calendar.DECEMBER, 22, year + 1, Calendar.JANUARY, 19)));
DateTimeUtils.getRange(year, Calendar.DECEMBER, 22, year + 1, Calendar.JANUARY, 19, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.CAPRICORN,
DateTimeUtils.getRange(year - 1, Calendar.DECEMBER, 22, year, Calendar.JANUARY, 19)));
DateTimeUtils.getRange(year - 1, Calendar.DECEMBER, 22, year, Calendar.JANUARY, 19, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.AQUARIUS,
DateTimeUtils.getRange(year, Calendar.JANUARY, 20, year, Calendar.FEBRUARY, 18)));
DateTimeUtils.getRange(year, Calendar.JANUARY, 20, year, Calendar.FEBRUARY, 18, zone, locale)));
zodiacs.add(new SunZodiac(ZodiacSign.PISCES,
DateTimeUtils.getRange(year, Calendar.FEBRUARY, 19, year, Calendar.MARCH, 20)));
DateTimeUtils.getRange(year, Calendar.FEBRUARY, 19, year, Calendar.MARCH, 20, zone, locale)));
return zodiacs;
}
@@ -59,8 +59,9 @@ public class AstroDiscoveryService extends AbstractDiscoveryService {
private final LocationProvider locationProvider;
// All access must be guarded by "this"
private @Nullable ScheduledFuture<?> astroDiscoveryJob;
private @Nullable PointType previousLocation;
private volatile @Nullable PointType previousLocation;
@Activate
public AstroDiscoveryService(final @Reference LocationProvider locationProvider,
@@ -85,7 +86,7 @@ public class AstroDiscoveryService extends AbstractDiscoveryService {
}
@Override
protected void startBackgroundDiscovery() {
protected synchronized void startBackgroundDiscovery() {
if (astroDiscoveryJob == null) {
astroDiscoveryJob = scheduler.scheduleWithFixedDelay(() -> {
PointType currentLocation = locationProvider.getLocation();
@@ -103,11 +104,13 @@ public class AstroDiscoveryService extends AbstractDiscoveryService {
@Override
protected void stopBackgroundDiscovery() {
logger.debug("Stopping Astro device background discovery");
ScheduledFuture<?> discoveryJob = astroDiscoveryJob;
if (discoveryJob != null) {
discoveryJob.cancel(true);
synchronized (this) {
ScheduledFuture<?> discoveryJob = astroDiscoveryJob;
if (discoveryJob != null) {
discoveryJob.cancel(true);
}
astroDiscoveryJob = null;
}
astroDiscoveryJob = null;
}
public void createResults(PointType location) {
@@ -26,7 +26,9 @@ import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
@@ -44,6 +46,7 @@ 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.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.scheduler.CronScheduler;
@@ -77,20 +80,27 @@ public abstract class AstroThingHandler extends BaseThingHandler {
protected final TimeZoneProvider timeZoneProvider;
protected final LocaleProvider localeProvider;
private final Lock monitor = new ReentrantLock();
// All access must be guarded by "monitor"
private final Set<ScheduledFuture<?>> scheduledFutures = new HashSet<>();
// All access must be guarded by "monitor"
private boolean linkedPositionalChannels;
protected AstroThingConfig thingConfig = new AstroThingConfig();
// All access must be guarded by "monitor"
private @Nullable ScheduledCompletableFuture<?> dailyJob;
public AstroThingHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider) {
public AstroThingHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider,
LocaleProvider localeProvider) {
super(thing);
this.cronScheduler = scheduler;
this.timeZoneProvider = timeZoneProvider;
this.localeProvider = localeProvider;
}
@Override
@@ -175,8 +185,8 @@ public abstract class AstroThingHandler extends BaseThingHandler {
}
try {
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
updateState(channelUID,
PropertyUtils.getState(channelUID, config, planet, timeZoneProvider.getTimeZone()));
updateState(channelUID, PropertyUtils.getState(channelUID, config, planet,
TimeZone.getTimeZone(timeZoneProvider.getTimeZone())));
} catch (Exception ex) {
logger.error("Can't update state for channel {} : {}", channelUID, ex.getMessage(), ex);
}
@@ -193,9 +203,10 @@ public abstract class AstroThingHandler extends BaseThingHandler {
try {
stopJobs();
if (getThing().getStatus() == ONLINE) {
String thingUID = getThing().getUID().toString();
TimeZone zone = TimeZone.getTimeZone(timeZoneProvider.getTimeZone());
Locale locale = localeProvider.getLocale();
// Daily Job
Job runnable = getDailyJob();
Job runnable = getDailyJob(zone, locale);
dailyJob = cronScheduler.schedule(runnable, DAILY_MIDNIGHT);
logger.debug("Scheduled {} at midnight", dailyJob);
// Execute daily startup job immediately
@@ -205,7 +216,7 @@ public abstract class AstroThingHandler extends BaseThingHandler {
// Use scheduleAtFixedRate to avoid time drift associated with scheduleWithFixedDelay
linkedPositionalChannels = isPositionalChannelLinked();
if (linkedPositionalChannels) {
Job positionalJob = new PositionalJob(thingUID);
Job positionalJob = new PositionalJob(this);
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(positionalJob, 0, thingConfig.interval,
TimeUnit.SECONDS);
scheduledFutures.add(future);
@@ -258,10 +269,16 @@ public abstract class AstroThingHandler extends BaseThingHandler {
*/
private void linkedChannelChange(ChannelUID channelUID) {
if (Arrays.asList(getPositionalChannelIds()).contains(channelUID.getId())) {
boolean oldValue = linkedPositionalChannels;
linkedPositionalChannels = isPositionalChannelLinked();
if (oldValue != linkedPositionalChannels) {
restartJobs();
boolean newValue = isPositionalChannelLinked();
monitor.lock();
try {
boolean oldValue = linkedPositionalChannels;
linkedPositionalChannels = newValue;
if (oldValue != linkedPositionalChannels) {
restartJobs();
}
} finally {
monitor.unlock();
}
}
}
@@ -313,12 +330,17 @@ public abstract class AstroThingHandler extends BaseThingHandler {
}
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();
monitor.lock();
try {
for (Iterator<ScheduledFuture<?>> iterator = scheduledFutures.iterator(); iterator.hasNext();) {
ScheduledFuture<?> future = iterator.next();
if (future.isDone()) {
logger.trace("Tidying up done future {}", future);
iterator.remove();
}
}
} finally {
monitor.unlock();
}
}
@@ -347,7 +369,7 @@ public abstract class AstroThingHandler extends BaseThingHandler {
/**
* Returns the daily calculation {@link Job} (cannot be {@code null})
*/
protected abstract Job getDailyJob();
protected abstract Job getDailyJob(TimeZone zone, Locale locale);
public abstract @Nullable Position getPositionAt(ZonedDateTime date);
@@ -12,9 +12,12 @@
*/
package org.openhab.binding.astro.internal.handler;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -24,6 +27,7 @@ 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.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Thing;
@@ -40,24 +44,29 @@ public class MoonHandler extends AstroThingHandler {
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;
private volatile @Nullable Moon moon;
/**
* Constructor
*/
public MoonHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider) {
super(thing, scheduler, timeZoneProvider);
public MoonHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider,
LocaleProvider localeProvider) {
super(thing, scheduler, timeZoneProvider, localeProvider);
}
@Override
public void publishPositionalInfo() {
moon = getMoonAt(ZonedDateTime.now());
ZoneId zoneId = timeZoneProvider.getTimeZone();
TimeZone zone = TimeZone.getTimeZone(zoneId);
Locale locale = localeProvider.getLocale();
Moon moon = getMoonAt(ZonedDateTime.now(zoneId), locale);
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
moonCalc.setPositionalInfo(Calendar.getInstance(), latitude != null ? latitude : 0,
longitude != null ? longitude : 0, moon);
moonCalc.setPositionalInfo(Calendar.getInstance(zone, locale), latitude != null ? latitude : 0,
longitude != null ? longitude : 0, moon, zone, locale);
moon.getEclipse().setElevations(this, timeZoneProvider);
this.moon = moon;
publishPlanet();
}
@@ -79,24 +88,24 @@ public class MoonHandler extends AstroThingHandler {
}
@Override
protected Job getDailyJob() {
return new DailyJobMoon(thing.getUID().getAsString(), this);
protected Job getDailyJob(TimeZone zone, Locale locale) {
return new DailyJobMoon(this, zone, locale);
}
private Moon getMoonAt(ZonedDateTime date) {
private Moon getMoonAt(ZonedDateTime date, Locale locale) {
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
return moonCalc.getMoonInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
longitude != null ? longitude : 0);
longitude != null ? longitude : 0, TimeZone.getTimeZone(date.getZone()), locale);
}
@Override
public @Nullable Position getPositionAt(ZonedDateTime date) {
Moon localMoon = getMoonAt(date);
Moon localMoon = getMoonAt(date, Locale.ROOT);
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
moonCalc.setPositionalInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
longitude != null ? longitude : 0, localMoon);
longitude != null ? longitude : 0, localMoon, TimeZone.getTimeZone(date.getZone()), Locale.ROOT);
return localMoon.getPosition();
}
}
@@ -12,9 +12,12 @@
*/
package org.openhab.binding.astro.internal.handler;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -27,6 +30,7 @@ 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.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Thing;
@@ -43,25 +47,30 @@ public class SunHandler extends AstroThingHandler {
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;
private volatile @Nullable Sun sun;
/**
* Constructor
*/
public SunHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider) {
super(thing, scheduler, timeZoneProvider);
public SunHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider,
LocaleProvider localeProvider) {
super(thing, scheduler, timeZoneProvider, localeProvider);
}
@Override
public void publishPositionalInfo() {
sun = getSunAt(ZonedDateTime.now());
ZoneId zoneId = timeZoneProvider.getTimeZone();
TimeZone zone = TimeZone.getTimeZone(zoneId);
Locale locale = localeProvider.getLocale();
Sun sun = getSunAt(ZonedDateTime.now(zoneId));
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
Double altitude = thingConfig.altitude;
sunCalc.setPositionalInfo(Calendar.getInstance(), latitude != null ? latitude : 0,
sunCalc.setPositionalInfo(Calendar.getInstance(zone, locale), latitude != null ? latitude : 0,
longitude != null ? longitude : 0, altitude != null ? altitude : 0, sun);
sun.getEclipse().setElevations(this, timeZoneProvider);
this.sun = sun;
publishPlanet();
}
@@ -83,8 +92,8 @@ public class SunHandler extends AstroThingHandler {
}
@Override
protected Job getDailyJob() {
return new DailyJobSun(thing.getUID().getAsString(), this);
protected Job getDailyJob(TimeZone zone, Locale locale) {
return new DailyJobSun(this, zone, locale);
}
private Sun getSunAt(ZonedDateTime date) {
@@ -92,8 +101,8 @@ public class SunHandler extends AstroThingHandler {
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);
longitude != null ? longitude : 0, altitude != null ? altitude : 0, thingConfig.useMeteorologicalSeason,
TimeZone.getTimeZone(timeZoneProvider.getTimeZone()), Locale.ROOT);
}
private Sun getPositionedSunAt(ZonedDateTime date) {
@@ -110,7 +119,7 @@ public class SunHandler extends AstroThingHandler {
Range eventRange = getSunAt(date).getAllRanges().get(sunPhase);
if (eventRange != null) {
Calendar cal = begin ? eventRange.getStart() : eventRange.getEnd();
return ZonedDateTime.ofInstant(cal.toInstant(), date.getZone());
return cal == null ? null : ZonedDateTime.ofInstant(cal.toInstant(), date.getZone());
} else {
return null;
}
@@ -13,6 +13,7 @@
package org.openhab.binding.astro.internal.job;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
/**
* This class contains the default methods required for different jobs
@@ -22,15 +23,15 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
@NonNullByDefault
public abstract class AbstractJob implements Job {
private final String thingUID;
protected final AstroThingHandler handler;
public AbstractJob(String thingUID) {
this.thingUID = thingUID;
public AbstractJob(AstroThingHandler handler) {
this.handler = handler;
}
@Override
public String getThingUID() {
return thingUID;
public AstroThingHandler getHandler() {
return handler;
}
/**
@@ -16,6 +16,7 @@ import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
/**
* {@link CompositeJob} comprises multiple {@link Job}s to be executed in order
@@ -31,18 +32,18 @@ public final class CompositeJob extends AbstractJob {
/**
* Constructor
*
* @param thingUID thing UID
* @param handler thing thing handler
* @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);
public CompositeJob(AstroThingHandler handler, List<Job> jobs) {
super(handler);
this.jobs = jobs;
this.jobs = List.copyOf(jobs);
boolean notMatched = jobs.stream().anyMatch(j -> !j.getThingUID().equals(thingUID));
checkArgument(!notMatched, "The jobs must associate the same thing UID");
boolean notMatched = jobs.stream().anyMatch(j -> !j.getHandler().equals(handler));
checkArgument(!notMatched, "The jobs must associate the same thing handler");
}
@Override
@@ -50,8 +51,9 @@ public final class CompositeJob extends AbstractJob {
jobs.forEach(j -> {
try {
j.run();
} catch (RuntimeException ex) {
LOGGER.warn("Job execution failed.", ex);
} catch (Exception e) {
logger.warn("Job execution of \"{}\" failed: {}", j, e.getMessage());
logger.trace("", e);
}
});
}
@@ -16,6 +16,8 @@ import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import static org.openhab.binding.astro.internal.job.Job.scheduleEvent;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
@@ -33,57 +35,90 @@ import org.openhab.binding.astro.internal.model.Planet;
@NonNullByDefault
public final class DailyJobMoon extends AbstractJob {
private final AstroThingHandler handler;
public final TimeZone zone;
public final Locale locale;
/**
* 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;
public DailyJobMoon(AstroThingHandler handler, TimeZone zone, Locale locale) {
super(handler);
this.zone = zone;
this.locale = locale;
}
@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);
try {
handler.publishDailyInfo();
if (logger.isDebugEnabled()) {
logger.debug("Scheduled Astro event-jobs for thing {}", handler.getThing().getUID());
}
});
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);
Planet planet = handler.getPlanet();
if (planet == null) {
logger.error("Planet not instantiated");
return;
}
Moon moon = (Moon) planet;
Calendar cal = moon.getRise().getStart();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_START, EVENT_CHANNEL_ID_RISE, false, zone, locale);
}
cal = moon.getSet().getEnd();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_END, EVENT_CHANNEL_ID_SET, false, zone, locale);
}
MoonPhase moonPhase = moon.getPhase();
cal = moonPhase.getFirstQuarter();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_PHASE_FIRST_QUARTER, EVENT_CHANNEL_ID_MOON_PHASE, false, zone,
locale);
}
cal = moonPhase.getThirdQuarter();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_PHASE_THIRD_QUARTER, EVENT_CHANNEL_ID_MOON_PHASE, false, zone,
locale);
}
cal = moonPhase.getFull();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_PHASE_FULL, EVENT_CHANNEL_ID_MOON_PHASE, false, zone, locale);
}
cal = moonPhase.getNew();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_PHASE_NEW, EVENT_CHANNEL_ID_MOON_PHASE, false, zone, locale);
}
Eclipse eclipse = moon.getEclipse();
eclipse.getKinds().forEach(eclipseKind -> {
Calendar eclipseDate = eclipse.getDate(eclipseKind);
if (eclipseDate != null) {
scheduleEvent(handler, eclipseDate, eclipseKind.toString(), EVENT_CHANNEL_ID_ECLIPSE, false, zone,
locale);
}
});
cal = moon.getPerigee().getDate();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_PERIGEE, EVENT_CHANNEL_ID_PERIGEE, false, zone, locale);
}
cal = moon.getApogee().getDate();
if (cal != null) {
scheduleEvent(handler, cal, EVENT_APOGEE, EVENT_CHANNEL_ID_APOGEE, false, zone, locale);
}
} catch (Exception e) {
logger.warn("The daily moon job execution for \"{}\" failed: {}", handler.getThing().getUID(),
e.getMessage());
logger.trace("", e);
}
}
@Override
public String toString() {
return "Daily job moon " + getThingUID();
return "Daily job moon " + handler.getThing().getUID();
}
}
@@ -17,12 +17,16 @@ import static org.openhab.binding.astro.internal.job.Job.*;
import static org.openhab.binding.astro.internal.model.SunPhaseName.*;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
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.Range;
import org.openhab.binding.astro.internal.model.Sun;
import org.openhab.binding.astro.internal.model.SunZodiac;
/**
* Daily scheduled jobs For Sun planet
@@ -33,74 +37,150 @@ import org.openhab.binding.astro.internal.model.Sun;
@NonNullByDefault
public final class DailyJobSun extends AbstractJob {
private final AstroThingHandler handler;
private final TimeZone zone;
private final Locale locale;
/**
* 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;
public DailyJobSun(AstroThingHandler handler, TimeZone zone, Locale locale) {
super(handler);
this.zone = zone;
this.locale = locale;
}
@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);
try {
handler.publishDailyInfo();
if (logger.isDebugEnabled()) {
logger.debug("Scheduled Astro event-jobs for thing {}", handler.getThing().getUID());
}
});
// schedule republish jobs
schedulePublishPlanet(thingUID, handler, sun.getZodiac().getEnd());
schedulePublishPlanet(thingUID, handler, sun.getSeason().getNextSeason());
Planet planet = handler.getPlanet();
if (planet == null) {
logger.error("Planet not instantiated");
return;
}
Sun sun = (Sun) planet;
scheduleRange(handler, sun.getRise(), EVENT_CHANNEL_ID_RISE, zone, locale);
scheduleRange(handler, sun.getSet(), EVENT_CHANNEL_ID_SET, zone, locale);
Range range = sun.getNoon();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NOON, zone, locale);
}
range = sun.getNight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NIGHT, zone, locale);
}
range = sun.getMorningNight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_MORNING_NIGHT, zone, locale);
}
range = sun.getAstroDawn();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_ASTRO_DAWN, zone, locale);
}
range = sun.getNauticDawn();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NAUTIC_DAWN, zone, locale);
}
range = sun.getCivilDawn();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_CIVIL_DAWN, zone, locale);
}
range = sun.getAstroDusk();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_ASTRO_DUSK, zone, locale);
}
range = sun.getNauticDusk();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NAUTIC_DUSK, zone, locale);
}
range = sun.getCivilDusk();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_CIVIL_DUSK, zone, locale);
}
range = sun.getEveningNight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_EVENING_NIGHT, zone, locale);
}
range = sun.getDaylight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_DAYLIGHT, zone, locale);
}
// 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());
Eclipse eclipse = sun.getEclipse();
eclipse.getKinds().forEach(eclipseKind -> {
Calendar eclipseDate = eclipse.getDate(eclipseKind);
if (eclipseDate != null) {
scheduleEvent(handler, eclipseDate, eclipseKind.toString(), EVENT_CHANNEL_ID_ECLIPSE, false, zone,
locale);
}
});
// schedule republish jobs
SunZodiac sunZodiac;
Calendar cal = (sunZodiac = sun.getZodiac()) == null ? null : sunZodiac.getEnd();
if (cal != null) {
schedulePublishPlanet(handler, cal, zone, locale);
}
schedulePublishPlanet(handler, sun.getSeason().getNextSeason(zone, locale), zone, locale);
// schedule phase jobs
cal = sun.getRise().getStart();
if (cal != null) {
scheduleSunPhase(handler, SUN_RISE, cal, zone, locale);
}
cal = sun.getSet().getStart();
if (cal != null) {
scheduleSunPhase(handler, SUN_SET, cal, zone, locale);
}
cal = (range = sun.getNight()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, NIGHT, cal, zone, locale);
}
cal = (range = sun.getDaylight()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, DAYLIGHT, cal, zone, locale);
}
cal = (range = sun.getAstroDawn()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, ASTRO_DAWN, cal, zone, locale);
}
cal = (range = sun.getNauticDawn()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, NAUTIC_DAWN, cal, zone, locale);
}
cal = (range = sun.getCivilDawn()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, CIVIL_DAWN, cal, zone, locale);
}
cal = (range = sun.getAstroDusk()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, ASTRO_DUSK, cal, zone, locale);
}
cal = (range = sun.getNauticDusk()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, NAUTIC_DUSK, cal, zone, locale);
}
cal = (range = sun.getCivilDusk()) == null ? null : range.getStart();
if (cal != null) {
scheduleSunPhase(handler, CIVIL_DUSK, cal, zone, locale);
}
} catch (Exception e) {
logger.warn("The daily sun job execution for \"{}\" failed: {}", handler.getThing().getUID(),
e.getMessage());
logger.trace("", e);
}
}
@Override
public String toString() {
return "Daily job sun " + getThingUID();
return "Daily job sun " + handler.getThing().getUID();
}
}
@@ -13,7 +13,6 @@
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;
/**
@@ -31,30 +30,31 @@ public final class EventJob extends AbstractJob {
/**
* Constructor
*
* @param thingUID thing UID
* @param handler the thing handler
* @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);
public EventJob(AstroThingHandler handler, String channelID, String event) {
super(handler);
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");
try {
handler.triggerEvent(channelID, event);
} catch (Exception e) {
logger.warn("The triggering of event \"{}\" for \"{}\" failed: {}", event, handler.getThing().getUID(),
e.getMessage());
logger.trace("", e);
}
}
@Override
public String toString() {
return "Event job " + getThingUID() + "/" + channelID + "/" + event;
return "Event job " + handler.getThing().getUID() + "/" + channelID + "/" + event;
}
}
@@ -19,6 +19,8 @@ import static org.openhab.binding.astro.internal.util.DateTimeUtils.*;
import java.lang.invoke.MethodHandles;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
@@ -38,53 +40,50 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault
public interface Job extends SchedulerRunnable, Runnable {
/** Logger Instance */
final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/** The {@link Logger} Instance */
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
*/
static void schedule(String thingUID, AstroThingHandler astroHandler, Job job, Calendar eventAt) {
static void schedule(AstroThingHandler astroHandler, Job job, Calendar eventAt, TimeZone zone, Locale locale) {
try {
Calendar today = Calendar.getInstance();
Calendar today = Calendar.getInstance(zone, locale);
if (isSameDay(eventAt, today) && isTimeGreaterEquals(eventAt, today)) {
astroHandler.schedule(job, eventAt);
}
} catch (Exception ex) {
LOGGER.error("{}", ex.getMessage(), 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
*/
static void scheduleEvent(String thingUID, AstroThingHandler astroHandler, Calendar eventAt, String event,
String channelId, boolean configAlreadyApplied) {
scheduleEvent(thingUID, astroHandler, eventAt, List.of(event), channelId, configAlreadyApplied);
static void scheduleEvent(AstroThingHandler astroHandler, Calendar eventAt, String event, String channelId,
boolean configAlreadyApplied, TimeZone zone, Locale locale) {
scheduleEvent(astroHandler, eventAt, List.of(event), channelId, configAlreadyApplied, zone, locale);
}
/**
* 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
*/
static void scheduleEvent(String thingUID, AstroThingHandler astroHandler, Calendar eventAt, List<String> events,
String channelId, boolean configAlreadyApplied) {
static void scheduleEvent(AstroThingHandler astroHandler, Calendar eventAt, List<String> events, String channelId,
boolean configAlreadyApplied, TimeZone zone, Locale locale) {
if (events.isEmpty()) {
return;
}
@@ -92,7 +91,7 @@ public interface Job extends SchedulerRunnable, Runnable {
if (!configAlreadyApplied) {
final Channel channel = astroHandler.getThing().getChannel(channelId);
if (channel == null) {
LOGGER.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
logger.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
return;
}
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
@@ -100,48 +99,48 @@ public interface Job extends SchedulerRunnable, Runnable {
} 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);
List<Job> jobs = events.stream().map(e -> new EventJob(astroHandler, channelId, e)).collect(toList());
schedule(astroHandler, new CompositeJob(astroHandler, jobs), instant, zone, locale);
}
/**
* 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
*/
static void scheduleRange(String thingUID, AstroThingHandler astroHandler, Range range, String channelId) {
static void scheduleRange(AstroThingHandler astroHandler, Range range, String channelId, TimeZone zone,
Locale locale) {
final Channel channel = astroHandler.getThing().getChannel(channelId);
if (channel == null) {
LOGGER.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
logger.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
return;
}
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
Range adjustedRange = adjustRangeToConfig(range, config);
Range adjustedRange = adjustRangeToConfig(range, config, zone, locale);
Calendar start = adjustedRange.getStart();
Calendar end = adjustedRange.getEnd();
if (start == null || end == null) {
LOGGER.debug("event was not scheduled as either start or end was null");
logger.debug("event was not scheduled as either start or end was null");
return;
}
scheduleEvent(thingUID, astroHandler, start, EVENT_START, channelId, true);
scheduleEvent(thingUID, astroHandler, end, EVENT_END, channelId, true);
scheduleEvent(astroHandler, start, EVENT_START, channelId, true, zone, locale);
scheduleEvent(astroHandler, end, EVENT_END, channelId, true, zone, locale);
}
static Range adjustRangeToConfig(Range range, AstroChannelConfig config) {
static Range adjustRangeToConfig(Range range, AstroChannelConfig config, TimeZone zone, Locale locale) {
Calendar start = range.getStart();
Calendar end = range.getEnd();
if (config.forceEvent && start == null) {
start = getAdjustedEarliest(Calendar.getInstance(), config);
start = getAdjustedEarliest(Calendar.getInstance(zone, locale), config);
}
if (config.forceEvent && end == null) {
end = getAdjustedLatest(Calendar.getInstance(), config);
end = getAdjustedLatest(Calendar.getInstance(zone, locale), config);
}
// depending on the location and configuration you might not have a valid range for day/night, so skip the
@@ -156,31 +155,29 @@ public interface Job extends SchedulerRunnable, Runnable {
/**
* Schedules Planet events
*
* @param thingUID the Thing UID
* @param astroHandler the {@link AstroThingHandler} instance
* @param eventAt the {@link Calendar} instance denoting scheduled instant
*/
static void schedulePublishPlanet(String thingUID, AstroThingHandler astroHandler, Calendar eventAt) {
Job publishJob = new PublishPlanetJob(thingUID);
schedule(thingUID, astroHandler, publishJob, eventAt);
static void schedulePublishPlanet(AstroThingHandler astroHandler, Calendar eventAt, TimeZone zone, Locale locale) {
Job publishJob = new PublishPlanetJob(astroHandler);
schedule(astroHandler, publishJob, eventAt, zone, locale);
}
/**
* 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
*/
static void scheduleSunPhase(String thingUID, AstroThingHandler astroHandler, SunPhaseName sunPhaseName,
Calendar eventAt) {
Job sunPhaseJob = new SunPhaseJob(thingUID, sunPhaseName);
schedule(thingUID, astroHandler, sunPhaseJob, eventAt);
static void scheduleSunPhase(AstroThingHandler astroHandler, SunPhaseName sunPhaseName, Calendar eventAt,
TimeZone zone, Locale locale) {
Job sunPhaseJob = new SunPhaseJob(astroHandler, sunPhaseName);
schedule(astroHandler, sunPhaseJob, eventAt, zone, locale);
}
/**
* Returns the thing UID that is associated with this {@link Job} (cannot be {@code null})
* @return The {@link AstroThingHandler} associated with this {@link Job}.
*/
String getThingUID();
AstroThingHandler getHandler();
}
@@ -13,7 +13,6 @@
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;
/**
@@ -28,26 +27,27 @@ public final class PositionalJob extends AbstractJob {
/**
* Constructor
*
* @param thingUID thing UID
* @param handler the thing handler
* @throws IllegalArgumentException
* if the provided argument is {@code null}
*/
public PositionalJob(String thingUID) {
super(thingUID);
public PositionalJob(AstroThingHandler handler) {
super(handler);
}
@Override
public void run() {
AstroThingHandler astroHandler = AstroHandlerFactory.getHandler(getThingUID());
if (astroHandler != null) {
astroHandler.publishPositionalInfo();
} else {
LOGGER.trace("AstroThingHandler is null");
try {
handler.publishPositionalInfo();
} catch (Exception e) {
logger.warn("The publishing of positional info for \"{}\" failed: {}", handler.getThing().getUID(),
e.getMessage());
logger.trace("", e);
}
}
@Override
public String toString() {
return "Positional job " + getThingUID();
return "Positional job " + handler.getThing().getUID();
}
}
@@ -13,7 +13,6 @@
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;
/**
@@ -28,26 +27,27 @@ public final class PublishPlanetJob extends AbstractJob {
/**
* Constructor
*
* @param thingUID thing UID
* @param handler the thing handler
* @throws IllegalArgumentException
* if the provided argument is {@code null}
*/
public PublishPlanetJob(String thingUID) {
super(thingUID);
public PublishPlanetJob(AstroThingHandler handler) {
super(handler);
}
@Override
public void run() {
AstroThingHandler astroHandler = AstroHandlerFactory.getHandler(getThingUID());
if (astroHandler != null) {
astroHandler.publishDailyInfo();
} else {
LOGGER.trace("AstroThingHandler is null");
try {
handler.publishDailyInfo();
} catch (Exception e) {
logger.warn("The publishing of daily info for \"{}\" failed: {}", handler.getThing().getUID(),
e.getMessage());
logger.trace("", e);
}
}
@Override
public String toString() {
return "Publish planet job " + getThingUID();
return "Publish planet job " + handler.getThing().getUID();
}
}
@@ -15,9 +15,7 @@ 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;
@@ -36,37 +34,37 @@ public final class SunPhaseJob extends AbstractJob {
/**
* Constructor
*
* @param thingUID thing UID
* @param handler the thing handler
* @param sunPhaseName {@link SunPhaseName} name
* @throws IllegalArgumentException
* if any of the arguments is {@code null}
*/
public SunPhaseJob(String thingUID, SunPhaseName sunPhaseName) {
super(thingUID);
public SunPhaseJob(AstroThingHandler handler, SunPhaseName sunPhaseName) {
super(handler);
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);
try {
Channel phaseNameChannel = handler.getThing().getChannel(CHANNEL_ID_SUN_PHASE_NAME);
if (phaseNameChannel != null) {
Planet planet = astroHandler.getPlanet();
if (planet instanceof Sun theSun) {
if (handler.getPlanet() instanceof Sun theSun) {
theSun.getPhase().setName(sunPhaseName);
astroHandler.publishChannelIfLinked(phaseNameChannel.getUID());
handler.publishChannelIfLinked(phaseNameChannel.getUID());
}
} else {
LOGGER.trace("{}", "Phase Name Channel is null");
logger.trace("Phase Name Channel for {} is null", handler.getThing().getUID());
}
} else {
LOGGER.trace("AstroThingHandler is null");
} catch (Exception e) {
logger.warn("The publishing of the sun phase for \"{}\" failed: {}", handler.getThing().getUID(),
e.getMessage());
logger.trace("", e);
}
}
@Override
public String toString() {
return "Sun phase job " + getThingUID() + "/" + sunPhaseName;
return "Sun phase job " + handler.getThing().getUID() + "/" + sunPhaseName;
}
}
@@ -12,11 +12,14 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Holds the calculated moon data.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public class Moon extends RiseSet implements Planet {
private MoonPhase phase = new MoonPhase();
private MoonDistance apogee = new MoonDistance();
@@ -19,6 +19,8 @@ import java.util.Calendar;
import javax.measure.quantity.Length;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.QuantityType;
/**
@@ -27,14 +29,16 @@ import org.openhab.core.library.types.QuantityType;
* @author Gerhard Riegler - Initial contribution
* @author Christoph Weitkamp - Introduced UoM
*/
@NonNullByDefault
public class MoonDistance {
private Calendar date;
private @Nullable Calendar date;
private double distance;
/**
* Returns the date of the calculated distance.
*/
@Nullable
public Calendar getDate() {
return date;
}
@@ -42,7 +46,7 @@ public class MoonDistance {
/**
* Sets the date of the calculated distance.
*/
public void setDate(Calendar date) {
public void setDate(@Nullable Calendar date) {
this.date = date;
}
@@ -18,6 +18,8 @@ import javax.measure.quantity.Angle;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Time;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
@@ -27,21 +29,23 @@ import org.openhab.core.library.unit.Units;
* @author Gerhard Riegler - Initial contribution
* @author Christoph Weitkamp - Introduced UoM
*/
@NonNullByDefault
public class MoonPhase {
private Calendar firstQuarter;
private Calendar full;
private Calendar thirdQuarter;
private Calendar newCalendar;
private @Nullable Calendar firstQuarter;
private @Nullable Calendar full;
private @Nullable Calendar thirdQuarter;
private @Nullable Calendar newCalendar;
private double age;
private double illumination;
private double agePercent;
private double ageDegree;
private MoonPhaseName name;
private @Nullable MoonPhaseName name;
/**
* Returns the date at which the moon is in the first quarter.
*/
@Nullable
public Calendar getFirstQuarter() {
return firstQuarter;
}
@@ -49,13 +53,14 @@ public class MoonPhase {
/**
* Sets the date at which the moon is in the first quarter.
*/
public void setFirstQuarter(Calendar firstQuarter) {
public void setFirstQuarter(@Nullable Calendar firstQuarter) {
this.firstQuarter = firstQuarter;
}
/**
* Returns the date of the full moon.
*/
@Nullable
public Calendar getFull() {
return full;
}
@@ -63,13 +68,14 @@ public class MoonPhase {
/**
* Sets the date of the full moon.
*/
public void setFull(Calendar full) {
public void setFull(@Nullable Calendar full) {
this.full = full;
}
/**
* Returns the date at which the moon is in the third quarter.
*/
@Nullable
public Calendar getThirdQuarter() {
return thirdQuarter;
}
@@ -77,13 +83,14 @@ public class MoonPhase {
/**
* Sets the date at which the moon is in the third quarter.
*/
public void setThirdQuarter(Calendar thirdQuarter) {
public void setThirdQuarter(@Nullable Calendar thirdQuarter) {
this.thirdQuarter = thirdQuarter;
}
/**
* Returns the date of the new moon.
*/
@Nullable
public Calendar getNew() {
return newCalendar;
}
@@ -91,7 +98,7 @@ public class MoonPhase {
/**
* Sets the date of the new moon.
*/
public void setNew(Calendar newCalendar) {
public void setNew(@Nullable Calendar newCalendar) {
this.newCalendar = newCalendar;
}
@@ -126,6 +133,7 @@ public class MoonPhase {
/**
* Returns the phase name.
*/
@Nullable
public MoonPhaseName getName() {
return name;
}
@@ -133,7 +141,7 @@ public class MoonPhase {
/**
* Sets the phase name.
*/
public void setName(MoonPhaseName name) {
public void setName(@Nullable MoonPhaseName name) {
this.name = name;
}
@@ -12,11 +12,14 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* All moon phases.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public enum MoonPhaseName {
NEW,
WAXING_CRESCENT,
@@ -12,10 +12,13 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Marker interface for all planets.
*
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public interface Planet {
}
@@ -12,6 +12,7 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.dimension.Intensity;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
@@ -22,6 +23,7 @@ import org.openhab.core.library.unit.Units;
* @author Gaël L'hopital - Initial contribution
* @author Christoph Weitkamp - Introduced UoM
*/
@NonNullByDefault
public class Radiation {
private double direct;
@@ -19,6 +19,8 @@ import java.util.Comparator;
import javax.measure.quantity.Time;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
@@ -29,15 +31,16 @@ import org.openhab.core.library.unit.Units;
* @author Gerhard Riegler - Initial contribution
* @author Christoph Weitkamp - Introduced UoM
*/
@NonNullByDefault
public class Range {
private Calendar start;
private Calendar end;
private @Nullable Calendar start;
private @Nullable Calendar end;
public Range() {
}
public Range(Calendar start, Calendar end) {
public Range(@Nullable Calendar start, @Nullable Calendar end) {
this.start = start;
this.end = end;
}
@@ -45,6 +48,7 @@ public class Range {
/**
* Returns the start of the range.
*/
@Nullable
public Calendar getStart() {
return start;
}
@@ -52,6 +56,7 @@ public class Range {
/**
* Returns the end of the range.
*/
@Nullable
public Calendar getEnd() {
return end;
}
@@ -59,7 +64,10 @@ public class Range {
/**
* Returns the duration in minutes.
*/
@Nullable
public QuantityType<Time> getDuration() {
Calendar start = this.start;
Calendar end = this.end;
if (start == null || end == null) {
return null;
}
@@ -83,7 +91,12 @@ public class Range {
return cal.getTimeInMillis() >= matchStart && cal.getTimeInMillis() < matchEnd;
}
private static Comparator<Calendar> nullSafeCalendarComparator = Comparator.nullsFirst(Calendar::compareTo);
private static Comparator<@Nullable Calendar> nullSafeCalendarComparator = (c1, c2) -> {
if (c1 == null) {
return (c2 == null) ? 0 : -1;
}
return c2 == null ? 1 : c1.compareTo(c2);
};
private static Comparator<Range> rangeComparator = Comparator.comparing(Range::getStart, nullSafeCalendarComparator)
.thenComparing(Range::getEnd, nullSafeCalendarComparator);
@@ -15,9 +15,13 @@ package org.openhab.binding.astro.internal.model;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import javax.measure.quantity.Time;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
@@ -27,17 +31,19 @@ import org.openhab.core.library.unit.Units;
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public class Season {
private Calendar spring;
private Calendar summer;
private Calendar autumn;
private Calendar winter;
private @Nullable Calendar spring;
private @Nullable Calendar summer;
private @Nullable Calendar autumn;
private @Nullable Calendar winter;
private SeasonName name;
private @Nullable SeasonName name;
/**
* Returns the date of the beginning of spring.
*/
@Nullable
public Calendar getSpring() {
return spring;
}
@@ -45,13 +51,14 @@ public class Season {
/**
* Sets the date of the beginning of spring.
*/
public void setSpring(Calendar spring) {
public void setSpring(@Nullable Calendar spring) {
this.spring = spring;
}
/**
* Returns the date of the beginning of summer.
*/
@Nullable
public Calendar getSummer() {
return summer;
}
@@ -59,13 +66,14 @@ public class Season {
/**
* Sets the date of the beginning of summer.
*/
public void setSummer(Calendar summer) {
public void setSummer(@Nullable Calendar summer) {
this.summer = summer;
}
/**
* Returns the date of the beginning of autumn.
*/
@Nullable
public Calendar getAutumn() {
return autumn;
}
@@ -73,13 +81,14 @@ public class Season {
/**
* Sets the date of the beginning of autumn.
*/
public void setAutumn(Calendar autumn) {
public void setAutumn(@Nullable Calendar autumn) {
this.autumn = autumn;
}
/**
* Returns the date of the beginning of winter.
*/
@Nullable
public Calendar getWinter() {
return winter;
}
@@ -87,13 +96,14 @@ public class Season {
/**
* Returns the date of the beginning of winter.
*/
public void setWinter(Calendar winter) {
public void setWinter(@Nullable Calendar winter) {
this.winter = winter;
}
/**
* Returns the current season name.
*/
@Nullable
public SeasonName getName() {
return name;
}
@@ -101,22 +111,22 @@ public class Season {
/**
* Sets the current season name.
*/
public void setName(SeasonName name) {
public void setName(@Nullable SeasonName name) {
this.name = name;
}
/**
* Returns the next season.
*/
public Calendar getNextSeason() {
return DateTimeUtils.getNextFromToday(spring, summer, autumn, winter);
public Calendar getNextSeason(TimeZone zone, Locale locale) {
return DateTimeUtils.getNextFromToday(zone, locale, spring, summer, autumn, winter);
}
/**
* Returns the next season name.
*/
public SeasonName getNextName() {
int ordinal = name.ordinal() + 1;
int ordinal = name == null ? 0 : name.ordinal() + 1;
if (ordinal > 3) {
ordinal = 0;
}
@@ -126,9 +136,9 @@ public class Season {
/**
* Returns the time left for current season
*/
public QuantityType<Time> getTimeLeft() {
final Calendar now = Calendar.getInstance();
final Calendar next = getNextSeason();
public QuantityType<Time> getTimeLeft(TimeZone zone, Locale locale) {
final Calendar now = Calendar.getInstance(zone, locale);
final Calendar next = getNextSeason(zone, locale);
final Duration timeLeft = Duration.of(next.getTimeInMillis() - now.getTimeInMillis(), ChronoUnit.MILLIS);
return new QuantityType<>(timeLeft.toDays(), Units.DAY);
@@ -12,11 +12,14 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* All season names.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public enum SeasonName {
SPRING,
SUMMER,
@@ -15,18 +15,22 @@ package org.openhab.binding.astro.internal.model;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Holds the calculated sun data.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
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 @Nullable SunZodiac zodiac;
private Season season = new Season();
@@ -39,6 +43,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the astro dawn range.
*/
@Nullable
public Range getAstroDawn() {
return ranges.get(SunPhaseName.ASTRO_DAWN);
}
@@ -53,6 +58,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the nautic dawn range.
*/
@Nullable
public Range getNauticDawn() {
return ranges.get(SunPhaseName.NAUTIC_DAWN);
}
@@ -67,6 +73,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the civil dawn range.
*/
@Nullable
public Range getCivilDawn() {
return ranges.get(SunPhaseName.CIVIL_DAWN);
}
@@ -81,6 +88,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the civil dusk range.
*/
@Nullable
public Range getCivilDusk() {
return ranges.get(SunPhaseName.CIVIL_DUSK);
}
@@ -95,6 +103,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the nautic dusk range.
*/
@Nullable
public Range getNauticDusk() {
return ranges.get(SunPhaseName.NAUTIC_DUSK);
}
@@ -109,6 +118,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the astro dusk range.
*/
@Nullable
public Range getAstroDusk() {
return ranges.get(SunPhaseName.ASTRO_DUSK);
}
@@ -123,6 +133,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the noon range, start and end is always equal.
*/
@Nullable
public Range getNoon() {
return ranges.get(SunPhaseName.NOON);
}
@@ -137,6 +148,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the daylight range.
*/
@Nullable
public Range getDaylight() {
return ranges.get(SunPhaseName.DAYLIGHT);
}
@@ -151,6 +163,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the morning night range.
*/
@Nullable
public Range getMorningNight() {
return ranges.get(SunPhaseName.MORNING_NIGHT);
}
@@ -165,6 +178,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the evening night range.
*/
@Nullable
public Range getEveningNight() {
return ranges.get(SunPhaseName.EVENING_NIGHT);
}
@@ -179,6 +193,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the night range.
*/
@Nullable
public Range getNight() {
return ranges.get(SunPhaseName.NIGHT);
}
@@ -232,6 +247,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Returns the zodiac.
*/
@Nullable
public SunZodiac getZodiac() {
return zodiac;
}
@@ -239,7 +255,7 @@ public class Sun extends RiseSet implements Planet {
/**
* Sets the zodiac.
*/
public void setZodiac(SunZodiac zodiac) {
public void setZodiac(@Nullable SunZodiac zodiac) {
this.zodiac = zodiac;
}
@@ -12,17 +12,22 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Holds the calculated sun phase informations.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public class SunPhase {
private SunPhaseName name;
private @Nullable SunPhaseName name;
/**
* Returns the sun phase.
*/
@Nullable
public SunPhaseName getName() {
return name;
}
@@ -30,7 +35,7 @@ public class SunPhase {
/**
* Sets the sun phase.
*/
public void setName(SunPhaseName name) {
public void setName(@Nullable SunPhaseName name) {
this.name = name;
}
}
@@ -14,11 +14,15 @@ package org.openhab.binding.astro.internal.model;
import java.util.Calendar;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Extends the zodiac with a date range.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public class SunZodiac extends Zodiac {
private Range range;
@@ -33,26 +37,30 @@ public class SunZodiac extends Zodiac {
/**
* Returns she start of the zodiac.
*/
@Nullable
public Calendar getStart() {
return range == null ? null : range.getStart();
return range.getStart();
}
/**
* Returns the end of the zodiac.
*/
@Nullable
public Calendar getEnd() {
return range == null ? null : range.getEnd();
return 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) {
Calendar start = range.getStart();
Calendar end = range.getEnd();
if (start == null || end == null) {
return false;
}
return range.getStart().getTimeInMillis() <= calendar.getTimeInMillis()
&& range.getEnd().getTimeInMillis() >= calendar.getTimeInMillis();
return start.getTimeInMillis() <= calendar.getTimeInMillis()
&& end.getTimeInMillis() >= calendar.getTimeInMillis();
}
}
@@ -12,21 +12,26 @@
*/
package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Holds the sign of the zodiac.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public class Zodiac {
private ZodiacSign sign;
private @Nullable ZodiacSign sign;
public Zodiac(ZodiacSign sign) {
public Zodiac(@Nullable ZodiacSign sign) {
this.sign = sign;
}
/**
* Returns the sign of the zodiac.
*/
@Nullable
public ZodiacSign getSign() {
return sign;
}
@@ -13,8 +13,12 @@
package org.openhab.binding.astro.internal.util;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.binding.astro.internal.model.Range;
import org.slf4j.Logger;
@@ -25,6 +29,7 @@ import org.slf4j.LoggerFactory;
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
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])$");
@@ -49,7 +54,7 @@ public class DateTimeUtils {
/**
* Truncates the time from the calendar object.
*/
private static Calendar truncateToMinute(Calendar calendar) {
public static Calendar truncateToMinute(Calendar calendar) {
Calendar cal = truncateToSecond(calendar);
cal.set(Calendar.SECOND, 0);
return cal;
@@ -69,14 +74,15 @@ public class DateTimeUtils {
* 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();
public static Range getRange(int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay,
TimeZone zone, Locale locale) {
Calendar end = Calendar.getInstance(zone, locale);
Calendar start = (Calendar) end.clone();
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);
@@ -91,18 +97,15 @@ public class DateTimeUtils {
/**
* Returns a calendar object from a julian date.
*/
public static Calendar toCalendar(double julianDate) {
@Nullable
public static Calendar toCalendar(double julianDate, TimeZone zone, Locale locale) {
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();
Calendar cal = Calendar.getInstance(zone, locale);
cal.setTimeInMillis(millis);
int second = cal.get(Calendar.SECOND);
if (second > 30) {
cal.add(Calendar.MINUTE, 1);
}
return truncateToMinute(cal);
return cal;
}
/**
@@ -140,6 +143,7 @@ public class DateTimeUtils {
/**
* Converts the time (hour.minute) to a calendar object.
*/
@Nullable
public static Calendar timeToCalendar(Calendar calendar, double time) {
if (time < 0.0) {
return null;
@@ -147,12 +151,13 @@ public class DateTimeUtils {
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));
int days = (int) time / 24;
double remains = time % 24;
if (days != 0) {
cal.add(Calendar.DAY_OF_MONTH, days);
}
hour = (int) remains;
minute = (int) ((remains * 100) - (hour * 100));
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
return truncateToMinute(cal);
@@ -161,7 +166,7 @@ public class DateTimeUtils {
/**
* Returns true, if two calendar objects are on the same day ignoring time.
*/
public static boolean isSameDay(Calendar cal1, Calendar cal2) {
public static boolean isSameDay(@Nullable Calendar cal1, @Nullable Calendar cal2) {
return cal1 != null && cal2 != null && cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA)
&& cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
&& cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
@@ -170,10 +175,13 @@ public class DateTimeUtils {
/**
* Returns the next Calendar from today.
*/
public static Calendar getNextFromToday(Calendar... calendars) {
return getNext(Calendar.getInstance(), calendars);
public static Calendar getNextFromToday(TimeZone zone, Locale locale, Calendar... calendars) {
Calendar now = Calendar.getInstance(zone, locale);
Calendar result = getNext(now, calendars);
return result == null ? now : result;
}
@Nullable
static Calendar getNext(Calendar now, Calendar... calendars) {
Calendar next = null;
Calendar firstSeasonOfYear = null;
@@ -185,7 +193,7 @@ public class DateTimeUtils {
next = calendar;
}
}
if (next == null) {
if (next == null && firstSeasonOfYear != null) {
final Calendar nextYearSeason = (Calendar) firstSeasonOfYear.clone();
nextYearSeason.add(Calendar.YEAR, 1);
@@ -205,11 +213,11 @@ public class DateTimeUtils {
}
public static Calendar getAdjustedEarliest(Calendar cal, AstroChannelConfig config) {
return adjustTime(cal, getMinutesFromTime(config.earliest));
return config.earliest == null ? cal : adjustTime(cal, getMinutesFromTime(config.earliest));
}
public static Calendar getAdjustedLatest(Calendar cal, AstroChannelConfig config) {
return adjustTime(cal, getMinutesFromTime(config.latest));
return config.latest == null ? cal : adjustTime(cal, getMinutesFromTime(config.latest));
}
/**
@@ -218,17 +226,15 @@ public class DateTimeUtils {
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;
cCal = (Calendar) cal.clone();
cCal.add(Calendar.MINUTE, config.offset);
}
Calendar cEarliest = getAdjustedEarliest(cCal, config);
Calendar cEarliest = getAdjustedEarliest(cal, config);
if (cCal.before(cEarliest)) {
return cEarliest;
}
Calendar cLatest = getAdjustedLatest(cCal, config);
Calendar cLatest = getAdjustedLatest(cal, config);
if (cCal.after(cLatest)) {
return cLatest;
}
@@ -245,14 +251,14 @@ public class DateTimeUtils {
return cal;
}
public static Calendar createCalendarForToday(int hour, int minute) {
return DateTimeUtils.adjustTime(Calendar.getInstance(), hour * 60 + minute);
public static Calendar createCalendarForToday(int hour, int minute, TimeZone zone, Locale locale) {
return DateTimeUtils.adjustTime(Calendar.getInstance(zone, locale), hour * 60 + minute);
}
/**
* Parses a HH:MM string and returns the minutes.
*/
private static int getMinutesFromTime(String configTime) {
private static int getMinutesFromTime(@Nullable String configTime) {
if (configTime != null) {
String time = configTime.trim();
if (!time.isEmpty()) {
@@ -15,7 +15,6 @@ 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.ArrayList;
import java.util.Calendar;
@@ -52,7 +51,7 @@ public class PropertyUtils {
/**
* Returns the state of the channel.
*/
public static State getState(ChannelUID channelUID, AstroChannelConfig config, Object instance, ZoneId zoneId)
public static State getState(ChannelUID channelUID, AstroChannelConfig config, Object instance, TimeZone zone)
throws Exception {
Object value = getPropertyValue(channelUID, instance);
if (value == null) {
@@ -61,7 +60,7 @@ public class PropertyUtils {
return state;
} else if (value instanceof Calendar cal) {
GregorianCalendar gregorianCal = (GregorianCalendar) DateTimeUtils.applyConfig(cal, config);
cal.setTimeZone(TimeZone.getTimeZone(zoneId));
cal.setTimeZone(zone);
ZonedDateTime zoned = gregorianCal.toZonedDateTime().withFixedOffsetZone();
return new DateTimeType(zoned);
} else if (value instanceof Number) {
@@ -16,12 +16,14 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.astro.internal.model.Moon;
import org.openhab.binding.astro.internal.model.ZodiacSign;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
/***
* Specific unit tests to check if {@link MoonCalc} generates correct data for
@@ -55,7 +57,7 @@ public class MoonCalcTest {
@Test
public void testGetMoonInfoForOldDate() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
assertNotNull(moon.getApogee());
assertNotNull(moon.getPerigee());
@@ -75,55 +77,63 @@ public class MoonCalcTest {
@Test
public void testGetMoonInfoForApogeeAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
// expected result from haevens-above.com is 406,391 km @ 04 March 2019 12:27
assertEquals(406391, moon.getApogee().getDistance().doubleValue(), ACCURACY_IN_KILOMETRES);
Calendar apogeeDate = moon.getApogee().getDate();
assertNotNull(apogeeDate);
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.MARCH, 4, 12, 27, TIME_ZONE).getTimeInMillis(),
moon.getApogee().getDate().getTimeInMillis(), ACCURACY_IN_MILLIS);
apogeeDate.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetMoonInfoForPerigeeAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
// expected result from haevens-above.com is 359,377 km @ 19 February 2019 20:44
assertEquals(359377, moon.getPerigee().getDistance().doubleValue(), ACCURACY_IN_KILOMETRES);
Calendar perigeeDate = moon.getPerigee().getDate();
assertNotNull(perigeeDate);
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.MARCH, 19, 20, 48, TIME_ZONE).getTimeInMillis(),
moon.getPerigee().getDate().getTimeInMillis(), ACCURACY_IN_MILLIS);
perigeeDate.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetMoonInfoForRiseAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
Calendar riseStart = moon.getRise().getStart();
assertNotNull(riseStart);
// expected result from haevens-above.com is 03:00
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 3, 0, TIME_ZONE).getTimeInMillis(),
moon.getRise().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
riseStart.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetMoonInfoForSetAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
Calendar setStart = moon.getSet().getStart();
assertNotNull(setStart);
// expected result from haevens-above.com is 11:35
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 11, 35, TIME_ZONE).getTimeInMillis(),
moon.getSet().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS);
setStart.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetMoonInfoForZodiac() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon, TIME_ZONE, Locale.ROOT);
assertEquals(ZodiacSign.SAGITTARIUS, moon.getZodiac().getSign());
}
@Test
public void testGetMoonInfoForMoonPositionAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon, TIME_ZONE, Locale.ROOT);
// expected result from haevens-above.com is Azimuth: 100.5, altitude -17
assertEquals(100.5, moon.getPosition().getAzimuth().doubleValue(), ACCURACY_IN_DEGREE);
@@ -132,8 +142,8 @@ public class MoonCalcTest {
@Test
public void testGetMoonInfoForMoonDistanceAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon, TIME_ZONE, Locale.ROOT);
// expected result from haevens-above.com is 392612 km
assertEquals(392612, moon.getDistance().getDistance().doubleValue(), ACCURACY_IN_KILOMETRES);
@@ -141,21 +151,29 @@ public class MoonCalcTest {
@Test
public void testGetMoonInfoForMoonPhaseAccuracy() {
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon);
Moon moon = moonCalc.getMoonInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, TIME_ZONE, Locale.ROOT);
moonCalc.setPositionalInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, moon, TIME_ZONE, Locale.ROOT);
// New moon 06 March 2019 17:04
// First quarter 14 March 2019 11:27
// Full moon 21 March 2019 02:43
// Last quarter 28 March 2019 05:10
Calendar phaseCal = moon.getPhase().getNew();
assertNotNull(phaseCal);
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.MARCH, 06, 17, 04, TIME_ZONE).getTimeInMillis(),
moon.getPhase().getNew().getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal.getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal = moon.getPhase().getFirstQuarter();
assertNotNull(phaseCal);
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.MARCH, 14, 11, 27, TIME_ZONE).getTimeInMillis(),
moon.getPhase().getFirstQuarter().getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal.getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal = moon.getPhase().getFull();
assertNotNull(phaseCal);
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.MARCH, 21, 02, 43, TIME_ZONE).getTimeInMillis(),
moon.getPhase().getFull().getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal.getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal = moon.getPhase().getThirdQuarter();
assertNotNull(phaseCal);
assertEquals(MoonCalcTest.newCalendar(2019, Calendar.MARCH, 28, 05, 10, TIME_ZONE).getTimeInMillis(),
moon.getPhase().getThirdQuarter().getTimeInMillis(), ACCURACY_IN_MILLIS);
phaseCal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
/***
@@ -182,9 +200,9 @@ public class MoonCalcTest {
* @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);
Calendar result = new GregorianCalendar(zone, Locale.ROOT);
result.set(year, month, dayOfMonth, hourOfDay, minute);
return result;
return DateTimeUtils.truncateToMinute(result);
}
}
@@ -16,13 +16,16 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
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;
/***
* Specific unit tests to check if {@link SunCalc} generates correct data for
@@ -58,7 +61,8 @@ public class SunCalcTest {
@Test
public void testGetSunInfoForOldDate() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertNotNull(sun.getNight());
@@ -86,192 +90,297 @@ public class SunCalcTest {
@Test
public void testGetSunInfoForAstronomicalDawnAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getAstroDawn();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
cal = range.getEnd();
assertNotNull(cal);
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, TIME_ZONE).getTimeInMillis(),
sun.getAstroDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForNauticDawnAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getNauticDawn();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
cal = range.getEnd();
assertNotNull(cal);
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, TIME_ZONE).getTimeInMillis(),
sun.getNauticDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForCivilDawnAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getCivilDawn();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
cal = range.getEnd();
assertNotNull(cal);
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, TIME_ZONE).getTimeInMillis(),
sun.getCivilDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForRiseAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getRise();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForSunNoonAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getNoon();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForSetAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getSet();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForCivilDuskAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getCivilDusk();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
cal = range.getEnd();
assertNotNull(cal);
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, TIME_ZONE).getTimeInMillis(),
sun.getCivilDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForNauticDuskAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getNauticDusk();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
cal = range.getEnd();
assertNotNull(cal);
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, TIME_ZONE).getTimeInMillis(),
sun.getNauticDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
public void testGetSunInfoForAstronomicalDuskAccuracy() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
Range range = sun.getAstroDusk();
assertNotNull(range);
Calendar cal = range.getStart();
assertNotNull(cal);
// 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);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
cal = range.getEnd();
assertNotNull(cal);
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 20, 9, TIME_ZONE).getTimeInMillis(),
sun.getAstroDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS);
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
}
@Test
@Disabled
public void testRangesForCoherenceBetweenNightEndAndAstroDawnStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.NIGHT).getEnd(),
sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.NIGHT);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenMorningNightEndAndAstroDawnStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.MORNING_NIGHT).getEnd(),
sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.MORNING_NIGHT);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenAstroDownEndAndNauticDawnStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getEnd(),
sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenNauticDawnEndAndCivilDawnStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN).getEnd(),
sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenCivilDawnEndAndSunRiseStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN).getEnd(),
sun.getAllRanges().get(SunPhaseName.SUN_RISE).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.SUN_RISE);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenSunRiseEndAndDaylightStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.SUN_RISE).getEnd(),
sun.getAllRanges().get(SunPhaseName.DAYLIGHT).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.SUN_RISE);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.DAYLIGHT);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenDaylightEndAndSunSetStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.DAYLIGHT).getEnd(),
sun.getAllRanges().get(SunPhaseName.SUN_SET).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.DAYLIGHT);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.SUN_SET);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenSunSetEndAndCivilDuskStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.SUN_SET).getEnd(),
sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.SUN_SET);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenCivilDuskEndAndNauticDuskStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK).getEnd(),
sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenNauticDuskEndAndAstroDuskStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK).getEnd(),
sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenAstroDuskEndAndNightStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getEnd(),
sun.getAllRanges().get(SunPhaseName.NIGHT).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.NIGHT);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
public void testRangesForCoherenceBetweenAstroDuskEndAndEveningNightStart() {
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false);
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
TIME_ZONE, Locale.ROOT);
assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getEnd(),
sun.getAllRanges().get(SunPhaseName.EVENING_NIGHT).getStart());
Range range = sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK);
assertNotNull(range);
Range range2 = sun.getAllRanges().get(SunPhaseName.EVENING_NIGHT);
assertNotNull(range2);
assertEquals(range.getEnd(), range2.getStart());
}
@Test
@@ -279,7 +388,7 @@ public class SunCalcTest {
TimeZone tZone = TimeZone.getTimeZone("Europe/London");
Calendar tDate = SunCalcTest.newCalendar(2020, Calendar.MAY, 13, 5, 12, tZone);
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true);
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, TIME_ZONE, Locale.ROOT);
assertEquals(SunPhaseName.CIVIL_DAWN, sun.getPhase().getName());
}
@@ -288,8 +397,9 @@ public class SunCalcTest {
// SunCalc.ranges was not sorted, causing unexpected output in corner cases.
TimeZone tZone = TimeZone.getTimeZone("Europe/London");
Calendar tDate = SunCalcTest.newCalendar(2020, Calendar.MAY, 13, 5, 13, tZone);
tDate.set(Calendar.SECOND, 4);
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true);
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, TIME_ZONE, Locale.ROOT);
assertEquals(SunPhaseName.SUN_RISE, sun.getPhase().getName());
}
@@ -298,7 +408,7 @@ public class SunCalcTest {
TimeZone tZone = TimeZone.getTimeZone("Europe/London");
Calendar tDate = SunCalcTest.newCalendar(2020, Calendar.MAY, 13, 5, 18, tZone);
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true);
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, TIME_ZONE, Locale.ROOT);
assertEquals(SunPhaseName.DAYLIGHT, sun.getPhase().getName());
}
@@ -326,25 +436,26 @@ public class SunCalcTest {
* @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);
Calendar result = new GregorianCalendar(zone, Locale.ROOT);
result.set(year, month, dayOfMonth, hourOfDay, minute);
return result;
return DateTimeUtils.truncateToMinute(result);
}
@Test
public void testAstroAndMeteoSeasons() {
Sun meteoSun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE,
true);
true, TIME_ZONE, Locale.ROOT);
Sun equiSun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE,
false);
false, TIME_ZONE, Locale.ROOT);
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));
Calendar cal = meteoSun.getSeason().getSpring();
assertNotNull(cal);
Calendar cal2 = equiSun.getSeason().getSpring();
assertNotNull(cal2);
assertEquals(cal.get(Calendar.MONTH), cal2.get(Calendar.MONTH));
assertEquals(cal.get(Calendar.YEAR), cal2.get(Calendar.YEAR));
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
assertFalse(cal.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH));
}
}
@@ -12,9 +12,11 @@
*/
package org.openhab.binding.astro.internal.job;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
@@ -32,6 +34,8 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
@NonNullByDefault
public class JobTest {
private final TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Tbilisi");
@BeforeEach
public void init() {
}
@@ -43,35 +47,101 @@ public class JobTest {
config.earliest = "08:00";
config.latest = "22:00";
config.forceEvent = true;
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0);
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0, TIME_ZONE, Locale.ROOT);
Range startNull = new Range(null, pointInTime);
Range endNull = new Range(pointInTime, null);
Range bothNull = new Range(null, null);
Range bothNNShouldCorrect = new Range(DateTimeUtils.createCalendarForToday(6, 0),
DateTimeUtils.createCalendarForToday(22, 0));
Range bothNNShouldCorrect = new Range(DateTimeUtils.createCalendarForToday(6, 0, TIME_ZONE, Locale.ROOT),
DateTimeUtils.createCalendarForToday(23, 10, TIME_ZONE, Locale.ROOT));
Range bothNNShouldNotCorrect = new Range(pointInTime, pointInTime);
// act
Range startNullResult = Job.adjustRangeToConfig(startNull, config);
Range endNullResult = Job.adjustRangeToConfig(endNull, config);
Range bothNullResult = Job.adjustRangeToConfig(bothNull, config);
Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config);
Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config);
Range startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT);
Range endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT);
Range bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT);
Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT);
Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE,
Locale.ROOT);
Calendar fixedStart = DateTimeUtils.getAdjustedEarliest(pointInTime, config);
Calendar fixdedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config);
Calendar fixedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config);
// assert
assertEquals(fixedStart.getTime(), startNullResult.getStart().getTime());
assertEquals(pointInTime.getTime(), startNullResult.getEnd().getTime());
Calendar startNullResultStart = startNullResult.getStart();
Calendar startNullResultEnd = startNullResult.getEnd();
assertNotNull(startNullResultStart);
assertNotNull(startNullResultEnd);
assertEquals(fixedStart.getTime(), startNullResultStart.getTime());
assertEquals(pointInTime.getTime(), startNullResultEnd.getTime());
assertEquals(pointInTime, endNullResult.getStart());
assertEquals(fixdedEnd, endNullResult.getEnd());
assertEquals(fixedEnd, endNullResult.getEnd());
assertEquals(fixedStart, bothNullResult.getStart());
assertEquals(fixdedEnd, bothNullResult.getEnd());
assertEquals(fixedEnd, bothNullResult.getEnd());
assertEquals(fixedStart, bothNNShouldCorrectResult.getStart());
assertEquals(fixdedEnd, bothNNShouldCorrectResult.getEnd());
assertEquals(fixedEnd, bothNNShouldCorrectResult.getEnd());
assertEquals(pointInTime, bothNNSouldNotCorrectResult.getStart());
assertEquals(pointInTime, bothNNSouldNotCorrectResult.getEnd());
// arrange more (add negative offset)
config.offset = -49;
Calendar newPointInTime = (Calendar) pointInTime.clone();
newPointInTime.add(Calendar.MINUTE, -49);
Calendar outerFixedPoint = (Calendar) fixedEnd.clone();
fixedEnd.add(Calendar.MINUTE, -49);
// act again
startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT);
endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT);
bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT);
bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT);
bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE, Locale.ROOT);
// assert again
startNullResultStart = startNullResult.getStart();
startNullResultEnd = startNullResult.getEnd();
assertNotNull(startNullResultStart);
assertNotNull(startNullResultEnd);
assertEquals(fixedStart.getTime(), startNullResultStart.getTime());
assertEquals(newPointInTime.getTime(), startNullResultEnd.getTime());
assertEquals(newPointInTime, endNullResult.getStart());
assertEquals(fixedEnd, endNullResult.getEnd());
assertEquals(fixedStart, bothNullResult.getStart());
assertEquals(fixedEnd, bothNullResult.getEnd());
assertEquals(fixedStart, bothNNShouldCorrectResult.getStart());
assertEquals(outerFixedPoint, bothNNShouldCorrectResult.getEnd());
assertEquals(newPointInTime, bothNNSouldNotCorrectResult.getStart());
assertEquals(newPointInTime, bothNNSouldNotCorrectResult.getEnd());
// arrange even more (add negative offset)
config.offset = 93;
newPointInTime = (Calendar) pointInTime.clone();
newPointInTime.add(Calendar.MINUTE, 93);
fixedEnd.add(Calendar.MINUTE, 49);
outerFixedPoint = (Calendar) fixedStart.clone();
fixedStart.add(Calendar.MINUTE, 93);
// act yet again
startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT);
endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT);
bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT);
bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT);
bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE, Locale.ROOT);
// assert yet again
startNullResultStart = startNullResult.getStart();
startNullResultEnd = startNullResult.getEnd();
assertNotNull(startNullResultStart);
assertNotNull(startNullResultEnd);
assertEquals(fixedStart.getTime(), startNullResultStart.getTime());
assertEquals(newPointInTime.getTime(), startNullResultEnd.getTime());
assertEquals(newPointInTime, endNullResult.getStart());
assertEquals(fixedEnd, endNullResult.getEnd());
assertEquals(fixedStart, bothNullResult.getStart());
assertEquals(fixedEnd, bothNullResult.getEnd());
assertEquals(outerFixedPoint, bothNNShouldCorrectResult.getStart());
assertEquals(fixedEnd, bothNNShouldCorrectResult.getEnd());
assertEquals(newPointInTime, bothNNSouldNotCorrectResult.getStart());
assertEquals(newPointInTime, bothNNSouldNotCorrectResult.getEnd());
}
@Test
@@ -81,20 +151,21 @@ public class JobTest {
config.earliest = "08:00";
config.latest = "22:00";
config.forceEvent = false;
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0);
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0, TIME_ZONE, Locale.ROOT);
Range startNull = new Range(null, pointInTime);
Range endNull = new Range(pointInTime, null);
Range bothNull = new Range(null, null);
Range bothNNShouldCorrect = new Range(DateTimeUtils.createCalendarForToday(6, 0),
DateTimeUtils.createCalendarForToday(22, 0));
Range bothNNShouldCorrect = new Range(DateTimeUtils.createCalendarForToday(6, 0, TIME_ZONE, Locale.ROOT),
DateTimeUtils.createCalendarForToday(23, 10, TIME_ZONE, Locale.ROOT));
Range bothNNShouldNotCorrect = new Range(pointInTime, pointInTime);
// act
Range startNullResult = Job.adjustRangeToConfig(startNull, config);
Range endNullResult = Job.adjustRangeToConfig(endNull, config);
Range bothNullResult = Job.adjustRangeToConfig(bothNull, config);
Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config);
Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config);
Range startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT);
Range endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT);
Range bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT);
Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT);
Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE,
Locale.ROOT);
Calendar fixedStart = DateTimeUtils.getAdjustedEarliest(pointInTime, config);
Calendar fixdedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config);
@@ -14,7 +14,7 @@ package org.openhab.binding.astro.internal.model;
import static org.junit.jupiter.api.Assertions.*;
import java.time.ZoneId;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -37,7 +37,7 @@ public class SunTest {
private Sun sun;
private AstroChannelConfig config;
private static final ZoneId ZONE = ZoneId.systemDefault();
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Gaza");
@BeforeEach
public void init() {
@@ -49,7 +49,7 @@ public class SunTest {
public void testConstructor() throws Exception {
assertNotNull(sun.getPhase());
assertEquals(UnDefType.UNDEF,
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, TIME_ZONE));
}
@Test
@@ -57,7 +57,7 @@ public class SunTest {
sun.getPhase().setName(null);
assertEquals(UnDefType.UNDEF,
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, TIME_ZONE));
}
@Test
@@ -65,17 +65,7 @@ public class SunTest {
sun.getPhase().setName(SunPhaseName.DAYLIGHT);
assertEquals(new StringType("DAYLIGHT"),
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE));
}
@Test
public void testGetStateWhenNullPhase() throws Exception {
sun.setPhase(null);
assertNull(sun.getPhase());
assertThrows(NullPointerException.class, () -> assertEquals(UnDefType.UNDEF,
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, ZONE)));
PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), config, sun, TIME_ZONE));
}
@Test
@@ -12,10 +12,11 @@
*/
package org.openhab.binding.astro.internal.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
@@ -48,24 +49,25 @@ public class DateTimeUtilsTest {
@Test
public void testGetSeasonAmsterdam() {
final Season season = seasonCalc.getSeason(DEC_10_2020, AMSTERDAM_LATITUDE, true);
final Season season = seasonCalc.getSeason(DEC_10_2020, AMSTERDAM_LATITUDE, true, TIME_ZONE, Locale.ROOT);
assertNextSeason(season.getSpring(), 2020, JAN_20_2020, season);
assertNextSeason(season.getSummer(), 2020, MAY_20_2020, season);
assertNextSeason(season.getWinter(), 2020, SEPT_20_2020, season);
assertNextSeason(seasonCalc.getSeason(DEC_10_2021, AMSTERDAM_LATITUDE, true).getSpring(), 2021, DEC_10_2020,
season);
assertNextSeason(
seasonCalc.getSeason(DEC_10_2021, AMSTERDAM_LATITUDE, true, TIME_ZONE, Locale.ROOT).getSpring(), 2021,
DEC_10_2020, season);
}
@Test
public void testGetSeasonSydney() {
final Season season = seasonCalc.getSeason(DEC_10_2020, SYDNEY_LATITUDE, true);
final Season season = seasonCalc.getSeason(DEC_10_2020, SYDNEY_LATITUDE, true, TIME_ZONE, Locale.ROOT);
assertNextSeason(season.getAutumn(), 2020, JAN_20_2020, season);
assertNextSeason(season.getWinter(), 2020, MAY_20_2020, season);
assertNextSeason(season.getSummer(), 2020, SEPT_20_2020, season);
assertNextSeason(seasonCalc.getSeason(DEC_10_2021, SYDNEY_LATITUDE, true).getAutumn(), 2021, DEC_10_2020,
season);
assertNextSeason(seasonCalc.getSeason(DEC_10_2021, SYDNEY_LATITUDE, true, TIME_ZONE, Locale.ROOT).getAutumn(),
2021, DEC_10_2020, season);
}
@Test
@@ -75,8 +77,8 @@ public class DateTimeUtilsTest {
Calendar truncated = DateTimeUtils.truncateToMidnight(cal);
assertEquals(truncated, target);
Calendar endOfDay = DateTimeUtils.endOfDayDate(cal);
Calendar target2 = new GregorianCalendar(2021, 9, 30, 23, 59, 59);
target2.setTimeZone(TIME_ZONE);
Calendar target2 = newCalendar(2021, 9, 30, 23, 59, TIME_ZONE);
target2.set(Calendar.SECOND, 59);
target2.set(Calendar.MILLISECOND, 999);
assertEquals(endOfDay, target2);
}
@@ -85,12 +87,14 @@ public class DateTimeUtilsTest {
final Calendar nextSeason = DateTimeUtils.getNext(date, season.getSpring(), season.getSummer(),
season.getAutumn(), season.getWinter());
assertEquals(expectedSeason, nextSeason, "Should return the expected season name.");
assertNotNull(nextSeason);
assertEquals(expectedYear, nextSeason.get(Calendar.YEAR), "Should return the year matching the next season.");
}
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);
Calendar result = new GregorianCalendar(zone, Locale.ROOT);
result.set(Calendar.MILLISECOND, 0);
result.set(year, month, dayOfMonth, hourOfDay, minute, 0);
return result;
}
@@ -24,6 +24,7 @@ import org.openhab.binding.astro.internal.handler.AstroThingHandler;
import org.openhab.binding.astro.internal.handler.SunHandler;
import org.openhab.binding.astro.internal.model.Sun;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Channel;
@@ -64,8 +65,9 @@ public class AstroCommandTest {
ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
CronScheduler cronScheduler = mock(CronScheduler.class);
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
LocaleProvider localeProvider = mock(LocaleProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.systemDefault());
AstroThingHandler sunHandler = spy(new SunHandler(thing, cronScheduler, timeZoneProvider));
AstroThingHandler sunHandler = spy(new SunHandler(thing, cronScheduler, timeZoneProvider, localeProvider));
// Required from the AstroThingHandler to send the status update
doReturn(true).when(callback).isChannelLinked(eq(channelUID));
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
import org.openhab.binding.astro.internal.handler.SunHandler;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Thing;
@@ -147,8 +148,9 @@ public class AstroValidConfigurationTest {
ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
CronScheduler cronScheduler = mock(CronScheduler.class);
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
LocaleProvider localeProvider = mock(LocaleProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.systemDefault());
ThingHandler sunHandler = new SunHandler(thing, cronScheduler, timeZoneProvider);
ThingHandler sunHandler = new SunHandler(thing, cronScheduler, timeZoneProvider, localeProvider);
sunHandler.setCallback(callback);
sunHandler.initialize();
@@ -23,6 +23,8 @@ import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -67,7 +69,8 @@ public class AstroStateTest {
private void assertStateUpdate(String thingID, String channelId, State expectedState) throws Exception {
ChannelUID testItemChannelUID = new ChannelUID(getThingUID(thingID), channelId);
State state = PropertyUtils.getState(testItemChannelUID, new AstroChannelConfig(), getPlanet(thingID), ZONE_ID);
State state = PropertyUtils.getState(testItemChannelUID, new AstroChannelConfig(), getPlanet(thingID),
TimeZone.getTimeZone(ZONE_ID));
assertEquals(expectedState, state);
}
@@ -89,10 +92,12 @@ public class AstroStateTest {
switch (thingID) {
case (TEST_SUN_THING_ID):
SunCalc sunCalc = new SunCalc();
return sunCalc.getSunInfo(calendar, TEST_LATITUDE, TEST_LONGITUDE, null, false);
return sunCalc.getSunInfo(calendar, TEST_LATITUDE, TEST_LONGITUDE, null, false,
TimeZone.getTimeZone(ZONE_ID), Locale.getDefault());
case (TEST_MOON_THING_ID):
MoonCalc moonCalc = new MoonCalc();
return moonCalc.getMoonInfo(calendar, TEST_LATITUDE, TEST_LONGITUDE);
return moonCalc.getMoonInfo(calendar, TEST_LATITUDE, TEST_LONGITUDE, TimeZone.getTimeZone(ZONE_ID),
Locale.getDefault());
default:
return null;
}