From 48c506b60603e417a54bdd2652b1de59f196d83c Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sun, 29 Jan 2023 22:15:38 +0100 Subject: [PATCH] [astro] Add option to force event to occur (#14132) * fix issue 11424 Signed-off-by: lsiepel --- bundles/org.openhab.binding.astro/README.md | 6 + .../internal/config/AstroChannelConfig.java | 1 + .../binding/astro/internal/job/Job.java | 43 +++++-- .../astro/internal/util/DateTimeUtils.java | 16 ++- .../main/resources/OH-INF/config/config.xml | 5 + .../resources/OH-INF/i18n/astro.properties | 2 + .../binding/astro/internal/job/JobTest.java | 114 ++++++++++++++++++ 7 files changed, 173 insertions(+), 14 deletions(-) create mode 100644 bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/job/JobTest.java diff --git a/bundles/org.openhab.binding.astro/README.md b/bundles/org.openhab.binding.astro/README.md index 0ba39332f63ac..3f9de1e514dcd 100644 --- a/bundles/org.openhab.binding.astro/README.md +++ b/bundles/org.openhab.binding.astro/README.md @@ -130,6 +130,12 @@ OR sunset is 22:10 but `latest` is set to 20:00 so the event/datetime value is moved 20:00. +**Force event:** For each trigger channel and `start`, `end` datetime value, you can force the `earliest`, `latest` time of the day, when the event is actually not taking place (e.g. astronomic dawn during summer in Sweden) +e.g `sun#astroDawn earliest=6:00, latest=20:00 forceEvent=true` + +astronomic dawn start is null but `earliest` is set to 06:00 so the event/datetime value is set to 06:00. + + ## Full Example Things: diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/config/AstroChannelConfig.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/config/AstroChannelConfig.java index e308aa525cbfe..c07dac61581eb 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/config/AstroChannelConfig.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/config/AstroChannelConfig.java @@ -25,4 +25,5 @@ public class AstroChannelConfig { public int offset = 0; public @Nullable String earliest; public @Nullable String latest; + public boolean forceEvent = false; } diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java index 530280fcd15c9..29f7772bce8ac 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java @@ -114,25 +114,44 @@ public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler * @param channelId the channel ID */ public static void scheduleRange(String thingUID, AstroThingHandler astroHandler, Range range, String channelId) { - Calendar start = range.getStart(); - Calendar end = range.getEnd(); - - // depending on the location you might not have a valid range for day/night, so skip the events: - if (start == null || end == null) { - return; - } - final Channel channel = astroHandler.getThing().getChannel(channelId); if (channel == null) { LOGGER.warn("Cannot find channel '{}' for thing '{}'.", channelId, astroHandler.getThing().getUID()); return; } AstroChannelConfig config = channel.getConfiguration().as(AstroChannelConfig.class); - Calendar configStart = truncateToSecond(applyConfig(start, config)); - Calendar configEnd = truncateToSecond(applyConfig(end, config)); + Range adjustedRange = adjustRangeToConfig(range, config); + + Calendar start = adjustedRange.getStart(); + Calendar end = adjustedRange.getEnd(); + + if (start == null || end == null) { + LOGGER.debug("event was not scheduled as either start or end was null"); + return; + } + + scheduleEvent(thingUID, astroHandler, start, EVENT_START, channelId, true); + scheduleEvent(thingUID, astroHandler, end, EVENT_END, channelId, true); + } + + public static Range adjustRangeToConfig(Range range, AstroChannelConfig config) { + Calendar start = range.getStart(); + Calendar end = range.getEnd(); + + if (config.forceEvent && start == null) { + start = getAdjustedEarliest(Calendar.getInstance(), config); + } + if (config.forceEvent && end == null) { + end = getAdjustedLatest(Calendar.getInstance(), config); + } + + // depending on the location and configuration you might not have a valid range for day/night, so skip the + // events: + if (start == null || end == null) { + return range; + } - scheduleEvent(thingUID, astroHandler, configStart, EVENT_START, channelId, true); - scheduleEvent(thingUID, astroHandler, configEnd, EVENT_END, channelId, true); + return new Range(truncateToSecond(applyConfig(start, config)), truncateToSecond(applyConfig(end, config))); } /** diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java index 05eb5bffea207..1718d34e533c7 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java @@ -211,6 +211,14 @@ public static boolean isTimeGreaterEquals(Calendar cal1, Calendar cal2) { return truncCal1.getTimeInMillis() >= truncCal2.getTimeInMillis(); } + public static Calendar getAdjustedEarliest(Calendar cal, AstroChannelConfig config) { + return adjustTime(cal, getMinutesFromTime(config.earliest)); + } + + public static Calendar getAdjustedLatest(Calendar cal, AstroChannelConfig config) { + return adjustTime(cal, getMinutesFromTime(config.latest)); + } + /** * Applies the config to the given calendar. */ @@ -223,11 +231,11 @@ public static Calendar applyConfig(Calendar cal, AstroChannelConfig config) { cCal = cOffset; } - Calendar cEarliest = adjustTime(cCal, getMinutesFromTime(config.earliest)); + Calendar cEarliest = getAdjustedEarliest(cCal, config); if (cCal.before(cEarliest)) { return cEarliest; } - Calendar cLatest = adjustTime(cCal, getMinutesFromTime(config.latest)); + Calendar cLatest = getAdjustedLatest(cCal, config); if (cCal.after(cLatest)) { return cLatest; } @@ -244,6 +252,10 @@ private static Calendar adjustTime(Calendar cal, int minutes) { return cal; } + public static Calendar createCalendarForToday(int hour, int minute) { + return DateTimeUtils.adjustTime(Calendar.getInstance(), hour * 60 + minute); + } + /** * Parses a HH:MM string and returns the minutes. */ diff --git a/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/config/config.xml index 40f87cd121d14..d5fd63cff9db2 100644 --- a/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/config/config.xml @@ -53,6 +53,11 @@ The latest time of the day for the event or the datetime value (hh:mm). time + + + Force event to occur according to Earliest/Latest, even when the event doesn't exist (null) + false + diff --git a/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/i18n/astro.properties b/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/i18n/astro.properties index bb619a600bd0e..e61681d9bcb41 100644 --- a/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/i18n/astro.properties +++ b/bundles/org.openhab.binding.astro/src/main/resources/OH-INF/i18n/astro.properties @@ -217,6 +217,8 @@ channel-type.astro.winter.state.pattern = %1$tF %1$tR channel-type.config.astro.config.earliest.label = Earliest channel-type.config.astro.config.earliest.description = The earliest time of the day for the event or the datetime value (hh:mm). +channel-type.config.astro.config.forceEvent.label = Force Event +channel-type.config.astro.config.forceEvent.description = Force event to occur according to Earliest/Latest, even when the event doesn't exist (null) channel-type.config.astro.config.latest.label = Latest channel-type.config.astro.config.latest.description = The latest time of the day for the event or the datetime value (hh:mm). channel-type.config.astro.config.offset.label = Offset diff --git a/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/job/JobTest.java b/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/job/JobTest.java new file mode 100644 index 0000000000000..9949ce1b2c74a --- /dev/null +++ b/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/job/JobTest.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.astro.internal.job; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Calendar; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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.model.Range; +import org.openhab.binding.astro.internal.util.DateTimeUtils; + +/** + * Test class for {@link Job}. + * + * @author Leo Siepel - Initial contribution + */ + +@NonNullByDefault +public class JobTest { + + @BeforeEach + public void init() { + } + + @Test + public void adjustRangeToConfigForceTest() { + // arrange + AstroChannelConfig config = new AstroChannelConfig(); + config.earliest = "08:00"; + config.latest = "22:00"; + config.forceEvent = true; + Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0); + Range startNull = new Range(null, pointInTime); + Range endNull = new Range(pointInTime, null); + Range bothNull = new Range(null, null); + Range bothNNShouldCorrect = new Range(DateTimeUtils.createCalendarForToday(6, 0), + DateTimeUtils.createCalendarForToday(22, 0)); + Range bothNNShouldNotCorrect = new Range(pointInTime, pointInTime); + + // act + Range startNullResult = Job.adjustRangeToConfig(startNull, config); + Range endNullResult = Job.adjustRangeToConfig(endNull, config); + Range bothNullResult = Job.adjustRangeToConfig(bothNull, config); + Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config); + Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config); + + Calendar fixedStart = DateTimeUtils.getAdjustedEarliest(pointInTime, config); + Calendar fixdedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config); + + // assert + assertEquals(fixedStart.getTime(), startNullResult.getStart().getTime()); + assertEquals(pointInTime.getTime(), startNullResult.getEnd().getTime()); + assertEquals(pointInTime, endNullResult.getStart()); + assertEquals(fixdedEnd, endNullResult.getEnd()); + assertEquals(fixedStart, bothNullResult.getStart()); + assertEquals(fixdedEnd, bothNullResult.getEnd()); + assertEquals(fixedStart, bothNNShouldCorrectResult.getStart()); + assertEquals(fixdedEnd, bothNNShouldCorrectResult.getEnd()); + assertEquals(pointInTime, bothNNSouldNotCorrectResult.getStart()); + assertEquals(pointInTime, bothNNSouldNotCorrectResult.getEnd()); + } + + @Test + public void adjustRangeToConfigTestSkipForceTest() { + // arrange + AstroChannelConfig config = new AstroChannelConfig(); + config.earliest = "08:00"; + config.latest = "22:00"; + config.forceEvent = false; + Calendar pointInTime = DateTimeUtils.createCalendarForToday(12, 0); + Range startNull = new Range(null, pointInTime); + Range endNull = new Range(pointInTime, null); + Range bothNull = new Range(null, null); + Range bothNNShouldCorrect = new Range(DateTimeUtils.createCalendarForToday(6, 0), + DateTimeUtils.createCalendarForToday(22, 0)); + Range bothNNShouldNotCorrect = new Range(pointInTime, pointInTime); + + // act + Range startNullResult = Job.adjustRangeToConfig(startNull, config); + Range endNullResult = Job.adjustRangeToConfig(endNull, config); + Range bothNullResult = Job.adjustRangeToConfig(bothNull, config); + Range bothNNShouldCorrectResult = Job.adjustRangeToConfig(bothNNShouldCorrect, config); + Range bothNNSouldNotCorrectResult = Job.adjustRangeToConfig(bothNNShouldNotCorrect, config); + + Calendar fixedStart = DateTimeUtils.getAdjustedEarliest(pointInTime, config); + Calendar fixdedEnd = DateTimeUtils.getAdjustedLatest(pointInTime, config); + + // assert + assertEquals(null, startNullResult.getStart()); + assertEquals(pointInTime, startNullResult.getEnd()); + assertEquals(pointInTime, endNullResult.getStart()); + assertEquals(null, endNullResult.getEnd()); + assertEquals(null, bothNullResult.getStart()); + assertEquals(null, bothNullResult.getEnd()); + assertEquals(fixedStart, bothNNShouldCorrectResult.getStart()); + assertEquals(fixdedEnd, bothNNShouldCorrectResult.getEnd()); + assertEquals(pointInTime, bothNNSouldNotCorrectResult.getStart()); + assertEquals(pointInTime, bothNNSouldNotCorrectResult.getEnd()); + } +}