diff --git a/bundles/org.openhab.binding.robonect/README.md b/bundles/org.openhab.binding.robonect/README.md index 9bcfd4c004f7b..5899a0af5cbdb 100644 --- a/bundles/org.openhab.binding.robonect/README.md +++ b/bundles/org.openhab.binding.robonect/README.md @@ -35,6 +35,7 @@ following configuration settings are supported for the `mower` thing. | offlineTimeout | no | the maximum time, the mower can be offline before the binding triggers the offlineTrigger channel | | user | no | the username if authentication is enabled in the firmware. | | password | no | the password if authenticaiton is enabled in the firmware. | +| timezone | no | the timezone as configured in Robonect on the robot (default: Europe/Berlin) | An example things configuration might look like diff --git a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java index 09d7939f21de2..6cdb9c7f86b84 100644 --- a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java +++ b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java @@ -18,6 +18,7 @@ import java.util.Set; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.smarthome.core.i18n.TimeZoneProvider; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; @@ -25,6 +26,7 @@ import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; import org.eclipse.smarthome.io.net.http.HttpClientFactory; import org.openhab.binding.robonect.internal.handler.RobonectHandler; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -40,6 +42,14 @@ public class RobonectHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_AUTOMOWER); private HttpClient httpClient; + private TimeZoneProvider timeZoneProvider; + + @Activate + public RobonectHandlerFactory(@Reference HttpClientFactory httpClientFactory, + @Reference TimeZoneProvider timeZoneProvider) { + this.httpClient = httpClientFactory.getCommonHttpClient(); + this.timeZoneProvider = timeZoneProvider; + } @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -51,18 +61,9 @@ protected ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(THING_TYPE_AUTOMOWER)) { - return new RobonectHandler(thing, httpClient); + return new RobonectHandler(thing, httpClient, timeZoneProvider); } return null; } - - @Reference - protected void setHttpClientFactory(HttpClientFactory httpClientFactory) { - this.httpClient = httpClientFactory.getCommonHttpClient(); - } - - protected void unsetHttpClientFactory(HttpClientFactory httpClientFactory) { - this.httpClient = null; - } } diff --git a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/config/RobonectConfig.java b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/config/RobonectConfig.java index db5b246b5e567..17ecd76ff65aa 100644 --- a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/config/RobonectConfig.java +++ b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/config/RobonectConfig.java @@ -13,9 +13,9 @@ package org.openhab.binding.robonect.internal.config; /** - * + * * This class acts simply a structure for holding the thing configuration. - * + * * @author Marco Meyer - Initial contribution */ public class RobonectConfig { @@ -30,6 +30,8 @@ public class RobonectConfig { private int offlineTimeout; + private String timezone; + public String getHost() { return host; } @@ -49,4 +51,8 @@ public int getPollInterval() { public int getOfflineTimeout() { return offlineTimeout; } + + public String getTimezone() { + return timezone; + } } diff --git a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java index 583eaf317c654..e1029cd7551b7 100644 --- a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java +++ b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java @@ -14,8 +14,10 @@ import static org.openhab.binding.robonect.internal.RobonectBindingConstants.*; +import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; @@ -23,6 +25,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.smarthome.core.i18n.TimeZoneProvider; import org.eclipse.smarthome.core.library.types.DateTimeType; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -71,12 +74,16 @@ public class RobonectHandler extends BaseThingHandler { private ScheduledFuture pollingJob; private HttpClient httpClient; + private TimeZoneProvider timeZoneProvider; + + private ZoneId timeZone; private RobonectClient robonectClient; - public RobonectHandler(Thing thing, HttpClient httpClient) { + public RobonectHandler(Thing thing, HttpClient httpClient, TimeZoneProvider timeZoneProvider) { super(thing); this.httpClient = httpClient; + this.timeZoneProvider = timeZoneProvider; } @Override @@ -277,8 +284,21 @@ private void updateNextTimer(MowerInfo info) { } private State convertUnixToDateTimeType(String unixTimeSec) { - Instant ns = Instant.ofEpochMilli(Long.valueOf(unixTimeSec) * 1000); - ZonedDateTime zdt = ZonedDateTime.ofInstant(ns, ZoneId.of("UTC")); + // the value in unixTimeSec represents the time on the robot in its configured timezone. However, it does not + // provide which zone this is. Thus we have to add the zone information from the Thing configuration in order to + // provide correct results. + Instant rawInstant = Instant.ofEpochMilli(Long.valueOf(unixTimeSec) * 1000); + + ZoneId timeZoneOfThing = timeZone; + if (timeZoneOfThing == null) { + timeZoneOfThing = timeZoneProvider.getTimeZone(); + } + ZoneOffset offsetToConfiguredZone = timeZoneOfThing.getRules().getOffset(rawInstant); + long adjustedTime = rawInstant.getEpochSecond() - offsetToConfiguredZone.getTotalSeconds(); + Instant adjustedInstant = Instant.ofEpochMilli(adjustedTime * 1000); + + // we provide the time in the format as configured in the openHAB settings + ZonedDateTime zdt = adjustedInstant.atZone(timeZoneProvider.getTimeZone()); return new DateTimeType(zdt); } @@ -329,6 +349,20 @@ public void initialize() { RobonectConfig robonectConfig = getConfigAs(RobonectConfig.class); RobonectEndpoint endpoint = new RobonectEndpoint(robonectConfig.getHost(), robonectConfig.getUser(), robonectConfig.getPassword()); + + String timeZoneString = robonectConfig.getTimezone(); + try { + if (timeZoneString != null) { + timeZone = ZoneId.of(timeZoneString); + } else { + logger.warn("No timezone provided, falling back to the default timezone configured in openHAB: '{}'", + timeZoneProvider.getTimeZone()); + } + } catch (DateTimeException e) { + logger.warn("Error setting timezone '{}', falling back to the default timezone configured in openHAB: '{}'", + timeZoneString, timeZoneProvider.getTimeZone(), e); + } + try { httpClient.start(); robonectClient = new RobonectClient(httpClient, endpoint); diff --git a/bundles/org.openhab.binding.robonect/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.robonect/src/main/resources/ESH-INF/thing/thing-types.xml index 478a66b1966b2..f27bf214a62e2 100644 --- a/bundles/org.openhab.binding.robonect/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.robonect/src/main/resources/ESH-INF/thing/thing-types.xml @@ -80,6 +80,11 @@ The maximum time the mower may be offline before the offline trigger is triggered. + + + Europe/Berlin + The timezone configured on the robot (e.g. Europe/Berlin). + diff --git a/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java b/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java index 9fd6fa89c0026..4790904caadd7 100644 --- a/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java +++ b/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java @@ -12,16 +12,15 @@ */ package org.openhab.binding.robonect.internal.handler; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import java.time.ZoneId; import java.util.Calendar; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.smarthome.core.i18n.TimeZoneProvider; import org.eclipse.smarthome.core.library.types.DateTimeType; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -38,6 +37,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.openhab.binding.robonect.internal.RobonectBindingConstants; import org.openhab.binding.robonect.internal.RobonectClient; @@ -53,7 +53,7 @@ /** * The goal of this class is to test RobonectHandler in isolation. - * + * * @author Marco Meyer - Initial contribution */ public class RobonectHandlerTest { @@ -72,12 +72,17 @@ public class RobonectHandlerTest { @Mock private HttpClient httpClientMock; + @Mock + private TimeZoneProvider timezoneProvider; + @Before public void setUp() { MockitoAnnotations.initMocks(this); - subject = new RobonectHandler(robonectThingMock, httpClientMock); + subject = new RobonectHandler(robonectThingMock, httpClientMock, timezoneProvider); subject.setCallback(callbackMock); subject.setRobonectClient(robonectClientMock); + + Mockito.when(timezoneProvider.getTimeZone()).thenReturn(ZoneId.of("Europe/Berlin")); } @Test