[astro] Remove use of reflection (#20000)

* Remove the use of reflection

Signed-off-by: Ravi Nadahar <nadahar@rediffmail.com>
This commit is contained in:
Nadahar
2026-01-15 09:25:26 +01:00
committed by GitHub
parent b584fe9737
commit 05f85336e6
30 changed files with 859 additions and 502 deletions
@@ -49,6 +49,103 @@ public final class AstroBindingConstants {
public static final String EVENT_PHASE_FULL = "FULL";
public static final String EVENT_PHASE_NEW = "NEW";
// regular channelIds
public static final String CHANNEL_ID_MOON_RISE_START = "rise#start";
public static final String CHANNEL_ID_MOON_RISE_END = "rise#end";
public static final String CHANNEL_ID_MOON_RISE_DURATION = "rise#duration";
public static final String CHANNEL_ID_MOON_SET_START = "set#start";
public static final String CHANNEL_ID_MOON_SET_END = "set#end";
public static final String CHANNEL_ID_MOON_SET_DURATION = "set#duration";
public static final String CHANNEL_ID_MOON_PHASE_FIRST_QUARTER = "phase#firstQuarter";
public static final String CHANNEL_ID_MOON_PHASE_THIRD_QUARTER = "phase#thirdQuarter";
public static final String CHANNEL_ID_MOON_PHASE_FULL = "phase#full";
public static final String CHANNEL_ID_MOON_PHASE_NEW = "phase#new";
public static final String CHANNEL_ID_MOON_PHASE_AGE = "phase#age";
public static final String CHANNEL_ID_MOON_PHASE_AGE_DEGREE = "phase#ageDegree";
public static final String CHANNEL_ID_MOON_PHASE_AGE_PERCENT = "phase#agePercent";
public static final String CHANNEL_ID_MOON_PHASE_ILLUMINATION = "phase#illumination";
public static final String CHANNEL_ID_MOON_PHASE_NAME = "phase#name";
public static final String CHANNEL_ID_MOON_ECLIPSE_TOTAL = "eclipse#total";
public static final String CHANNEL_ID_MOON_ECLIPSE_TOTAL_ELEVATION = "eclipse#totalElevation";
public static final String CHANNEL_ID_MOON_ECLIPSE_PARTIAL = "eclipse#partial";
public static final String CHANNEL_ID_MOON_ECLIPSE_PARTIAL_ELEVATION = "eclipse#partialElevation";
public static final String CHANNEL_ID_MOON_DISTANCE_DATE = "distance#date";
public static final String CHANNEL_ID_MOON_DISTANCE_DISTANCE = "distance#distance";
public static final String CHANNEL_ID_MOON_PERIGEE_DATE = "perigee#date";
public static final String CHANNEL_ID_MOON_PERIGEE_DISTANCE = "perigee#distance";
public static final String CHANNEL_ID_MOON_APOGEE_DATE = "apogee#date";
public static final String CHANNEL_ID_MOON_APOGEE_DISTANCE = "apogee#distance";
public static final String CHANNEL_ID_MOON_POSITION_AZIMUTH = "position#azimuth";
public static final String CHANNEL_ID_MOON_POSITION_ELEVATION = "position#elevation";
public static final String CHANNEL_ID_MOON_POSITION_SHADE_LENGTH = "position#shadeLength";
public static final String CHANNEL_ID_MOON_ZODIAC_SIGN = "zodiac#sign";
public static final String CHANNEL_ID_SUN_RISE_START = "rise#start";
public static final String CHANNEL_ID_SUN_RISE_END = "rise#end";
public static final String CHANNEL_ID_SUN_RISE_DURATION = "rise#duration";
public static final String CHANNEL_ID_SUN_SET_START = "set#start";
public static final String CHANNEL_ID_SUN_SET_END = "set#end";
public static final String CHANNEL_ID_SUN_SET_DURATION = "set#duration";
public static final String CHANNEL_ID_SUN_NOON_START = "noon#start";
public static final String CHANNEL_ID_SUN_NOON_END = "noon#end";
public static final String CHANNEL_ID_SUN_NOON_DURATION = "noon#duration";
public static final String CHANNEL_ID_SUN_NIGHT_START = "night#start";
public static final String CHANNEL_ID_SUN_NIGHT_END = "night#end";
public static final String CHANNEL_ID_SUN_NIGHT_DURATION = "night#duration";
public static final String CHANNEL_ID_SUN_MORNING_NIGHT_START = "morningNight#start";
public static final String CHANNEL_ID_SUN_MORNING_NIGHT_END = "morningNight#end";
public static final String CHANNEL_ID_SUN_MORNING_NIGHT_DURATION = "morningNight#duration";
public static final String CHANNEL_ID_SUN_ASTRO_DAWN_START = "astroDawn#start";
public static final String CHANNEL_ID_SUN_ASTRO_DAWN_END = "astroDawn#end";
public static final String CHANNEL_ID_SUN_ASTRO_DAWN_DURATION = "astroDawn#duration";
public static final String CHANNEL_ID_SUN_NAUTIC_DAWN_START = "nauticDawn#start";
public static final String CHANNEL_ID_SUN_NAUTIC_DAWN_END = "nauticDawn#end";
public static final String CHANNEL_ID_SUN_NAUTIC_DAWN_DURATION = "nauticDawn#duration";
public static final String CHANNEL_ID_SUN_CIVIL_DAWN_START = "civilDawn#start";
public static final String CHANNEL_ID_SUN_CIVIL_DAWN_END = "civilDawn#end";
public static final String CHANNEL_ID_SUN_CIVIL_DAWN_DURATION = "civilDawn#duration";
public static final String CHANNEL_ID_SUN_ASTRO_DUSK_START = "astroDusk#start";
public static final String CHANNEL_ID_SUN_ASTRO_DUSK_END = "astroDusk#end";
public static final String CHANNEL_ID_SUN_ASTRO_DUSK_DURATION = "astroDusk#duration";
public static final String CHANNEL_ID_SUN_NAUTIC_DUSK_START = "nauticDusk#start";
public static final String CHANNEL_ID_SUN_NAUTIC_DUSK_END = "nauticDusk#end";
public static final String CHANNEL_ID_SUN_NAUTIC_DUSK_DURATION = "nauticDusk#duration";
public static final String CHANNEL_ID_SUN_CIVIL_DUSK_START = "civilDusk#start";
public static final String CHANNEL_ID_SUN_CIVIL_DUSK_END = "civilDusk#end";
public static final String CHANNEL_ID_SUN_CIVIL_DUSK_DURATION = "civilDusk#duration";
public static final String CHANNEL_ID_SUN_EVENING_NIGHT_START = "eveningNight#start";
public static final String CHANNEL_ID_SUN_EVENING_NIGHT_END = "eveningNight#end";
public static final String CHANNEL_ID_SUN_EVENING_NIGHT_DURATION = "eveningNight#duration";
public static final String CHANNEL_ID_SUN_DAYLIGHT_START = "daylight#start";
public static final String CHANNEL_ID_SUN_DAYLIGHT_END = "daylight#end";
public static final String CHANNEL_ID_SUN_DAYLIGHT_DURATION = "daylight#duration";
public static final String CHANNEL_ID_SUN_POSITION_AZIMUTH = "position#azimuth";
public static final String CHANNEL_ID_SUN_POSITION_ELEVATION = "position#elevation";
public static final String CHANNEL_ID_SUN_POSITION_SHADE_LENGTH = "position#shadeLength";
public static final String CHANNEL_ID_SUN_RADIATION_DIRECT = "radiation#direct";
public static final String CHANNEL_ID_SUN_RADIATION_DIFFUSE = "radiation#diffuse";
public static final String CHANNEL_ID_SUN_RADIATION_TOTAL = "radiation#total";
public static final String CHANNEL_ID_SUN_ZODIAC_START = "zodiac#start";
public static final String CHANNEL_ID_SUN_ZODIAC_END = "zodiac#end";
public static final String CHANNEL_ID_SUN_ZODIAC_SIGN = "zodiac#sign";
public static final String CHANNEL_ID_SUN_SEASON_NAME = "season#name";
public static final String CHANNEL_ID_SUN_SEASON_SPRING = "season#spring";
public static final String CHANNEL_ID_SUN_SEASON_SUMMER = "season#summer";
public static final String CHANNEL_ID_SUN_SEASON_AUTUMN = "season#autumn";
public static final String CHANNEL_ID_SUN_SEASON_WINTER = "season#winter";
public static final String CHANNEL_ID_SUN_SEASON_NEXT_NAME = "season#nextName";
public static final String CHANNEL_ID_SUN_SEASON_TIME_LEFT = "season#timeLeft";
public static final String CHANNEL_ID_SUN_ECLIPSE_TOTAL = "eclipse#total";
public static final String CHANNEL_ID_SUN_ECLIPSE_TOTAL_ELEVATION = "eclipse#totalElevation";
public static final String CHANNEL_ID_SUN_ECLIPSE_PARTIAL = "eclipse#partial";
public static final String CHANNEL_ID_SUN_ECLIPSE_PARTIAL_ELEVATION = "eclipse#partialElevation";
public static final String CHANNEL_ID_SUN_ECLIPSE_RING = "eclipse#ring";
public static final String CHANNEL_ID_SUN_ECLIPSE_RING_ELEVATION = "eclipse#ringElevation";
public static final String CHANNEL_ID_SUN_PHASE_NAME = "phase#name";
public static final String CHANNEL_ID_SUN_CIRCADIAN_BRIGHTNESS = "circadian#brightness";
public static final String CHANNEL_ID_SUN_CIRCADIAN_TEMPERATURE = "circadian#temperature";
// event channelIds
public static final String EVENT_CHANNEL_ID_MOON_PHASE = "phase#event";
public static final String EVENT_CHANNEL_ID_ECLIPSE = "eclipse#event";
@@ -66,6 +163,4 @@ public final class AstroBindingConstants {
public static final String EVENT_CHANNEL_ID_CIVIL_DUSK = "civilDusk#event";
public static final String EVENT_CHANNEL_ID_EVENING_NIGHT = "eveningNight#event";
public static final String EVENT_CHANNEL_ID_DAYLIGHT = "daylight#event";
public static final String CHANNEL_ID_SUN_PHASE_NAME = "phase#name";
}
@@ -14,6 +14,7 @@ package org.openhab.binding.astro.internal;
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import java.time.InstantSource;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -65,9 +66,9 @@ public class AstroHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
AstroThingHandler thingHandler = null;
if (thingTypeUID.equals(THING_TYPE_SUN)) {
thingHandler = new SunHandler(thing, scheduler, timeZoneProvider, localeProvider);
thingHandler = new SunHandler(thing, scheduler, timeZoneProvider, localeProvider, InstantSource.system());
} else if (thingTypeUID.equals(THING_TYPE_MOON)) {
thingHandler = new MoonHandler(thing, scheduler, timeZoneProvider, localeProvider);
thingHandler = new MoonHandler(thing, scheduler, timeZoneProvider, localeProvider, InstantSource.system());
}
return thingHandler;
}
@@ -16,6 +16,7 @@ import static org.openhab.binding.astro.internal.util.MathUtils.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.Locale;
import java.util.Set;
@@ -51,6 +52,17 @@ public class MoonCalc {
private static final double FIRST_QUARTER = 0.25;
private static final double LAST_QUARTER = 0.75;
private final InstantSource instantSource;
/**
* Creates a new instance using the specified {@link InstantSource}.
*
* @param instantSource the source of the current time.
*/
public MoonCalc(InstantSource instantSource) {
this.instantSource = instantSource;
}
/**
* Calculates all moon data at the specified coordinates
*/
@@ -135,7 +147,7 @@ public class MoonCalc {
return;
}
long ageRangeTimeMillis = cal.getTimeInMillis() - parentNewMoonMillis;
long ageCurrentMillis = System.currentTimeMillis() - parentNewMoonMillis;
long ageCurrentMillis = instantSource.millis() - parentNewMoonMillis;
double agePercent = ageRangeTimeMillis != 0 ? ageCurrentMillis * 100.0 / ageRangeTimeMillis : 0;
phase.setAgePercent(agePercent);
phase.setAgeDegree(3.6 * agePercent);
@@ -13,6 +13,7 @@
package org.openhab.binding.astro.internal.calc;
import java.time.Instant;
import java.time.InstantSource;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -40,9 +41,11 @@ public class SeasonCalc {
/**
* Returns the seasons of the year of the specified calendar.
*/
public static Season calculate(int year, double latitude, boolean useMeteorologicalSeason, TimeZone zone) {
return new Season(latitude, useMeteorologicalSeason, zone, calcEquiSol(3, year - 1), calcEquiSol(0, year),
calcEquiSol(1, year), calcEquiSol(2, year), calcEquiSol(3, year), calcEquiSol(0, year + 1));
public static Season calculate(int year, double latitude, boolean useMeteorologicalSeason, TimeZone zone,
InstantSource instantSource) {
return new Season(latitude, useMeteorologicalSeason, zone, instantSource, calcEquiSol(3, year - 1),
calcEquiSol(0, year), calcEquiSol(1, year), calcEquiSol(2, year), calcEquiSol(3, year),
calcEquiSol(0, year + 1));
}
/**
@@ -12,6 +12,7 @@
*/
package org.openhab.binding.astro.internal.calc;
import java.time.InstantSource;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -65,6 +66,17 @@ public class SunCalc {
private static final int CURVE_TIME_INTERVAL = 20; // 20 minutes
private static final double JD_ONE_MINUTE_FRACTION = 1.0 / 60 / 24;
private final InstantSource instantSource;
/**
* Creates a new instance using the specified {@link InstantSource}.
*
* @param instantSource the source of the current time.
*/
public SunCalc(InstantSource instantSource) {
this.instantSource = instantSource;
}
/**
* Calculates the sun position (azimuth and elevation).
*/
@@ -222,7 +234,7 @@ public class SunCalc {
// eclipse
Eclipse eclipse = sun.getEclipse();
MoonCalc mc = new MoonCalc();
MoonCalc mc = new MoonCalc(instantSource);
eclipse.getKinds().forEach(eclipseKind -> {
double jdate = mc.getEclipse(calendar, EclipseType.SUN, j, eclipseKind);
@@ -237,7 +249,7 @@ public class SunCalc {
Season season = sun.getSeason();
var year = calendar.get(Calendar.YEAR);
if (season == null || season.getYear() != year) {
sun.setSeason(SeasonCalc.calculate(year, latitude, useMeteorologicalSeason, zone));
sun.setSeason(SeasonCalc.calculate(year, latitude, useMeteorologicalSeason, zone, instantSource));
}
// phase
@@ -17,13 +17,17 @@ import static org.openhab.core.thing.type.ChannelKind.TRIGGER;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -44,9 +48,12 @@ import org.openhab.binding.astro.internal.job.Job;
import org.openhab.binding.astro.internal.job.PositionalJob;
import org.openhab.binding.astro.internal.model.Planet;
import org.openhab.binding.astro.internal.model.Position;
import org.openhab.binding.astro.internal.util.PropertyUtils;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.scheduler.ScheduledCompletableFuture;
import org.openhab.core.thing.Channel;
@@ -95,12 +102,16 @@ public abstract class AstroThingHandler extends BaseThingHandler {
// All access must be guarded by "monitor"
private @Nullable ScheduledCompletableFuture<?> dailyJob;
/** The source of the current time */
protected final InstantSource instantSource;
public AstroThingHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider,
LocaleProvider localeProvider) {
LocaleProvider localeProvider, InstantSource instantSource) {
super(thing);
this.cronScheduler = scheduler;
this.timeZoneProvider = timeZoneProvider;
this.localeProvider = localeProvider;
this.instantSource = instantSource;
}
@Override
@@ -188,15 +199,23 @@ public abstract class AstroThingHandler extends BaseThingHandler {
return;
}
try {
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
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);
updateState(channelUID, getState(channel));
} catch (IllegalArgumentException e) {
logger.warn("Can't retrieve the state for channel '{}': {}", channelUID, e.getMessage());
logger.trace("", e);
}
}
}
/**
* Retrieve the channel state and convert it to an appropriate {@link State} instance.
*
* @param channel the {@link Channel} whose {@link State} to get.
* @return The resulting channel {@link State}.
* @throws IllegalArgumentException If the channel has a state of an unsupported type.
*/
protected abstract State getState(Channel channel);
/**
* Schedules a positional and a daily job at midnight for Astro calculation and starts it immediately too. Removes
* already scheduled jobs first.
@@ -331,6 +350,7 @@ public abstract class AstroThingHandler extends BaseThingHandler {
* Adds the provided {@link Job} to the queue (cannot be {@code null})
*/
public void schedule(Job job, Calendar eventAt) {
// We don't use instantSource here, because we always want to schedule relative to the system clock
long sleepTime = eventAt.getTimeInMillis() - System.currentTimeMillis();
if (sleepTime >= 0L) {
schedule(job, sleepTime);
@@ -345,6 +365,7 @@ public abstract class AstroThingHandler extends BaseThingHandler {
}
public void schedule(Job job, Instant eventAt) {
// We don't use instantSource here, because we always want to schedule relative to the system clock
long sleepTime = eventAt.toEpochMilli() + 1L - System.currentTimeMillis();
if (sleepTime >= 0L) {
schedule(job, sleepTime);
@@ -415,4 +436,35 @@ public abstract class AstroThingHandler extends BaseThingHandler {
public Collection<Class<? extends ThingHandlerService>> getServices() {
return List.of(AstroActions.class);
}
/**
* Convert an untyped value into the appropriate {@link State} type for the specified {@link Channel}.
*
* @param value the {@link Object} to convert.
* @param channel the {@link Channel} whose type to convert to.
* @return The appropriate {@link State} instance.
* @throws IllegalArgumentException If {@code value} is of an unsupported type.
*/
protected State toState(@Nullable Object value, Channel channel) {
if (value == null) {
return UnDefType.UNDEF;
} else if (value instanceof State state) {
return state;
} else if (value instanceof Calendar cal) {
cal.setTimeZone(TimeZone.getTimeZone(timeZoneProvider.getTimeZone()));
GregorianCalendar gregorianCal = (GregorianCalendar) DateTimeUtils.applyConfig(cal,
channel.getConfiguration().as(AstroChannelConfig.class));
return new DateTimeType(gregorianCal.toInstant());
} else if (value instanceof Instant instant) {
return new DateTimeType(
DateTimeUtils.applyConfig(instant, channel.getConfiguration().as(AstroChannelConfig.class)));
} else if (value instanceof Number) {
BigDecimal decimalValue = new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_UP);
return new DecimalType(decimalValue);
} else if (value instanceof String || value instanceof Enum) {
return new StringType(value.toString());
} else {
throw new IllegalArgumentException("Unsupported value type " + value.getClass().getSimpleName());
}
}
}
@@ -12,9 +12,11 @@
*/
package org.openhab.binding.astro.internal.handler;
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import java.time.InstantSource;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
@@ -27,10 +29,16 @@ 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.binding.astro.internal.util.DateTimeUtils;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The MoonHandler is responsible for updating calculated moon data.
@@ -43,15 +51,17 @@ 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 final MoonCalc moonCalc;
private final Logger logger = LoggerFactory.getLogger(MoonHandler.class);
private volatile @Nullable Moon moon;
/**
* Constructor
*/
public MoonHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider,
LocaleProvider localeProvider) {
super(thing, scheduler, timeZoneProvider, localeProvider);
LocaleProvider localeProvider, InstantSource instantSource) {
super(thing, scheduler, timeZoneProvider, localeProvider, instantSource);
moonCalc = new MoonCalc(instantSource);
}
@Override
@@ -59,11 +69,12 @@ public class MoonHandler extends AstroThingHandler {
ZoneId zoneId = timeZoneProvider.getTimeZone();
TimeZone zone = TimeZone.getTimeZone(zoneId);
Locale locale = localeProvider.getLocale();
Moon moon = getMoonAt(ZonedDateTime.now(zoneId), locale);
ZonedDateTime now = instantSource.instant().atZone(zone.toZoneId());
Moon moon = getMoonAt(now, locale);
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
moonCalc.setPositionalInfo(Calendar.getInstance(zone, locale), latitude != null ? latitude : 0,
longitude != null ? longitude : 0, moon, zone, locale);
moonCalc.setPositionalInfo(DateTimeUtils.calFromInstantSource(instantSource, zone, locale),
latitude != null ? latitude : 0, longitude != null ? longitude : 0, moon, zone, locale);
moon.getEclipse().setElevations(this, timeZoneProvider);
this.moon = moon;
@@ -82,6 +93,78 @@ public class MoonHandler extends AstroThingHandler {
moon = null;
}
@Override
protected State getState(Channel channel) {
Moon moon = this.moon;
if (moon == null) {
return UnDefType.UNDEF;
}
switch (channel.getUID().getId()) {
case CHANNEL_ID_MOON_RISE_START:
return toState(moon.getRise().getStart(), channel);
case CHANNEL_ID_MOON_RISE_END:
return toState(moon.getRise().getEnd(), channel);
case CHANNEL_ID_MOON_RISE_DURATION:
return toState(moon.getRise().getDuration(), channel);
case CHANNEL_ID_MOON_SET_START:
return toState(moon.getSet().getStart(), channel);
case CHANNEL_ID_MOON_SET_END:
return toState(moon.getSet().getEnd(), channel);
case CHANNEL_ID_MOON_SET_DURATION:
return toState(moon.getSet().getDuration(), channel);
case CHANNEL_ID_MOON_PHASE_FIRST_QUARTER:
return toState(moon.getPhase().getFirstQuarter(), channel);
case CHANNEL_ID_MOON_PHASE_THIRD_QUARTER:
return toState(moon.getPhase().getThirdQuarter(), channel);
case CHANNEL_ID_MOON_PHASE_FULL:
return toState(moon.getPhase().getFull(), channel);
case CHANNEL_ID_MOON_PHASE_NEW:
return toState(moon.getPhase().getNew(), channel);
case CHANNEL_ID_MOON_PHASE_AGE:
return toState(moon.getPhase().getAge(), channel);
case CHANNEL_ID_MOON_PHASE_AGE_DEGREE:
return toState(moon.getPhase().getAgeDegree(), channel);
case CHANNEL_ID_MOON_PHASE_AGE_PERCENT:
return toState(moon.getPhase().getAgePercent(), channel);
case CHANNEL_ID_MOON_PHASE_ILLUMINATION:
return toState(moon.getPhase().getIllumination(), channel);
case CHANNEL_ID_MOON_PHASE_NAME:
return toState(moon.getPhase().getName(), channel);
case CHANNEL_ID_MOON_ECLIPSE_TOTAL:
return toState(moon.getEclipse().getTotal(), channel);
case CHANNEL_ID_MOON_ECLIPSE_TOTAL_ELEVATION:
return toState(moon.getEclipse().getTotalElevation(), channel);
case CHANNEL_ID_MOON_ECLIPSE_PARTIAL:
return toState(moon.getEclipse().getPartial(), channel);
case CHANNEL_ID_MOON_ECLIPSE_PARTIAL_ELEVATION:
return toState(moon.getEclipse().getPartialElevation(), channel);
case CHANNEL_ID_MOON_DISTANCE_DATE:
return toState(moon.getDistance().getDate(), channel);
case CHANNEL_ID_MOON_DISTANCE_DISTANCE:
return toState(moon.getDistance().getDistance(), channel);
case CHANNEL_ID_MOON_PERIGEE_DATE:
return toState(moon.getPerigee().getDate(), channel);
case CHANNEL_ID_MOON_PERIGEE_DISTANCE:
return toState(moon.getPerigee().getDistance(), channel);
case CHANNEL_ID_MOON_APOGEE_DATE:
return toState(moon.getApogee().getDate(), channel);
case CHANNEL_ID_MOON_APOGEE_DISTANCE:
return toState(moon.getApogee().getDistance(), channel);
case CHANNEL_ID_MOON_POSITION_AZIMUTH:
return toState(moon.getPosition().getAzimuth(), channel);
case CHANNEL_ID_MOON_POSITION_ELEVATION:
return toState(moon.getPosition().getElevation(), channel);
case CHANNEL_ID_MOON_POSITION_SHADE_LENGTH:
return toState(moon.getPosition().getShadeLength(), channel);
case CHANNEL_ID_MOON_ZODIAC_SIGN:
return toState(moon.getZodiac().getSign(), channel);
default:
logger.warn("Unsupported channel: {}", channel.getUID());
}
return UnDefType.UNDEF;
}
@Override
protected String[] getPositionalChannelIds() {
return positionalChannelIds;
@@ -12,7 +12,9 @@
*/
package org.openhab.binding.astro.internal.handler;
import java.time.Instant;
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import java.time.InstantSource;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
@@ -31,12 +33,19 @@ import org.openhab.binding.astro.internal.model.Planet;
import org.openhab.binding.astro.internal.model.Position;
import org.openhab.binding.astro.internal.model.Radiation;
import org.openhab.binding.astro.internal.model.Range;
import org.openhab.binding.astro.internal.model.Season;
import org.openhab.binding.astro.internal.model.Sun;
import org.openhab.binding.astro.internal.model.SunPhaseName;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The SunHandler is responsible for updating calculated sun data.
@@ -49,15 +58,17 @@ 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 volatile @Nullable Sun sun;
private final SunCalc sunCalc;
private final Logger logger = LoggerFactory.getLogger(SunHandler.class);
volatile @Nullable Sun sun;
/**
* Constructor
*/
public SunHandler(Thing thing, final CronScheduler scheduler, final TimeZoneProvider timeZoneProvider,
LocaleProvider localeProvider) {
super(thing, scheduler, timeZoneProvider, localeProvider);
LocaleProvider localeProvider, InstantSource instantSource) {
super(thing, scheduler, timeZoneProvider, localeProvider, instantSource);
sunCalc = new SunCalc(instantSource);
}
@Override
@@ -65,12 +76,12 @@ public class SunHandler extends AstroThingHandler {
ZoneId zoneId = timeZoneProvider.getTimeZone();
TimeZone zone = TimeZone.getTimeZone(zoneId);
Locale locale = localeProvider.getLocale();
Sun sun = getSunAt(ZonedDateTime.now(zoneId));
ZonedDateTime now = instantSource.instant().atZone(zoneId);
Sun sun = getSunAt(now);
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
Double altitude = thingConfig.altitude;
Calendar calendar = Calendar.getInstance(zone, locale);
ZonedDateTime now = Instant.now().atZone(zoneId);
Calendar calendar = DateTimeUtils.calFromInstantSource(instantSource, zone, locale);
sunCalc.setPositionalInfo(calendar, latitude != null ? latitude : 0, longitude != null ? longitude : 0,
altitude != null ? altitude : 0, sun);
@@ -95,6 +106,190 @@ public class SunHandler extends AstroThingHandler {
sun = null;
}
@Override
protected State getState(Channel channel) {
Sun sun = this.sun;
if (sun == null) {
return UnDefType.UNDEF;
}
Range r;
Season s;
switch (channel.getUID().getId()) {
case CHANNEL_ID_SUN_RISE_START:
return toState(sun.getRise().getStart(), channel);
case CHANNEL_ID_SUN_RISE_END:
return toState(sun.getRise().getEnd(), channel);
case CHANNEL_ID_SUN_RISE_DURATION:
return toState(sun.getRise().getDuration(), channel);
case CHANNEL_ID_SUN_SET_START:
return toState(sun.getSet().getStart(), channel);
case CHANNEL_ID_SUN_SET_END:
return toState(sun.getSet().getEnd(), channel);
case CHANNEL_ID_SUN_SET_DURATION:
return toState(sun.getSet().getDuration(), channel);
case CHANNEL_ID_SUN_NOON_START:
r = sun.getNoon();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_NOON_END:
r = sun.getNoon();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_NOON_DURATION:
r = sun.getNoon();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_NIGHT_START:
r = sun.getNight();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_NIGHT_END:
r = sun.getNight();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_NIGHT_DURATION:
r = sun.getNight();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_MORNING_NIGHT_START:
r = sun.getMorningNight();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_MORNING_NIGHT_END:
r = sun.getMorningNight();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_MORNING_NIGHT_DURATION:
r = sun.getMorningNight();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_ASTRO_DAWN_START:
r = sun.getAstroDawn();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_ASTRO_DAWN_END:
r = sun.getAstroDawn();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_ASTRO_DAWN_DURATION:
r = sun.getAstroDawn();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_NAUTIC_DAWN_START:
r = sun.getNauticDawn();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_NAUTIC_DAWN_END:
r = sun.getNauticDawn();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_NAUTIC_DAWN_DURATION:
r = sun.getNauticDawn();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_CIVIL_DAWN_START:
r = sun.getCivilDawn();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_CIVIL_DAWN_END:
r = sun.getCivilDawn();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_CIVIL_DAWN_DURATION:
r = sun.getCivilDawn();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_ASTRO_DUSK_START:
r = sun.getAstroDusk();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_ASTRO_DUSK_END:
r = sun.getAstroDusk();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_ASTRO_DUSK_DURATION:
r = sun.getAstroDusk();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_NAUTIC_DUSK_START:
r = sun.getNauticDusk();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_NAUTIC_DUSK_END:
r = sun.getNauticDusk();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_NAUTIC_DUSK_DURATION:
r = sun.getNauticDusk();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_CIVIL_DUSK_START:
r = sun.getCivilDusk();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_CIVIL_DUSK_END:
r = sun.getCivilDusk();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_CIVIL_DUSK_DURATION:
r = sun.getCivilDusk();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_EVENING_NIGHT_START:
r = sun.getEveningNight();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_EVENING_NIGHT_END:
r = sun.getEveningNight();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_EVENING_NIGHT_DURATION:
r = sun.getEveningNight();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_DAYLIGHT_START:
r = sun.getDaylight();
return r == null ? UnDefType.UNDEF : toState(r.getStart(), channel);
case CHANNEL_ID_SUN_DAYLIGHT_END:
r = sun.getDaylight();
return r == null ? UnDefType.UNDEF : toState(r.getEnd(), channel);
case CHANNEL_ID_SUN_DAYLIGHT_DURATION:
r = sun.getDaylight();
return r == null ? UnDefType.UNDEF : toState(r.getDuration(), channel);
case CHANNEL_ID_SUN_POSITION_AZIMUTH:
return toState(sun.getPosition().getAzimuth(), channel);
case CHANNEL_ID_SUN_POSITION_ELEVATION:
return toState(sun.getPosition().getElevation(), channel);
case CHANNEL_ID_SUN_POSITION_SHADE_LENGTH:
return toState(sun.getPosition().getShadeLength(), channel);
case CHANNEL_ID_SUN_RADIATION_DIRECT:
return toState(sun.getRadiation().getDirect(), channel);
case CHANNEL_ID_SUN_RADIATION_DIFFUSE:
return toState(sun.getRadiation().getDiffuse(), channel);
case CHANNEL_ID_SUN_RADIATION_TOTAL:
return toState(sun.getRadiation().getTotal(), channel);
case CHANNEL_ID_SUN_ZODIAC_START:
return toState(sun.getZodiac().getStart(), channel);
case CHANNEL_ID_SUN_ZODIAC_END:
return toState(sun.getZodiac().getEnd(), channel);
case CHANNEL_ID_SUN_ZODIAC_SIGN:
return toState(sun.getZodiac().getSign(), channel);
case CHANNEL_ID_SUN_SEASON_NAME:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getName(), channel);
case CHANNEL_ID_SUN_SEASON_SPRING:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getSpring(), channel);
case CHANNEL_ID_SUN_SEASON_SUMMER:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getSummer(), channel);
case CHANNEL_ID_SUN_SEASON_AUTUMN:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getAutumn(), channel);
case CHANNEL_ID_SUN_SEASON_WINTER:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getWinter(), channel);
case CHANNEL_ID_SUN_SEASON_NEXT_NAME:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getNextName(), channel);
case CHANNEL_ID_SUN_SEASON_TIME_LEFT:
s = sun.getSeason();
return s == null ? UnDefType.UNDEF : toState(s.getTimeLeft(), channel);
case CHANNEL_ID_SUN_ECLIPSE_TOTAL:
return toState(sun.getEclipse().getTotal(), channel);
case CHANNEL_ID_SUN_ECLIPSE_TOTAL_ELEVATION:
return toState(sun.getEclipse().getTotalElevation(), channel);
case CHANNEL_ID_SUN_ECLIPSE_PARTIAL:
return toState(sun.getEclipse().getPartial(), channel);
case CHANNEL_ID_SUN_ECLIPSE_PARTIAL_ELEVATION:
return toState(sun.getEclipse().getPartialElevation(), channel);
case CHANNEL_ID_SUN_ECLIPSE_RING:
return toState(sun.getEclipse().getRing(), channel);
case CHANNEL_ID_SUN_ECLIPSE_RING_ELEVATION:
return toState(sun.getEclipse().getRingElevation(), channel);
case CHANNEL_ID_SUN_PHASE_NAME:
return toState(sun.getPhase().getName(), channel);
case CHANNEL_ID_SUN_CIRCADIAN_BRIGHTNESS:
return toState(sun.getCircadian().getBrightness(), channel);
case CHANNEL_ID_SUN_CIRCADIAN_TEMPERATURE:
return toState(sun.getCircadian().getTemperature(), channel);
default:
logger.warn("Unsupported channel: {}", channel.getUID());
}
return UnDefType.UNDEF;
}
@Override
protected String[] getPositionalChannelIds() {
return positionalChannelIds;
@@ -102,7 +297,7 @@ public class SunHandler extends AstroThingHandler {
@Override
protected Job getDailyJob(TimeZone zone, Locale locale) {
return new DailyJobSun(this, zone, locale);
return new DailyJobSun(this, zone, locale, instantSource);
}
private Sun getSunAt(ZonedDateTime date) {
@@ -17,6 +17,7 @@ import static org.openhab.binding.astro.internal.job.Job.*;
import static org.openhab.binding.astro.internal.model.SunPhaseName.*;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
@@ -40,18 +41,23 @@ public final class DailyJobSun extends AbstractJob {
private final TimeZone zone;
private final Locale locale;
private final InstantSource instantSource;
/**
* Constructor
*
* @param handler the {@link AstroThingHandler} instance
* @param zone the {@link TimeZone} to use.
* @param locale the {@link Locale} to use.
* @param instantSource the time source to use.
* @throws IllegalArgumentException
* if {@code thingUID} or {@code handler} is {@code null}
*/
public DailyJobSun(AstroThingHandler handler, TimeZone zone, Locale locale) {
public DailyJobSun(AstroThingHandler handler, TimeZone zone, Locale locale, InstantSource instantSource) {
super(handler);
this.zone = zone;
this.locale = locale;
this.instantSource = instantSource;
}
@Override
@@ -68,51 +74,51 @@ public final class DailyJobSun extends AbstractJob {
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);
scheduleRange(handler, sun.getRise(), EVENT_CHANNEL_ID_RISE, zone, locale, instantSource);
scheduleRange(handler, sun.getSet(), EVENT_CHANNEL_ID_SET, zone, locale, instantSource);
Range range = sun.getNoon();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NOON, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_NOON, zone, locale, instantSource);
}
range = sun.getNight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NIGHT, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_NIGHT, zone, locale, instantSource);
}
range = sun.getMorningNight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_MORNING_NIGHT, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_MORNING_NIGHT, zone, locale, instantSource);
}
range = sun.getAstroDawn();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_ASTRO_DAWN, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_ASTRO_DAWN, zone, locale, instantSource);
}
range = sun.getNauticDawn();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NAUTIC_DAWN, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_NAUTIC_DAWN, zone, locale, instantSource);
}
range = sun.getCivilDawn();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_CIVIL_DAWN, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_CIVIL_DAWN, zone, locale, instantSource);
}
range = sun.getAstroDusk();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_ASTRO_DUSK, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_ASTRO_DUSK, zone, locale, instantSource);
}
range = sun.getNauticDusk();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_NAUTIC_DUSK, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_NAUTIC_DUSK, zone, locale, instantSource);
}
range = sun.getCivilDusk();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_CIVIL_DUSK, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_CIVIL_DUSK, zone, locale, instantSource);
}
range = sun.getEveningNight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_EVENING_NIGHT, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_EVENING_NIGHT, zone, locale, instantSource);
}
range = sun.getDaylight();
if (range != null) {
scheduleRange(handler, range, EVENT_CHANNEL_ID_DAYLIGHT, zone, locale);
scheduleRange(handler, range, EVENT_CHANNEL_ID_DAYLIGHT, zone, locale, instantSource);
}
Eclipse eclipse = sun.getEclipse();
@@ -18,6 +18,7 @@ import static org.openhab.binding.astro.internal.util.DateTimeUtils.*;
import java.lang.invoke.MethodHandles;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.List;
@@ -29,6 +30,7 @@ import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
import org.openhab.binding.astro.internal.model.Range;
import org.openhab.binding.astro.internal.model.SunPhaseName;
import org.openhab.binding.astro.internal.util.DateTimeUtils;
import org.openhab.core.scheduler.SchedulerRunnable;
import org.openhab.core.thing.Channel;
import org.slf4j.Logger;
@@ -56,6 +58,7 @@ public interface Job extends SchedulerRunnable, Runnable {
*/
static void schedule(AstroThingHandler astroHandler, Job job, Calendar eventAt, TimeZone zone, Locale locale) {
try {
// Don't use InstantSource here, because we always want to schedule relative to the system clock
Calendar today = Calendar.getInstance(zone, locale);
boolean sameDay = isSameDay(eventAt, today);
if (sameDay && isTimeGreaterEquals(eventAt, today)) {
@@ -81,6 +84,7 @@ public interface Job extends SchedulerRunnable, Runnable {
* @param zone the configured time zone
*/
static void schedule(AstroThingHandler astroHandler, Job job, Instant eventAt, ZoneId zone) {
// Don't use InstantSource here, because we always want to schedule relative to the system clock
Instant now = Instant.now();
boolean sameDay = isSameDay(eventAt.atZone(zone), now.atZone(zone));
if (sameDay && !eventAt.isBefore(now)) {
@@ -196,14 +200,14 @@ public interface Job extends SchedulerRunnable, Runnable {
* @param channelId the channel ID
*/
static void scheduleRange(AstroThingHandler astroHandler, Range range, String channelId, TimeZone zone,
Locale locale) {
Locale locale, InstantSource instantSource) {
final Channel channel = astroHandler.getThing().getChannel(channelId);
if (channel == null) {
LOGGER.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID());
return;
}
AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class);
Range adjustedRange = adjustRangeToConfig(range, config, zone, locale);
Range adjustedRange = adjustRangeToConfig(range, config, zone, locale, instantSource);
Calendar start = adjustedRange.getStart();
Calendar end = adjustedRange.getEnd();
@@ -217,14 +221,15 @@ public interface Job extends SchedulerRunnable, Runnable {
scheduleEvent(astroHandler, end, EVENT_END, channelId, true, zone, locale);
}
static Range adjustRangeToConfig(Range range, AstroChannelConfig config, TimeZone zone, Locale locale) {
static Range adjustRangeToConfig(Range range, AstroChannelConfig config, TimeZone zone, Locale locale,
InstantSource instantSource) {
Calendar start = range.getStart();
Calendar end = range.getEnd();
if (config.forceEvent) {
Calendar reference = start != null ? start : end;
if (reference == null) {
reference = Calendar.getInstance(zone, locale);
reference = DateTimeUtils.calFromInstantSource(instantSource, zone, locale);
}
if (start == null) {
start = getAdjustedEarliest(truncateToMidnight(reference), config);
@@ -14,6 +14,7 @@ package org.openhab.binding.astro.internal.model;
import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
@@ -51,12 +52,15 @@ public class Season {
private final List<LocalSeason> seasons = new ArrayList<>(5);
private final int year;
private final InstantSource instantSource;
public Season(double latitude, boolean useMeteorologicalSeason, TimeZone zone, Instant... equiSols) {
public Season(double latitude, boolean useMeteorologicalSeason, TimeZone zone, InstantSource instantSource,
Instant... equiSols) {
// Expect to receive last of previous year, all from current year, first of next year
if (equiSols.length != SeasonName.values().length + 2) {
throw new IllegalArgumentException("Incorrect number of seasons provided");
}
this.instantSource = instantSource;
var hemisphere = Hemisphere.getHemisphere(latitude);
List<Instant> moments = Arrays.stream(equiSols).sorted()
.map(i -> useMeteorologicalSeason ? DateTimeUtils.atMidnightOfFirstMonthDay(i, zone) : i).toList();
@@ -115,28 +119,28 @@ public class Season {
* Returns the current season name.
*/
public SeasonName getName() {
return getSeason(Instant.now()).name;
return getSeason(instantSource.instant()).name;
}
/**
* Returns the next season.
*/
public Instant getNextSeason() {
return getSeason(Instant.now()).endsOn;
return getSeason(instantSource.instant()).endsOn;
}
/**
* Returns the next season name.
*/
public SeasonName getNextName() {
return getSeason(Instant.now()).name.next();
return getSeason(instantSource.instant()).name.next();
}
/**
* Returns the time left for current season
*/
public QuantityType<Time> getTimeLeft() {
var now = Instant.now();
var now = instantSource.instant();
var timeLeft = Duration.between(now, getSeason(now).endsOn);
return new QuantityType<>(timeLeft.toDays(), Units.DAY);
@@ -13,6 +13,7 @@
package org.openhab.binding.astro.internal.util;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
@@ -217,8 +218,9 @@ public class DateTimeUtils {
/**
* Returns the next Calendar from today.
*/
public static Calendar getNextFromToday(TimeZone zone, Locale locale, Calendar... calendars) {
Calendar now = Calendar.getInstance(zone, locale);
public static Calendar getNextFromToday(TimeZone zone, Locale locale, InstantSource instantSource,
Calendar... calendars) {
Calendar now = calFromInstantSource(instantSource, zone, locale);
Calendar result = getNext(now, calendars);
return result == null ? now : result;
}
@@ -400,8 +402,9 @@ public class DateTimeUtils {
return (jd - JD_J2000) / JULIAN_CENTURY_DAYS;
}
public static Calendar createCalendarForToday(int hour, int minute, TimeZone zone, Locale locale) {
return DateTimeUtils.adjustTime(Calendar.getInstance(zone, locale), hour * 60 + minute);
public static Calendar createCalendarForToday(int hour, int minute, TimeZone zone, Locale locale,
InstantSource instantSource) {
return adjustTime(calFromInstantSource(instantSource, zone, locale), hour * 60 + minute);
}
/**
@@ -450,4 +453,19 @@ public class DateTimeUtils {
public static Instant atMidnightOfFirstMonthDay(Instant instant, TimeZone zone) {
return instant.atZone(zone.toZoneId()).withDayOfMonth(1).truncatedTo(ChronoUnit.DAYS).toInstant();
}
/**
* Creates a new {@link Calendar} instance with the specified time zone and locale, with the specified
* {@link InstantSource} as the time source.
*
* @param instantSource the time source.
* @param zone the {@link TimeZone} to use.
* @param locale the {@link Locale} to use.
* @return The new {@link Calendar} instance.
*/
public static Calendar calFromInstantSource(InstantSource instantSource, TimeZone zone, Locale locale) {
Calendar result = Calendar.getInstance(zone, locale);
result.setTimeInMillis(instantSource.millis());
return result;
}
}
@@ -1,114 +0,0 @@
/*
* Copyright (c) 2010-2026 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.internal.util;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* Methods to get the value from a property of an object.
*
* @author Gerhard Riegler - Initial contribution
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
* @author Christoph Weitkamp - Introduced UoM
*/
@NonNullByDefault
public class PropertyUtils {
/** Constructor */
private PropertyUtils() {
throw new IllegalAccessError("Non-instantiable");
}
/**
* Returns the state of the channel.
*/
public static State getState(ChannelUID channelUID, AstroChannelConfig config, Object instance, TimeZone zone)
throws Exception {
Object value = getPropertyValue(channelUID, instance);
if (value == null) {
return UnDefType.UNDEF;
} else if (value instanceof State state) {
return state;
} else if (value instanceof Calendar cal) {
GregorianCalendar gregorianCal = (GregorianCalendar) DateTimeUtils.applyConfig(cal, config);
cal.setTimeZone(zone);
ZonedDateTime zoned = gregorianCal.toZonedDateTime().withFixedOffsetZone();
return new DateTimeType(zoned);
} else if (value instanceof Instant instant) {
Instant configuredInstant = DateTimeUtils.applyConfig(instant, config);
return new DateTimeType(configuredInstant.atZone(zone.toZoneId()).withFixedOffsetZone());
} else if (value instanceof Number) {
BigDecimal decimalValue = new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_UP);
return new DecimalType(decimalValue);
} else if (value instanceof String || value instanceof Enum) {
return new StringType(value.toString());
} else {
throw new IllegalStateException("Unsupported value type " + value.getClass().getSimpleName());
}
}
/**
* Returns the property value from the object instance, nested properties are possible. If the propertyName is for
* example rise.start, the methods getRise().getStart() are called.
*/
private static @Nullable Object getPropertyValue(ChannelUID channelUID, Object instance) throws Exception {
ArrayList<String> properties = new ArrayList<>(List.of(channelUID.getId().split("#")));
return getPropertyValue(instance, properties);
}
/**
* Iterates through the nested properties and returns the getter value.
*/
@SuppressWarnings("all")
private static @Nullable Object getPropertyValue(Object instance, List<String> properties) throws Exception {
String propertyName = properties.remove(0);
Method m = instance.getClass().getMethod(toGetterString(propertyName), null);
Object result = m.invoke(instance, (Object[]) null);
if (!properties.isEmpty()) {
Objects.requireNonNull(result);
return getPropertyValue(result, properties);
}
return result;
}
/**
* Converts the string to a getter property.
*/
private static String toGetterString(String str) {
StringBuilder sb = new StringBuilder();
sb.append("get");
sb.append(Character.toTitleCase(str.charAt(0)));
sb.append(str.substring(1));
return sb.toString();
}
}
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.test.cases;
package org.openhab.binding.astro.internal;
import java.math.BigDecimal;
@@ -20,7 +20,7 @@ import java.math.BigDecimal;
* @author Svilen Valkanov - Initial contribution
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public final class AstroBindingTestsData {
public final class CommonTestConstants {
public static final String TEST_SUN_THING_ID = "testSunThingId";
public static final String TEST_MOON_THING_ID = "testMoonThingId";
@@ -14,6 +14,8 @@ package org.openhab.binding.astro.internal.calc;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
@@ -54,7 +56,7 @@ public class MoonCalcTest {
@BeforeEach
public void init() {
moonCalc = new MoonCalc();
moonCalc = new MoonCalc(InstantSource.fixed(Instant.ofEpochMilli(1645671600000L)));
}
@Test
@@ -15,6 +15,7 @@ package org.openhab.binding.astro.internal.calc;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
@@ -55,12 +56,13 @@ public class SeasonCalcTest {
AMSTERDAM_TZ);
private static final double AMSTERDAM_LONGITUDE = 4.8978293;
private static final double AMSTERDAM_ALTITUDE = 0.0;
private static final InstantSource INSTANT_SOURCE = InstantSource.fixed(Instant.ofEpochMilli(1645671600000L));
private @Nullable SunCalc sunCalc;
@BeforeEach
public void init() {
sunCalc = new SunCalc();
sunCalc = new SunCalc(INSTANT_SOURCE);
}
/***
@@ -128,23 +130,23 @@ public class SeasonCalcTest {
@Test
public void testGetSeasonAmsterdam() {
final Season season = SeasonCalc.calculate(2020, AMSTERDAM_LATITUDE, true, AMSTERDAM_TZ);
final Season season = SeasonCalc.calculate(2020, AMSTERDAM_LATITUDE, true, AMSTERDAM_TZ, INSTANT_SOURCE);
assertNextSeason(season.getSpring(), 2020, JAN_20_2020, season, AMSTERDAM_TZ);
assertNextSeason(season.getSummer(), 2020, MAY_20_2020, season, AMSTERDAM_TZ);
assertNextSeason(season.getWinter(), 2020, SEPT_20_2020, season, AMSTERDAM_TZ);
assertNextSeason(SeasonCalc.calculate(2021, AMSTERDAM_LATITUDE, true, AMSTERDAM_TZ).getSpring(), 2021,
DEC_10_2020, season, AMSTERDAM_TZ);
assertNextSeason(SeasonCalc.calculate(2021, AMSTERDAM_LATITUDE, true, AMSTERDAM_TZ, INSTANT_SOURCE).getSpring(),
2021, DEC_10_2020, season, AMSTERDAM_TZ);
}
@Test
public void testGetSeasonSydney() {
final Season season = SeasonCalc.calculate(2020, SYDNEY_LATITUDE, true, SYDNEY_TZ);
final Season season = SeasonCalc.calculate(2020, SYDNEY_LATITUDE, true, SYDNEY_TZ, INSTANT_SOURCE);
assertNextSeason(season.getAutumn(), 2020, JAN_20_2020, season, SYDNEY_TZ);
assertNextSeason(season.getWinter(), 2020, MAY_20_2020, season, SYDNEY_TZ);
assertNextSeason(season.getSummer(), 2020, SEPT_20_2020, season, SYDNEY_TZ);
assertNextSeason(SeasonCalc.calculate(2021, SYDNEY_LATITUDE, true, SYDNEY_TZ).getAutumn(), 2021, DEC_10_2020,
season, SYDNEY_TZ);
assertNextSeason(SeasonCalc.calculate(2021, SYDNEY_LATITUDE, true, SYDNEY_TZ, INSTANT_SOURCE).getAutumn(), 2021,
DEC_10_2020, season, SYDNEY_TZ);
}
}
@@ -14,6 +14,8 @@ package org.openhab.binding.astro.internal.calc;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
@@ -61,7 +63,7 @@ public class SunCalcTest {
@BeforeEach
public void init() {
sunCalc = new SunCalc();
sunCalc = new SunCalc(InstantSource.fixed(Instant.ofEpochMilli(1645671600000L)));
}
@Test
@@ -14,6 +14,8 @@ package org.openhab.binding.astro.internal.calc.zodiac;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
@@ -53,7 +55,7 @@ public class MoonZodiacCalcTest {
@BeforeEach
public void init() {
moonCalc = new MoonCalc();
moonCalc = new MoonCalc(InstantSource.fixed(Instant.ofEpochMilli(1645671600000L)));
}
@Test
@@ -10,18 +10,18 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.handler.test;
package org.openhab.binding.astro.internal.handler;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_SUN;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import static org.openhab.binding.astro.internal.CommonTestConstants.*;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZoneId;
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.binding.astro.internal.model.Sun;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
@@ -45,7 +45,7 @@ import org.openhab.core.types.State;
* @author Svilen Valkanov - Reworked to plain unit tests
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public class AstroCommandTest {
public class CommandTest {
@Test
public void testRefreshCommandUpdatesTheStateOfTheChannels() {
@@ -67,7 +67,8 @@ public class AstroCommandTest {
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
LocaleProvider localeProvider = mock(LocaleProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.systemDefault());
AstroThingHandler sunHandler = spy(new SunHandler(thing, cronScheduler, timeZoneProvider, localeProvider));
AstroThingHandler sunHandler = spy(new SunHandler(thing, cronScheduler, timeZoneProvider, localeProvider,
InstantSource.fixed(Instant.ofEpochMilli(1645671600000L))));
// Required from the AstroThingHandler to send the status update
doReturn(true).when(callback).isChannelLinked(eq(channelUID));
@@ -10,20 +10,19 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.test.cases;
package org.openhab.binding.astro.internal.handler;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import static org.openhab.binding.astro.internal.CommonTestConstants.*;
import java.util.Arrays;
import java.util.List;
import org.openhab.binding.astro.test.AstroStateTest;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
/**
* Test cases used in the {@link AstroStateTest}
* Test cases used in the {@link StateTest}
*
* @author Petar Valchev - Initial contribution
* @author Svilen Valakanov - Added test data from
@@ -32,7 +31,7 @@ import org.openhab.core.library.unit.Units;
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
* @author Christoph Weitkamp - Introduced UoM and migrated tests to pure Java
*/
public final class AstroParametrizedTestCases {
public final class ParametrizedStateTestCases {
public static final double TEST_LATITUDE = 22.4343;
public static final double TEST_LONGITUDE = 54.3225;
@@ -42,38 +41,38 @@ public final class AstroParametrizedTestCases {
public Object[][] cases = new Object[42][3];
public AstroParametrizedTestCases() {
public ParametrizedStateTestCases() {
cases[0][0] = TEST_SUN_THING_ID;
cases[0][1] = "rise#start";
cases[0][2] = new DateTimeType("2016-02-29T05:46:00+03:00");
cases[0][2] = new DateTimeType("2016-02-29T05:46:07+03:00");
cases[1][0] = TEST_SUN_THING_ID;
cases[1][1] = "rise#end";
cases[1][2] = new DateTimeType("2016-02-29T05:48:00+03:00");
cases[1][2] = new DateTimeType("2016-02-29T05:48:26+03:00");
cases[2][0] = TEST_SUN_THING_ID;
cases[2][1] = "rise#duration";
cases[2][2] = new QuantityType<>(2, Units.MINUTE);
cases[2][2] = new QuantityType<>(2.32, Units.MINUTE);
cases[3][0] = TEST_SUN_THING_ID;
cases[3][1] = "set#start";
cases[3][2] = new DateTimeType("2016-02-29T17:25:00+03:00");
cases[3][2] = new DateTimeType("2016-02-29T17:24:56+03:00");
cases[4][0] = TEST_SUN_THING_ID;
cases[4][1] = "set#end";
cases[4][2] = new DateTimeType("2016-02-29T17:27:00+03:00");
cases[4][2] = new DateTimeType("2016-02-29T17:27:15+03:00");
cases[5][0] = TEST_SUN_THING_ID;
cases[5][1] = "set#duration";
cases[5][2] = new QuantityType<>(2, Units.MINUTE);
cases[5][2] = new QuantityType<>(2.32, Units.MINUTE);
cases[6][0] = TEST_SUN_THING_ID;
cases[6][1] = "noon#start";
cases[6][2] = new DateTimeType("2016-02-29T11:37:00+03:00");
cases[6][2] = new DateTimeType("2016-02-29T11:36:41+03:00");
cases[7][0] = TEST_SUN_THING_ID;
cases[7][1] = "noon#end";
cases[7][2] = new DateTimeType("2016-02-29T11:38:00+03:00");
cases[7][2] = new DateTimeType("2016-02-29T11:37:41+03:00");
cases[8][0] = TEST_SUN_THING_ID;
cases[8][1] = "noon#duration";
@@ -81,11 +80,11 @@ public final class AstroParametrizedTestCases {
cases[9][0] = TEST_SUN_THING_ID;
cases[9][1] = "night#start";
cases[9][2] = new DateTimeType("2016-02-29T18:42:00+03:00");
cases[9][2] = new DateTimeType("2016-02-29T18:41:51+03:00");
cases[10][0] = TEST_SUN_THING_ID;
cases[10][1] = "night#end";
cases[10][2] = new DateTimeType("2016-03-01T04:31:00+03:00");
cases[10][2] = new DateTimeType("2016-03-01T04:30:44+03:00");
cases[11][0] = TEST_SUN_THING_ID;
cases[11][1] = "night#duration";
@@ -97,7 +96,7 @@ public final class AstroParametrizedTestCases {
cases[13][0] = TEST_SUN_THING_ID;
cases[13][1] = "morningNight#end";
cases[13][2] = new DateTimeType("2016-02-29T04:32:00+03:00");
cases[13][2] = new DateTimeType("2016-02-29T04:31:31+03:00");
cases[14][0] = TEST_SUN_THING_ID;
cases[14][1] = "morningNight#duration";
@@ -105,11 +104,11 @@ public final class AstroParametrizedTestCases {
cases[15][0] = TEST_SUN_THING_ID;
cases[15][1] = "astroDawn#start";
cases[15][2] = new DateTimeType("2016-02-29T04:32:00+03:00");
cases[15][2] = new DateTimeType("2016-02-29T04:31:31+03:00");
cases[16][0] = TEST_SUN_THING_ID;
cases[16][1] = "astroDawn#end";
cases[16][2] = new DateTimeType("2016-02-29T04:58:00+03:00");
cases[16][2] = new DateTimeType("2016-02-29T04:57:31+03:00");
cases[17][0] = TEST_SUN_THING_ID;
cases[17][1] = "astroDawn#duration";
@@ -117,35 +116,35 @@ public final class AstroParametrizedTestCases {
cases[18][0] = TEST_SUN_THING_ID;
cases[18][1] = "nauticDawn#start";
cases[18][2] = new DateTimeType("2016-02-29T04:58:00+03:00");
cases[18][2] = new DateTimeType("2016-02-29T04:57:31+03:00");
cases[19][0] = TEST_SUN_THING_ID;
cases[19][1] = "nauticDawn#end";
cases[19][2] = new DateTimeType("2016-02-29T05:24:00+03:00");
cases[19][2] = new DateTimeType("2016-02-29T05:23:34+03:00");
cases[20][0] = TEST_SUN_THING_ID;
cases[20][1] = "nauticDawn#duration";
cases[20][2] = new QuantityType<>(26, Units.MINUTE);
cases[20][2] = new QuantityType<>(26.1, Units.MINUTE);
cases[21][0] = TEST_SUN_THING_ID;
cases[21][1] = "civilDawn#start";
cases[21][2] = new DateTimeType("2016-02-29T05:24:00+03:00");
cases[21][2] = new DateTimeType("2016-02-29T05:23:34+03:00");
cases[22][0] = TEST_SUN_THING_ID;
cases[22][1] = "civilDawn#end";
cases[22][2] = new DateTimeType("2016-02-29T05:46:00+03:00");
cases[22][2] = new DateTimeType("2016-02-29T05:46:07+03:00");
cases[23][0] = TEST_SUN_THING_ID;
cases[23][1] = "civilDawn#duration";
cases[23][2] = new QuantityType<>(22, Units.MINUTE);
cases[23][2] = new QuantityType<>(22.5, Units.MINUTE);
cases[24][0] = TEST_SUN_THING_ID;
cases[24][1] = "astroDusk#start";
cases[24][2] = new DateTimeType("2016-02-29T18:16:00+03:00");
cases[24][2] = new DateTimeType("2016-02-29T18:15:52+03:00");
cases[25][0] = TEST_SUN_THING_ID;
cases[25][1] = "astroDusk#end";
cases[25][2] = new DateTimeType("2016-02-29T18:42:00+03:00");
cases[25][2] = new DateTimeType("2016-02-29T18:41:51+03:00");
cases[26][0] = TEST_SUN_THING_ID;
cases[26][1] = "astroDusk#duration";
@@ -153,31 +152,31 @@ public final class AstroParametrizedTestCases {
cases[27][0] = TEST_SUN_THING_ID;
cases[27][1] = "nauticDusk#start";
cases[27][2] = new DateTimeType("2016-02-29T17:50:00+03:00");
cases[27][2] = new DateTimeType("2016-02-29T17:49:48+03:00");
cases[28][0] = TEST_SUN_THING_ID;
cases[28][1] = "nauticDusk#end";
cases[28][2] = new DateTimeType("2016-02-29T18:16:00+03:00");
cases[28][2] = new DateTimeType("2016-02-29T18:15:52+03:00");
cases[29][0] = TEST_SUN_THING_ID;
cases[29][1] = "nauticDusk#duration";
cases[29][2] = new QuantityType<>(26, Units.MINUTE);
cases[29][2] = new QuantityType<>(26.1, Units.MINUTE);
cases[30][0] = TEST_SUN_THING_ID;
cases[30][1] = "civilDusk#start";
cases[30][2] = new DateTimeType("2016-02-29T17:27:00+03:00");
cases[30][2] = new DateTimeType("2016-02-29T17:27:15+03:00");
cases[31][0] = TEST_SUN_THING_ID;
cases[31][1] = "civilDusk#end";
cases[31][2] = new DateTimeType("2016-02-29T17:50:00+03:00");
cases[31][2] = new DateTimeType("2016-02-29T17:49:48+03:00");
cases[32][0] = TEST_SUN_THING_ID;
cases[32][1] = "civilDusk#duration";
cases[32][2] = new QuantityType<>(23, Units.MINUTE);
cases[32][2] = new QuantityType<>(22.5, Units.MINUTE);
cases[33][0] = TEST_SUN_THING_ID;
cases[33][1] = "eveningNight#start";
cases[33][2] = new DateTimeType("2016-02-29T18:42:00+03:00");
cases[33][2] = new DateTimeType("2016-02-29T18:41:51+03:00");
cases[34][0] = TEST_SUN_THING_ID;
cases[34][1] = "eveningNight#end";
@@ -189,15 +188,15 @@ public final class AstroParametrizedTestCases {
cases[36][0] = TEST_SUN_THING_ID;
cases[36][1] = "daylight#start";
cases[36][2] = new DateTimeType("2016-02-29T05:48:00+03:00");
cases[36][2] = new DateTimeType("2016-02-29T05:48:26+03:00");
cases[37][0] = TEST_SUN_THING_ID;
cases[37][1] = "daylight#end";
cases[37][2] = new DateTimeType("2016-02-29T17:25:00+03:00");
cases[37][2] = new DateTimeType("2016-02-29T17:24:56+03:00");
cases[38][0] = TEST_SUN_THING_ID;
cases[38][1] = "daylight#duration";
cases[38][2] = new QuantityType<>(697, Units.MINUTE);
cases[38][2] = new QuantityType<>(696, Units.MINUTE);
cases[39][0] = TEST_MOON_THING_ID;
cases[39][1] = "rise#start";
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2010-2026 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import static org.openhab.binding.astro.internal.CommonTestConstants.*;
import static org.openhab.binding.astro.internal.handler.ParametrizedStateTestCases.*;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.InstantSource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import javax.measure.Unit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.types.State;
/**
* Tests for the Astro Channels state
*
* @see ParametrizedStateTestCases
* @author Petar Valchev - Initial contribution
* @author Svilen Valkanov - Reworked to plain unit tests
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class StateTest {
// These test result timestamps are adapted for the +03:00 time zone
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Baghdad");
private static final ZoneId ZONE_ID = TIME_ZONE.toZoneId();
private static final Locale LOCALE = Locale.of("ar", "iq");
private @Mock TimeZoneProvider timeZoneProvider;
private @Mock Thing thing;
private @Mock CronScheduler scheduler;
private @Mock LocaleProvider localeProvider;
private @Mock Channel channel;
@BeforeEach
public void init() {
when(timeZoneProvider.getTimeZone()).thenReturn(TIME_ZONE.toZoneId());
when(localeProvider.getLocale()).thenReturn(LOCALE);
Configuration c = new Configuration(
Map.of("geolocation", Double.toString(TEST_LATITUDE) + ',' + Double.toString(TEST_LONGITUDE),
"latitude", Double.valueOf(TEST_LATITUDE), "longitude", Double.valueOf(TEST_LONGITUDE),
INTERVAL_PROPERTY, INTERVAL_DEFAULT_VALUE));
when(thing.getConfiguration()).thenReturn(c);
}
public static List<Object[]> data() {
ParametrizedStateTestCases cases = new ParametrizedStateTestCases();
return cases.getCases();
}
@ParameterizedTest
@MethodSource("data")
public void testParametrized(String thingID, String channelId, State expectedState) throws Exception {
assertStateUpdate(thingID, channelId, expectedState);
}
private void assertStateUpdate(String thingID, String channelId, State expectedState) throws Exception {
AstroThingHandler handler = getHandler(thingID);
when(channel.getUID()).thenReturn(new ChannelUID(getThingUID(thingID), channelId));
when(channel.getConfiguration()).thenReturn(new Configuration());
State state = handler.getState(channel);
if (state instanceof DateTimeType dateTime) {
// Truncate to second
state = new DateTimeType(Instant.ofEpochMilli((dateTime.getInstant().toEpochMilli() / 1000L) * 1000L));
} else if (state instanceof QuantityType quantity) {
Unit<?> unit = quantity.getUnit();
state = new QuantityType<>(quantity.toBigDecimal().round(new MathContext(3, RoundingMode.HALF_UP)), unit);
}
assertEquals(expectedState, state);
handler.dispose();
}
private ThingUID getThingUID(String thingID) {
switch (thingID) {
case (TEST_SUN_THING_ID):
return new ThingUID(THING_TYPE_SUN, thingID);
case (TEST_MOON_THING_ID):
return new ThingUID(THING_TYPE_MOON, thingID);
default:
return null;
}
}
private AstroThingHandler getHandler(String thingID) {
LocalDateTime time = LocalDateTime.of(TEST_YEAR, TEST_MONTH, TEST_DAY, 0, 0);
ZonedDateTime zonedTime = ZonedDateTime.ofLocal(time, ZONE_ID, null);
InstantSource instantSource = InstantSource.fixed(zonedTime.toInstant());
when(thing.getUID()).thenReturn(getThingUID(thingID));
switch (thingID) {
case (TEST_SUN_THING_ID):
SunHandler sunHandler = new SunHandler(thing, scheduler, timeZoneProvider, localeProvider,
instantSource);
sunHandler.initialize();
sunHandler.publishDailyInfo();
return sunHandler;
case (TEST_MOON_THING_ID):
MoonHandler moonHandler = new MoonHandler(thing, scheduler, timeZoneProvider, localeProvider,
instantSource);
moonHandler.initialize();
moonHandler.publishDailyInfo();
return moonHandler;
default:
return null;
}
}
}
@@ -10,21 +10,36 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.internal.model;
package org.openhab.binding.astro.internal.handler;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import java.util.Objects;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Locale;
import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.binding.astro.internal.util.PropertyUtils;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
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.library.types.StringType;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.UnDefType;
/***
@@ -35,18 +50,35 @@ import org.openhab.core.types.UnDefType;
* @see <a href="https://github.com/openhab/openhab-addons/issues/5006">[astro]
* Sun Phase returns UNDEF</a>
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@NonNullByDefault
public class SunTest {
private @Nullable Sun sun;
private @Nullable AstroChannelConfig config;
private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProvider;
private @Mock @NonNullByDefault({}) Thing thing;
private @Mock @NonNullByDefault({}) CronScheduler scheduler;
private @Mock @NonNullByDefault({}) LocaleProvider localeProvider;
private @Mock @NonNullByDefault({}) Channel channel;
private @NonNullByDefault({}) SunHandler handler;
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Gaza");
@BeforeEach
public void init() {
sun = new Sun();
config = new AstroChannelConfig();
when(timeZoneProvider.getTimeZone()).thenReturn(TIME_ZONE.toZoneId());
when(localeProvider.getLocale()).thenReturn(Locale.ROOT);
handler = new SunHandler(thing, scheduler, timeZoneProvider, localeProvider,
InstantSource.fixed(Instant.ofEpochMilli(1645671600000L)));
}
@AfterEach
public void dispose() {
handler.dispose();
}
@Test
@@ -54,28 +86,27 @@ public class SunTest {
Sun sun = this.sun;
assertNotNull(sun);
assertNotNull(sun.getPhase());
assertEquals(UnDefType.UNDEF, PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"),
Objects.requireNonNull(config), sun, TIME_ZONE));
when(channel.getUID()).thenReturn(new ChannelUID("astro:sun:home:phase#name"));
assertEquals(UnDefType.UNDEF, handler.getState(channel));
}
@Test
public void testGetStateWhenNullPhaseName() throws Exception {
Sun sun = this.sun;
handler.publishDailyInfo();
Sun sun = handler.sun;
assertNotNull(sun);
sun.getPhase().setName(null);
assertEquals(UnDefType.UNDEF, PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"),
Objects.requireNonNull(config), sun, TIME_ZONE));
when(channel.getUID()).thenReturn(new ChannelUID("astro:sun:home:phase#name"));
assertEquals(UnDefType.UNDEF, handler.getState(channel));
}
@Test
public void testGetStateWhenNotNullPhaseName() throws Exception {
Sun sun = this.sun;
assertNotNull(sun);
sun.getPhase().setName(SunPhaseName.DAYLIGHT);
assertEquals(new StringType("DAYLIGHT"), PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"),
Objects.requireNonNull(config), sun, TIME_ZONE));
handler.publishPositionalInfo();
when(channel.getUID()).thenReturn(new ChannelUID("astro:sun:home:phase#name"));
assertEquals(new StringType("NIGHT"), handler.getState(channel));
}
@Test
@@ -10,17 +10,17 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.handler.test;
package org.openhab.binding.astro.internal.handler;
import static org.mockito.Mockito.*;
import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_SUN;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import static org.openhab.binding.astro.internal.CommonTestConstants.*;
import java.time.Instant;
import java.time.InstantSource;
import java.time.ZoneId;
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;
@@ -42,7 +42,7 @@ import org.openhab.core.thing.binding.ThingHandlerCallback;
* @author Svilen Valkanov - Reworked to plain unit tests, removed irrelevant tests
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public class AstroValidConfigurationTest {
public class ValidConfigurationTest {
private static final String NULL_LONGITUDE = "51.2,null";
private static final String NULL_LATITUDE = "null,25.4";
@@ -150,7 +150,8 @@ public class AstroValidConfigurationTest {
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
LocaleProvider localeProvider = mock(LocaleProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.systemDefault());
ThingHandler sunHandler = new SunHandler(thing, cronScheduler, timeZoneProvider, localeProvider);
ThingHandler sunHandler = new SunHandler(thing, cronScheduler, timeZoneProvider, localeProvider,
InstantSource.fixed(Instant.ofEpochMilli(1645671600000L)));
sunHandler.setCallback(callback);
sunHandler.initialize();
@@ -14,6 +14,8 @@ package org.openhab.binding.astro.internal.job;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
@@ -35,6 +37,7 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
public class JobTest {
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Tbilisi");
private static final InstantSource INSTANT_SOURCE = InstantSource.fixed(Instant.ofEpochMilli(1645671600000L));
@BeforeEach
public void init() {
@@ -47,21 +50,23 @@ public class JobTest {
config.earliest = "08:00";
config.latest = "22:00";
config.forceEvent = true;
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0, TIME_ZONE, Locale.ROOT);
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
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, TIME_ZONE, Locale.ROOT),
DateTimeUtils.createCalendarForToday(23, 10, TIME_ZONE, Locale.ROOT));
Range bothNNShouldCorrect = new Range(
DateTimeUtils.createCalendarForToday(6, 0, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE),
DateTimeUtils.createCalendarForToday(23, 10, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE));
Range bothNNShouldNotCorrect = new Range(pointInTime, pointInTime);
// act
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 startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
Range endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
Range bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT,
INSTANT_SOURCE);
Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE,
Locale.ROOT);
Locale.ROOT, INSTANT_SOURCE);
Calendar fixedStart = DateTimeUtils.getAdjustedEarliest(pointInTime, config);
Calendar fixedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config);
@@ -90,11 +95,13 @@ public class JobTest {
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);
startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT,
INSTANT_SOURCE);
bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE, Locale.ROOT,
INSTANT_SOURCE);
// assert again
startNullResultStart = startNullResult.getStart();
@@ -121,11 +128,13 @@ public class JobTest {
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);
startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT,
INSTANT_SOURCE);
bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE, Locale.ROOT,
INSTANT_SOURCE);
// assert yet again
startNullResultStart = startNullResult.getStart();
@@ -151,21 +160,23 @@ public class JobTest {
config.earliest = "08:00";
config.latest = "22:00";
config.forceEvent = false;
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0, TIME_ZONE, Locale.ROOT);
Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
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, TIME_ZONE, Locale.ROOT),
DateTimeUtils.createCalendarForToday(23, 10, TIME_ZONE, Locale.ROOT));
Range bothNNShouldCorrect = new Range(
DateTimeUtils.createCalendarForToday(6, 0, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE),
DateTimeUtils.createCalendarForToday(23, 10, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE));
Range bothNNShouldNotCorrect = new Range(pointInTime, pointInTime);
// act
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 startNullResult = Job.adjustRangeToConfig(startNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
Range endNullResult = Job.adjustRangeToConfig(endNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
Range bothNullResult = Job.adjustRangeToConfig(bothNull, config, TIME_ZONE, Locale.ROOT, INSTANT_SOURCE);
Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config, TIME_ZONE, Locale.ROOT,
INSTANT_SOURCE);
Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config, TIME_ZONE,
Locale.ROOT);
Locale.ROOT, INSTANT_SOURCE);
Calendar fixedStart = DateTimeUtils.getAdjustedEarliest(pointInTime, config);
Calendar fixdedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config);
@@ -15,6 +15,7 @@ package org.openhab.binding.astro.internal.util;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
@@ -65,17 +66,18 @@ public class DateTimeUtilsTest {
@Test
public void testCreateCalendarForToday() {
Calendar cal = DateTimeUtils.createCalendarForToday(8, 0, TIME_ZONE, Locale.ROOT);
InstantSource is = InstantSource.fixed(Instant.ofEpochMilli(1645671600000L));
Calendar cal = DateTimeUtils.createCalendarForToday(8, 0, TIME_ZONE, Locale.ROOT, is);
assertEquals(8, cal.get(Calendar.HOUR_OF_DAY));
assertEquals(0, cal.get(Calendar.MINUTE));
assertEquals(0, cal.get(Calendar.SECOND));
assertEquals(0, cal.get(Calendar.MILLISECOND));
cal = DateTimeUtils.createCalendarForToday(22, 59, TIME_ZONE, Locale.ROOT);
cal = DateTimeUtils.createCalendarForToday(22, 59, TIME_ZONE, Locale.ROOT, is);
assertEquals(22, cal.get(Calendar.HOUR_OF_DAY));
assertEquals(59, cal.get(Calendar.MINUTE));
assertEquals(0, cal.get(Calendar.SECOND));
assertEquals(0, cal.get(Calendar.MILLISECOND));
cal = DateTimeUtils.createCalendarForToday(0, 0, TIME_ZONE, Locale.ROOT);
cal = DateTimeUtils.createCalendarForToday(0, 0, TIME_ZONE, Locale.ROOT, is);
assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
assertEquals(0, cal.get(Calendar.MINUTE));
assertEquals(0, cal.get(Calendar.SECOND));
@@ -1,13 +0,0 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons
@@ -1,77 +0,0 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.astro
-runrequires: bnd.identity;id='org.openhab.binding.astro.tests'
#
# done
#
-runbundles: \
jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\
org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\
org.glassfish.hk2.osgi-resource-locator;version='[3.0.0,3.0.1)',\
jakarta.inject.jakarta.inject-api;version='[2.0.1,2.0.2)',\
si-units;version='[2.2.3,2.2.4)',\
si-quantity;version='[2.2.3,2.2.4)',\
org.osgi.util.function;version='[1.2.0,1.2.1)',\
org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\
org.osgi.service.component;version='[1.5.1,1.5.2)',\
net.bytebuddy.byte-buddy;version='[1.17.6,1.17.7)',\
net.bytebuddy.byte-buddy-agent;version='[1.17.6,1.17.7)',\
org.mockito.mockito-core;version='[5.19.0,5.19.1)',\
org.objenesis;version='[3.3.0,3.3.1)',\
xstream;version='[1.4.21,1.4.22)',\
javax.measure.unit-api;version='[2.2.0,2.2.1)',\
tech.units.indriya;version='[2.2.3,2.2.4)',\
uom-lib-common;version='[2.2.0,2.2.1)',\
io.methvin.directory-watcher;version='[0.19.1,0.19.2)',\
com.sun.xml.bind.jaxb-osgi;version='[2.3.9,2.3.10)',\
org.apache.felix.scr;version='[2.2.6,2.2.7)',\
org.eclipse.equinox.event;version='[1.6.300,1.6.301)',\
org.osgi.service.event;version='[1.4.1,1.4.2)',\
org.osgi.util.promise;version='[1.3.0,1.3.1)',\
com.google.gson;version='[2.13.2,2.13.3)',\
junit-jupiter-api;version='[5.13.4,5.13.5)',\
junit-jupiter-engine;version='[5.13.4,5.13.5)',\
junit-jupiter-params;version='[5.13.4,5.13.5)',\
junit-platform-commons;version='[1.13.4,1.13.5)',\
junit-platform-engine;version='[1.13.4,1.13.5)',\
junit-platform-launcher;version='[1.13.4,1.13.5)',\
org.opentest4j;version='[1.3.0,1.3.1)',\
org.osgi.service.component.annotations;version='[1.5.1,1.5.2)',\
com.sun.jna;version='[5.18.1,5.18.2)',\
org.ops4j.pax.logging.pax-logging-api;version='[2.3.0,2.3.1)',\
org.openhab.binding.astro;version='[5.2.0,5.2.1)',\
org.openhab.binding.astro.tests;version='[5.2.0,5.2.1)',\
org.openhab.core;version='[5.2.0,5.2.1)',\
org.openhab.core.addon;version='[5.2.0,5.2.1)',\
org.openhab.core.config.core;version='[5.2.0,5.2.1)',\
org.openhab.core.config.discovery;version='[5.2.0,5.2.1)',\
org.openhab.core.io.console;version='[5.2.0,5.2.1)',\
org.openhab.core.semantics;version='[5.2.0,5.2.1)',\
org.openhab.core.thing;version='[5.2.0,5.2.1)',\
org.openhab.core.transform;version='[5.2.0,5.2.1)',\
biz.aQute.tester.junit-platform;version='[7.1.0,7.1.1)',\
ch.qos.logback.classic;version='[1.5.24,1.5.25)',\
ch.qos.logback.core;version='[1.5.24,1.5.25)',\
org.apache.aries.spifly.dynamic.bundle;version='[1.3.7,1.3.8)',\
org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\
org.eclipse.jetty.http;version='[9.4.57,9.4.58)',\
org.eclipse.jetty.io;version='[9.4.57,9.4.58)',\
org.eclipse.jetty.security;version='[9.4.57,9.4.58)',\
org.eclipse.jetty.server;version='[9.4.57,9.4.58)',\
org.eclipse.jetty.servlet;version='[9.4.57,9.4.58)',\
org.eclipse.jetty.util;version='[9.4.57,9.4.58)',\
org.eclipse.jetty.util.ajax;version='[9.4.57,9.4.58)',\
org.hamcrest;version='[3.0.0,3.0.1)',\
org.objectweb.asm;version='[9.9.0,9.9.1)',\
org.objectweb.asm.commons;version='[9.6.0,9.6.1)',\
org.objectweb.asm.tree;version='[9.6.0,9.6.1)',\
org.objectweb.asm.tree.analysis;version='[9.6.0,9.6.1)',\
org.objectweb.asm.util;version='[9.6.0,9.6.1)',\
org.openhab.core.test;version='[5.2.0,5.2.1)',\
org.osgi.service.cm;version='[1.6.1,1.6.2)',\
jakarta.annotation-api;version='[2.1.1,2.1.2)'
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>5.2.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.astro.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Astro Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.astro</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
@@ -1,105 +0,0 @@
/*
* Copyright (c) 2010-2026 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import static org.openhab.binding.astro.test.cases.AstroParametrizedTestCases.*;
import java.time.LocalDateTime;
import java.time.ZoneId;
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;
import org.openhab.binding.astro.internal.calc.MoonCalc;
import org.openhab.binding.astro.internal.calc.SunCalc;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.binding.astro.internal.model.Planet;
import org.openhab.binding.astro.internal.util.PropertyUtils;
import org.openhab.binding.astro.test.cases.AstroParametrizedTestCases;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.types.State;
/**
* Tests for the Astro Channels state
*
* @see AstroParametrizedTestCases
* @author Petar Valchev - Initial contribution
* @author Svilen Valkanov - Reworked to plain unit tests
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public class AstroStateTest {
// These test result timestamps are adapted for the +03:00 time zone
private static final ZoneId ZONE_ID = ZoneId.of("+03:00");
public static List<Object[]> data() {
AstroParametrizedTestCases cases = new AstroParametrizedTestCases();
return cases.getCases();
}
@ParameterizedTest
@MethodSource("data")
public void testParametrized(String thingID, String channelId, State expectedState) {
try {
assertStateUpdate(thingID, channelId, expectedState);
} catch (Exception e) {
// do nothing
}
}
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),
TimeZone.getTimeZone(ZONE_ID));
assertEquals(expectedState, state);
}
private ThingUID getThingUID(String thingID) {
switch (thingID) {
case (TEST_SUN_THING_ID):
return new ThingUID(THING_TYPE_SUN, thingID);
case (TEST_MOON_THING_ID):
return new ThingUID(THING_TYPE_MOON, thingID);
default:
return null;
}
}
private Planet getPlanet(String thingID) {
LocalDateTime time = LocalDateTime.of(TEST_YEAR, TEST_MONTH, TEST_DAY, 0, 0);
ZonedDateTime zonedTime = ZonedDateTime.ofLocal(time, ZONE_ID, null);
Calendar calendar = GregorianCalendar.from(zonedTime);
switch (thingID) {
case (TEST_SUN_THING_ID):
SunCalc sunCalc = new SunCalc();
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, TimeZone.getTimeZone(ZONE_ID),
Locale.getDefault());
default:
return null;
}
}
}
-1
View File
@@ -19,7 +19,6 @@
<modules>
<module>org.openhab.automation.groovyscripting.tests</module>
<module>org.openhab.automation.jsscriptingnashorn.tests</module>
<module>org.openhab.binding.astro.tests</module>
<module>org.openhab.binding.avmfritz.tests</module>
<module>org.openhab.binding.feed.tests</module>
<module>org.openhab.binding.homeassistant.tests</module>