[netatmo] Consider timezone of the house when defining the end time of a setpoint (#17586)

* Consider timezone of the house when defining the end time of a setpoint

Signed-off-by: Gaël L'hopital <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2024-10-21 20:32:01 +02:00 committed by GitHub
parent 78636e6018
commit f807864daa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 71 additions and 21 deletions

View File

@ -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<String, @Nullable Object> config) {
final @Reference TimeZoneProvider timeZoneProvider, Map<String, @Nullable Object> 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<ChannelHelper> 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);
}
});

View File

@ -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<ThermProgram> 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<String> getTimezone() {
return Optional.ofNullable(timezone);
public @Nullable String getTimezone() {
return timezone;
}
public NAObjectMap<HomeDataRoom> getRooms() {

View File

@ -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<String> getCountry();
Optional<String> 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;
}
}

View File

@ -42,8 +42,8 @@ public class Place implements LocationEx {
}
@Override
public Optional<String> getTimezone() {
return Optional.ofNullable(timezone);
public @Nullable String getTimezone() {
return timezone;
}
@Override

View File

@ -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()

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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)) {

View File

@ -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<EnergyApi> {
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<EnergyApi> {
});
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<EnergyApi> {
}
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);
}
}

View File

@ -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<HomeApi> {
private final Logger logger = LoggerFactory.getLogger(HomeCapability.class);
private final Set<FeatureArea> featureAreas = new HashSet<>();
private final NetatmoDescriptionProvider descriptionProvider;
private final Set<String> 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<HomeApi> {
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(",")));

View File

@ -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: