[ecobee] Fix issue with UTC and local dates (#14170)

* Correctly handle UTC and local date/times
* Eliminate use of Date class

Signed-off-by: Mark Hilbush <mark@hilbush.com>
This commit is contained in:
Mark Hilbush 2023-01-08 17:35:01 -05:00 committed by GitHub
parent 2c2097d646
commit 3ba37b431a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 132 additions and 19 deletions

View File

@ -409,4 +409,5 @@ public class EcobeeBindingConstants {
public static final String ECOBEE_AUTHORIZE_URL = ECOBEE_BASE_URL + "authorize"; public static final String ECOBEE_AUTHORIZE_URL = ECOBEE_BASE_URL + "authorize";
public static final String ECOBEE_TOKEN_URL = ECOBEE_BASE_URL + "token"; public static final String ECOBEE_TOKEN_URL = ECOBEE_BASE_URL + "token";
public static final String ECOBEE_SCOPE = "smartWrite"; public static final String ECOBEE_SCOPE = "smartWrite";
public static final String ECOBEE_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
} }

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@ -32,6 +33,8 @@ import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.ecobee.internal.dto.AbstractResponseDTO; import org.openhab.binding.ecobee.internal.dto.AbstractResponseDTO;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO; import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
import org.openhab.binding.ecobee.internal.dto.SelectionType; import org.openhab.binding.ecobee.internal.dto.SelectionType;
import org.openhab.binding.ecobee.internal.dto.thermostat.InstantDeserializer;
import org.openhab.binding.ecobee.internal.dto.thermostat.LocalDateTimeDeserializer;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO; import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatRequestDTO; import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatRequestDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatResponseDTO; import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatResponseDTO;
@ -66,7 +69,8 @@ import com.google.gson.JsonSyntaxException;
@NonNullByDefault @NonNullByDefault
public class EcobeeApi implements AccessTokenRefreshListener { public class EcobeeApi implements AccessTokenRefreshListener {
private static final Gson GSON = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss") private static final Gson GSON = new GsonBuilder().registerTypeAdapter(Instant.class, new InstantDeserializer())
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer())
.registerTypeAdapter(RevisionDTO.class, new RevisionDTODeserializer()) .registerTypeAdapter(RevisionDTO.class, new RevisionDTODeserializer())
.registerTypeAdapter(RunningDTO.class, new RunningDTODeserializer()).create(); .registerTypeAdapter(RunningDTO.class, new RunningDTODeserializer()).create();

View File

@ -0,0 +1,52 @@
/**
* 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.ecobee.internal.dto.thermostat;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.ECOBEE_DATETIME_FORMAT;
import java.lang.reflect.Type;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* The {@link InstantDeserializer} is responsible for handling the UTC dates returned from
* the Ecobee API service.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class InstantDeserializer implements JsonDeserializer<Instant> {
@Override
public @Nullable Instant deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2)
throws JsonParseException {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(ECOBEE_DATETIME_FORMAT);
LocalDateTime localDateTime = formatter.parse(element.getAsString(), LocalDateTime::from);
return localDateTime.toInstant(ZoneOffset.UTC);
} catch (DateTimeParseException e) {
return null;
}
}
}

View File

@ -0,0 +1,49 @@
/**
* 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.ecobee.internal.dto.thermostat;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.ECOBEE_DATETIME_FORMAT;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* The {@link LocalDateTimeDeserializer} is responsible for handling the local dates returned from
* the Ecobee API service.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class LocalDateTimeDeserializer implements JsonDeserializer<LocalDateTime> {
@Override
public @Nullable LocalDateTime deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2)
throws JsonParseException {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(ECOBEE_DATETIME_FORMAT);
return formatter.parse(element.getAsString(), LocalDateTime::from);
} catch (DateTimeParseException e) {
return null;
}
}
}

View File

@ -12,7 +12,7 @@
*/ */
package org.openhab.binding.ecobee.internal.dto.thermostat; package org.openhab.binding.ecobee.internal.dto.thermostat;
import java.util.Date; import java.time.Instant;
import java.util.List; import java.util.List;
/** /**
@ -48,29 +48,29 @@ public class RuntimeDTO {
* The UTC date/time stamp of when the thermostat first connected * The UTC date/time stamp of when the thermostat first connected
* to the ecobee server. * to the ecobee server.
*/ */
public Date firstConnected; public Instant firstConnected;
/* /*
* The last recorded connection date and time. * The last recorded connection date and time.
*/ */
public Date connectDateTime; public Instant connectDateTime;
/* /*
* The last recorded disconnection date and time. * The last recorded disconnection date and time.
*/ */
public Date disconnectDateTime; public Instant disconnectDateTime;
/* /*
* The UTC date/time stamp of when the thermostat was updated. * The UTC date/time stamp of when the thermostat was updated.
* Format: YYYY-MM-DD HH:MM:SS * Format: YYYY-MM-DD HH:MM:SS
*/ */
public Date lastModified; public Instant lastModified;
/* /*
* The UTC date/time stamp of when the thermostat last posted its * The UTC date/time stamp of when the thermostat last posted its
* runtime information. Format: YYYY-MM-DD HH:MM:SS * runtime information. Format: YYYY-MM-DD HH:MM:SS
*/ */
public Date lastStatusModified; public Instant lastStatusModified;
/* /*
* The UTC date of the last runtime reading. Format: YYYY-MM-DD * The UTC date of the last runtime reading. Format: YYYY-MM-DD

View File

@ -12,7 +12,8 @@
*/ */
package org.openhab.binding.ecobee.internal.dto.thermostat; package org.openhab.binding.ecobee.internal.dto.thermostat;
import java.util.Date; import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -64,12 +65,12 @@ public class ThermostatDTO {
/* /*
* The last modified date time for the thermostat configuration. * The last modified date time for the thermostat configuration.
*/ */
public Date lastModified; public Instant lastModified;
/* /*
* The current time in the thermostat's time zone. * The current time in the thermostat's time zone.
*/ */
public Date thermostatTime; public LocalDateTime thermostatTime;
/* /*
* The current time in UTC. * The current time in UTC.

View File

@ -12,7 +12,7 @@
*/ */
package org.openhab.binding.ecobee.internal.dto.thermostat; package org.openhab.binding.ecobee.internal.dto.thermostat;
import java.util.Date; import java.time.Instant;
import java.util.List; import java.util.List;
/** /**
@ -25,7 +25,7 @@ public class WeatherDTO {
/* /*
* The time stamp in UTC of the weather forecast * The time stamp in UTC of the weather forecast
*/ */
public Date timestamp; public Instant timestamp;
/* /*
* The weather station identifier * The weather station identifier

View File

@ -12,7 +12,7 @@
*/ */
package org.openhab.binding.ecobee.internal.dto.thermostat; package org.openhab.binding.ecobee.internal.dto.thermostat;
import java.util.Date; import java.time.LocalDateTime;
/** /**
* The {@link WeatherForecastDTO} contains the weather forecast information for * The {@link WeatherForecastDTO} contains the weather forecast information for
@ -31,9 +31,9 @@ public class WeatherForecastDTO {
public Integer weatherSymbol; public Integer weatherSymbol;
/* /*
* The time stamp of the weather forecast. * The time stamp of the weather forecast in the thermostat's time zone.
*/ */
public Date dateTime; public LocalDateTime dateTime;
/* /*
* A text value representing the current weather condition. * A text value representing the current weather condition.

View File

@ -662,6 +662,7 @@ public class EcobeeThermostatBridgeHandler extends BaseBridgeHandler {
return; return;
} }
final String weatherGrp = CHGRP_WEATHER + "#"; final String weatherGrp = CHGRP_WEATHER + "#";
updateChannel(weatherGrp + CH_WEATHER_TIMESTAMP, EcobeeUtils.undefOrDate(weather.timestamp, timeZoneProvider)); updateChannel(weatherGrp + CH_WEATHER_TIMESTAMP, EcobeeUtils.undefOrDate(weather.timestamp, timeZoneProvider));
updateChannel(weatherGrp + CH_WEATHER_WEATHER_STATION, EcobeeUtils.undefOrString(weather.weatherStation)); updateChannel(weatherGrp + CH_WEATHER_WEATHER_STATION, EcobeeUtils.undefOrString(weather.weatherStation));

View File

@ -12,8 +12,9 @@
*/ */
package org.openhab.binding.ecobee.internal.handler; package org.openhab.binding.ecobee.internal.handler;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Date;
import javax.measure.Unit; import javax.measure.Unit;
import javax.measure.quantity.Temperature; import javax.measure.quantity.Temperature;
@ -87,9 +88,13 @@ public final class EcobeeUtils {
return value == null ? UnDefType.UNDEF : new PointType(value); return value == null ? UnDefType.UNDEF : new PointType(value);
} }
public static State undefOrDate(@Nullable Date date, TimeZoneProvider timeZoneProvider) { public static State undefOrDate(@Nullable Instant instant, TimeZoneProvider timeZoneProvider) {
return date == null ? UnDefType.UNDEF return instant == null ? UnDefType.UNDEF
: new DateTimeType(ZonedDateTime.ofInstant(date.toInstant(), timeZoneProvider.getTimeZone())); : new DateTimeType(ZonedDateTime.ofInstant(instant, timeZoneProvider.getTimeZone()));
}
public static State undefOrDate(@Nullable LocalDateTime ldt, TimeZoneProvider timeZoneProvider) {
return ldt == null ? UnDefType.UNDEF : new DateTimeType(ldt.atZone(timeZoneProvider.getTimeZone()));
} }
private static boolean isUnknown(Number value) { private static boolean isUnknown(Number value) {