mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[OJElectronics] Add SignalR for requesting data from OJ Electronics cloud (#13782)
* Fixed some Nullable annotation warnings * Use SignalR instead pooling Signed-off-by: Christian Kittel <ckittel@gmx.de>
This commit is contained in:
parent
6748dfedd7
commit
64723db7aa
@ -11,3 +11,8 @@ https://www.eclipse.org/legal/epl-2.0/.
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
|
||||
Signalr4j
|
||||
* License: Apache 2.0 License
|
||||
* Project: https://github.com/sputnikdev/bluetooth-gatt-parser
|
||||
* Source: https://github.com/sputnikdev/bluetooth-gatt-parser
|
||||
|
@ -23,7 +23,6 @@ After the ojcloud bridge is successfully initialized all thermostats will be dis
|
||||
| password | password from the OJElectronics App (required) |
|
||||
| apiKey | API key. You get the key from your local distributor. |
|
||||
| apiUrl | URL of the API endpoint. Optional, the default value should always work. |
|
||||
| refreshDelayInSeconds | Refresh interval in seconds. Optional, the default value is 30 seconds. |
|
||||
| customerId | Customer ID. Optional, the default value should always work. |
|
||||
| softwareVersion | Software version. Optional, the default value should always work. |
|
||||
|
||||
|
@ -13,4 +13,21 @@
|
||||
<artifactId>org.openhab.binding.ojelectronics</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: OJElectronics Binding</name>
|
||||
|
||||
<dependencies>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.signalr4j/signalr4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.signalr4j</groupId>
|
||||
<artifactId>signalr4j</artifactId>
|
||||
<version>2.0.4</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket -->
|
||||
<dependency>
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -13,7 +13,7 @@
|
||||
package org.openhab.binding.ojelectronics.internal;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -21,10 +21,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
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.SignalRResultModel;
|
||||
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.RefreshThermostatsService;
|
||||
import org.openhab.binding.ojelectronics.internal.services.SignInService;
|
||||
import org.openhab.binding.ojelectronics.internal.services.UpdateService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
@ -32,7 +34,6 @@ 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;
|
||||
@ -44,7 +45,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Christian Kittel - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
public class OJCloudHandler extends BaseBridgeHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OJCloudHandler.class);
|
||||
private final HttpClient httpClient;
|
||||
@ -54,6 +55,7 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
private @Nullable SignInService signInService;
|
||||
private OJElectronicsBridgeConfiguration configuration;
|
||||
private @Nullable ScheduledFuture<?> signTask;
|
||||
private @Nullable ScheduledFuture<?> updateTask;
|
||||
private @Nullable OJDiscoveryService discoveryService;
|
||||
|
||||
/**
|
||||
@ -82,9 +84,9 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
final RefreshService refreshService = this.refreshService;
|
||||
if (refreshService != null) {
|
||||
refreshService.stop();
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
final ScheduledFuture<?> signTask = this.signTask;
|
||||
if (signTask != null) {
|
||||
@ -99,64 +101,90 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
public synchronized void updateThinksChannelValuesToCloud() {
|
||||
final UpdateService localUpdateService = this.updateService;
|
||||
if (localUpdateService != null) {
|
||||
final ScheduledFuture<?> localUpdateTask = this.updateTask;
|
||||
if (localUpdateTask != null) {
|
||||
localUpdateTask.cancel(false);
|
||||
}
|
||||
this.updateTask = scheduler.schedule(() -> {
|
||||
localUpdateService.updateAllThermostats(getThing().getThings());
|
||||
this.updateTask = null;
|
||||
}, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureSignIn() {
|
||||
if (signInService == null) {
|
||||
signInService = new SignInService(configuration, httpClient);
|
||||
}
|
||||
final SignInService signInService = this.signInService;
|
||||
if (signInService != null) {
|
||||
signInService.signIn(this::handleSignInDone, this::handleConnectionLost,
|
||||
final SignInService localSignInService = this.signInService;
|
||||
if (localSignInService != null) {
|
||||
localSignInService.signIn(this::handleSignInDone, this::handleConnectionLost,
|
||||
this::handleUnauthorizedWhileSignIn);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRefreshDone(@Nullable GroupContentResponseModel groupContentResponse,
|
||||
private void initializationDone(@Nullable GroupContentResponseModel groupContentResponse,
|
||||
@Nullable String errorMessage) {
|
||||
logger.trace("OJElectronicsCloudHandler.handleRefreshDone({})", groupContentResponse);
|
||||
logger.trace("OJElectronicsCloudHandler.initializationDone({})", groupContentResponse);
|
||||
if (groupContentResponse != null && groupContentResponse.errorCode == 0) {
|
||||
internalRefreshDone(groupContentResponse);
|
||||
internalInitializationDone(groupContentResponse);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
(errorMessage == null) ? "Wrong or no result model; Refreshing stoppped" : errorMessage);
|
||||
final RefreshService refreshService = this.refreshService;
|
||||
if (refreshService != null) {
|
||||
refreshService.stop();
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void internalRefreshDone(GroupContentResponseModel groupContentResponse) {
|
||||
new RefreshGroupContentService(groupContentResponse.groupContents, getThing().getThings()).handle();
|
||||
final OJDiscoveryService discoveryService = this.discoveryService;
|
||||
if (discoveryService != null) {
|
||||
discoveryService.setScanResultForDiscovery(groupContentResponse.groupContents);
|
||||
private void refreshDone(@Nullable SignalRResultModel resultModel, @Nullable String errorMessage) {
|
||||
logger.trace("OJElectronicsCloudHandler.refreshDone({})", resultModel);
|
||||
if (resultModel != null) {
|
||||
new RefreshThermostatsService(resultModel.getThermostats(), resultModel.getThermostatRealTimes(),
|
||||
getThing().getThings()).handle();
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
(errorMessage == null) ? "Wrong or no result model; Refreshing stoppped" : errorMessage);
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
}
|
||||
final UpdateService updateService = this.updateService;
|
||||
if (updateService != null) {
|
||||
updateService.updateAllThermostats(getThing().getThings());
|
||||
}
|
||||
|
||||
private void internalInitializationDone(GroupContentResponseModel groupContentResponse) {
|
||||
new RefreshGroupContentService(groupContentResponse.groupContents, getThing().getThings()).handle();
|
||||
final OJDiscoveryService localDiscoveryService = this.discoveryService;
|
||||
if (localDiscoveryService != null) {
|
||||
localDiscoveryService.setScanResultForDiscovery(groupContentResponse.groupContents);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSignInDone(String sessionId) {
|
||||
logger.trace("OJElectronicsCloudHandler.handleSignInDone({})", sessionId);
|
||||
if (refreshService == null) {
|
||||
refreshService = new RefreshService(configuration, httpClient, scheduler);
|
||||
refreshService = new RefreshService(configuration, httpClient);
|
||||
}
|
||||
final RefreshService refreshService = this.refreshService;
|
||||
if (refreshService != null) {
|
||||
refreshService.start(sessionId, this::handleRefreshDone, this::handleConnectionLost,
|
||||
this::handleUnauthorized);
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.start(sessionId, this::initializationDone, this::refreshDone,
|
||||
this::handleConnectionLost, this::handleUnauthorized);
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
this.updateService = new UpdateService(configuration, httpClient, sessionId);
|
||||
this.updateService = new UpdateService(configuration, httpClient, this::handleConnectionLost,
|
||||
this::handleUnauthorized);
|
||||
}
|
||||
|
||||
private void handleUnauthorized() {
|
||||
logger.trace("OJElectronicsCloudHandler.handleUnauthorized()");
|
||||
final RefreshService refreshService = this.refreshService;
|
||||
if (refreshService != null) {
|
||||
refreshService.stop();
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
restartRefreshServiceAsync(1);
|
||||
}
|
||||
@ -165,20 +193,29 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
logger.trace("OJElectronicsCloudHandler.handleUnauthorizedWhileSignIn()");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Could not sign in. Check user name and password.");
|
||||
final RefreshService refreshService = this.refreshService;
|
||||
if (refreshService != null) {
|
||||
refreshService.stop();
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnectionLost() {
|
||||
logger.trace("OJElectronicsCloudHandler.handleConnectionLost()");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
final RefreshService refreshService = this.refreshService;
|
||||
if (refreshService != null) {
|
||||
refreshService.stop();
|
||||
public void reInitialize() {
|
||||
logger.trace("OJElectronicsCloudHandler.reInitialize()");
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
restartRefreshServiceAsync(configuration.refreshDelayInSeconds);
|
||||
restartRefreshServiceAsync(1);
|
||||
}
|
||||
|
||||
private void handleConnectionLost(@Nullable String message) {
|
||||
logger.trace("OJElectronicsCloudHandler.handleConnectionLost()");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
|
||||
final RefreshService localRefreshService = this.refreshService;
|
||||
if (localRefreshService != null) {
|
||||
localRefreshService.stop();
|
||||
}
|
||||
restartRefreshServiceAsync(30);
|
||||
}
|
||||
|
||||
private void restartRefreshServiceAsync(long delayInSeconds) {
|
||||
@ -191,6 +228,6 @@ public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(OJDiscoveryService.class);
|
||||
return Set.of(OJDiscoveryService.class);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ package org.openhab.binding.ojelectronics.internal;
|
||||
|
||||
import static org.openhab.binding.ojelectronics.internal.BindingConstants.THING_TYPE_OJCLOUD;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -40,7 +39,7 @@ import org.osgi.service.component.annotations.Reference;
|
||||
@Component(configurationPid = "binding.ojelectronics", service = ThingHandlerFactory.class)
|
||||
public class OJCloudHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OJCLOUD);
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_OJCLOUD);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
|
@ -20,6 +20,7 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
@ -27,7 +28,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.Thermostat;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatModel;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatRealTimeValuesModel;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -35,10 +37,12 @@ import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
@ -58,12 +62,13 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
|
||||
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<ThermostatModel>> channelRefreshActions = createChannelRefreshActionMap();
|
||||
private final Map<String, Consumer<ThermostatRealTimeValuesModel>> channelRealTimeRefreshActions = createRealTimeChannelRefreshActionMap();
|
||||
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;
|
||||
private @Nullable ThermostatModel currentThermostat;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link ThermostatHandler}
|
||||
@ -92,9 +97,9 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
final Thermostat thermostat = currentThermostat;
|
||||
if (thermostat != null && channelrefreshActions.containsKey(channelUID.getId())) {
|
||||
final @Nullable Consumer<Thermostat> consumer = channelrefreshActions.get(channelUID.getId());
|
||||
final ThermostatModel thermostat = currentThermostat;
|
||||
if (thermostat != null && channelRefreshActions.containsKey(channelUID.getId())) {
|
||||
final @Nullable Consumer<ThermostatModel> consumer = channelRefreshActions.get(channelUID.getId());
|
||||
if (consumer != null) {
|
||||
consumer.accept(thermostat);
|
||||
}
|
||||
@ -102,6 +107,14 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
} else {
|
||||
synchronized (this) {
|
||||
updatedValues.add(new AbstractMap.SimpleImmutableEntry<String, Command>(channelUID.getId(), command));
|
||||
|
||||
BridgeHandler bridgeHandler = Objects.requireNonNull(getBridge()).getHandler();
|
||||
if (bridgeHandler != null) {
|
||||
((OJCloudHandler) (bridgeHandler)).updateThinksChannelValuesToCloud();
|
||||
} else {
|
||||
currentThermostat = null;
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,7 +124,15 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
@Nullable
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE && currentThermostat == null) {
|
||||
@Nullable
|
||||
OJCloudHandler bridgeHandler = (OJCloudHandler) (bridge.getHandler());
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.reInitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,17 +140,37 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
*
|
||||
* @param thermostat thermostat values
|
||||
*/
|
||||
public void handleThermostatRefresh(Thermostat thermostat) {
|
||||
public void handleThermostatRefresh(ThermostatModel thermostat) {
|
||||
if (currentThermostat == null) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
currentThermostat = thermostat;
|
||||
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
|
||||
* Sets the values after refreshing the thermostats values
|
||||
*
|
||||
* @return The changed {@link Thermostat}
|
||||
* @param thermostat thermostat values
|
||||
*/
|
||||
public @Nullable Thermostat tryHandleAndGetUpdatedThermostat() {
|
||||
public void handleThermostatRefresh(ThermostatRealTimeValuesModel thermostat) {
|
||||
final ThermostatModel currentThermostat = this.currentThermostat;
|
||||
if (currentThermostat != null) {
|
||||
currentThermostat.heating = thermostat.heating;
|
||||
currentThermostat.floorTemperature = thermostat.floorTemperature;
|
||||
currentThermostat.action = thermostat.action;
|
||||
currentThermostat.online = thermostat.online;
|
||||
currentThermostat.roomTemperature = thermostat.roomTemperature;
|
||||
channelRealTimeRefreshActions.forEach((channelUID, action) -> action.accept(thermostat));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link ThermostatModel} with changed values or null if nothing has changed
|
||||
*
|
||||
* @return The changed {@link ThermostatModel}
|
||||
*/
|
||||
public @Nullable ThermostatModel tryHandleAndGetUpdatedThermostat() {
|
||||
final LinkedList<SimpleImmutableEntry<String, Command>> updatedValues = this.updatedValues;
|
||||
if (updatedValues.isEmpty()) {
|
||||
return null;
|
||||
@ -146,59 +187,64 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
return currentThermostat;
|
||||
}
|
||||
|
||||
private void updateManualSetpoint(Thermostat thermostat) {
|
||||
private ThermostatModel getCurrentThermostat() {
|
||||
return Objects.requireNonNull(currentThermostat);
|
||||
}
|
||||
|
||||
private void updateManualSetpoint(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT,
|
||||
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);
|
||||
getCurrentThermostat().manualModeSetpoint = (int) (((QuantityType<?>) command).floatValue() * 100);
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBoostEndTime(Thermostat thermostat) {
|
||||
private void updateBoostEndTime(ThermostatModel thermostat) {
|
||||
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());
|
||||
getCurrentThermostat().boostEndTime = Date.from(((DateTimeType) command).getZonedDateTime().toInstant());
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateComfortEndTime(Thermostat thermostat) {
|
||||
private void updateComfortEndTime(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, new DateTimeType(
|
||||
ZonedDateTime.ofInstant(thermostat.comfortEndTime.toInstant(), timeZoneProvider.getTimeZone())));
|
||||
}
|
||||
|
||||
private void updateComfortEndTime(Command command) {
|
||||
if (command instanceof DateTimeType) {
|
||||
currentThermostat.comfortEndTime = Date.from(((DateTimeType) command).getZonedDateTime().toInstant());
|
||||
getCurrentThermostat().comfortEndTime = Objects
|
||||
.requireNonNull(Date.from(((DateTimeType) command).getZonedDateTime().toInstant()));
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateComfortSetpoint(Thermostat thermostat) {
|
||||
private void updateComfortSetpoint(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT,
|
||||
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);
|
||||
getCurrentThermostat().comfortSetpoint = (int) (((QuantityType<?>) command).floatValue() * 100);
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRegulationMode(Thermostat thermostat) {
|
||||
private void updateRegulationMode(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE,
|
||||
StringType.valueOf(getRegulationMode(thermostat.regulationMode)));
|
||||
}
|
||||
@ -207,51 +253,71 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
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;
|
||||
getCurrentThermostat().regulationMode = mode;
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateThermostatName(Thermostat thermostat) {
|
||||
private void updateThermostatName(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, StringType.valueOf(thermostat.thermostatName));
|
||||
}
|
||||
|
||||
private void updateFloorTemperature(Thermostat thermostat) {
|
||||
private void updateFloorTemperature(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE,
|
||||
new QuantityType<Temperature>(thermostat.floorTemperature / (double) 100, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
private void updateRoomTemperature(Thermostat thermostat) {
|
||||
private void updateFloorTemperature(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE, new QuantityType<Temperature>(
|
||||
thermostatRealTimeValues.floorTemperature / (double) 100, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
private void updateRoomTemperature(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE,
|
||||
new QuantityType<Temperature>(thermostat.roomTemperature / (double) 100, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
private void updateHeating(Thermostat thermostat) {
|
||||
private void updateRoomTemperature(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE, new QuantityType<Temperature>(
|
||||
thermostatRealTimeValues.roomTemperature / (double) 100, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
private void updateHeating(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_HEATING,
|
||||
thermostat.heating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||
}
|
||||
|
||||
private void updateOnline(Thermostat thermostat) {
|
||||
private void updateHeating(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_HEATING,
|
||||
thermostatRealTimeValues.heating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||
}
|
||||
|
||||
private void updateOnline(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_ONLINE,
|
||||
thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||
}
|
||||
|
||||
private void updateGroupId(Thermostat thermostat) {
|
||||
private void updateOnline(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_ONLINE,
|
||||
thermostatRealTimeValues.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||
}
|
||||
|
||||
private void updateGroupId(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_GROUPID, new DecimalType(thermostat.groupId));
|
||||
}
|
||||
|
||||
private void updateGroupName(Thermostat thermostat) {
|
||||
private void updateGroupName(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_GROUPNAME, StringType.valueOf(thermostat.groupName));
|
||||
}
|
||||
|
||||
private void updateVacationEnabled(Thermostat thermostat) {
|
||||
private void updateVacationEnabled(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_VACATIONENABLED,
|
||||
thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||
}
|
||||
|
||||
private void updateVacationBeginDay(Thermostat thermostat) {
|
||||
private void updateVacationBeginDay(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY,
|
||||
new DateTimeType(
|
||||
ZonedDateTime.ofInstant(thermostat.vacationBeginDay.toInstant(), timeZoneProvider.getTimeZone())
|
||||
@ -260,14 +326,14 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
|
||||
private void updateVacationBeginDay(Command command) {
|
||||
if (command instanceof DateTimeType) {
|
||||
currentThermostat.vacationBeginDay = Date
|
||||
getCurrentThermostat().vacationBeginDay = Date
|
||||
.from(((DateTimeType) command).getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS));
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVacationEndDay(Thermostat thermostat) {
|
||||
private void updateVacationEndDay(ThermostatModel thermostat) {
|
||||
updateState(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY,
|
||||
new DateTimeType(
|
||||
ZonedDateTime.ofInstant(thermostat.vacationEndDay.toInstant(), timeZoneProvider.getTimeZone())
|
||||
@ -276,7 +342,7 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
|
||||
private void updateVacationEndDay(Command command) {
|
||||
if (command instanceof DateTimeType) {
|
||||
currentThermostat.vacationEndDay = Date
|
||||
getCurrentThermostat().vacationEndDay = Date
|
||||
.from(((DateTimeType) command).getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS));
|
||||
} else {
|
||||
logger.warn("Unable to set value {}", command);
|
||||
@ -311,8 +377,8 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
return map;
|
||||
};
|
||||
|
||||
private Map<String, Consumer<Thermostat>> createChannelRefreshActionMap() {
|
||||
HashMap<String, Consumer<Thermostat>> map = new HashMap<>();
|
||||
private Map<String, Consumer<ThermostatModel>> createChannelRefreshActionMap() {
|
||||
HashMap<String, Consumer<ThermostatModel>> map = new HashMap<>();
|
||||
map.put(BindingConstants.CHANNEL_OWD5_GROUPNAME, this::updateGroupName);
|
||||
map.put(BindingConstants.CHANNEL_OWD5_GROUPID, this::updateGroupId);
|
||||
map.put(BindingConstants.CHANNEL_OWD5_ONLINE, this::updateOnline);
|
||||
@ -331,6 +397,15 @@ public class ThermostatHandler extends BaseThingHandler {
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Consumer<ThermostatRealTimeValuesModel>> createRealTimeChannelRefreshActionMap() {
|
||||
HashMap<String, Consumer<ThermostatRealTimeValuesModel>> map = new HashMap<>();
|
||||
map.put(BindingConstants.CHANNEL_OWD5_ONLINE, this::updateOnline);
|
||||
map.put(BindingConstants.CHANNEL_OWD5_HEATING, this::updateHeating);
|
||||
map.put(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE, this::updateRoomTemperature);
|
||||
map.put(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE, this::updateFloorTemperature);
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Consumer<Command>> createUpdateThermostatValueActionMap() {
|
||||
HashMap<String, Consumer<Command>> map = new HashMap<>();
|
||||
map.put(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, this::updateRegulationMode);
|
||||
|
@ -14,7 +14,6 @@ package org.openhab.binding.ojelectronics.internal;
|
||||
|
||||
import static org.openhab.binding.ojelectronics.internal.BindingConstants.THING_TYPE_OWD5;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -38,7 +37,7 @@ import org.osgi.service.component.annotations.Reference;
|
||||
@Component(configurationPid = "binding.ojelectronics", service = ThingHandlerFactory.class)
|
||||
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 = Set.of(THING_TYPE_OWD5);
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.common;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.signalr4j.client.LogLevel;
|
||||
import com.github.signalr4j.client.Logger;
|
||||
|
||||
/**
|
||||
* Logs SignalR information
|
||||
*
|
||||
* @author Christian Kittel - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SignalRLogger implements Logger {
|
||||
|
||||
private final org.slf4j.Logger logger = LoggerFactory.getLogger(SignalRLogger.class);
|
||||
|
||||
@Override
|
||||
public void log(@Nullable String message, @Nullable LogLevel level) {
|
||||
if (message == null || level == null) {
|
||||
return;
|
||||
}
|
||||
switch (level) {
|
||||
case Critical:
|
||||
logger.warn("Critical SignalR Message: {}", message);
|
||||
break;
|
||||
case Information:
|
||||
logger.info("SignalR information message: {}", message);
|
||||
break;
|
||||
case Verbose:
|
||||
default:
|
||||
logger.trace("SignalR information message: {}", message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
package org.openhab.binding.ojelectronics.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The configuration for {@link org.openhab.binding.ojelectronics.internal.OJElectronicsCloudHandler}
|
||||
@ -40,7 +41,7 @@ public class OJElectronicsBridgeConfiguration {
|
||||
/**
|
||||
* Url for API
|
||||
*/
|
||||
public String apiUrl = "https://OWD5-OJ001-App.ojelectronics.com/api";
|
||||
private String apiUrl = "https://OWD5-OJ001-App.ojelectronics.com";
|
||||
|
||||
/**
|
||||
* API-Key
|
||||
@ -52,8 +53,29 @@ public class OJElectronicsBridgeConfiguration {
|
||||
*/
|
||||
public int softwareVersion = 1060;
|
||||
|
||||
/**
|
||||
* Refresh-Delay
|
||||
private @Nullable String restApiUrl;
|
||||
|
||||
/*
|
||||
* Gets the Api-URL
|
||||
*/
|
||||
public long refreshDelayInSeconds = 30;
|
||||
public String getRestApiUrl() {
|
||||
String localRestApiUrl = restApiUrl;
|
||||
if (localRestApiUrl == null) {
|
||||
localRestApiUrl = restApiUrl = apiUrl.replace("/api", "") + "/api";
|
||||
}
|
||||
return localRestApiUrl;
|
||||
}
|
||||
|
||||
private @Nullable String signalRApiUrl;
|
||||
|
||||
/*
|
||||
* Gets the SignalR Notification URL
|
||||
*/
|
||||
public String getSignalRUrl() {
|
||||
String localSignalRApiUrl = signalRApiUrl;
|
||||
if (localSignalRApiUrl == null) {
|
||||
localSignalRApiUrl = signalRApiUrl = apiUrl.replace("/api", "") + "/ocd5notification";
|
||||
}
|
||||
return localSignalRApiUrl;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentModel;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatModel;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatRealTimeValuesModel;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Model for a SignalR query result
|
||||
*
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SignalRResultModel {
|
||||
@SerializedName("Groups")
|
||||
private List<GroupContentModel> groups = List.of();
|
||||
@SerializedName("SequenceNr")
|
||||
private int sequenceNr;
|
||||
|
||||
@SerializedName("ThermostatRealTimes")
|
||||
private List<ThermostatRealTimeValuesModel> thermostatRealTimes = List.of();
|
||||
|
||||
@SerializedName("Thermostats")
|
||||
private List<ThermostatModel> thermostats = List.of();
|
||||
|
||||
public List<GroupContentModel> getGroups() {
|
||||
return this.groups;
|
||||
}
|
||||
|
||||
public int getSequenceNr() {
|
||||
return this.sequenceNr;
|
||||
}
|
||||
|
||||
public List<ThermostatRealTimeValuesModel> getThermostatRealTimes() {
|
||||
return this.thermostatRealTimes;
|
||||
}
|
||||
|
||||
public List<ThermostatModel> getThermostats() {
|
||||
return this.thermostats;
|
||||
}
|
||||
|
||||
public void setGroups(List<GroupContentModel> paramArrayList) {
|
||||
this.groups = paramArrayList;
|
||||
}
|
||||
|
||||
public void setSequenceNr(int paramInt) {
|
||||
this.sequenceNr = paramInt;
|
||||
}
|
||||
|
||||
public void setThermostatRealTimes(List<ThermostatRealTimeValuesModel> paramArrayList) {
|
||||
this.thermostatRealTimes = paramArrayList;
|
||||
}
|
||||
|
||||
public void setThermostats(List<ThermostatModel> paramArrayList) {
|
||||
this.thermostats = paramArrayList;
|
||||
}
|
||||
}
|
@ -23,9 +23,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Day {
|
||||
public class DayModel {
|
||||
|
||||
public int weekDayGrpNo;
|
||||
|
||||
public List<Event> events = new ArrayList<>();
|
||||
public List<EventModel> events = new ArrayList<>();
|
||||
}
|
@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Event {
|
||||
public class EventModel {
|
||||
|
||||
public int scheduleType;
|
||||
|
@ -17,7 +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;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatModel;
|
||||
|
||||
/**
|
||||
* Model for content of a group
|
||||
@ -25,7 +25,7 @@ import org.openhab.binding.ojelectronics.internal.models.Thermostat;
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GroupContent {
|
||||
public class GroupContentModel {
|
||||
|
||||
public int action;
|
||||
|
||||
@ -33,11 +33,11 @@ public class GroupContent {
|
||||
|
||||
public String groupName = "";
|
||||
|
||||
public List<Thermostat> thermostats = new ArrayList<Thermostat>();
|
||||
public List<ThermostatModel> thermostats = new ArrayList<ThermostatModel>();
|
||||
|
||||
public int regulationMode;
|
||||
|
||||
public @Nullable Schedule schedule;
|
||||
public @Nullable ScheduleModel schedule;
|
||||
|
||||
public int comfortSetpoint;
|
||||
|
@ -26,5 +26,5 @@ import org.openhab.binding.ojelectronics.internal.models.ResponseModelBase;
|
||||
@NonNullByDefault
|
||||
public class GroupContentResponseModel extends ResponseModelBase {
|
||||
|
||||
public List<GroupContent> groupContents = new ArrayList<GroupContent>();
|
||||
public List<GroupContentModel> groupContents = new ArrayList<GroupContentModel>();
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Schedule {
|
||||
public class ScheduleModel {
|
||||
|
||||
public List<Day> days = new ArrayList<Day>();
|
||||
public List<DayModel> days = new ArrayList<DayModel>();
|
||||
|
||||
public boolean modifiedDueToVerification;
|
||||
}
|
@ -10,13 +10,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.models;
|
||||
package org.openhab.binding.ojelectronics.internal.models.thermostat;
|
||||
|
||||
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 org.openhab.binding.ojelectronics.internal.models.groups.ScheduleModel;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@ -26,14 +26,12 @@ import com.google.gson.annotations.SerializedName;
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Thermostat {
|
||||
public class ThermostatModel extends ThermostatModelBase {
|
||||
|
||||
public int id;
|
||||
|
||||
public int action;
|
||||
|
||||
public String serialNumber = "";
|
||||
|
||||
public String groupName = "";
|
||||
|
||||
public int groupId;
|
||||
@ -53,7 +51,7 @@ public class Thermostat {
|
||||
|
||||
public int regulationMode;
|
||||
|
||||
public @Nullable Schedule schedule;
|
||||
public @Nullable ScheduleModel schedule;
|
||||
|
||||
public int comfortSetpoint;
|
||||
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.models.thermostat;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Base-Model for thermostat models
|
||||
*
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ThermostatModelBase {
|
||||
|
||||
public String serialNumber = "";
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.models.thermostat;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Model for realtime values of a thermostat
|
||||
*
|
||||
* @author Christian Kittel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ThermostatRealTimeValuesModel extends ThermostatModelBase {
|
||||
|
||||
public int action;
|
||||
|
||||
public int floorTemperature;
|
||||
|
||||
public boolean heating;
|
||||
|
||||
public int id;
|
||||
|
||||
public boolean online;
|
||||
|
||||
public int roomTemperature;
|
||||
|
||||
public int sensorAppl;
|
||||
}
|
@ -14,7 +14,6 @@ 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
|
||||
@ -24,12 +23,12 @@ import org.openhab.binding.ojelectronics.internal.models.Thermostat;
|
||||
@NonNullByDefault
|
||||
public class UpdateThermostatRequestModel extends RequestModelBase {
|
||||
|
||||
public UpdateThermostatRequestModel(Thermostat thermostat) {
|
||||
public UpdateThermostatRequestModel(ThermostatModel thermostat) {
|
||||
setThermostat = thermostat;
|
||||
thermostatID = thermostat.serialNumber;
|
||||
}
|
||||
|
||||
public Thermostat setThermostat;
|
||||
public ThermostatModel setThermostat;
|
||||
|
||||
public String thermostatID;
|
||||
}
|
||||
|
@ -15,14 +15,13 @@ 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.binding.ojelectronics.internal.models.groups.GroupContentModel;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
@ -39,12 +38,11 @@ import org.osgi.service.component.annotations.Component;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.ojelectronics")
|
||||
public final class OJDiscoveryService extends AbstractDiscoveryService
|
||||
implements DiscoveryService, ThingHandlerService {
|
||||
public final class OJDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OJCLOUD);
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_OJCLOUD);
|
||||
private @Nullable OJCloudHandler bridgeHandler;
|
||||
private @Nullable Collection<GroupContent> groupContents;
|
||||
private @Nullable Collection<GroupContentModel> groupContents;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link OJDiscoveryService}
|
||||
@ -59,14 +57,14 @@ public final class OJDiscoveryService extends AbstractDiscoveryService
|
||||
*
|
||||
* @param groupContents Content from API
|
||||
*/
|
||||
public void setScanResultForDiscovery(List<GroupContent> groupContents) {
|
||||
public void setScanResultForDiscovery(List<GroupContentModel> groupContents) {
|
||||
this.groupContents = groupContents;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
final OJCloudHandler bridgeHandler = this.bridgeHandler;
|
||||
final Collection<GroupContent> groupContents = this.groupContents;
|
||||
final Collection<GroupContentModel> groupContents = this.groupContents;
|
||||
if (groupContents != null && bridgeHandler != null) {
|
||||
groupContents.stream().flatMap(content -> content.thermostats.stream())
|
||||
.forEach(thermostat -> thingDiscovered(bridgeHandler.getThing().getUID(), thermostat.serialNumber));
|
||||
|
@ -12,12 +12,13 @@
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.services;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.GroupContentModel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -30,17 +31,17 @@ import org.slf4j.LoggerFactory;
|
||||
@NonNullByDefault
|
||||
public class RefreshGroupContentService {
|
||||
|
||||
private final List<GroupContent> groupContentList;
|
||||
private final List<GroupContentModel> groupContentList;
|
||||
private final Logger logger = LoggerFactory.getLogger(RefreshGroupContentService.class);
|
||||
private List<Thing> things;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link RefreshGroupContentService}
|
||||
*
|
||||
* @param groupContents {@link GroupContent}
|
||||
* @param groupContents {@link GroupContentModel}
|
||||
* @param things Things
|
||||
*/
|
||||
public RefreshGroupContentService(List<GroupContent> groupContents, List<Thing> things) {
|
||||
public RefreshGroupContentService(List<GroupContentModel> groupContents, List<Thing> things) {
|
||||
this.groupContentList = groupContents;
|
||||
this.things = things;
|
||||
if (this.things.isEmpty()) {
|
||||
@ -52,13 +53,7 @@ public class RefreshGroupContentService {
|
||||
* Handles the changes to all things.
|
||||
*/
|
||||
public void handle() {
|
||||
groupContentList.stream().flatMap(entry -> entry.thermostats.stream()).forEach(this::handleThermostat);
|
||||
}
|
||||
|
||||
private void handleThermostat(Thermostat thermostat) {
|
||||
things.stream().filter(thing -> thing.getHandler() instanceof ThermostatHandler)
|
||||
.map(thing -> (ThermostatHandler) thing.getHandler())
|
||||
.filter(thingHandler -> thingHandler.getSerialNumber().equals(thermostat.serialNumber))
|
||||
.forEach(thingHandler -> thingHandler.handleThermostatRefresh(thermostat));
|
||||
new RefreshThermostatsService(groupContentList.stream().flatMap(entry -> entry.thermostats.stream())
|
||||
.collect(Collectors.toCollection(ArrayList::new)), things).handle();
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,9 @@
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.services;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -26,11 +25,16 @@ 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.common.SignalRLogger;
|
||||
import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration;
|
||||
import org.openhab.binding.ojelectronics.internal.models.SignalRResultModel;
|
||||
import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.signalr4j.client.Connection;
|
||||
import com.github.signalr4j.client.ConnectionState;
|
||||
import com.github.signalr4j.client.Platform;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
@ -47,14 +51,14 @@ public final class RefreshService implements AutoCloseable {
|
||||
private final HttpClient httpClient;
|
||||
private final Gson gson = OJGSonBuilder.getGSon();
|
||||
|
||||
private final ScheduledExecutorService schedulerService;
|
||||
|
||||
private @Nullable Runnable connectionLost;
|
||||
private @Nullable BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone;
|
||||
private @Nullable ScheduledFuture<?> scheduler;
|
||||
private @Nullable Consumer<@Nullable String> connectionLost;
|
||||
private @Nullable BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> initializationDone;
|
||||
private @Nullable BiConsumer<@Nullable SignalRResultModel, @Nullable String> refreshDone;
|
||||
private @Nullable Runnable unauthorized;
|
||||
private @Nullable String sessionId;
|
||||
private static boolean destroyed = false;
|
||||
private @Nullable Connection signalRConnection;
|
||||
private boolean destroyed = false;
|
||||
private boolean isInitializing = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link RefreshService}
|
||||
@ -63,11 +67,10 @@ public final class RefreshService implements AutoCloseable {
|
||||
* @param httpClient HTTP client
|
||||
* @param updateService Service to update the thermostat in the cloud
|
||||
*/
|
||||
public RefreshService(OJElectronicsBridgeConfiguration config, HttpClient httpClient,
|
||||
ScheduledExecutorService schedulerService) {
|
||||
public RefreshService(OJElectronicsBridgeConfiguration config, HttpClient httpClient) {
|
||||
this.config = config;
|
||||
this.httpClient = httpClient;
|
||||
this.schedulerService = schedulerService;
|
||||
Platform.loadPlatformComponent(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,17 +81,21 @@ public final class RefreshService implements AutoCloseable {
|
||||
* @param connectionLosed This method is called if no connection could established.
|
||||
* @param unauthorized This method is called if the result is unauthorized.
|
||||
*/
|
||||
public void start(String sessionId, BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone,
|
||||
Runnable connectionLost, Runnable unauthorized) {
|
||||
public void start(String sessionId,
|
||||
BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> initializationDone,
|
||||
BiConsumer<@Nullable SignalRResultModel, @Nullable String> refreshDone,
|
||||
Consumer<@Nullable String> connectionLost, Runnable unauthorized) {
|
||||
logger.trace("RefreshService.startService({})", sessionId);
|
||||
this.connectionLost = connectionLost;
|
||||
this.initializationDone = initializationDone;
|
||||
this.refreshDone = refreshDone;
|
||||
this.unauthorized = unauthorized;
|
||||
this.sessionId = sessionId;
|
||||
long refreshTime = config.refreshDelayInSeconds;
|
||||
scheduler = schedulerService.scheduleWithFixedDelay(this::refresh, refreshTime, refreshTime, TimeUnit.SECONDS);
|
||||
refresh();
|
||||
|
||||
signalRConnection = createSignalRConnection();
|
||||
destroyed = false;
|
||||
isInitializing = false;
|
||||
initializeGroups(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,25 +103,70 @@ public final class RefreshService implements AutoCloseable {
|
||||
*/
|
||||
public void stop() {
|
||||
destroyed = true;
|
||||
final ScheduledFuture<?> scheduler = this.scheduler;
|
||||
if (scheduler != null) {
|
||||
scheduler.cancel(false);
|
||||
final Connection localSignalRConnection = signalRConnection;
|
||||
if (localSignalRConnection != null) {
|
||||
localSignalRConnection.stop();
|
||||
signalRConnection = null;
|
||||
}
|
||||
this.scheduler = null;
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
private Connection createSignalRConnection() {
|
||||
Connection signalRConnection = new Connection(config.getSignalRUrl(), new SignalRLogger());
|
||||
signalRConnection.setReconnectOnError(false);
|
||||
signalRConnection.received(json -> {
|
||||
if (json != null && json.isJsonObject()) {
|
||||
BiConsumer<@Nullable SignalRResultModel, @Nullable String> refreshDone = this.refreshDone;
|
||||
if (refreshDone != null) {
|
||||
logger.trace("refresh {}", json);
|
||||
try {
|
||||
SignalRResultModel content = Objects
|
||||
.requireNonNull(gson.fromJson(json, SignalRResultModel.class));
|
||||
refreshDone.accept(content, null);
|
||||
} catch (JsonSyntaxException exception) {
|
||||
logger.debug("Error mapping Result to model", exception);
|
||||
refreshDone.accept(null, exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
signalRConnection.stateChanged((oldState, newState) -> {
|
||||
logger.trace("Connection state changed from {} to {}", oldState, newState);
|
||||
if (newState == ConnectionState.Disconnected && !destroyed) {
|
||||
handleConnectionLost("Connection broken");
|
||||
}
|
||||
});
|
||||
signalRConnection.reconnected(() -> {
|
||||
initializeGroups(false);
|
||||
});
|
||||
signalRConnection.connected(() -> {
|
||||
signalRConnection.send(sessionId);
|
||||
});
|
||||
signalRConnection.error(error -> logger.info("SignalR error {}", error.getLocalizedMessage()));
|
||||
return signalRConnection;
|
||||
}
|
||||
|
||||
private void initializeGroups(boolean shouldStartSignalRService) {
|
||||
if (destroyed || isInitializing) {
|
||||
return;
|
||||
}
|
||||
final String sessionId = this.sessionId;
|
||||
if (sessionId == null) {
|
||||
handleConnectionLost();
|
||||
handleConnectionLost("No session id");
|
||||
}
|
||||
isInitializing = true;
|
||||
logger.trace("initializeGroups started");
|
||||
final Runnable unauthorized = this.unauthorized;
|
||||
createRequest().send(new BufferingResponseListener() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Result result) {
|
||||
if (!destroyed) {
|
||||
if (result == null || result.isFailed()) {
|
||||
handleConnectionLost();
|
||||
try {
|
||||
if (destroyed || result == null) {
|
||||
return;
|
||||
}
|
||||
if (result.isFailed()) {
|
||||
final Throwable failure = result.getFailure();
|
||||
logger.error("Error initializing groups", failure);
|
||||
handleConnectionLost(failure.getLocalizedMessage());
|
||||
} else {
|
||||
int status = result.getResponse().getStatus();
|
||||
logger.trace("HTTP-Status {}", status);
|
||||
@ -122,32 +174,40 @@ public final class RefreshService implements AutoCloseable {
|
||||
if (unauthorized != null) {
|
||||
unauthorized.run();
|
||||
} else {
|
||||
handleConnectionLost();
|
||||
handleConnectionLost(null);
|
||||
}
|
||||
} else if (status == HttpStatus.OK_200) {
|
||||
handleRefreshDone(getContentAsString());
|
||||
initializationDone(Objects.requireNonNull(getContentAsString()));
|
||||
final Connection localSignalRConnection = signalRConnection;
|
||||
if (shouldStartSignalRService && localSignalRConnection != null) {
|
||||
localSignalRConnection.start();
|
||||
}
|
||||
} else {
|
||||
logger.warn("unsupported HTTP-Status {}", status);
|
||||
handleConnectionLost();
|
||||
handleConnectionLost(null);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
logger.trace("initializeGroups completed");
|
||||
isInitializing = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Request createRequest() {
|
||||
Request request = httpClient.newRequest(config.apiUrl + "/Group/GroupContents").param("sessionid", sessionId)
|
||||
.param("apiKey", config.apiKey).method(HttpMethod.GET);
|
||||
Request request = httpClient.newRequest(config.getRestApiUrl() + "/Group/GroupContents")
|
||||
.param("sessionid", sessionId).param("apiKey", config.apiKey).method(HttpMethod.GET);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void handleRefreshDone(String responseBody) {
|
||||
BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone = this.refreshDone;
|
||||
private void initializationDone(String responseBody) {
|
||||
BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone = this.initializationDone;
|
||||
if (refreshDone != null) {
|
||||
logger.trace("refresh {}", responseBody);
|
||||
logger.trace("initializationDone {}", responseBody);
|
||||
try {
|
||||
GroupContentResponseModel content = gson.fromJson(responseBody, GroupContentResponseModel.class);
|
||||
GroupContentResponseModel content = Objects
|
||||
.requireNonNull(gson.fromJson(responseBody, GroupContentResponseModel.class));
|
||||
refreshDone.accept(content, null);
|
||||
} catch (JsonSyntaxException exception) {
|
||||
logger.debug("Error mapping Result to model", exception);
|
||||
@ -156,15 +216,15 @@ public final class RefreshService implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnectionLost() {
|
||||
final Runnable connectionLost = this.connectionLost;
|
||||
private void handleConnectionLost(@Nullable String message) {
|
||||
final Consumer<@Nullable String> connectionLost = this.connectionLost;
|
||||
if (connectionLost != null) {
|
||||
connectionLost.run();
|
||||
connectionLost.accept(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
public void close() {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.services;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ojelectronics.internal.ThermostatHandler;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatModel;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatModelBase;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatRealTimeValuesModel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Refreshes values of {@link ThermostatHandler}
|
||||
*
|
||||
* @author Christian Kittel - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RefreshThermostatsService {
|
||||
|
||||
private final List<ThermostatModel> thermostats;
|
||||
private final Logger logger = LoggerFactory.getLogger(RefreshThermostatsService.class);
|
||||
private final List<Thing> things;
|
||||
private final List<ThermostatRealTimeValuesModel> realTimeValues;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link RefreshThermostatsService}
|
||||
*
|
||||
* @param thermostats {@link ThermostatModel}
|
||||
* @param things Things
|
||||
*/
|
||||
public RefreshThermostatsService(List<ThermostatModel> thermostats, List<Thing> things) {
|
||||
this(thermostats, new ArrayList<>(), things);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link RefreshThermostatsService}
|
||||
*
|
||||
* @param thermostats {@link ThermostatModel}
|
||||
* @param realTimeValues {@link ThermostatRealTimeValuesModel}
|
||||
* @param things Things
|
||||
*/
|
||||
public RefreshThermostatsService(List<ThermostatModel> thermostats,
|
||||
List<ThermostatRealTimeValuesModel> realTimeValues, List<Thing> things) {
|
||||
this.thermostats = thermostats;
|
||||
this.things = things;
|
||||
this.realTimeValues = realTimeValues;
|
||||
if (this.things.isEmpty()) {
|
||||
logger.warn("Bridge contains no thermostats.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the changes to all things.
|
||||
*/
|
||||
public synchronized void handle() {
|
||||
thermostats.forEach(thermostat -> handleThermostat(thermostat, this::handleThermostatRefresh));
|
||||
realTimeValues.forEach(thermostat -> handleThermostat(thermostat, this::handleThermostatRealTimeValueRefresh));
|
||||
}
|
||||
|
||||
private <T extends ThermostatModelBase> void handleThermostat(T thermostat,
|
||||
BiConsumer<ThermostatHandler, T> refreshHandler) {
|
||||
things.stream().filter(thing -> thing.getHandler() instanceof ThermostatHandler)
|
||||
.map(thing -> (ThermostatHandler) thing.getHandler())
|
||||
.filter(thingHandler -> thingHandler.getSerialNumber().equals(thermostat.serialNumber))
|
||||
.forEach(thingHandler -> {
|
||||
try {
|
||||
refreshHandler.accept(Objects.requireNonNull(thingHandler), thermostat);
|
||||
} catch (Exception e) {
|
||||
logger.info("Error Handling Refresh of thermostat {}", thermostat, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleThermostatRefresh(ThermostatHandler thingHandler, ThermostatModel thermostat) {
|
||||
thingHandler.handleThermostatRefresh(thermostat);
|
||||
}
|
||||
|
||||
private void handleThermostatRealTimeValueRefresh(ThermostatHandler thingHandler,
|
||||
ThermostatRealTimeValuesModel thermostat) {
|
||||
thingHandler.handleThermostatRefresh(thermostat);
|
||||
}
|
||||
}
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.ojelectronics.internal.services;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -27,6 +29,8 @@ import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConf
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@ -39,7 +43,7 @@ import com.google.gson.Gson;
|
||||
public class SignInService {
|
||||
|
||||
private final Gson gson = OJGSonBuilder.getGSon();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SignInService.class);
|
||||
private final HttpClient httpClient;
|
||||
private final OJElectronicsBridgeConfiguration config;
|
||||
|
||||
@ -61,30 +65,41 @@ public class SignInService {
|
||||
* @param connectionLosed This method is called if no connection could established.
|
||||
* @param unauthorized This method is called if the result is unauthorized.
|
||||
*/
|
||||
public void signIn(Consumer<String> signInDone, Runnable connectionLosed, Runnable unauthorized) {
|
||||
Request request = httpClient.POST(config.apiUrl + "/UserProfile/SignIn")
|
||||
public void signIn(Consumer<String> signInDone, Consumer<@Nullable String> connectionLosed, Runnable unauthorized) {
|
||||
logger.trace("Trying to sign in");
|
||||
|
||||
Request request = httpClient.POST(config.getRestApiUrl() + "/UserProfile/SignIn")
|
||||
.header(HttpHeader.CONTENT_TYPE, "application/json")
|
||||
.content(new StringContentProvider(gson.toJson(getPostSignInQueryModel())));
|
||||
.content(new StringContentProvider(gson.toJson(getPostSignInQueryModel())))
|
||||
.timeout(1, TimeUnit.MINUTES);
|
||||
|
||||
request.send(new BufferingResponseListener() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Result result) {
|
||||
if (result == null || result.isFailed()) {
|
||||
connectionLosed.run();
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.isFailed()) {
|
||||
final Throwable failure = result.getFailure();
|
||||
logger.error("Signing in failed", failure);
|
||||
connectionLosed.accept(failure.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getResponse().getStatus() == 200) {
|
||||
PostSignInResponseModel signInModel = gson.fromJson(getContentAsString(),
|
||||
PostSignInResponseModel.class);
|
||||
if (signInModel == null || signInModel.errorCode != 0 || signInModel.sessionId.equals("")) {
|
||||
PostSignInResponseModel signInModel = Objects
|
||||
.requireNonNull(gson.fromJson(getContentAsString(), PostSignInResponseModel.class));
|
||||
if (signInModel.errorCode != 0 || signInModel.sessionId.equals("")) {
|
||||
unauthorized.run();
|
||||
return;
|
||||
}
|
||||
logger.trace("Signing in successful {}", getContentAsString());
|
||||
signInDone.accept(signInModel.sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
connectionLosed.run();
|
||||
connectionLosed.accept(null);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
@ -13,6 +13,8 @@
|
||||
package org.openhab.binding.ojelectronics.internal.services;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -26,7 +28,7 @@ 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.ThermostatModel;
|
||||
import org.openhab.binding.ojelectronics.internal.models.thermostat.UpdateThermostatRequestModel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.slf4j.Logger;
|
||||
@ -45,14 +47,17 @@ 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;
|
||||
private final Runnable unauthorized;
|
||||
private final Consumer<@Nullable String> connectionLost;
|
||||
|
||||
public UpdateService(OJElectronicsBridgeConfiguration configuration, HttpClient httpClient, String sessionId) {
|
||||
public UpdateService(OJElectronicsBridgeConfiguration configuration, HttpClient httpClient,
|
||||
Consumer<@Nullable String> connectionLost, Runnable unauthorized) {
|
||||
this.configuration = configuration;
|
||||
this.httpClient = httpClient;
|
||||
this.sessionId = sessionId;
|
||||
this.unauthorized = unauthorized;
|
||||
this.connectionLost = connectionLost;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,34 +66,42 @@ public final class UpdateService {
|
||||
* @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);
|
||||
new SignInService(configuration, httpClient).signIn((sessionId) -> updateAllThermostats(things, sessionId),
|
||||
connectionLost, unauthorized);
|
||||
}
|
||||
|
||||
private void updateThermostat(@Nullable Thermostat thermostat) {
|
||||
private void updateAllThermostats(List<Thing> things, String sessionId) {
|
||||
things.stream().filter(thing -> thing.getHandler() instanceof ThermostatHandler)
|
||||
.map(thing -> (ThermostatHandler) thing.getHandler())
|
||||
.map(handler -> handler.tryHandleAndGetUpdatedThermostat())
|
||||
.forEach((thermostat) -> updateThermostat(thermostat, sessionId));
|
||||
}
|
||||
|
||||
private void updateThermostat(@Nullable ThermostatModel thermostat, String sessionId) {
|
||||
if (thermostat == null) {
|
||||
return;
|
||||
}
|
||||
Request request = httpClient.POST(configuration.apiUrl + "/Thermostat/UpdateThermostat")
|
||||
String jsonPayload = gson.toJson(new UpdateThermostatRequestModel(thermostat).withApiKey(configuration.apiKey));
|
||||
Request request = httpClient.POST(configuration.getRestApiUrl() + "/Thermostat/UpdateThermostat")
|
||||
.param("sessionid", sessionId).header(HttpHeader.CONTENT_TYPE, "application/json")
|
||||
.content(new StringContentProvider(
|
||||
gson.toJson(new UpdateThermostatRequestModel(thermostat).withApiKey(configuration.apiKey))));
|
||||
.content(new StringContentProvider(jsonPayload));
|
||||
logger.trace("updateThermostat payload for themostat with serial {} is {}", thermostat.serialNumber,
|
||||
jsonPayload);
|
||||
|
||||
request.send(new BufferingResponseListener() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Result result) {
|
||||
if (result != null) {
|
||||
logger.trace("onComplete {}", result);
|
||||
logger.trace("onComplete Http Status {} {}", result.getResponse().getStatus(), result);
|
||||
if (result.isFailed()) {
|
||||
logger.warn("updateThermostat failed {}", thermostat);
|
||||
logger.warn("updateThermostat failed for themostat with serial {}", thermostat.serialNumber);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
SimpleResponseModel responseModel = Objects
|
||||
.requireNonNull(gson.fromJson(getContentAsString(), SimpleResponseModel.class));
|
||||
if (responseModel.errorCode != 0) {
|
||||
logger.warn("updateThermostat failed with errorCode {} for thermostat with serial {}",
|
||||
responseModel.errorCode, thermostat.serialNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,11 @@ thing-type.ojelectronics.owd5.description = OWD5/MWD5 Thermostat
|
||||
thing-type.config.ojelectronics.ojcloud.apiKey.label = API Key
|
||||
thing-type.config.ojelectronics.ojcloud.apiKey.description = API-Key from your local distributor
|
||||
thing-type.config.ojelectronics.ojcloud.apiUrl.label = API-URL
|
||||
thing-type.config.ojelectronics.ojcloud.apiUrl.description = URL to cloud API-service.
|
||||
thing-type.config.ojelectronics.ojcloud.apiUrl.description = URL to cloud API-service and Socket-Notification.
|
||||
thing-type.config.ojelectronics.ojcloud.customerId.label = Customer ID
|
||||
thing-type.config.ojelectronics.ojcloud.customerId.description = Customer ID
|
||||
thing-type.config.ojelectronics.ojcloud.password.label = Password
|
||||
thing-type.config.ojelectronics.ojcloud.password.description = Password for access cloud service.
|
||||
thing-type.config.ojelectronics.ojcloud.refreshDelayInSeconds.label = Refresh Delay
|
||||
thing-type.config.ojelectronics.ojcloud.refreshDelayInSeconds.description = Refresh delay in seconds.
|
||||
thing-type.config.ojelectronics.ojcloud.softwareVersion.label = Software Version
|
||||
thing-type.config.ojelectronics.ojcloud.softwareVersion.description = Software Version
|
||||
thing-type.config.ojelectronics.ojcloud.userName.label = User Name
|
||||
|
@ -22,16 +22,10 @@
|
||||
</parameter>
|
||||
<parameter name="apiUrl" type="text" required="true">
|
||||
<label>API-URL</label>
|
||||
<description>URL to cloud API-service.</description>
|
||||
<description>URL to cloud API-service and Socket-Notification.</description>
|
||||
<context>url</context>
|
||||
<advanced>true</advanced>
|
||||
<default>https://OWD5-OJ001-App.ojelectronics.com/api</default>
|
||||
</parameter>
|
||||
<parameter name="refreshDelayInSeconds" type="integer" required="true" min="15" unit="s">
|
||||
<label>Refresh Delay</label>
|
||||
<description>Refresh delay in seconds.</description>
|
||||
<advanced>true</advanced>
|
||||
<default>30</default>
|
||||
<default>https://OWD5-OJ001-App.ojelectronics.com</default>
|
||||
</parameter>
|
||||
<parameter name="customerId" type="integer" required="true">
|
||||
<label>Customer ID</label>
|
||||
|
Loading…
Reference in New Issue
Block a user