From 8d389b7e2ef8426f8575d9999205809f597d608e Mon Sep 17 00:00:00 2001 From: Christian Kittel Date: Tue, 8 Dec 2020 02:23:04 +0100 Subject: [PATCH] [ojelectronics] Add discovery; enable writable properties (#9168) * Add methods to update via Command; Add discovery * add vacation channels; rework README * Fixed code review issues: Corrected TimeZone-Handling Signed-off-by: Christian Kittel --- .../README.md | 12 +- .../internal/BindingConstants.java | 2 + .../internal/OJCloudHandler.java | 41 ++++- .../internal/ThermostatHandler.java | 170 +++++++++++++++++- .../internal/ThermostatHandlerFactory.java | 16 +- .../internal/common/OJGSonBuilder.java | 38 ++++ .../internal/models/RequestModelBase.java | 40 +++++ .../internal/models/ResponseModelBase.java | 26 +++ .../internal/models/SimpleResponseModel.java | 24 +++ .../models/{groups => }/Thermostat.java | 3 +- .../internal/models/groups/GroupContent.java | 1 + .../groups/GroupContentResponseModel.java | 5 +- .../UpdateThermostatRequestModel.java | 35 ++++ .../userprofile/PostSignInQueryModel.java | 19 +- .../userprofile/PostSignInResponseModel.java | 20 +-- .../internal/services/OJDiscoveryService.java | 100 +++++++++++ .../services/RefreshGroupContentService.java | 2 +- .../internal/services/RefreshService.java | 13 +- .../internal/services/SignInService.java | 18 +- .../internal/services/UpdateService.java | 97 ++++++++++ .../i18n/ojelectronics_de_DE.properties | 2 + .../resources/OH-INF/thing/thing-types.xml | 19 +- 22 files changed, 620 insertions(+), 83 deletions(-) create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/common/OJGSonBuilder.java create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/RequestModelBase.java create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/ResponseModelBase.java create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/SimpleResponseModel.java rename bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/{groups => }/Thermostat.java (93%) create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/thermostat/UpdateThermostatRequestModel.java create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/OJDiscoveryService.java create mode 100644 bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/UpdateService.java diff --git a/bundles/org.openhab.binding.ojelectronics/README.md b/bundles/org.openhab.binding.ojelectronics/README.md index 43e1234e1..11b4bb492 100644 --- a/bundles/org.openhab.binding.ojelectronics/README.md +++ b/bundles/org.openhab.binding.ojelectronics/README.md @@ -1,8 +1,6 @@ # OJElectronics Binding -With this binding it is possible to connect [OWD5/MWD5 Thermostat](https://www.ojelectronics.com/business-areas/wifi-thermostat-owd5-prod400) of OJ Electronics. - -At this moment all information is read only. +With this binding it is possible to connect [OWD5/MWD5 Thermostat](https://ojelectronics.com/floorheating/products/wifi-thermostat-owd5/) of OJ Electronics. ## Supported Things @@ -15,9 +13,7 @@ There are two things: ## Discovery -Not supported at the moment. - -## Thing Configuration +After the ojcloud bridge is succesfully initialized all thermostats will be discovered. ### OJ Electronics Bridge configuration (ojcloud) @@ -54,7 +50,9 @@ Not supported at the moment. | comfortEndTime | Date time | Date and time when the thermostat switchs back from comfort mode to automatic mode | | boostEndTime | Date time | Date and time when the thermostat switchs back from boost mode to automatic mode | | manualModeSetpoint | Number:Temperature | Target temperature of the manual mode | -| vacationEnabled | Switch | Vacation is enabled | +| vacationEnabled | Contact | Vacation is enabled | +| vacationBeginDay | Date time | Vacation start date | +| vacationEndDay | Date time | Vacation end date | ## Example diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java index c6cddd7d1..83ea4362d 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java @@ -44,4 +44,6 @@ public class BindingConstants { public static final String CHANNEL_OWD5_BOOSTENDTIME = "boostEndTime"; public static final String CHANNEL_OWD5_MANUALSETPOINT = "manualSetpoint"; public static final String CHANNEL_OWD5_VACATIONENABLED = "vacationEnabled"; + public static final String CHANNEL_OWD5_VACATIONBEGINDAY = "vacationBeginDay"; + public static final String CHANNEL_OWD5_VACATIONENDDAY = "vacationEndDay"; } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java index 0bcdb2b6e..1b7c9f2ea 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.ojelectronics.internal; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -20,15 +22,18 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration; import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel; +import org.openhab.binding.ojelectronics.internal.services.OJDiscoveryService; import org.openhab.binding.ojelectronics.internal.services.RefreshGroupContentService; import org.openhab.binding.ojelectronics.internal.services.RefreshService; import org.openhab.binding.ojelectronics.internal.services.SignInService; +import org.openhab.binding.ojelectronics.internal.services.UpdateService; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,10 +50,18 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { private final HttpClient httpClient; private @Nullable RefreshService refreshService; + private @Nullable UpdateService updateService; private @Nullable SignInService signInService; private OJElectronicsBridgeConfiguration configuration; private @Nullable ScheduledFuture signTask; + private @Nullable OJDiscoveryService discoveryService; + /** + * Creates a new instance of {@link OJCloudHandler} + * + * @param bridge {@link Bridge} + * @param httpClient HttpClient + */ public OJCloudHandler(Bridge bridge, HttpClient httpClient) { super(bridge); this.httpClient = httpClient; @@ -100,9 +113,8 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { private void handleRefreshDone(@Nullable GroupContentResponseModel groupContentResponse, @Nullable String errorMessage) { logger.trace("OJElectronicsCloudHandler.handleRefreshDone({})", groupContentResponse); - if (groupContentResponse != null && groupContentResponse.errorCode == 0) { - new RefreshGroupContentService(groupContentResponse.groupContents, getThing().getThings()).handle(); + internalRefreshDone(groupContentResponse); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, (errorMessage == null) ? "Wrong or no result model; Refreshing stoppped" : errorMessage); @@ -113,6 +125,18 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { } } + private void internalRefreshDone(GroupContentResponseModel groupContentResponse) { + new RefreshGroupContentService(groupContentResponse.groupContents, getThing().getThings()).handle(); + final OJDiscoveryService discoveryService = this.discoveryService; + if (discoveryService != null) { + discoveryService.setScanResultForDiscovery(groupContentResponse.groupContents); + } + final UpdateService updateService = this.updateService; + if (updateService != null) { + updateService.updateAllThermostats(getThing().getThings()); + } + } + private void handleSignInDone(String sessionId) { logger.trace("OJElectronicsCloudHandler.handleSignInDone({})", sessionId); if (refreshService == null) { @@ -125,9 +149,11 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { updateStatus(ThingStatus.ONLINE); } + this.updateService = new UpdateService(configuration, httpClient, sessionId); } private void handleUnauthorized() { + logger.trace("OJElectronicsCloudHandler.handleUnauthorized()"); final RefreshService refreshService = this.refreshService; if (refreshService != null) { refreshService.stop(); @@ -136,6 +162,7 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { } private void handleUnauthorizedWhileSignIn() { + logger.trace("OJElectronicsCloudHandler.handleUnauthorizedWhileSignIn()"); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Could not sign in. Check user name and password."); final RefreshService refreshService = this.refreshService; @@ -145,6 +172,7 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { } private void handleConnectionLost() { + logger.trace("OJElectronicsCloudHandler.handleConnectionLost()"); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); final RefreshService refreshService = this.refreshService; if (refreshService != null) { @@ -156,4 +184,13 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler { private void restartRefreshServiceAsync(long delayInSeconds) { signTask = scheduler.schedule(this::ensureSignIn, delayInSeconds, TimeUnit.SECONDS); } + + public void setDiscoveryService(OJDiscoveryService ojDiscoveryService) { + this.discoveryService = ojDiscoveryService; + } + + @Override + public Collection> getServices() { + return Collections.singleton(OJDiscoveryService.class); + } } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java index 7ad55b5c5..26b68378b 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java @@ -12,9 +12,13 @@ */ package org.openhab.binding.ojelectronics.internal; -import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Date; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import java.util.function.Consumer; @@ -23,7 +27,8 @@ import javax.measure.quantity.Temperature; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.ojelectronics.internal.config.OJElectronicsThermostatConfiguration; -import org.openhab.binding.ojelectronics.internal.models.groups.Thermostat; +import org.openhab.binding.ojelectronics.internal.models.Thermostat; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OpenClosedType; @@ -36,6 +41,8 @@ import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link ThermostatHandler} is responsible for handling commands, which are @@ -46,19 +53,28 @@ import org.openhab.core.types.RefreshType; @NonNullByDefault public class ThermostatHandler extends BaseThingHandler { - private final String serialNumber; - private @Nullable Thermostat currentThermostat; private static final Map REGULATION_MODES = createRegulationMap(); + private static final Map REVERSE_REGULATION_MODES = createRegulationReverseMap(); + + private final String serialNumber; + private final Logger logger = LoggerFactory.getLogger(ThermostatHandler.class); private final Map> channelrefreshActions = createChannelRefreshActionMap(); + private final Map> updateThermostatValueActions = createUpdateThermostatValueActionMap(); + private final TimeZoneProvider timeZoneProvider; + + private LinkedList> updatedValues = new LinkedList<>(); + private @Nullable Thermostat currentThermostat; /** * Creates a new instance of {@link ThermostatHandler} * * @param thing Thing + * @param timeZoneProvider Time zone */ - public ThermostatHandler(Thing thing) { + public ThermostatHandler(Thing thing, TimeZoneProvider timeZoneProvider) { super(thing); serialNumber = getConfigAs(OJElectronicsThermostatConfiguration.class).serialNumber; + this.timeZoneProvider = timeZoneProvider; } /** @@ -78,7 +94,14 @@ public class ThermostatHandler extends BaseThingHandler { if (command instanceof RefreshType) { final Thermostat thermostat = currentThermostat; if (thermostat != null && channelrefreshActions.containsKey(channelUID.getId())) { - channelrefreshActions.get(channelUID.getId()).accept(thermostat); + final @Nullable Consumer consumer = channelrefreshActions.get(channelUID.getId()); + if (consumer != null) { + consumer.accept(thermostat); + } + } + } else { + synchronized (this) { + updatedValues.add(new AbstractMap.SimpleImmutableEntry(channelUID.getId(), command)); } } } @@ -101,19 +124,65 @@ public class ThermostatHandler extends BaseThingHandler { channelrefreshActions.forEach((channelUID, action) -> action.accept(thermostat)); } + /** + * Gets a {@link Thermostat} with changed values or null if nothing has changed + * + * @return The changed {@link Thermostat} + */ + public @Nullable Thermostat tryHandleAndGetUpdatedThermostat() { + final LinkedList> updatedValues = this.updatedValues; + if (updatedValues.size() == 0) { + return null; + } + this.updatedValues = new LinkedList<>(); + updatedValues.forEach(item -> { + if (updateThermostatValueActions.containsKey(item.getKey())) { + final @Nullable Consumer consumer = updateThermostatValueActions.get(item.getKey()); + if (consumer != null) { + consumer.accept(item.getValue()); + } + } + }); + return currentThermostat; + } + private void updateManualSetpoint(Thermostat thermostat) { updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, new QuantityType(thermostat.manualModeSetpoint / (double) 100, SIUnits.CELSIUS)); } + private void updateManualSetpoint(Command command) { + if (command instanceof QuantityType) { + currentThermostat.manualModeSetpoint = (int) (((QuantityType) command).floatValue() * 100); + } else { + logger.warn("Unable to set value {}", command); + } + } + private void updateBoostEndTime(Thermostat thermostat) { - updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, - new DateTimeType(ZonedDateTime.ofInstant(thermostat.boostEndTime.toInstant(), ZoneId.systemDefault()))); + updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, new DateTimeType( + ZonedDateTime.ofInstant(thermostat.boostEndTime.toInstant(), timeZoneProvider.getTimeZone()))); + } + + private void updateBoostEndTime(Command command) { + if (command instanceof DateTimeType) { + currentThermostat.boostEndTime = Date.from(((DateTimeType) command).getZonedDateTime().toInstant()); + } else { + logger.warn("Unable to set value {}", command); + } } private void updateComfortEndTime(Thermostat thermostat) { updateState(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, new DateTimeType( - ZonedDateTime.ofInstant(thermostat.comfortEndTime.toInstant(), ZoneId.systemDefault()))); + ZonedDateTime.ofInstant(thermostat.comfortEndTime.toInstant(), timeZoneProvider.getTimeZone()))); + } + + private void updateComfortEndTime(Command command) { + if (command instanceof DateTimeType) { + currentThermostat.comfortEndTime = Date.from(((DateTimeType) command).getZonedDateTime().toInstant()); + } else { + logger.warn("Unable to set value {}", command); + } } private void updateComfortSetpoint(Thermostat thermostat) { @@ -121,11 +190,30 @@ public class ThermostatHandler extends BaseThingHandler { new QuantityType(thermostat.comfortSetpoint / (double) 100, SIUnits.CELSIUS)); } + private void updateComfortSetpoint(Command command) { + if (command instanceof QuantityType) { + currentThermostat.comfortSetpoint = (int) (((QuantityType) command).floatValue() * 100); + } else { + logger.warn("Unable to set value {}", command); + } + } + private void updateRegulationMode(Thermostat thermostat) { updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, StringType.valueOf(getRegulationMode(thermostat.regulationMode))); } + private void updateRegulationMode(Command command) { + if (command instanceof StringType && (REVERSE_REGULATION_MODES.containsKey(command.toString().toLowerCase()))) { + final @Nullable Integer mode = REVERSE_REGULATION_MODES.get(command.toString().toLowerCase()); + if (mode != null) { + currentThermostat.regulationMode = mode; + } + } else { + logger.warn("Unable to set value {}", command); + } + } + private void updateThermostatName(Thermostat thermostat) { updateState(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, StringType.valueOf(thermostat.thermostatName)); } @@ -158,6 +246,43 @@ public class ThermostatHandler extends BaseThingHandler { updateState(BindingConstants.CHANNEL_OWD5_GROUPNAME, StringType.valueOf(thermostat.groupName)); } + private void updateVacationEnabled(Thermostat thermostat) { + updateState(BindingConstants.CHANNEL_OWD5_VACATIONENABLED, + thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED); + } + + private void updateVacationBeginDay(Thermostat thermostat) { + updateState(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY, + new DateTimeType( + ZonedDateTime.ofInstant(thermostat.vacationBeginDay.toInstant(), timeZoneProvider.getTimeZone()) + .truncatedTo(ChronoUnit.DAYS))); + } + + private void updateVacationBeginDay(Command command) { + if (command instanceof DateTimeType) { + currentThermostat.vacationBeginDay = Date + .from(((DateTimeType) command).getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS)); + } else { + logger.warn("Unable to set value {}", command); + } + } + + private void updateVacationEndDay(Thermostat thermostat) { + updateState(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY, + new DateTimeType( + ZonedDateTime.ofInstant(thermostat.vacationEndDay.toInstant(), timeZoneProvider.getTimeZone()) + .truncatedTo(ChronoUnit.DAYS))); + } + + private void updateVacationEndDay(Command command) { + if (command instanceof DateTimeType) { + currentThermostat.vacationEndDay = Date + .from(((DateTimeType) command).getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS)); + } else { + logger.warn("Unable to set value {}", command); + } + } + private @Nullable String getRegulationMode(int regulationMode) { return REGULATION_MODES.get(regulationMode); } @@ -174,6 +299,18 @@ public class ThermostatHandler extends BaseThingHandler { return map; }; + private static Map createRegulationReverseMap() { + HashMap map = new HashMap<>(); + map.put("auto", 1); + map.put("comfort", 2); + map.put("manual", 3); + map.put("vacation", 4); + map.put("frostprotection", 6); + map.put("boost", 8); + map.put("eco", 9); + return map; + }; + private Map> createChannelRefreshActionMap() { HashMap> map = new HashMap<>(); map.put(BindingConstants.CHANNEL_OWD5_GROUPNAME, this::updateGroupName); @@ -188,6 +325,21 @@ public class ThermostatHandler extends BaseThingHandler { map.put(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, this::updateComfortEndTime); map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime); map.put(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, this::updateManualSetpoint); + map.put(BindingConstants.CHANNEL_OWD5_VACATIONENABLED, this::updateVacationEnabled); + map.put(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY, this::updateVacationBeginDay); + map.put(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY, this::updateVacationEndDay); + return map; + } + + private Map> createUpdateThermostatValueActionMap() { + HashMap> map = new HashMap<>(); + map.put(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, this::updateRegulationMode); + map.put(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, this::updateManualSetpoint); + map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime); + map.put(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, this::updateComfortEndTime); + map.put(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT, this::updateComfortSetpoint); + map.put(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY, this::updateVacationBeginDay); + map.put(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY, this::updateVacationEndDay); return map; } } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java index 0a7abbe5e..70843e20a 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java @@ -19,12 +19,15 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * The {@link ThermostatHandlerFactory} is responsible for creating {@link OJElectronicsThermostatHandler}. @@ -36,6 +39,17 @@ import org.osgi.service.component.annotations.Component; public class ThermostatHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OWD5); + private final TimeZoneProvider timeZoneProvider; + + /** + * Creates a new factory + * + * @param httpClientFactory Factory for HttpClient + */ + @Activate + public ThermostatHandlerFactory(@Reference TimeZoneProvider timeZoneProvider) { + this.timeZoneProvider = timeZoneProvider; + } /** * Supported things of this factory. @@ -50,7 +64,7 @@ public class ThermostatHandlerFactory extends BaseThingHandlerFactory { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_OWD5.equals(thingTypeUID)) { - return new ThermostatHandler(thing); + return new ThermostatHandler(thing, timeZoneProvider); } return null; diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/common/OJGSonBuilder.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/common/OJGSonBuilder.java new file mode 100644 index 000000000..acae43dfa --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/common/OJGSonBuilder.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Builder for Gson + * + * @author Christian Kittel - Initial Contribution + */ +@NonNullByDefault +public final class OJGSonBuilder { + + /** + * Gets a correct initialized {@link Gson} + * + * @return {@link GSon} + */ + public static Gson getGSon() { + return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create(); + } +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/RequestModelBase.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/RequestModelBase.java new file mode 100644 index 000000000..114f4ada3 --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/RequestModelBase.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.models; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.annotations.SerializedName; + +/** + * Base model for all requests + * + * @author Christian Kittel - Initial contribution + */ +@NonNullByDefault +public abstract class RequestModelBase { + + @SerializedName("APIKEY") + public String apiKey = ""; + + /** + * Add API-Key + * + * @param apiKey API-Key + * @return Model + */ + public RequestModelBase withApiKey(String apiKey) { + this.apiKey = apiKey; + return this; + } +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/ResponseModelBase.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/ResponseModelBase.java new file mode 100644 index 000000000..6abf160f2 --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/ResponseModelBase.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.models; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Base model for all responses + * + * @author Christian Kittel - Initial contribution + */ +@NonNullByDefault +public abstract class ResponseModelBase { + + public int errorCode; +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/SimpleResponseModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/SimpleResponseModel.java new file mode 100644 index 000000000..3b2401702 --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/SimpleResponseModel.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.models; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Response model without additional properties + * + * @author Christian Kittel - Initial contribution + */ +@NonNullByDefault +public class SimpleResponseModel extends ResponseModelBase { +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Thermostat.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/Thermostat.java similarity index 93% rename from bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Thermostat.java rename to bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/Thermostat.java index e08132cad..e194cd6d3 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Thermostat.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/Thermostat.java @@ -10,12 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.ojelectronics.internal.models.groups; +package org.openhab.binding.ojelectronics.internal.models; import java.util.Date; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.ojelectronics.internal.models.groups.Schedule; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java index 78fdb1c65..4e4325c1a 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java @@ -17,6 +17,7 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.ojelectronics.internal.models.Thermostat; /** * Model for content of a group diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java index bd9aa9429..fb80e7503 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.ojelectronics.internal.models.ResponseModelBase; /** * Model for the response of a content group @@ -23,9 +24,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; * @author Christian Kittel - Initial contribution */ @NonNullByDefault -public class GroupContentResponseModel { +public class GroupContentResponseModel extends ResponseModelBase { public List groupContents = new ArrayList(); - - public int errorCode; } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/thermostat/UpdateThermostatRequestModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/thermostat/UpdateThermostatRequestModel.java new file mode 100644 index 000000000..62585ad9b --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/thermostat/UpdateThermostatRequestModel.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.models.thermostat; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.ojelectronics.internal.models.RequestModelBase; +import org.openhab.binding.ojelectronics.internal.models.Thermostat; + +/** + * Model for updating a thermostat + * + * @author Christian Kittel - Initial contribution + */ +@NonNullByDefault +public class UpdateThermostatRequestModel extends RequestModelBase { + + public UpdateThermostatRequestModel(Thermostat thermostat) { + setThermostat = thermostat; + thermostatID = thermostat.serialNumber; + } + + public Thermostat setThermostat; + + public String thermostatID; +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java index 1c606ae4e..abbfd3f3e 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java @@ -13,8 +13,7 @@ package org.openhab.binding.ojelectronics.internal.models.userprofile; import org.eclipse.jdt.annotation.NonNullByDefault; - -import com.google.gson.annotations.SerializedName; +import org.openhab.binding.ojelectronics.internal.models.RequestModelBase; /** * Model for signing sin @@ -22,10 +21,7 @@ import com.google.gson.annotations.SerializedName; * @author Christian Kittel - Initial contribution */ @NonNullByDefault -public class PostSignInQueryModel { - - @SerializedName("APIKEY") - public String apiKey = ""; +public class PostSignInQueryModel extends RequestModelBase { public String userName = ""; @@ -35,17 +31,6 @@ public class PostSignInQueryModel { public int clientSWVersion; - /** - * Add API-Key - * - * @param apiKey API-Key - * @return Model - */ - public PostSignInQueryModel withApiKey(String apiKey) { - this.apiKey = apiKey; - return this; - } - /** * Add User-Name * diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.java index 334a5791c..60f5c92c2 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.java @@ -13,6 +13,7 @@ package org.openhab.binding.ojelectronics.internal.models.userprofile; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.ojelectronics.internal.models.ResponseModelBase; /** * Response-Model after signing in @@ -20,26 +21,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; * @author Christian Kittel - Initial Contribution */ @NonNullByDefault -public class PostSignInResponseModel { +public class PostSignInResponseModel extends ResponseModelBase { public String sessionId = ""; public String userName = ""; - - public int errorCode; - - public PostSignInResponseModel withSessionId(String sessionId) { - this.sessionId = sessionId; - return this; - } - - public PostSignInResponseModel withUserName(String userName) { - this.userName = userName; - return this; - } - - public PostSignInResponseModel withErrorCode(int errorCode) { - this.errorCode = errorCode; - return this; - } } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/OJDiscoveryService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/OJDiscoveryService.java new file mode 100644 index 000000000..ab1372ee5 --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/OJDiscoveryService.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.services; + +import static org.openhab.binding.ojelectronics.internal.BindingConstants.*; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.ojelectronics.internal.OJCloudHandler; +import org.openhab.binding.ojelectronics.internal.models.groups.GroupContent; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.osgi.service.component.annotations.Component; + +/** + * DiscoveryService for OJ Components + * + * @author Christian Kittel - Initial Contribution + */ +@NonNullByDefault +@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.ojelectronics") +public final class OJDiscoveryService extends AbstractDiscoveryService + implements DiscoveryService, ThingHandlerService { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OJCLOUD); + private @Nullable OJCloudHandler bridgeHandler; + private @Nullable Collection groupContents; + + /** + * Creates a new instance of {@link OJDiscoveryService} + * + */ + public OJDiscoveryService() throws IllegalArgumentException { + super(SUPPORTED_THING_TYPES_UIDS, 10); + } + + /** + * Sets the scan result for discovering + * + * @param groupContents Content from API + */ + public void setScanResultForDiscovery(List groupContents) { + this.groupContents = groupContents; + } + + @Override + protected void startScan() { + final OJCloudHandler bridgeHandler = this.bridgeHandler; + final Collection groupContents = this.groupContents; + if (groupContents != null && bridgeHandler != null) { + groupContents.stream().flatMap(content -> content.thermostats.stream()) + .forEach(thermostat -> thingDiscovered(bridgeHandler.getThing().getUID(), thermostat.serialNumber)); + } + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof OJCloudHandler) { + final OJCloudHandler bridgeHandler = (OJCloudHandler) handler; + this.bridgeHandler = bridgeHandler; + bridgeHandler.setDiscoveryService(this); + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } + + @Override + public void deactivate() { + super.deactivate(); + } + + private void thingDiscovered(ThingUID bridgeUID, String serialNumber) { + thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_OWD5, bridgeUID, serialNumber)) + .withBridge(bridgeUID).withRepresentationProperty("serialNumber") + .withProperty("serialNumber", serialNumber).withLabel("Thermostat " + serialNumber).build()); + } +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java index e1fe77c91..6ba6be20a 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java @@ -16,8 +16,8 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.ojelectronics.internal.ThermostatHandler; +import org.openhab.binding.ojelectronics.internal.models.Thermostat; import org.openhab.binding.ojelectronics.internal.models.groups.GroupContent; -import org.openhab.binding.ojelectronics.internal.models.groups.Thermostat; import org.openhab.core.thing.Thing; /** diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java index fa489152a..838822b65 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java @@ -25,14 +25,13 @@ import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.ojelectronics.internal.common.OJGSonBuilder; import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration; import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; /** @@ -46,7 +45,7 @@ public final class RefreshService implements AutoCloseable { private final OJElectronicsBridgeConfiguration config; private final Logger logger = LoggerFactory.getLogger(RefreshService.class); private final HttpClient httpClient; - private final Gson gson = createGson(); + private final Gson gson = OJGSonBuilder.getGSon(); private final ScheduledExecutorService schedulerService; @@ -62,6 +61,7 @@ public final class RefreshService implements AutoCloseable { * * @param config Configuration of the bridge * @param httpClient HTTP client + * @param updateService Service to update the thermostat in the cloud */ public RefreshService(OJElectronicsBridgeConfiguration config, HttpClient httpClient, ScheduledExecutorService schedulerService) { @@ -103,11 +103,6 @@ public final class RefreshService implements AutoCloseable { this.scheduler = null; } - private Gson createGson() { - return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create(); - } - private void refresh() { final String sessionId = this.sessionId; if (sessionId == null) { @@ -124,6 +119,8 @@ public final class RefreshService implements AutoCloseable { if (unauthorized != null) { unauthorized.run(); } + } else if (result.getResponse().getStatus() == HttpStatus.FORBIDDEN_403) { + handleConnectionLost(); } else { handleRefreshDone(getContentAsString()); } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java index 8897fc8d4..ab36f8880 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java @@ -22,13 +22,13 @@ import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpHeader; +import org.openhab.binding.ojelectronics.internal.common.OJGSonBuilder; import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration; +import org.openhab.binding.ojelectronics.internal.models.RequestModelBase; import org.openhab.binding.ojelectronics.internal.models.userprofile.PostSignInQueryModel; import org.openhab.binding.ojelectronics.internal.models.userprofile.PostSignInResponseModel; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; /** * Handles the sign in process. @@ -38,7 +38,7 @@ import com.google.gson.GsonBuilder; @NonNullByDefault public class SignInService { - private final Gson gson = createGson(); + private final Gson gson = OJGSonBuilder.getGSon(); private final HttpClient httpClient; private final OJElectronicsBridgeConfiguration config; @@ -79,7 +79,7 @@ public class SignInService { } PostSignInResponseModel signInModel = gson.fromJson(getContentAsString(), PostSignInResponseModel.class); - if (signInModel.errorCode != 0 || signInModel.sessionId.equals("")) { + if (signInModel == null || signInModel.errorCode != 0 || signInModel.sessionId.equals("")) { unauthorized.run(); return; } @@ -88,12 +88,8 @@ public class SignInService { }); } - private Gson createGson() { - return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create(); - } - - private PostSignInQueryModel getPostSignInQueryModel() { - return new PostSignInQueryModel().withApiKey(config.apiKey).withClientSWVersion(config.softwareVersion) - .withCustomerId(config.customerId).withUserName(config.userName).withPassword(config.password); + private RequestModelBase getPostSignInQueryModel() { + return new PostSignInQueryModel().withClientSWVersion(config.softwareVersion).withCustomerId(config.customerId) + .withUserName(config.userName).withPassword(config.password).withApiKey(config.apiKey); } } diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/UpdateService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/UpdateService.java new file mode 100644 index 000000000..0e70d3fce --- /dev/null +++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/UpdateService.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010-2020 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.ojelectronics.internal.services; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.openhab.binding.ojelectronics.internal.ThermostatHandler; +import org.openhab.binding.ojelectronics.internal.common.OJGSonBuilder; +import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration; +import org.openhab.binding.ojelectronics.internal.models.SimpleResponseModel; +import org.openhab.binding.ojelectronics.internal.models.Thermostat; +import org.openhab.binding.ojelectronics.internal.models.thermostat.UpdateThermostatRequestModel; +import org.openhab.core.thing.Thing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + +/** + * Handles the update of the devices of a session + * + * @author Christian Kittel - Initial Contribution + */ +@NonNullByDefault +public final class UpdateService { + + private final Gson gson = OJGSonBuilder.getGSon(); + private final Logger logger = LoggerFactory.getLogger(UpdateService.class); + + private final String sessionId; + private final HttpClient httpClient; + private final OJElectronicsBridgeConfiguration configuration; + + public UpdateService(OJElectronicsBridgeConfiguration configuration, HttpClient httpClient, String sessionId) { + this.configuration = configuration; + this.httpClient = httpClient; + this.sessionId = sessionId; + } + + /** + * Sends all changes of all {@link ThermostatHandler} to the API + * + * @param things + */ + public void updateAllThermostats(List things) { + things.stream().filter(thing -> thing.getHandler() instanceof ThermostatHandler) + .map(thing -> (ThermostatHandler) thing.getHandler()) + .map(handler -> handler.tryHandleAndGetUpdatedThermostat()).forEach(this::updateThermostat); + } + + private void updateThermostat(@Nullable Thermostat thermostat) { + if (thermostat == null) { + return; + } + Request request = httpClient.POST(configuration.apiUrl + "/Thermostat/UpdateThermostat") + .param("sessionid", sessionId).header(HttpHeader.CONTENT_TYPE, "application/json") + .content(new StringContentProvider( + gson.toJson(new UpdateThermostatRequestModel(thermostat).withApiKey(configuration.apiKey)))); + + request.send(new BufferingResponseListener() { + @Override + public void onComplete(@Nullable Result result) { + if (result != null) { + logger.trace("onComplete {}", result); + if (result.isFailed()) { + logger.warn("updateThermostat failed {}", thermostat); + } + SimpleResponseModel responseModel = gson.fromJson(getContentAsString(), SimpleResponseModel.class); + if (responseModel == null) { + logger.warn("updateThermostat failed with empty result {}", thermostat); + } else if (responseModel.errorCode != 0) { + logger.warn("updateThermostat failed with errorCode {} {}", responseModel.errorCode, + thermostat); + } + } + } + }); + } +} diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/i18n/ojelectronics_de_DE.properties b/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/i18n/ojelectronics_de_DE.properties index f39dae180..eade32637 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/i18n/ojelectronics_de_DE.properties +++ b/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/i18n/ojelectronics_de_DE.properties @@ -36,3 +36,5 @@ channel-type.ojelectronics.comfortEndTime.label = Komfort-Endzeit channel-type.ojelectronics.boostEndTime.label = Boost Endzeit channel-type.ojelectronics.manualSetpoint.label = manuelle Endzeit channel-type.ojelectronics.vacationEnabled.label = Urlausbmodus aktiviert +channel-type.ojelectronics.vacationBeginDay.label = Start des Urlausbmodus +channel-type.ojelectronics.vacationEndDay.label = Ende des Urlausbmodus diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/thing/thing-types.xml index f253b3177..ba64ef5d9 100644 --- a/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.ojelectronics/src/main/resources/OH-INF/thing/thing-types.xml @@ -68,10 +68,13 @@ + + OJ Electronics + serialNumber @@ -119,7 +122,7 @@ String - + @@ -135,27 +138,33 @@ Number:Temperature Temperature - + DateTime - DateTime - Number:Temperature Temperature - + Switch + + Switch + + + + Switch + +