[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 <ckittel@gmx.de>
This commit is contained in:
Christian Kittel 2020-12-08 02:23:04 +01:00 committed by GitHub
parent 20e03c257a
commit 8d389b7e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 620 additions and 83 deletions

View File

@ -1,8 +1,6 @@
# OJElectronics Binding # 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. With this binding it is possible to connect [OWD5/MWD5 Thermostat](https://ojelectronics.com/floorheating/products/wifi-thermostat-owd5/) of OJ Electronics.
At this moment all information is read only.
## Supported Things ## Supported Things
@ -15,9 +13,7 @@ There are two things:
## Discovery ## Discovery
Not supported at the moment. After the ojcloud bridge is succesfully initialized all thermostats will be discovered.
## Thing Configuration
### OJ Electronics Bridge configuration (ojcloud) ### 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 | | 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 | | 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 | | 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 ## Example

View File

@ -44,4 +44,6 @@ public class BindingConstants {
public static final String CHANNEL_OWD5_BOOSTENDTIME = "boostEndTime"; public static final String CHANNEL_OWD5_BOOSTENDTIME = "boostEndTime";
public static final String CHANNEL_OWD5_MANUALSETPOINT = "manualSetpoint"; public static final String CHANNEL_OWD5_MANUALSETPOINT = "manualSetpoint";
public static final String CHANNEL_OWD5_VACATIONENABLED = "vacationEnabled"; public static final String CHANNEL_OWD5_VACATIONENABLED = "vacationEnabled";
public static final String CHANNEL_OWD5_VACATIONBEGINDAY = "vacationBeginDay";
public static final String CHANNEL_OWD5_VACATIONENDDAY = "vacationEndDay";
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.ojelectronics.internal; package org.openhab.binding.ojelectronics.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -20,15 +22,18 @@ import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration; import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration;
import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel; 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.RefreshGroupContentService;
import org.openhab.binding.ojelectronics.internal.services.RefreshService; import org.openhab.binding.ojelectronics.internal.services.RefreshService;
import org.openhab.binding.ojelectronics.internal.services.SignInService; 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.Bridge;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.BridgeHandler; import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -45,10 +50,18 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
private final HttpClient httpClient; private final HttpClient httpClient;
private @Nullable RefreshService refreshService; private @Nullable RefreshService refreshService;
private @Nullable UpdateService updateService;
private @Nullable SignInService signInService; private @Nullable SignInService signInService;
private OJElectronicsBridgeConfiguration configuration; private OJElectronicsBridgeConfiguration configuration;
private @Nullable ScheduledFuture<?> signTask; 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) { public OJCloudHandler(Bridge bridge, HttpClient httpClient) {
super(bridge); super(bridge);
this.httpClient = httpClient; this.httpClient = httpClient;
@ -100,9 +113,8 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
private void handleRefreshDone(@Nullable GroupContentResponseModel groupContentResponse, private void handleRefreshDone(@Nullable GroupContentResponseModel groupContentResponse,
@Nullable String errorMessage) { @Nullable String errorMessage) {
logger.trace("OJElectronicsCloudHandler.handleRefreshDone({})", groupContentResponse); logger.trace("OJElectronicsCloudHandler.handleRefreshDone({})", groupContentResponse);
if (groupContentResponse != null && groupContentResponse.errorCode == 0) { if (groupContentResponse != null && groupContentResponse.errorCode == 0) {
new RefreshGroupContentService(groupContentResponse.groupContents, getThing().getThings()).handle(); internalRefreshDone(groupContentResponse);
} else { } else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
(errorMessage == null) ? "Wrong or no result model; Refreshing stoppped" : errorMessage); (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) { private void handleSignInDone(String sessionId) {
logger.trace("OJElectronicsCloudHandler.handleSignInDone({})", sessionId); logger.trace("OJElectronicsCloudHandler.handleSignInDone({})", sessionId);
if (refreshService == null) { if (refreshService == null) {
@ -125,9 +149,11 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
} }
this.updateService = new UpdateService(configuration, httpClient, sessionId);
} }
private void handleUnauthorized() { private void handleUnauthorized() {
logger.trace("OJElectronicsCloudHandler.handleUnauthorized()");
final RefreshService refreshService = this.refreshService; final RefreshService refreshService = this.refreshService;
if (refreshService != null) { if (refreshService != null) {
refreshService.stop(); refreshService.stop();
@ -136,6 +162,7 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
} }
private void handleUnauthorizedWhileSignIn() { private void handleUnauthorizedWhileSignIn() {
logger.trace("OJElectronicsCloudHandler.handleUnauthorizedWhileSignIn()");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Could not sign in. Check user name and password."); "Could not sign in. Check user name and password.");
final RefreshService refreshService = this.refreshService; final RefreshService refreshService = this.refreshService;
@ -145,6 +172,7 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
} }
private void handleConnectionLost() { private void handleConnectionLost() {
logger.trace("OJElectronicsCloudHandler.handleConnectionLost()");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
final RefreshService refreshService = this.refreshService; final RefreshService refreshService = this.refreshService;
if (refreshService != null) { if (refreshService != null) {
@ -156,4 +184,13 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
private void restartRefreshServiceAsync(long delayInSeconds) { private void restartRefreshServiceAsync(long delayInSeconds) {
signTask = scheduler.schedule(this::ensureSignIn, delayInSeconds, TimeUnit.SECONDS); signTask = scheduler.schedule(this::ensureSignIn, delayInSeconds, TimeUnit.SECONDS);
} }
public void setDiscoveryService(OJDiscoveryService ojDiscoveryService) {
this.discoveryService = ojDiscoveryService;
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(OJDiscoveryService.class);
}
} }

View File

@ -12,9 +12,13 @@
*/ */
package org.openhab.binding.ojelectronics.internal; package org.openhab.binding.ojelectronics.internal;
import java.time.ZoneId;
import java.time.ZonedDateTime; 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.HashMap;
import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; 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.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ojelectronics.internal.config.OJElectronicsThermostatConfiguration; 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.DateTimeType;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OpenClosedType; 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.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType; import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* The {@link ThermostatHandler} is responsible for handling commands, which are * The {@link ThermostatHandler} is responsible for handling commands, which are
@ -46,19 +53,28 @@ import org.openhab.core.types.RefreshType;
@NonNullByDefault @NonNullByDefault
public class ThermostatHandler extends BaseThingHandler { public class ThermostatHandler extends BaseThingHandler {
private final String serialNumber;
private @Nullable Thermostat currentThermostat;
private static final Map<Integer, String> REGULATION_MODES = createRegulationMap(); private static final Map<Integer, String> REGULATION_MODES = createRegulationMap();
private static final Map<String, Integer> REVERSE_REGULATION_MODES = createRegulationReverseMap();
private final String serialNumber;
private final Logger logger = LoggerFactory.getLogger(ThermostatHandler.class);
private final Map<String, Consumer<Thermostat>> channelrefreshActions = createChannelRefreshActionMap(); private final Map<String, Consumer<Thermostat>> channelrefreshActions = createChannelRefreshActionMap();
private final Map<String, Consumer<Command>> updateThermostatValueActions = createUpdateThermostatValueActionMap();
private final TimeZoneProvider timeZoneProvider;
private LinkedList<AbstractMap.SimpleImmutableEntry<String, Command>> updatedValues = new LinkedList<>();
private @Nullable Thermostat currentThermostat;
/** /**
* Creates a new instance of {@link ThermostatHandler} * Creates a new instance of {@link ThermostatHandler}
* *
* @param thing Thing * @param thing Thing
* @param timeZoneProvider Time zone
*/ */
public ThermostatHandler(Thing thing) { public ThermostatHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
super(thing); super(thing);
serialNumber = getConfigAs(OJElectronicsThermostatConfiguration.class).serialNumber; serialNumber = getConfigAs(OJElectronicsThermostatConfiguration.class).serialNumber;
this.timeZoneProvider = timeZoneProvider;
} }
/** /**
@ -78,7 +94,14 @@ public class ThermostatHandler extends BaseThingHandler {
if (command instanceof RefreshType) { if (command instanceof RefreshType) {
final Thermostat thermostat = currentThermostat; final Thermostat thermostat = currentThermostat;
if (thermostat != null && channelrefreshActions.containsKey(channelUID.getId())) { if (thermostat != null && channelrefreshActions.containsKey(channelUID.getId())) {
channelrefreshActions.get(channelUID.getId()).accept(thermostat); final @Nullable Consumer<Thermostat> consumer = channelrefreshActions.get(channelUID.getId());
if (consumer != null) {
consumer.accept(thermostat);
}
}
} else {
synchronized (this) {
updatedValues.add(new AbstractMap.SimpleImmutableEntry<String, Command>(channelUID.getId(), command));
} }
} }
} }
@ -101,19 +124,65 @@ public class ThermostatHandler extends BaseThingHandler {
channelrefreshActions.forEach((channelUID, action) -> action.accept(thermostat)); 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<SimpleImmutableEntry<String, Command>> updatedValues = this.updatedValues;
if (updatedValues.size() == 0) {
return null;
}
this.updatedValues = new LinkedList<>();
updatedValues.forEach(item -> {
if (updateThermostatValueActions.containsKey(item.getKey())) {
final @Nullable Consumer<Command> consumer = updateThermostatValueActions.get(item.getKey());
if (consumer != null) {
consumer.accept(item.getValue());
}
}
});
return currentThermostat;
}
private void updateManualSetpoint(Thermostat thermostat) { private void updateManualSetpoint(Thermostat thermostat) {
updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT,
new QuantityType<Temperature>(thermostat.manualModeSetpoint / (double) 100, SIUnits.CELSIUS)); new QuantityType<Temperature>(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) { private void updateBoostEndTime(Thermostat thermostat) {
updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, new DateTimeType(
new DateTimeType(ZonedDateTime.ofInstant(thermostat.boostEndTime.toInstant(), ZoneId.systemDefault()))); 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) { private void updateComfortEndTime(Thermostat thermostat) {
updateState(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, new DateTimeType( 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) { private void updateComfortSetpoint(Thermostat thermostat) {
@ -121,11 +190,30 @@ public class ThermostatHandler extends BaseThingHandler {
new QuantityType<Temperature>(thermostat.comfortSetpoint / (double) 100, SIUnits.CELSIUS)); new QuantityType<Temperature>(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) { private void updateRegulationMode(Thermostat thermostat) {
updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE,
StringType.valueOf(getRegulationMode(thermostat.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) { private void updateThermostatName(Thermostat thermostat) {
updateState(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, StringType.valueOf(thermostat.thermostatName)); 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)); 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) { private @Nullable String getRegulationMode(int regulationMode) {
return REGULATION_MODES.get(regulationMode); return REGULATION_MODES.get(regulationMode);
} }
@ -174,6 +299,18 @@ public class ThermostatHandler extends BaseThingHandler {
return map; return map;
}; };
private static Map<String, Integer> createRegulationReverseMap() {
HashMap<String, Integer> 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<String, Consumer<Thermostat>> createChannelRefreshActionMap() { private Map<String, Consumer<Thermostat>> createChannelRefreshActionMap() {
HashMap<String, Consumer<Thermostat>> map = new HashMap<>(); HashMap<String, Consumer<Thermostat>> map = new HashMap<>();
map.put(BindingConstants.CHANNEL_OWD5_GROUPNAME, this::updateGroupName); 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_COMFORTENDTIME, this::updateComfortEndTime);
map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime); map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime);
map.put(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, this::updateManualSetpoint); 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<String, Consumer<Command>> createUpdateThermostatValueActionMap() {
HashMap<String, Consumer<Command>> 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; return map;
} }
} }

View File

@ -19,12 +19,15 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory; 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.Component;
import org.osgi.service.component.annotations.Reference;
/** /**
* The {@link ThermostatHandlerFactory} is responsible for creating {@link OJElectronicsThermostatHandler}. * 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 { public class ThermostatHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OWD5); private static final Set<ThingTypeUID> 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. * Supported things of this factory.
@ -50,7 +64,7 @@ public class ThermostatHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_OWD5.equals(thingTypeUID)) { if (THING_TYPE_OWD5.equals(thingTypeUID)) {
return new ThermostatHandler(thing); return new ThermostatHandler(thing, timeZoneProvider);
} }
return null; return null;

View File

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

View File

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

View File

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

View File

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

View File

@ -10,12 +10,13 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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 java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ojelectronics.internal.models.groups.Schedule;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;

View File

@ -17,6 +17,7 @@ import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ojelectronics.internal.models.Thermostat;
/** /**
* Model for content of a group * Model for content of a group

View File

@ -16,6 +16,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ojelectronics.internal.models.ResponseModelBase;
/** /**
* Model for the response of a content group * Model for the response of a content group
@ -23,9 +24,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
* @author Christian Kittel - Initial contribution * @author Christian Kittel - Initial contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class GroupContentResponseModel { public class GroupContentResponseModel extends ResponseModelBase {
public List<GroupContent> groupContents = new ArrayList<GroupContent>(); public List<GroupContent> groupContents = new ArrayList<GroupContent>();
public int errorCode;
} }

View File

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

View File

@ -13,8 +13,7 @@
package org.openhab.binding.ojelectronics.internal.models.userprofile; package org.openhab.binding.ojelectronics.internal.models.userprofile;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ojelectronics.internal.models.RequestModelBase;
import com.google.gson.annotations.SerializedName;
/** /**
* Model for signing sin * Model for signing sin
@ -22,10 +21,7 @@ import com.google.gson.annotations.SerializedName;
* @author Christian Kittel - Initial contribution * @author Christian Kittel - Initial contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class PostSignInQueryModel { public class PostSignInQueryModel extends RequestModelBase {
@SerializedName("APIKEY")
public String apiKey = "";
public String userName = ""; public String userName = "";
@ -35,17 +31,6 @@ public class PostSignInQueryModel {
public int clientSWVersion; 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 * Add User-Name
* *

View File

@ -13,6 +13,7 @@
package org.openhab.binding.ojelectronics.internal.models.userprofile; package org.openhab.binding.ojelectronics.internal.models.userprofile;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ojelectronics.internal.models.ResponseModelBase;
/** /**
* Response-Model after signing in * Response-Model after signing in
@ -20,26 +21,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
* @author Christian Kittel - Initial Contribution * @author Christian Kittel - Initial Contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class PostSignInResponseModel { public class PostSignInResponseModel extends ResponseModelBase {
public String sessionId = ""; public String sessionId = "";
public String userName = ""; 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;
}
} }

View File

@ -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<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OJCLOUD);
private @Nullable OJCloudHandler bridgeHandler;
private @Nullable Collection<GroupContent> 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<GroupContent> groupContents) {
this.groupContents = groupContents;
}
@Override
protected void startScan() {
final OJCloudHandler bridgeHandler = this.bridgeHandler;
final Collection<GroupContent> 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());
}
}

View File

@ -16,8 +16,8 @@ import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ojelectronics.internal.ThermostatHandler; 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.GroupContent;
import org.openhab.binding.ojelectronics.internal.models.groups.Thermostat;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
/** /**

View File

@ -25,14 +25,13 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; 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.config.OJElectronicsBridgeConfiguration;
import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel; import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
/** /**
@ -46,7 +45,7 @@ public final class RefreshService implements AutoCloseable {
private final OJElectronicsBridgeConfiguration config; private final OJElectronicsBridgeConfiguration config;
private final Logger logger = LoggerFactory.getLogger(RefreshService.class); private final Logger logger = LoggerFactory.getLogger(RefreshService.class);
private final HttpClient httpClient; private final HttpClient httpClient;
private final Gson gson = createGson(); private final Gson gson = OJGSonBuilder.getGSon();
private final ScheduledExecutorService schedulerService; private final ScheduledExecutorService schedulerService;
@ -62,6 +61,7 @@ public final class RefreshService implements AutoCloseable {
* *
* @param config Configuration of the bridge * @param config Configuration of the bridge
* @param httpClient HTTP client * @param httpClient HTTP client
* @param updateService Service to update the thermostat in the cloud
*/ */
public RefreshService(OJElectronicsBridgeConfiguration config, HttpClient httpClient, public RefreshService(OJElectronicsBridgeConfiguration config, HttpClient httpClient,
ScheduledExecutorService schedulerService) { ScheduledExecutorService schedulerService) {
@ -103,11 +103,6 @@ public final class RefreshService implements AutoCloseable {
this.scheduler = null; 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() { private void refresh() {
final String sessionId = this.sessionId; final String sessionId = this.sessionId;
if (sessionId == null) { if (sessionId == null) {
@ -124,6 +119,8 @@ public final class RefreshService implements AutoCloseable {
if (unauthorized != null) { if (unauthorized != null) {
unauthorized.run(); unauthorized.run();
} }
} else if (result.getResponse().getStatus() == HttpStatus.FORBIDDEN_403) {
handleConnectionLost();
} else { } else {
handleRefreshDone(getContentAsString()); handleRefreshDone(getContentAsString());
} }

View File

@ -22,13 +22,13 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader; 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.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.PostSignInQueryModel;
import org.openhab.binding.ojelectronics.internal.models.userprofile.PostSignInResponseModel; import org.openhab.binding.ojelectronics.internal.models.userprofile.PostSignInResponseModel;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/** /**
* Handles the sign in process. * Handles the sign in process.
@ -38,7 +38,7 @@ import com.google.gson.GsonBuilder;
@NonNullByDefault @NonNullByDefault
public class SignInService { public class SignInService {
private final Gson gson = createGson(); private final Gson gson = OJGSonBuilder.getGSon();
private final HttpClient httpClient; private final HttpClient httpClient;
private final OJElectronicsBridgeConfiguration config; private final OJElectronicsBridgeConfiguration config;
@ -79,7 +79,7 @@ public class SignInService {
} }
PostSignInResponseModel signInModel = gson.fromJson(getContentAsString(), PostSignInResponseModel signInModel = gson.fromJson(getContentAsString(),
PostSignInResponseModel.class); PostSignInResponseModel.class);
if (signInModel.errorCode != 0 || signInModel.sessionId.equals("")) { if (signInModel == null || signInModel.errorCode != 0 || signInModel.sessionId.equals("")) {
unauthorized.run(); unauthorized.run();
return; return;
} }
@ -88,12 +88,8 @@ public class SignInService {
}); });
} }
private Gson createGson() { private RequestModelBase getPostSignInQueryModel() {
return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create(); return new PostSignInQueryModel().withClientSWVersion(config.softwareVersion).withCustomerId(config.customerId)
} .withUserName(config.userName).withPassword(config.password).withApiKey(config.apiKey);
private PostSignInQueryModel getPostSignInQueryModel() {
return new PostSignInQueryModel().withApiKey(config.apiKey).withClientSWVersion(config.softwareVersion)
.withCustomerId(config.customerId).withUserName(config.userName).withPassword(config.password);
} }
} }

View File

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

View File

@ -36,3 +36,5 @@ channel-type.ojelectronics.comfortEndTime.label = Komfort-Endzeit
channel-type.ojelectronics.boostEndTime.label = Boost Endzeit channel-type.ojelectronics.boostEndTime.label = Boost Endzeit
channel-type.ojelectronics.manualSetpoint.label = manuelle Endzeit channel-type.ojelectronics.manualSetpoint.label = manuelle Endzeit
channel-type.ojelectronics.vacationEnabled.label = Urlausbmodus aktiviert channel-type.ojelectronics.vacationEnabled.label = Urlausbmodus aktiviert
channel-type.ojelectronics.vacationBeginDay.label = Start des Urlausbmodus
channel-type.ojelectronics.vacationEndDay.label = Ende des Urlausbmodus

View File

@ -68,10 +68,13 @@
<channel id="boostEndTime" typeId="boostEndTime"/> <channel id="boostEndTime" typeId="boostEndTime"/>
<channel id="manualSetpoint" typeId="manualSetpoint"/> <channel id="manualSetpoint" typeId="manualSetpoint"/>
<channel id="vacationEnabled" typeId="vacationEnabled"/> <channel id="vacationEnabled" typeId="vacationEnabled"/>
<channel id="vacationBeginDay" typeId="vacationBeginDay"/>
<channel id="vacationEndDay" typeId="vacationEndDay"/>
</channels> </channels>
<properties> <properties>
<property name="vendor">OJ Electronics</property> <property name="vendor">OJ Electronics</property>
</properties> </properties>
<representation-property>serialNumber</representation-property>
<config-description> <config-description>
<parameter name="serialNumber" type="text" required="true"> <parameter name="serialNumber" type="text" required="true">
<label>Serial Number</label> <label>Serial Number</label>
@ -119,7 +122,7 @@
<channel-type id="regulationMode"> <channel-type id="regulationMode">
<item-type>String</item-type> <item-type>String</item-type>
<label>Regulation Mode</label> <label>Regulation Mode</label>
<state readOnly="true"> <state>
<options> <options>
<option value="auto">Auto</option> <option value="auto">Auto</option>
<option value="comfort">Comfort</option> <option value="comfort">Comfort</option>
@ -135,27 +138,33 @@
<item-type>Number:Temperature</item-type> <item-type>Number:Temperature</item-type>
<label>Comfort Set Point Temperature</label> <label>Comfort Set Point Temperature</label>
<category>Temperature</category> <category>Temperature</category>
<state pattern="%.1f %unit%" readOnly="true"/> <state pattern="%.1f %unit%"/>
</channel-type> </channel-type>
<channel-type id="comfortEndTime"> <channel-type id="comfortEndTime">
<item-type>DateTime</item-type> <item-type>DateTime</item-type>
<label>End Time of Comfort Mode</label> <label>End Time of Comfort Mode</label>
<state readOnly="true"/>
</channel-type> </channel-type>
<channel-type id="boostEndTime"> <channel-type id="boostEndTime">
<item-type>DateTime</item-type> <item-type>DateTime</item-type>
<label>End Time of Boost Mode</label> <label>End Time of Boost Mode</label>
<state readOnly="true"/>
</channel-type> </channel-type>
<channel-type id="manualSetpoint"> <channel-type id="manualSetpoint">
<item-type>Number:Temperature</item-type> <item-type>Number:Temperature</item-type>
<label>Manual Set Point Temperature</label> <label>Manual Set Point Temperature</label>
<category>Temperature</category> <category>Temperature</category>
<state pattern="%.1f %unit%" readOnly="true"/> <state pattern="%.1f %unit%"/>
</channel-type> </channel-type>
<channel-type id="vacationEnabled"> <channel-type id="vacationEnabled">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Vacation Mode Enabled</label> <label>Vacation Mode Enabled</label>
<state readOnly="true"/> <state readOnly="true"/>
</channel-type> </channel-type>
<channel-type id="vacationBeginDay">
<item-type>Switch</item-type>
<label>Vacation Begin Day</label>
</channel-type>
<channel-type id="vacationEndDay">
<item-type>Switch</item-type>
<label>Vacation End Day</label>
</channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>