Skip to content

Commit

Permalink
[Robonect] Use global openHAB timezone for DateTime objects (#7903)
Browse files Browse the repository at this point in the history
* [Robonect] Use global openHAB timezone for DateTime objects

Use the timezone that has been configured by the user in openHAB settings
for all robonect messages instead of UTC.

Fixes #7848

Signed-off-by: Stefan Triller <[email protected]>
  • Loading branch information
t2000 authored Jun 17, 2020
1 parent a1cfacd commit 00a2cd5
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 23 deletions.
1 change: 1 addition & 0 deletions bundles/org.openhab.binding.robonect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
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;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
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;

Expand All @@ -40,6 +42,14 @@ public class RobonectHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> 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) {
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -30,6 +30,8 @@ public class RobonectConfig {

private int offlineTimeout;

private String timezone;

public String getHost() {
return host;
}
Expand All @@ -49,4 +51,8 @@ public int getPollInterval() {
public int getOfflineTimeout() {
return offlineTimeout;
}

public String getTimezone() {
return timezone;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@

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;
import java.util.concurrent.ScheduledFuture;
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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@
<description>The maximum time the mower may be offline before the offline trigger is triggered.
</description>
</parameter>
<parameter name="timezone" type="text" required="false">
<label>Timezone</label>
<default>Europe/Berlin</default>
<description>The timezone configured on the robot (e.g. Europe/Berlin).</description>
</parameter>
</config-description>
</thing-type>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -53,7 +53,7 @@

/**
* The goal of this class is to test RobonectHandler in isolation.
*
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectHandlerTest {
Expand All @@ -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
Expand Down

0 comments on commit 00a2cd5

Please sign in to comment.