From f807864daaf524048f69bbe459ceb4ad227467a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20L=27hopital?= Date: Mon, 21 Oct 2024 20:32:01 +0200 Subject: [PATCH] [netatmo] Consider timezone of the house when defining the end time of a setpoint (#17586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Consider timezone of the house when defining the end time of a setpoint Signed-off-by: Gaƫl L'hopital --- .../netatmo/internal/NetatmoHandlerFactory.java | 11 ++++++++--- .../netatmo/internal/api/dto/HomeData.java | 9 +++++---- .../netatmo/internal/api/dto/LocationEx.java | 17 ++++++++++++++++- .../binding/netatmo/internal/api/dto/Place.java | 4 ++-- .../internal/handler/CommonInterface.java | 3 +++ .../netatmo/internal/handler/DeviceHandler.java | 13 +++++++++++-- .../netatmo/internal/handler/ModuleHandler.java | 13 +++++++++++-- .../handler/capability/DeviceCapability.java | 2 +- .../handler/capability/EnergyCapability.java | 11 ++++++++--- .../handler/capability/HomeCapability.java | 7 +++++-- .../channelhelper/EnergyChannelHelper.java | 2 +- 11 files changed, 71 insertions(+), 21 deletions(-) diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java index 9af2f0ad269..031595a9ed6 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java @@ -47,6 +47,7 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper; import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider; import org.openhab.core.auth.client.oauth2.OAuthFactory; import org.openhab.core.config.core.ConfigParser; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; @@ -80,17 +81,19 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory { private final HttpClient httpClient; private final HttpService httpService; private final OAuthFactory oAuthFactory; + private final TimeZoneProvider timeZoneProvider; @Activate public NetatmoHandlerFactory(final @Reference NetatmoDescriptionProvider stateDescriptionProvider, final @Reference HttpClientFactory factory, final @Reference NADeserializer deserializer, final @Reference HttpService httpService, final @Reference OAuthFactory oAuthFactory, - Map config) { + final @Reference TimeZoneProvider timeZoneProvider, Map config) { this.stateDescriptionProvider = stateDescriptionProvider; this.httpClient = factory.getCommonHttpClient(); this.deserializer = deserializer; this.httpService = httpService; this.oAuthFactory = oAuthFactory; + this.timeZoneProvider = timeZoneProvider; configChanged(config); } @@ -119,7 +122,8 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory { return new ApiBridgeHandler((Bridge) thing, httpClient, deserializer, configuration, httpService, oAuthFactory); } - CommonInterface handler = moduleType.isABridge() ? new DeviceHandler((Bridge) thing) : new ModuleHandler(thing); + CommonInterface handler = moduleType.isABridge() ? new DeviceHandler((Bridge) thing, timeZoneProvider) + : new ModuleHandler(thing, timeZoneProvider); List helpers = new ArrayList<>(); @@ -127,6 +131,7 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory { moduleType.capabilities.forEach(capability -> { Capability newCap = null; + if (capability == DeviceCapability.class) { newCap = new DeviceCapability(handler); } else if (capability == AirCareCapability.class) { @@ -161,7 +166,7 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory { if (newCap != null) { handler.getCapabilities().put(newCap); } else { - logger.warn("No factory entry defined to create Capability : {}", capability); + logger.warn("No factory entry defined to create Capability: {}", capability); } }); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java index e28a4a9a821..f19ab05fca6 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.netatmo.internal.api.dto; +import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.Set; @@ -59,8 +60,8 @@ public class HomeData extends NAThing implements NAModule, LocationEx { private int thermSetpointDefaultDuration; private List schedules = List.of(); - public int getThermSetpointDefaultDuration() { - return thermSetpointDefaultDuration; + public Duration getSetpointDefaultDuration() { + return Duration.ofMinutes(thermSetpointDefaultDuration); } public SetpointMode getThermMode() { @@ -109,8 +110,8 @@ public class HomeData extends NAThing implements NAModule, LocationEx { } @Override - public Optional getTimezone() { - return Optional.ofNullable(timezone); + public @Nullable String getTimezone() { + return timezone; } public NAObjectMap getRooms() { diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/LocationEx.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/LocationEx.java index d3ca9a7ec41..b6e60eba432 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/LocationEx.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/LocationEx.java @@ -12,9 +12,12 @@ */ package org.openhab.binding.netatmo.internal.api.dto; +import java.time.DateTimeException; +import java.time.ZoneId; import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; /** * The {@link LocationEx} is the common interface for dto holding an extra location data @@ -26,5 +29,17 @@ import org.eclipse.jdt.annotation.NonNullByDefault; public interface LocationEx extends Location { Optional getCountry(); - Optional getTimezone(); + @Nullable + String getTimezone(); + + public default ZoneId getZoneId(ZoneId fallback) { + String local = getTimezone(); + if (local != null) { + try { + return ZoneId.of(local); + } catch (DateTimeException ignore) { + } + } + return fallback; + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/Place.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/Place.java index f391f38ec39..b0855df2d57 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/Place.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/Place.java @@ -42,8 +42,8 @@ public class Place implements LocationEx { } @Override - public Optional getTimezone() { - return Optional.ofNullable(timezone); + public @Nullable String getTimezone() { + return timezone; } @Override diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java index aac0f4b0041..68ff8086ef9 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java @@ -13,6 +13,7 @@ package org.openhab.binding.netatmo.internal.handler; import java.time.Duration; +import java.time.ZoneId; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -83,6 +84,8 @@ public interface CommonInterface { @Nullable Bridge getBridge(); + ZoneId getSystemTimeZone(); + default @Nullable CommonInterface getBridgeHandler() { Bridge bridge = getBridge(); return bridge != null && bridge.getHandler() instanceof DeviceHandler ? (DeviceHandler) bridge.getHandler() diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/DeviceHandler.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/DeviceHandler.java index 61b60c6ddfd..5db67fba77a 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/DeviceHandler.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/DeviceHandler.java @@ -12,11 +12,13 @@ */ package org.openhab.binding.netatmo.internal.handler; +import java.time.ZoneId; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -39,10 +41,12 @@ import org.slf4j.LoggerFactory; @NonNullByDefault public class DeviceHandler extends BaseBridgeHandler implements CommonInterface { private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class); - private CapabilityMap capabilities = new CapabilityMap(); + private final CapabilityMap capabilities = new CapabilityMap(); + private final TimeZoneProvider timeZoneProvider; - public DeviceHandler(Bridge bridge) { + public DeviceHandler(Bridge bridge, TimeZoneProvider timeZoneProvider) { super(bridge); + this.timeZoneProvider = timeZoneProvider; } @Override @@ -118,4 +122,9 @@ public class DeviceHandler extends BaseBridgeHandler implements CommonInterface public ScheduledExecutorService getScheduler() { return scheduler; } + + @Override + public ZoneId getSystemTimeZone() { + return timeZoneProvider.getTimeZone(); + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ModuleHandler.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ModuleHandler.java index ab8625fc1fa..9c9a99cbc95 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ModuleHandler.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ModuleHandler.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.netatmo.internal.handler; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -20,6 +21,7 @@ import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -43,10 +45,12 @@ import org.slf4j.LoggerFactory; @NonNullByDefault public class ModuleHandler extends BaseThingHandler implements CommonInterface { private final Logger logger = LoggerFactory.getLogger(ModuleHandler.class); - private CapabilityMap capabilities = new CapabilityMap(); + private final CapabilityMap capabilities = new CapabilityMap(); + private final TimeZoneProvider timeZoneProvider; - public ModuleHandler(Thing thing) { + public ModuleHandler(Thing thing, TimeZoneProvider timeZoneProvider) { super(thing); + this.timeZoneProvider = timeZoneProvider; } @Override @@ -129,4 +133,9 @@ public class ModuleHandler extends BaseThingHandler implements CommonInterface { public ScheduledExecutorService getScheduler() { return scheduler; } + + @Override + public ZoneId getSystemTimeZone() { + return timeZoneProvider.getTimeZone(); + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/DeviceCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/DeviceCapability.java index f132c730310..eb52da6a7d4 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/DeviceCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/DeviceCapability.java @@ -47,7 +47,7 @@ public class DeviceCapability extends Capability { newData.getPlace().ifPresent(place -> { place.getCity().map(city -> properties.put(PROPERTY_CITY, city)); place.getCountry().map(country -> properties.put(PROPERTY_COUNTRY, country)); - place.getTimezone().map(tz -> properties.put(PROPERTY_TIMEZONE, tz)); + properties.put(PROPERTY_TIMEZONE, place.getZoneId(handler.getSystemTimeZone()).toString()); }); } if (!newData.hasFreshData(DATA_AGE_LIMIT_S)) { diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java index 50299eff8bb..1cdfd5d5042 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java @@ -14,6 +14,7 @@ package org.openhab.binding.netatmo.internal.handler.capability; import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*; +import java.time.Duration; import java.time.ZonedDateTime; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -48,7 +49,7 @@ public class EnergyCapability extends RestCapability { private final Logger logger = LoggerFactory.getLogger(EnergyCapability.class); private final NetatmoDescriptionProvider descriptionProvider; - private int setPointDefaultDuration = -1; + private Duration setPointDefaultDuration = Duration.ofMinutes(120); private String energyId = ""; EnergyCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider) { @@ -79,7 +80,7 @@ public class EnergyCapability extends RestCapability { }); descriptionProvider.setStateOptions(new ChannelUID(thingUID, GROUP_ENERGY, CHANNEL_PLANNING), energyData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName())).toList()); - setPointDefaultDuration = energyData.getThermSetpointDefaultDuration(); + setPointDefaultDuration = energyData.getSetpointDefaultDuration(); } } @@ -157,6 +158,10 @@ public class EnergyCapability extends RestCapability { } private long setpointEndTimeFromNow() { - return ZonedDateTime.now().plusMinutes(setPointDefaultDuration).toEpochSecond(); + return handler.getHomeCapability(HomeCapability.class).map(cap -> { + ZonedDateTime now = ZonedDateTime.now().plus(setPointDefaultDuration); + now = now.withZoneSameInstant(cap.zoneId); + return now.toEpochSecond(); + }).orElse(-1l); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java index d15646ea341..2089dfe6f71 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java @@ -15,6 +15,7 @@ package org.openhab.binding.netatmo.internal.handler.capability; import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*; import java.time.Duration; +import java.time.ZoneId; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -44,12 +45,13 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class HomeCapability extends CacheCapability { - private final Logger logger = LoggerFactory.getLogger(HomeCapability.class); private final Set featureAreas = new HashSet<>(); private final NetatmoDescriptionProvider descriptionProvider; private final Set homeIds = new HashSet<>(3); + protected ZoneId zoneId = ZoneId.systemDefault(); + public HomeCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider) { super(handler, Duration.ofSeconds(2), HomeApi.class); this.descriptionProvider = descriptionProvider; @@ -88,7 +90,8 @@ public class HomeCapability extends CacheCapability { handler.removeChannels(thing.getChannelsOfGroup(GROUP_ENERGY)); } home.getCountry().map(country -> properties.put(PROPERTY_COUNTRY, country)); - home.getTimezone().map(tz -> properties.put(PROPERTY_TIMEZONE, tz)); + zoneId = home.getZoneId(handler.getSystemTimeZone()); + properties.put(PROPERTY_TIMEZONE, zoneId.toString()); properties.put(GROUP_LOCATION, ((Location) home).getLocation().toString()); properties.put(PROPERTY_FEATURE, featureAreas.stream().map(FeatureArea::name).collect(Collectors.joining(","))); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/EnergyChannelHelper.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/EnergyChannelHelper.java index fb5352ec015..f2cb35fcbb6 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/EnergyChannelHelper.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/EnergyChannelHelper.java @@ -55,7 +55,7 @@ public class EnergyChannelHelper extends ChannelHelper { ThermProgram currentProgram = energyData.getActiveProgram(); switch (channelId) { case CHANNEL_SETPOINT_DURATION: - return toQuantityType(energyData.getThermSetpointDefaultDuration(), Units.MINUTE); + return toQuantityType(energyData.getSetpointDefaultDuration().getSeconds(), Units.SECOND); case CHANNEL_PLANNING: return (currentProgram != null ? toStringType(currentProgram.getName()) : null); case CHANNEL_SETPOINT_END_TIME: