diff --git a/CODEOWNERS b/CODEOWNERS index 0a9c9c64bae39..f1f204fa412cd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -301,7 +301,7 @@ /bundles/org.openhab.binding.siemensrds/ @andrewfg /bundles/org.openhab.binding.silvercrestwifisocket/ @jmvaz /bundles/org.openhab.binding.sinope/ @chaton78 -/bundles/org.openhab.binding.sleepiq/ @syphr42 +/bundles/org.openhab.binding.sleepiq/ @syphr42 @mhilbush /bundles/org.openhab.binding.smaenergymeter/ @monnimeter /bundles/org.openhab.binding.smartmeter/ @msteigenberger /bundles/org.openhab.binding.smartthings/ @BobRak @@ -339,7 +339,7 @@ /bundles/org.openhab.binding.touchwand/ @roieg /bundles/org.openhab.binding.tplinkrouter/ @olivierkeke /bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand -/bundles/org.openhab.binding.tr064/ @openhab/add-ons-maintainers +/bundles/org.openhab.binding.tr064/ @J-N-K /bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer /bundles/org.openhab.binding.twitter/ @computergeek1507 /bundles/org.openhab.binding.unifi/ @mgbowman @Hilbrand @@ -399,6 +399,7 @@ /bundles/org.openhab.transform.jsonpath/ @clinique /bundles/org.openhab.transform.map/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.regex/ @openhab/add-ons-maintainers +/bundles/org.openhab.transform.rollershutterposition/ @jsjames /bundles/org.openhab.transform.scale/ @clinique /bundles/org.openhab.transform.xpath/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.xslt/ @openhab/add-ons-maintainers diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index d446bf5b49ed9..efe39a63389da 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -206,6 +206,11 @@ org.openhab.binding.bluetooth.govee ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.bluetooth.radoneye + ${project.version} + org.openhab.addons.bundles org.openhab.binding.bluetooth.roaming @@ -1986,6 +1991,11 @@ org.openhab.transform.regex ${project.version} + + org.openhab.addons.bundles + org.openhab.transform.rollershutterposition + ${project.version} + org.openhab.addons.bundles org.openhab.transform.scale diff --git a/bundles/org.openhab.automation.jsscripting/pom.xml b/bundles/org.openhab.automation.jsscripting/pom.xml index f54bdb3e642d0..560a134fc066d 100644 --- a/bundles/org.openhab.automation.jsscripting/pom.xml +++ b/bundles/org.openhab.automation.jsscripting/pom.xml @@ -24,7 +24,7 @@ 22.0.0.2 ${project.version} - openhab@4.0.0 + openhab@4.1.0 diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java index d8cb3f9298212..7302b517ed741 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java @@ -89,8 +89,8 @@ public void scopeValues(ScriptEngine scriptEngine, Map scopeValu if (!SCRIPT_TYPES.contains(scriptType)) { return null; } - return new DebuggingGraalScriptEngine<>( - new OpenhabGraalJSScriptEngine(injectionEnabled, useIncludedLibrary, jsScriptServiceUtil)); + return new DebuggingGraalScriptEngine<>(new OpenhabGraalJSScriptEngine(injectionEnabled, useIncludedLibrary, + jsScriptServiceUtil, jsDependencyTracker)); } @Override diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/OpenhabGraalJSScriptEngine.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/OpenhabGraalJSScriptEngine.java index f16272e475bd7..91132e4f401b7 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/OpenhabGraalJSScriptEngine.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/OpenhabGraalJSScriptEngine.java @@ -139,7 +139,7 @@ public class OpenhabGraalJSScriptEngine * lifecycle and provides hooks for scripts to do so too. */ public OpenhabGraalJSScriptEngine(boolean injectionEnabled, boolean useIncludedLibrary, - JSScriptServiceUtil jsScriptServiceUtil) { + JSScriptServiceUtil jsScriptServiceUtil, JSDependencyTracker jsDependencyTracker) { super(null); // delegate depends on fields not yet initialised, so we cannot set it immediately this.injectionEnabled = injectionEnabled; this.useIncludedLibrary = useIncludedLibrary; @@ -149,7 +149,8 @@ public OpenhabGraalJSScriptEngine(boolean injectionEnabled, boolean useIncludedL delegate = GraalJSScriptEngine.create(ENGINE, Context.newBuilder("js").allowExperimentalOptions(true).allowAllAccess(true) - .allowHostAccess(HOST_ACCESS).option("js.commonjs-require-cwd", JSDependencyTracker.LIB_PATH) + .allowHostAccess(HOST_ACCESS) + .option("js.commonjs-require-cwd", jsDependencyTracker.getLibraryPath().toString()) .option("js.nashorn-compat", "true") // Enable Nashorn compat mode as openhab-js relies on // accessors, see // https://github.com/oracle/graaljs/blob/master/docs/user/NashornMigrationGuide.md#accessors diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java index 134bd5059a513..f744537fd93fc 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java @@ -13,7 +13,6 @@ package org.openhab.automation.jsscripting.internal.fs.watch; import java.io.File; -import java.nio.file.Files; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.automation.module.script.ScriptDependencyTracker; @@ -40,15 +39,11 @@ public class JSDependencyTracker extends AbstractScriptDependencyTracker { private final Logger logger = LoggerFactory.getLogger(JSDependencyTracker.class); - public static final String LIB_PATH = String.join(File.separator, "automation", "js", "node_modules"); + private static final String LIB_PATH = String.join(File.separator, "automation", "js", "node_modules"); @Activate public JSDependencyTracker(@Reference(target = WatchService.CONFIG_WATCHER_FILTER) WatchService watchService) { super(watchService, LIB_PATH); - - if (Files.isRegularFile(this.libraryPath)) { - logger.warn("Trying to watch directory '{}', however it is a file", this.libraryPath); - } } @Deactivate diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/NOTICE b/bundles/org.openhab.binding.bluetooth.radoneye/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/README.md b/bundles/org.openhab.binding.bluetooth.radoneye/README.md new file mode 100644 index 0000000000000..98e9cb2c5b2a6 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/README.md @@ -0,0 +1,47 @@ +# radoneye + +This extension adds support for [RadonEye](http://radonftlab.com/radon-sensor-product/radon-detector/rd200/) radon bluetooth detector. + +## Supported Things + +Following thing types are supported by this extension: + +| Thing Type ID | Description | +| ------------------- | -------------------------------------- | +| radoneye_rd200 | Original RadonEye (RD200) | + +## Discovery + +As any other Bluetooth device, RadonEye devices are discovered automatically by the corresponding bridge. + +## Thing Configuration + +Supported configuration parameters for the things: + +| Property | Type | Default | Required | Description | +|---------------------------------|---------|---------|----------|-----------------------------------------------------------------| +| address | String | | Yes | Bluetooth address of the device (in format "XX:XX:XX:XX:XX:XX") | +| refreshInterval | Integer | 300 | No | How often a refresh shall occur in seconds | + +## Channels + +Following channels are supported for `RadonEye` thing: + +| Channel ID | Item Type | Description | +| ------------------ | ------------------------ | ------------------------------------------- | +| radon | Number:Density | The measured radon level | + + +## Example + +radoneye.things (assuming you have a Bluetooth bridge with the ID `bluetooth:bluegiga:adapter1`: + +``` +bluetooth:radoneye_rd200:adapter1:sensor1 "radoneye Wave Plus Sensor 1" (bluetooth:bluegiga:adapter1) [ address="12:34:56:78:9A:BC", refreshInterval=300 ] +``` + +radoneye.items: + +``` +Number:Density radon "Radon level [%d %unit%]" { channel="bluetooth:radoneye_rd200:adapter1:sensor1:radon" } +``` diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/pom.xml b/bundles/org.openhab.binding.bluetooth.radoneye/pom.xml new file mode 100644 index 0000000000000..ad9bbd8239529 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.0.0-SNAPSHOT + + + org.openhab.binding.bluetooth.radoneye + + openHAB Add-ons :: Bundles :: RadonEye Bluetooth Adapter + + + + org.openhab.addons.bundles + org.openhab.binding.bluetooth + ${project.version} + provided + + + diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/feature/feature.xml b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/feature/feature.xml new file mode 100644 index 0000000000000..fa7af0f740e32 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/feature/feature.xml @@ -0,0 +1,12 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + openhab-transport-serial + mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.radoneye/${project.version} + + + diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java new file mode 100644 index 0000000000000..7b3497865faaa --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java @@ -0,0 +1,326 @@ +/** + * 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.bluetooth.radoneye.internal; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.bluetooth.BeaconBluetoothHandler; +import org.openhab.binding.bluetooth.BluetoothCharacteristic; +import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState; +import org.openhab.binding.bluetooth.BluetoothUtils; +import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link AbstractRadoneyeHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +abstract public class AbstractRadoneyeHandler extends BeaconBluetoothHandler { + + private static final int CHECK_PERIOD_SEC = 10; + + private final Logger logger = LoggerFactory.getLogger(AbstractRadoneyeHandler.class); + + private AtomicInteger sinceLastReadSec = new AtomicInteger(); + private Optional configuration = Optional.empty(); + private @Nullable ScheduledFuture scheduledTask; + + private volatile int refreshInterval; + private volatile int errorConnectCounter; + private volatile int errorReadCounter; + private volatile int errorWriteCounter; + private volatile int errorDisconnectCounter; + private volatile int errorResolvingCounter; + + private volatile ServiceState serviceState = ServiceState.NOT_RESOLVED; + private volatile ReadState readState = ReadState.IDLE; + + private enum ServiceState { + NOT_RESOLVED, + RESOLVING, + RESOLVED, + } + + private enum ReadState { + IDLE, + READING, + WRITING, + } + + public AbstractRadoneyeHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + logger.debug("Initialize"); + super.initialize(); + configuration = Optional.of(getConfigAs(RadoneyeConfiguration.class)); + logger.debug("Using configuration: {}", configuration.get()); + cancelScheduledTask(); + configuration.ifPresent(cfg -> { + refreshInterval = cfg.refreshInterval; + logger.debug("Start scheduled task to read device in every {} seconds", refreshInterval); + scheduledTask = scheduler.scheduleWithFixedDelay(this::executePeridioc, CHECK_PERIOD_SEC, CHECK_PERIOD_SEC, + TimeUnit.SECONDS); + }); + sinceLastReadSec.set(refreshInterval); // update immediately + } + + @Override + public void dispose() { + logger.debug("Dispose"); + cancelScheduledTask(); + serviceState = ServiceState.NOT_RESOLVED; + readState = ReadState.IDLE; + super.dispose(); + } + + private void cancelScheduledTask() { + if (scheduledTask != null) { + scheduledTask.cancel(true); + scheduledTask = null; + } + } + + private void executePeridioc() { + sinceLastReadSec.addAndGet(CHECK_PERIOD_SEC); + execute(); + } + + private synchronized void execute() { + ConnectionState connectionState = device.getConnectionState(); + logger.debug("Device {} state is {}, serviceState {}, readState {}", address, connectionState, serviceState, + readState); + + switch (connectionState) { + case DISCOVERING: + case DISCOVERED: + case DISCONNECTED: + if (isTimeToRead()) { + connect(); + } + break; + case CONNECTED: + read(); + break; + default: + break; + } + } + + private void connect() { + logger.debug("Connect to device {}...", address); + if (!device.connect()) { + errorConnectCounter++; + if (errorConnectCounter < 6) { + logger.debug("Connecting to device {} failed {} times", address, errorConnectCounter); + } else { + logger.debug("ERROR: Controller reset needed. Connecting to device {} failed {} times", address, + errorConnectCounter); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connecting to device failed"); + } + } else { + logger.debug("Connected to device {}", address); + errorConnectCounter = 0; + } + } + + private void disconnect() { + logger.debug("Disconnect from device {}...", address); + if (!device.disconnect()) { + errorDisconnectCounter++; + if (errorDisconnectCounter < 6) { + logger.debug("Disconnect from device {} failed {} times", address, errorDisconnectCounter); + } else { + logger.debug("ERROR: Controller reset needed. Disconnect from device {} failed {} times", address, + errorDisconnectCounter); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Disconnect from device failed"); + } + } else { + logger.debug("Disconnected from device {}", address); + errorDisconnectCounter = 0; + } + } + + private void read() { + switch (serviceState) { + case NOT_RESOLVED: + logger.debug("Discover services on device {}", address); + discoverServices(); + break; + case RESOLVED: + switch (readState) { + case IDLE: + if (getTriggerUUID() != null) { + logger.debug("Send trigger data to device {}...", address); + BluetoothCharacteristic characteristic = device.getCharacteristic(getTriggerUUID()); + if (characteristic != null) { + readState = ReadState.WRITING; + errorWriteCounter = 0; + device.writeCharacteristic(characteristic, getTriggerData()).whenComplete((v, ex) -> { + readSensorData(); + }); + } else { + errorWriteCounter++; + if (errorWriteCounter < 6) { + logger.debug("Read/write data from device {} failed {} times", address, + errorWriteCounter); + } else { + logger.debug( + "ERROR: Controller reset needed. Read/write data from device {} failed {} times", + address, errorWriteCounter); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Read/write data from device failed"); + } + disconnect(); + } + } else { + readSensorData(); + } + + break; + default: + logger.debug("Unhandled Resolved readState {} on device {}", readState, address); + break; + } + break; + default: // serviceState RESOLVING + errorResolvingCounter++; + if (errorResolvingCounter < 6) { + logger.debug("Unhandled serviceState {} on device {}", serviceState, address); + } else { + logger.debug("ERROR: Controller reset needed. Unhandled serviceState {} on device {}", + serviceState, address); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Service discovery for device failed"); + } + break; + } + } + + private void readSensorData() { + logger.debug("Read data from device {}...", address); + BluetoothCharacteristic characteristic = device.getCharacteristic(getDataUUID()); + if (characteristic != null) { + readState = ReadState.READING; + errorReadCounter = 0; + errorResolvingCounter = 0; + device.readCharacteristic(characteristic).whenComplete((data, ex) -> { + try { + logger.debug("Characteristic {} from device {}: {}", characteristic.getUuid(), address, data); + updateStatus(ThingStatus.ONLINE); + sinceLastReadSec.set(0); + updateChannels(BluetoothUtils.toIntArray(data)); + } finally { + readState = ReadState.IDLE; + disconnect(); + } + }); + } else { + errorReadCounter++; + if (errorReadCounter < 6) { + logger.debug("Read data from device {} failed {} times", address, errorReadCounter); + } else { + logger.debug("ERROR: Controller reset needed. Read data from device {} failed {} times", address, + errorReadCounter); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Read data from device failed"); + } + disconnect(); + } + } + + private void discoverServices() { + logger.debug("Discover services for device {}", address); + serviceState = ServiceState.RESOLVING; + device.discoverServices(); + } + + @Override + public void onServicesDiscovered() { + serviceState = ServiceState.RESOLVED; + logger.debug("Service discovery completed for device {}", address); + printServices(); + execute(); + } + + private void printServices() { + device.getServices().forEach(service -> logger.debug("Device {} Service '{}'", address, service)); + } + + @Override + public void onConnectionStateChange(BluetoothConnectionStatusNotification connectionNotification) { + logger.debug("Connection State Change Event is {}", connectionNotification.getConnectionState()); + switch (connectionNotification.getConnectionState()) { + case DISCONNECTED: + if (serviceState == ServiceState.RESOLVING) { + serviceState = ServiceState.NOT_RESOLVED; + } + readState = ReadState.IDLE; + break; + default: + break; + + } + execute(); + } + + private boolean isTimeToRead() { + int sinceLastRead = sinceLastReadSec.get(); + logger.debug("Time since last update: {} sec", sinceLastRead); + return sinceLastRead >= refreshInterval; + } + + /** + * Provides the UUID of the characteristic, which holds the sensor data + * + * @return the UUID of the data characteristic + */ + protected abstract UUID getDataUUID(); + + /** + * Provides the UUID of the characteristic, that triggers and update of the sensor data + * + * @return the UUID of the data characteristic + */ + protected abstract UUID getTriggerUUID(); + + /** + * Provides the data that sent to the trigger characteristic will update the sensor data + * + * @return the trigger data as an byte array + */ + protected abstract byte[] getTriggerData(); + + /** + * This method parses the content of the bluetooth characteristic and updates the Thing channels accordingly. + * + * @param is the content of the bluetooth characteristic + */ + abstract protected void updateChannels(int[] is); +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeBindingConstants.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeBindingConstants.java new file mode 100644 index 0000000000000..bfb97abe1f5e0 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeBindingConstants.java @@ -0,0 +1,60 @@ +/** + * 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.bluetooth.radoneye.internal; + +import java.math.BigInteger; +import java.util.Set; + +import javax.measure.Unit; +import javax.measure.quantity.Dimensionless; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.bluetooth.BluetoothBindingConstants; +import org.openhab.core.library.dimension.Density; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.ThingTypeUID; + +import tech.units.indriya.format.SimpleUnitFormat; +import tech.units.indriya.function.MultiplyConverter; +import tech.units.indriya.unit.ProductUnit; +import tech.units.indriya.unit.TransformedUnit; + +/** + * The {@link RadoneyeBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +public class RadoneyeBindingConstants { + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_RADONEYE = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, + "radoneye_rd200"); + + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_RADONEYE); + + // Channel IDs + public static final String CHANNEL_ID_RADON = "radon"; + + public static final Unit PARTS_PER_BILLION = new TransformedUnit<>(Units.ONE, + MultiplyConverter.ofRational(BigInteger.ONE, BigInteger.valueOf(1000000000))); + public static final Unit BECQUEREL_PER_CUBIC_METRE = new ProductUnit<>( + Units.BECQUEREL.divide(SIUnits.CUBIC_METRE)); + + static { + SimpleUnitFormat.getInstance().label(PARTS_PER_BILLION, "ppb"); + SimpleUnitFormat.getInstance().label(BECQUEREL_PER_CUBIC_METRE, "Bq/m³"); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java new file mode 100644 index 0000000000000..5ee2d7a2f5578 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java @@ -0,0 +1,31 @@ +/** + * 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.bluetooth.radoneye.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Configuration class for {@link RadoneyeBinding} device. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +public class RadoneyeConfiguration { + public String address = ""; + public int refreshInterval; + + @Override + public String toString() { + return "[address=" + address + ", refreshInterval=" + refreshInterval + "]"; + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java new file mode 100644 index 0000000000000..730834a40aea3 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java @@ -0,0 +1,76 @@ +/** + * 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.bluetooth.radoneye.internal; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link RadoneyeDataParser} is responsible for parsing data from Wave Plus device format. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +public class RadoneyeDataParser { + public static final String RADON = "radon"; + + private static final int EXPECTED_DATA_LEN = 20; + private static final int EXPECTED_VER_PLUS = 1; + + private static final Logger logger = LoggerFactory.getLogger(RadoneyeDataParser.class); + + private RadoneyeDataParser() { + } + + public static Map parseRd200Data(int[] data) throws RadoneyeParserException { + logger.debug("Parsed data length: {}", data.length); + logger.debug("Parsed data: {}", data); + if (data.length == EXPECTED_DATA_LEN) { + final Map result = new HashMap<>(); + + int[] radonArray = subArray(data, 2, 6); + result.put(RADON, new BigDecimal(readFloat(radonArray) * 37)); + return result; + } else { + throw new RadoneyeParserException(String.format("Illegal data structure length '%d'", data.length)); + } + } + + private static int intFromBytes(int lowByte, int highByte) { + return (highByte & 0xFF) << 8 | (lowByte & 0xFF); + } + + // Little endian + private static int fromByteArrayLE(int[] bytes) { + int result = 0; + for (int i = 0; i < bytes.length; i++) { + result |= (bytes[i] & 0xFF) << (8 * i); + } + return result; + } + + private static float readFloat(int[] bytes) { + int i = fromByteArrayLE(bytes); + return Float.intBitsToFloat(i); + } + + private static int[] subArray(int[] array, int beg, int end) { + return Arrays.copyOfRange(array, beg, end + 1); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDiscoveryParticipant.java new file mode 100644 index 0000000000000..decf69417ae3b --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDiscoveryParticipant.java @@ -0,0 +1,155 @@ +/** + * 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.bluetooth.radoneye.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.bluetooth.BluetoothBindingConstants; +import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice; +import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; + +/** + * This discovery participant is able to recognize RadonEye devices and create discovery results for them. + * + * @author Peter Obel - Initial contribution + * + */ +@NonNullByDefault +@Component +public class RadoneyeDiscoveryParticipant implements BluetoothDiscoveryParticipant { + + private static final String RADONEYE_BLUETOOTH_COMPANY_ID = "f24be3"; + + private static final String RD200 = "R20"; // RadonEye First Generation BLE + + @Override + public Set getSupportedThingTypeUIDs() { + return RadoneyeBindingConstants.SUPPORTED_THING_TYPES_UIDS; + } + + @Override + public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) { + if (isRadoneyeDevice(device)) { + if (RD200.equals(getModel(device))) { + return new ThingUID(RadoneyeBindingConstants.THING_TYPE_RADONEYE, device.getAdapter().getUID(), + device.getAddress().toString().toLowerCase().replace(":", "")); + } + } + return null; + } + + @Override + public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) { + if (!isRadoneyeDevice(device)) { + return null; + } + ThingUID thingUID = getThingUID(device); + if (thingUID == null) { + return null; + } + if (RD200.equals(getModel(device))) { + return createResult(device, thingUID, "RadonEye (BLE)"); + } + return null; + } + + @Override + public boolean requiresConnection(BluetoothDiscoveryDevice device) { + return isRadoneyeDevice(device); + } + + private boolean isRadoneyeDevice(BluetoothDiscoveryDevice device) { + String manufacturerMacId = device.getAddress().toString().toLowerCase().replace(":", "").substring(0, 6); + if (manufacturerMacId.equals(RADONEYE_BLUETOOTH_COMPANY_ID.toLowerCase())) { + return true; + } + return false; + } + + private String getSerial(BluetoothDiscoveryDevice device) { + String name = device.getName(); + String[] parts = name.split(":"); + if (parts.length == 3) { + return parts[2]; + } else { + return ""; + } + } + + private String getManufacturer(BluetoothDiscoveryDevice device) { + String name = device.getName(); + String[] parts = name.split(":"); + if (parts.length == 3) { + return parts[0]; + } else { + return ""; + } + } + + private String getModel(BluetoothDiscoveryDevice device) { + String name = device.getName(); + String[] parts = name.split(":"); + if (parts.length == 3) { + return parts[1]; + } else { + return ""; + } + } + + private DiscoveryResult createResult(BluetoothDiscoveryDevice device, ThingUID thingUID, String label) { + Map properties = new HashMap<>(); + properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString()); + properties.put(Thing.PROPERTY_VENDOR, "RadonEye"); + String name = device.getName(); + String serialNumber = device.getSerialNumber(); + String firmwareRevision = device.getFirmwareRevision(); + String model = device.getModel(); + String hardwareRevision = device.getHardwareRevision(); + Integer txPower = device.getTxPower(); + if (serialNumber != null) { + properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNumber); + } else { + properties.put(Thing.PROPERTY_MODEL_ID, getSerial(device)); + } + if (firmwareRevision != null) { + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, firmwareRevision); + } + if (model != null) { + properties.put(Thing.PROPERTY_MODEL_ID, model); + } else { + properties.put(Thing.PROPERTY_MODEL_ID, getModel(device)); + } + if (hardwareRevision != null) { + properties.put(Thing.PROPERTY_HARDWARE_VERSION, hardwareRevision); + } + if (txPower != null) { + properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower)); + } + properties.put(Thing.PROPERTY_MAC_ADDRESS, device.getAddress().toString()); + + // Create the discovery result and add to the inbox + return DiscoveryResultBuilder.create(thingUID).withProperties(properties) + .withRepresentationProperty(BluetoothBindingConstants.CONFIGURATION_ADDRESS) + .withBridge(device.getAdapter().getUID()).withLabel(label).build(); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java new file mode 100644 index 0000000000000..a5b36a8b0f78d --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java @@ -0,0 +1,80 @@ +/** + * 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.bluetooth.radoneye.internal; + +import static org.openhab.binding.bluetooth.radoneye.internal.RadoneyeBindingConstants.*; + +import java.util.Map; +import java.util.UUID; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.dimension.Density; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.Thing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link RadoneyeHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +public class RadoneyeHandler extends AbstractRadoneyeHandler { + + private static final String SERVICE_UUID = "00001523-1212-efde-1523-785feabcd123"; + private static final String TRIGGER_UID = "00001524-1212-efde-1523-785feabcd123"; + private static final String DATA_UUID = "00001525-1212-efde-1523-785feabcd123"; + + public RadoneyeHandler(Thing thing) { + super(thing); + } + + private final Logger logger = LoggerFactory.getLogger(RadoneyeHandler.class); + + private final UUID dataUuid = UUID.fromString(DATA_UUID); + private final UUID triggerUuid = UUID.fromString(TRIGGER_UID); + private final byte[] triggerData = new byte[] { 0x50 }; + + @Override + protected void updateChannels(int[] is) { + Map data; + try { + data = RadoneyeDataParser.parseRd200Data(is); + logger.debug("Parsed data: {}", data); + Number radon = data.get(RadoneyeDataParser.RADON); + logger.debug("Parsed data radon number: {}", radon); + if (radon != null) { + updateState(CHANNEL_ID_RADON, new QuantityType(radon, BECQUEREL_PER_CUBIC_METRE)); + } + } catch (RadoneyeParserException e) { + logger.error("Failed to parse data received from Radoneye sensor: {}", e.getMessage()); + } + } + + @Override + protected UUID getDataUUID() { + return dataUuid; + } + + @Override + protected UUID getTriggerUUID() { + return triggerUuid; + } + + @Override + protected byte[] getTriggerData() { + return triggerData; + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandlerFactory.java new file mode 100644 index 0000000000000..950ddcc65e328 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandlerFactory.java @@ -0,0 +1,46 @@ +/** + * 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.bluetooth.radoneye.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link RadoneyeHandlerFactory} is responsible for creating things and thing handlers. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +@Component(service = ThingHandlerFactory.class, configurationPid = "binding.radoneye") +public class RadoneyeHandlerFactory extends BaseThingHandlerFactory { + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return RadoneyeBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + if (thingTypeUID.equals(RadoneyeBindingConstants.THING_TYPE_RADONEYE)) { + return new RadoneyeHandler(thing); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeParserException.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeParserException.java new file mode 100644 index 0000000000000..b8861f1051cce --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeParserException.java @@ -0,0 +1,41 @@ +/** + * 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.bluetooth.radoneye.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Exception for data parsing errors. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +public class RadoneyeParserException extends Exception { + + private static final long serialVersionUID = 1; + + public RadoneyeParserException() { + } + + public RadoneyeParserException(String message) { + super(message); + } + + public RadoneyeParserException(String message, Throwable cause) { + super(message, cause); + } + + public RadoneyeParserException(Throwable cause) { + super(cause); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml new file mode 100644 index 0000000000000..4992c2a9c591d --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + Indoor radon monitor + + + + + + + + + + Bluetooth address in XX:XX:XX:XX:XX:XX format + + + + States how often a refresh shall occur in seconds. This could have impact to battery lifetime + 300 + + + + + + Number:Density + + Radon gas level + + + diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java new file mode 100644 index 0000000000000..47b0c68f0c7a0 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java @@ -0,0 +1,51 @@ +/** + * 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.bluetooth.radoneye; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.bluetooth.radoneye.internal.RadoneyeDataParser; +import org.openhab.binding.bluetooth.radoneye.internal.RadoneyeParserException; + +/** + * Tests {@link RadoneyeParserTest}. + * + * @author Peter Obel - Initial contribution + */ +@NonNullByDefault +public class RadoneyeParserTest { + + @Test + public void testEmptyData() { + int[] data = {}; + assertThrows(RadoneyeParserException.class, () -> RadoneyeDataParser.parseRd200Data(data)); + } + + @Test + public void testWrongDataLen() throws RadoneyeParserException { + int[] data = { 1, 55, 51, 0, 122, 0, 61, 0, 119, 9, 11, 194, 169, 2, 46, 0, 0 }; + assertThrows(RadoneyeParserException.class, () -> RadoneyeDataParser.parseRd200Data(data)); + } + + @Test + public void testParsingRd200() throws RadoneyeParserException { + int[] data = { 80, 16, 31, -123, 43, 64, 123, 20, 94, 64, 92, -113, -118, 64, 15, 0, 12, 0, 0, 0 }; + Map result = RadoneyeDataParser.parseRd200Data(data); + + assertEquals(99, result.get(RadoneyeDataParser.RADON).intValue()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java index 52786a248253a..f97100d3ea82e 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java @@ -28,7 +28,7 @@ @NonNullByDefault public class BoschSHCBindingConstants { - private static final String BINDING_ID = "boschshc"; + public static final String BINDING_ID = "boschshc"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_SHC = new ThingTypeUID(BINDING_ID, "shc"); diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java index d8dab396c25c4..5856316b45c0b 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java @@ -46,6 +46,7 @@ import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.types.Command; +import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,8 +96,10 @@ public BridgeHandler(Bridge bridge) { @Override public void initialize() { - logger.debug("Initialize {} Version {}", FrameworkUtil.getBundle(getClass()).getSymbolicName(), - FrameworkUtil.getBundle(getClass()).getVersion()); + Bundle bundle = FrameworkUtil.getBundle(getClass()); + if (bundle != null) { + logger.debug("Initialize {} Version {}", bundle.getSymbolicName(), bundle.getVersion()); + } // Read configuration BridgeConfiguration config = getConfigAs(BridgeConfiguration.class); @@ -190,8 +193,10 @@ private void scheduleInitialAccess(BoschHttpClient httpClient) { * to check if access if possible * pairs this Bosch SHC Bridge with the SHC if necessary * and starts the first log poll. + *

+ * This method is package-protected to enable unit testing. */ - private void initialAccess(BoschHttpClient httpClient) { + /* package */ void initialAccess(BoschHttpClient httpClient) { logger.debug("Initializing Bosch SHC Bridge: {} - HTTP client is: {}", this, httpClient); try { @@ -482,7 +487,7 @@ public Device getDeviceInfo(String deviceId) deviceId, errorResponse.statusCode, errorResponse.errorCode)); } } else { - return new BoschSHCException(String.format("Request for info for device %s failed with status code %d", + return new BoschSHCException(String.format("Request for info of device %s failed with status code %d", deviceId, statusCode)); } }); diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java index 1a243671c56dc..6e85cefdf6bcd 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java @@ -12,13 +12,21 @@ */ package org.openhab.binding.boschshc.internal.devices; -import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.RefreshType; import org.openhab.core.types.UnDefType; import com.google.gson.JsonElement; @@ -35,8 +43,20 @@ public abstract class AbstractBatteryPoweredDeviceHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + @BeforeEach + @Override + public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + super.beforeEach(); + + DeviceServiceData deviceServiceData = new DeviceServiceData(); + deviceServiceData.path = "/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel"; + deviceServiceData.id = "BatteryLevel"; + deviceServiceData.deviceId = "hdm:ZigBee:000d6f0004b93361"; + lenient().when(bridgeHandler.getServiceData(anyString(), anyString())).thenReturn(deviceServiceData); + } + @Test - public void testProcessUpdate_BatteryLevel_LowBattery() { + public void testProcessUpdateBatteryLevelLowBattery() { JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" @@ -44,15 +64,13 @@ public void testProcessUpdate_BatteryLevel_LowBattery() { + " \"type\":\"LOW_BATTERY\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + " ]\n" + " }\n" + "}"); getFixture().processUpdate("BatteryLevel", deviceServiceData); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), new DecimalType(10)); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); } @Test - public void testProcessUpdate_BatteryLevel_CriticalLow() { + public void testProcessUpdateBatteryLevelCriticalLow() { JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" @@ -60,15 +78,13 @@ public void testProcessUpdate_BatteryLevel_CriticalLow() { + " \"type\":\"CRITICAL_LOW\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + " ]\n" + " }\n" + "}"); getFixture().processUpdate("BatteryLevel", deviceServiceData); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), new DecimalType(1)); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); } @Test - public void testProcessUpdate_BatteryLevel_CriticallyLowBattery() { + public void testProcessUpdateBatteryLevelCriticallyLowBattery() { JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" @@ -76,15 +92,13 @@ public void testProcessUpdate_BatteryLevel_CriticallyLowBattery() { + " \"type\":\"CRITICALLY_LOW_BATTERY\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + " ]\n" + " }\n" + "}"); getFixture().processUpdate("BatteryLevel", deviceServiceData); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), new DecimalType(1)); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); } @Test - public void testProcessUpdate_BatteryLevel_OK() { + public void testProcessUpdateBatteryLevelOK() { JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\" }"); @@ -97,7 +111,7 @@ public void testProcessUpdate_BatteryLevel_OK() { } @Test - public void testProcessUpdate_BatteryLevel_NotAvailable() { + public void testProcessUpdateBatteryLevelNotAvailable() { JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" @@ -105,9 +119,21 @@ public void testProcessUpdate_BatteryLevel_NotAvailable() { + " \"type\":\"NOT_AVAILABLE\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + " ]\n" + " }\n" + "}"); getFixture().processUpdate("BatteryLevel", deviceServiceData); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), UnDefType.UNDEF); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + UnDefType.UNDEF); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF); + } + + @Test + public void testHandleCommandRefreshBatteryLevelChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + new DecimalType(100)); + } + + @Test + public void testHandleCommandRefreshLowBatteryChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF); } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java index 5f104b8219f71..ba11a32b005b9 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.boschshc.internal.devices; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.config.core.Configuration; /** @@ -21,8 +22,9 @@ * * @param type of the device handler to be tested */ +@NonNullByDefault public abstract class AbstractBoschSHCDeviceHandlerTest - extends AbstractSHCHandlerTest { + extends AbstractBoschSHCHandlerTest { @Override protected Configuration getConfiguration() { diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCHandlerTest.java similarity index 69% rename from bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java rename to bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCHandlerTest.java index 18fe7ff95c2ff..a3ccf28d93426 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCHandlerTest.java @@ -15,14 +15,20 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.core.config.core.Configuration; 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.ThingStatusDetail; @@ -38,32 +44,33 @@ * * @param type of the handler to be tested */ +@NonNullByDefault @ExtendWith(MockitoExtension.class) -public abstract class AbstractSHCHandlerTest { +public abstract class AbstractBoschSHCHandlerTest { private T fixture; - @Mock - private Thing thing; + private @Mock @NonNullByDefault({}) Thing thing; + + private @Mock @NonNullByDefault({}) Bridge bridge; - @Mock - private Bridge bridge; + protected @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler; - @Mock - private BridgeHandler bridgeHandler; + private @Mock @NonNullByDefault({}) ThingHandlerCallback callback; - @Mock - private ThingHandlerCallback callback; + protected AbstractBoschSHCHandlerTest() { + this.fixture = createFixture(); + } @BeforeEach - public void beforeEach() { + void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { fixture = createFixture(); lenient().when(thing.getUID()).thenReturn(getThingUID()); when(thing.getBridgeUID()).thenReturn(new ThingUID("boschshc", "shc", "myBridgeUID")); when(callback.getBridge(any())).thenReturn(bridge); fixture.setCallback(callback); when(bridge.getHandler()).thenReturn(bridgeHandler); - when(thing.getConfiguration()).thenReturn(getConfiguration()); + lenient().when(thing.getConfiguration()).thenReturn(getConfiguration()); fixture.initialize(); } @@ -80,6 +87,10 @@ protected ThingUID getThingUID() { protected abstract ThingTypeUID getThingTypeUID(); + protected ChannelUID getChannelUID(String channelID) { + return new ChannelUID(getThingUID(), channelID); + } + protected Configuration getConfiguration() { return new Configuration(); } @@ -88,11 +99,11 @@ protected Thing getThing() { return thing; } - public BridgeHandler getBridgeHandler() { + protected BridgeHandler getBridgeHandler() { return bridgeHandler; } - public ThingHandlerCallback getCallback() { + protected ThingHandlerCallback getCallback() { return callback; } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java index 1dc632bc6cc48..6ec8f4b40dce7 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java @@ -13,7 +13,7 @@ package org.openhab.binding.boschshc.internal.devices; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; @@ -22,16 +22,19 @@ import javax.measure.quantity.Energy; import javax.measure.quantity.Power; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.powermeter.dto.PowerMeterServiceState; import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState; import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; -import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.ThingUID; +import org.openhab.core.library.unit.Units; +import org.openhab.core.types.RefreshType; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -43,30 +46,42 @@ * * @param type of the handler to be tested */ +@NonNullByDefault public abstract class AbstractPowerSwitchHandlerTest extends AbstractBoschSHCDeviceHandlerTest { - @Captor - private ArgumentCaptor serviceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor serviceStateCaptor; - @Captor - private ArgumentCaptor> powerCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor> powerCaptor; - @Captor - private ArgumentCaptor> energyCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor> energyCaptor; + + @BeforeEach + @Override + public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + super.beforeEach(); + + PowerSwitchServiceState powerSwitchServiceState = new PowerSwitchServiceState(); + powerSwitchServiceState.switchState = PowerSwitchState.ON; + lenient().when(bridgeHandler.getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class))) + .thenReturn(powerSwitchServiceState); + + PowerMeterServiceState powerMeterServiceState = new PowerMeterServiceState(); + powerMeterServiceState.powerConsumption = 12.34d; + powerMeterServiceState.energyConsumption = 56.78d; + lenient().when(bridgeHandler.getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class))) + .thenReturn(powerMeterServiceState); + } @Test - public void testHandleCommand() + public void testHandleCommandPowerSwitchChannel() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { - - getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), - OnOffType.ON); + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("PowerSwitch"), serviceStateCaptor.capture()); PowerSwitchServiceState state = serviceStateCaptor.getValue(); assertSame(PowerSwitchState.ON, state.switchState); - getFixture().handleCommand(new ChannelUID(new ThingUID(getThingTypeUID(), "abcdef"), - BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF); + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF); verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("PowerSwitch"), serviceStateCaptor.capture()); state = serviceStateCaptor.getValue(); @@ -74,36 +89,54 @@ public void testHandleCommand() } @Test - public void testUpdateChannel_PowerSwitchState() { + public void testUpdateChannelPowerSwitchState() { JsonElement jsonObject = JsonParser .parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"ON\"\n" + "}"); getFixture().processUpdate("PowerSwitch", jsonObject); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); jsonObject = JsonParser .parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"OFF\"\n" + "}"); getFixture().processUpdate("PowerSwitch", jsonObject); - verify(getCallback()).stateUpdated( - new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF); } @Test - public void testUpdateChannel_PowerMeterServiceState() { + public void testUpdateChannelPowerMeterServiceState() { JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"powerMeterState\",\n" + " \"powerConsumption\": \"23\",\n" + " \"energyConsumption\": 42\n" + "}"); getFixture().processUpdate("PowerMeter", jsonObject); - verify(getCallback()).stateUpdated( - eq(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)), + verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)), powerCaptor.capture()); QuantityType powerValue = powerCaptor.getValue(); assertEquals(23, powerValue.intValue()); - verify(getCallback()).stateUpdated( - eq(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)), + verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)), energyCaptor.capture()); QuantityType energyValue = energyCaptor.getValue(); assertEquals(42, energyValue.intValue()); } + + @Test + public void testHandleCommandRefreshPowerSwitchChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); + } + + @Test + public void testHandleCommandRefreshPowerConsumptionChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION), + RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION), + new QuantityType(12.34d, Units.WATT)); + } + + @Test + public void testHandleCommandRefreshEnergyConsumptionChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION), + RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION), + new QuantityType(56.78d, Units.WATT_HOUR)); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java index a04452bdf4b15..76898ffaeeb2f 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java @@ -14,8 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -28,9 +27,14 @@ import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.SmokeDetectorCheckState; import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.dto.SmokeDetectorCheckServiceState; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PlayPauseType; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -51,7 +55,6 @@ public abstract class AbstractSmokeDetectorHandlerTest fields = ReflectionSupport.findFields(BoschHttpClient.class, + f -> f.getName().equalsIgnoreCase("logger"), HierarchyTraversalMode.TOP_DOWN); + Field field = fields.iterator().next(); + field.setAccessible(true); + field.set(mockedHttpClient, mockedLogger); + + Request request = mock(Request.class); + when(mockedHttpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(500); + assertFalse(mockedHttpClient.isOnline()); + } + + @Test + void isOnlineMockedResponse() throws InterruptedException, TimeoutException, ExecutionException, + IllegalArgumentException, IllegalAccessException { + BoschHttpClient mockedHttpClient = mock(BoschHttpClient.class); + when(mockedHttpClient.isOnline()).thenCallRealMethod(); + when(mockedHttpClient.getPublicInformationUrl()).thenCallRealMethod(); + + // mock a logger using reflection to avoid NPEs during logger calls + Logger mockedLogger = mock(Logger.class); + List fields = ReflectionSupport.findFields(BoschHttpClient.class, + f -> f.getName().equalsIgnoreCase("logger"), HierarchyTraversalMode.TOP_DOWN); + Field field = fields.iterator().next(); + field.setAccessible(true); + field.set(mockedHttpClient, mockedLogger); + + Request request = mock(Request.class); + when(mockedHttpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("response"); + assertTrue(mockedHttpClient.isOnline()); + } + @Test void doPairing() throws InterruptedException { assertFalse(httpClient.doPairing()); @@ -104,16 +164,103 @@ void createRequest() { @Test void createRequestWithObject() { - Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET, "someData"); + BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState(); + binarySwitchState.on = true; + Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET, binarySwitchState); assertNotNull(request); + assertEquals("{\"on\":true,\"stateType\":\"binarySwitchState\",\"@type\":\"binarySwitchState\"}", + StandardCharsets.UTF_8.decode(request.getContent().iterator().next()).toString()); } @Test - void sendRequest() { - Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET); - // Null pointer exception is expected, because localhost will not answer request - assertThrows(NullPointerException.class, () -> { - httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null); - }); + void sendRequest() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("{\"jsonrpc\": \"2.0\", \"result\": \"test result\"}"); + + SubscribeResult subscribeResult = httpClient.sendRequest(request, SubscribeResult.class, + SubscribeResult::isValid, null); + assertEquals("2.0", subscribeResult.getJsonrpc()); + assertEquals("test result", subscribeResult.getResult()); + } + + @Test + void sendRequestResponseError() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(500); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null)); + assertEquals("Request failed with status code 500", e.getMessage()); + } + + @Test + void sendRequestResponseErrorWithErrorHandler() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(500); + when(response.getContentAsString()).thenReturn( + "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}"); + + BoschSHCException e = assertThrows(BoschSHCException.class, () -> httpClient.sendRequest(request, Device.class, + Device::isValid, (Integer statusCode, String content) -> { + return new BoschSHCException("test exception"); + })); + assertEquals("test exception", e.getMessage()); + } + + @Test + void sendRequestEmptyResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null)); + assertEquals( + "Received no content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult", + e.getMessage()); + } + + @Test + void sendRequestInvalidResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn( + "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}"); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> { + return false; + }, null)); + String actualMessage = e.getMessage(); + assertTrue(actualMessage.contains( + "Received invalid content for type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult:")); + } + + @Test + void sendRequestInvalidSyntaxInResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("{\"@type\": \"JsonRestExceptionResponseEntity}"); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> { + return false; + }, null)); + assertEquals( + "Received invalid content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult: com.google.gson.stream.MalformedJsonException: Unterminated string at line 1 column 44 path $.@type", + e.getMessage()); } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java new file mode 100644 index 0000000000000..4a6a8d38f33c5 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java @@ -0,0 +1,35 @@ +/** + * 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.boschshc.internal.devices.bridge; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link BridgeConfiguration}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class BridgeConfigurationTest { + + @Test + void testConstructor() { + BridgeConfiguration fixture = new BridgeConfiguration(); + assertEquals("", fixture.ipAddress); + assertEquals("", fixture.password); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java index 3c61340c1fa64..1f77e17416b2b 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java @@ -12,45 +12,101 @@ */ package org.openhab.binding.boschshc.internal.devices.bridge; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import java.util.function.BiFunction; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpMethod; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceTest; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.binaryswitch.dto.BinarySwitchServiceState; import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.AlarmState; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.ArmingState; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionSystemState; +import org.openhab.binding.boschshc.internal.services.shuttercontact.ShutterContactState; +import org.openhab.binding.boschshc.internal.services.shuttercontact.dto.ShutterContactServiceState; +import org.openhab.core.config.core.Configuration; import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.ThingHandlerCallback; +import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; /** * Unit tests for the {@link BridgeHandler}. - * + * * @author David Pace - Initial contribution * */ @NonNullByDefault class BridgeHandlerTest { - @Nullable - private BridgeHandler fixture; + private @NonNullByDefault({}) BridgeHandler fixture; + + private @NonNullByDefault({}) BoschHttpClient httpClient; - @Nullable - private BoschHttpClient httpClient; + private @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback; + + @BeforeAll + static void beforeAll() throws IOException { + Path mavenTargetFolder = Paths.get("target"); + assertTrue(Files.exists(mavenTargetFolder), "Maven target folder does not exist."); + System.setProperty("openhab.userdata", mavenTargetFolder.toFile().getAbsolutePath()); + Path etc = mavenTargetFolder.resolve("etc"); + if (!Files.exists(etc)) { + Files.createDirectory(etc); + } + } @BeforeEach - void beforeEach() { + void beforeEach() throws Exception { Bridge bridge = mock(Bridge.class); fixture = new BridgeHandler(bridge); + + thingHandlerCallback = mock(ThingHandlerCallback.class); + fixture.setCallback(thingHandlerCallback); + + Configuration bridgeConfiguration = new Configuration(); + Map properties = new HashMap<>(); + properties.put("ipAddress", "localhost"); + properties.put("password", "test"); + bridgeConfiguration.setProperties(properties); + + Thing thing = mock(Bridge.class); + when(thing.getConfiguration()).thenReturn(bridgeConfiguration); + // this calls initialize() as well + fixture.thingUpdated(thing); + + // shut down the real HTTP client + if (fixture.httpClient != null) { + fixture.httpClient.stop(); + } + + // use a mocked HTTP client httpClient = mock(BoschHttpClient.class); fixture.httpClient = httpClient; } @@ -69,4 +125,265 @@ void postAction() throws InterruptedException, TimeoutException, ExecutionExcept verify(httpClient).createRequest(eq(url), same(HttpMethod.POST), same(request)); verify(mockRequest).send(); } + + @Test + void initialAccessHttpClientOffline() { + fixture.initialAccess(httpClient); + } + + @Test + void initialAccessHttpClientOnline() throws InterruptedException { + when(httpClient.isOnline()).thenReturn(true); + fixture.initialAccess(httpClient); + } + + @Test + void initialAccessAccessPossible() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.isOnline()).thenReturn(true); + when(httpClient.isAccessPossible()).thenReturn(true); + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + // mock a request and response to obtain rooms + Request roomsRequest = mock(Request.class); + ContentResponse roomsResponse = mock(ContentResponse.class); + when(roomsResponse.getStatus()).thenReturn(200); + when(roomsResponse.getContentAsString()).thenReturn( + "[{\"@type\":\"room\",\"id\":\"hz_1\",\"iconId\":\"icon_room_bedroom\",\"name\":\"Bedroom\"}]"); + when(roomsRequest.send()).thenReturn(roomsResponse); + when(httpClient.createRequest(contains("/rooms"), same(HttpMethod.GET))).thenReturn(roomsRequest); + + // mock a request and response to obtain devices + Request devicesRequest = mock(Request.class); + ContentResponse devicesResponse = mock(ContentResponse.class); + when(devicesResponse.getStatus()).thenReturn(200); + when(devicesResponse.getContentAsString()).thenReturn("[{\"@type\":\"device\",\r\n" + + " \"rootDeviceId\":\"64-da-a0-02-14-9b\",\r\n" + + " \"id\":\"hdm:HomeMaticIP:3014F711A00004953859F31B\",\r\n" + + " \"deviceServiceIds\":[\"PowerMeter\",\"PowerSwitch\",\"PowerSwitchProgram\",\"Routing\"],\r\n" + + " \"manufacturer\":\"BOSCH\",\r\n" + " \"roomId\":\"hz_3\",\r\n" + " \"deviceModel\":\"PSM\",\r\n" + + " \"serial\":\"3014F711A00004953859F31B\",\r\n" + " \"profile\":\"GENERIC\",\r\n" + + " \"name\":\"Coffee Machine\",\r\n" + " \"status\":\"AVAILABLE\",\r\n" + " \"childDeviceIds\":[]\r\n" + + " }]"); + when(devicesRequest.send()).thenReturn(devicesResponse); + when(httpClient.createRequest(contains("/devices"), same(HttpMethod.GET))).thenReturn(devicesRequest); + + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.initialAccess(httpClient); + verify(thingHandlerCallback).statusUpdated(any(), + eq(ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build())); + } + + @Test + void getState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("{\r\n" + " \"@type\": \"systemState\",\r\n" + + " \"systemAvailability\": {\r\n" + " \"@type\": \"systemAvailabilityState\",\r\n" + + " \"available\": true,\r\n" + " \"deleted\": false\r\n" + " },\r\n" + + " \"armingState\": {\r\n" + " \"@type\": \"armingState\",\r\n" + + " \"state\": \"SYSTEM_DISARMED\",\r\n" + " \"deleted\": false\r\n" + " },\r\n" + + " \"alarmState\": {\r\n" + " \"@type\": \"alarmState\",\r\n" + + " \"value\": \"ALARM_OFF\",\r\n" + " \"incidents\": [],\r\n" + + " \"deleted\": false\r\n" + " },\r\n" + " \"activeConfigurationProfile\": {\r\n" + + " \"@type\": \"activeConfigurationProfile\",\r\n" + " \"deleted\": false\r\n" + + " },\r\n" + " \"securityGapState\": {\r\n" + " \"@type\": \"securityGapState\",\r\n" + + " \"securityGaps\": [],\r\n" + " \"deleted\": false\r\n" + " },\r\n" + + " \"deleted\": false\r\n" + " }"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + IntrusionDetectionSystemState state = fixture.getState("intrusion/states/system", + IntrusionDetectionSystemState.class); + assertNotNull(state); + assertTrue(state.systemAvailability.available); + assertSame(AlarmState.ALARM_OFF, state.alarmState.value); + assertSame(ArmingState.SYSTEM_DISARMED, state.armingState.state); + } + + @Test + void getDeviceState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceStateUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()) + .thenReturn("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"OPEN\"\n" + " }"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + ShutterContactServiceState state = fixture.getState("hdm:HomeMaticIP:3014D711A000009D545DEB39D", + "ShutterContact", ShutterContactServiceState.class); + assertNotNull(state); + assertSame(ShutterContactState.OPEN, state.value); + } + + @Test + void getDeviceInfo() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(200); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + when(httpClient.sendRequest(same(request), same(Device.class), any(), any())) + .thenReturn(DeviceTest.createTestDevice()); + + String deviceId = "hdm:HomeMaticIP:3014F711A00004953859F31B"; + Device device = fixture.getDeviceInfo(deviceId); + assertEquals(deviceId, device.id); + } + + @Test + void getDeviceInfoErrorCases() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(200); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + @SuppressWarnings("unchecked") + ArgumentCaptor> errorResponseHandlerCaptor = ArgumentCaptor + .forClass(BiFunction.class); + + when(httpClient.sendRequest(same(request), same(Device.class), any(), errorResponseHandlerCaptor.capture())) + .thenReturn(DeviceTest.createTestDevice()); + + String deviceId = "hdm:HomeMaticIP:3014F711A00004953859F31B"; + fixture.getDeviceInfo(deviceId); + + BiFunction errorResponseHandler = errorResponseHandlerCaptor.getValue(); + Exception e = errorResponseHandler.apply(500, + "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}"); + assertEquals( + "Request for info of device hdm:HomeMaticIP:3014F711A00004953859F31B failed with status code 500 and error code testErrorCode", + e.getMessage()); + + e = errorResponseHandler.apply(404, + "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"ENTITY_NOT_FOUND\",\"statusCode\": 404}"); + assertNotNull(e); + + e = errorResponseHandler.apply(500, ""); + assertEquals("Request for info of device hdm:HomeMaticIP:3014F711A00004953859F31B failed with status code 500", + e.getMessage()); + } + + @Test + void getServiceData() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" + + " \"faults\":{ \n" + " \"entries\":[\n" + " {\n" + + " \"type\":\"LOW_BATTERY\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + + " ]\n" + " }\n" + "}"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + DeviceServiceData serviceData = fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel"); + assertNotNull(serviceData); + Faults faults = serviceData.faults; + assertNotNull(faults); + assertEquals("LOW_BATTERY", faults.entries.get(0).type); + } + + @Test + void getServiceDataError() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(500); + when(response.getContentAsString()).thenReturn( + "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + when(httpClient.sendRequest(same(request), same(Device.class), any(), any())) + .thenReturn(DeviceTest.createTestDevice()); + + BoschSHCException e = assertThrows(BoschSHCException.class, + () -> fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel")); + assertEquals( + "State request with URL https://null:8444/smarthome/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel failed with status code 500 and error code testErrorCode", + e.getMessage()); + } + + @Test + void getServiceDataErrorNoRestExceptionResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(500); + when(response.getContentAsString()).thenReturn(""); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + BoschSHCException e = assertThrows(BoschSHCException.class, + () -> fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel")); + assertEquals( + "State request with URL https://null:8444/smarthome/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel failed with status code 500", + e.getMessage()); + } + + @Test + void putState() throws InterruptedException, TimeoutException, ExecutionException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceStateUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + + when(httpClient.createRequest(anyString(), same(HttpMethod.PUT), any(BinarySwitchServiceState.class))) + .thenReturn(request); + when(request.send()).thenReturn(response); + + BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState(); + binarySwitchState.on = true; + fixture.putState("hdm:ZigBee:f0d1b80000f2a3e9", "BinarySwitch", binarySwitchState); + } + + @AfterEach + void afterEach() throws Exception { + fixture.dispose(); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java new file mode 100644 index 0000000000000..7d3b43b39b7b4 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java @@ -0,0 +1,69 @@ +/** + * 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.boschshc.internal.devices.bridge; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link JsonRpcRequest}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class JsonRpcRequestTest { + + private @NonNullByDefault({}) JsonRpcRequest fixture; + + @BeforeEach + protected void setUp() throws Exception { + fixture = new JsonRpcRequest("2.0", "RE/longPoll", new String[] { "subscriptionId", "20" }); + } + + @Test + public void testConstructor() { + assertEquals("2.0", fixture.getJsonrpc()); + assertEquals("RE/longPoll", fixture.getMethod()); + assertArrayEquals(new String[] { "subscriptionId", "20" }, fixture.getParams()); + } + + @Test + public void testNoArgConstructor() { + fixture = new JsonRpcRequest(); + assertEquals("", fixture.getJsonrpc()); + assertEquals("", fixture.getMethod()); + assertArrayEquals(new String[0], fixture.getParams()); + } + + @Test + public void testSetJsonrpc() { + fixture.setJsonrpc("test"); + assertEquals("test", fixture.getJsonrpc()); + } + + @Test + public void testSetMethod() { + fixture.setMethod("RE/subscribe"); + assertEquals("RE/subscribe", fixture.getMethod()); + } + + @Test + public void testSetParams() { + fixture.setParams(new String[] { "com/bosch/sh/remote/*", null }); + assertArrayEquals(new String[] { "com/bosch/sh/remote/*", null }, fixture.getParams()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java new file mode 100644 index 0000000000000..524d23fc111b8 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java @@ -0,0 +1,327 @@ +/** + * 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.boschshc.internal.devices.bridge; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Response.CompleteListener; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.http.HttpMethod; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException; + +import com.google.gson.JsonObject; + +/** + * Unit tests for {@link LongPolling}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +@ExtendWith(MockitoExtension.class) +public class LongPollingTest { + + /** + * A dummy implementation of {@link ScheduledFuture}. + *

+ * This is required because we can not return null in the executor service test implementation (see + * below). + * + * @author David Pace - Initial contribution + * + * @param The result type returned by this Future + */ + private static class NullScheduledFuture implements ScheduledFuture { + + @Override + public long getDelay(@Nullable TimeUnit unit) { + return 0; + } + + @Override + public int compareTo(@Nullable Delayed o) { + return 0; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return false; + } + + @Override + public T get() throws InterruptedException, ExecutionException { + return null; + } + + @Override + public T get(long timeout, @Nullable TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return null; + } + } + + /** + * Executor service implementation that runs all runnables in the same thread in order to enable deterministic + * testing. + * + * @author David Pace - Initial contribution + * + */ + private static class SameThreadExecutorService extends AbstractExecutorService implements ScheduledExecutorService { + + private volatile boolean terminated; + + @Override + public void shutdown() { + terminated = true; + } + + @NonNullByDefault({}) + @Override + public List shutdownNow() { + return Collections.emptyList(); + } + + @Override + public boolean isShutdown() { + return terminated; + } + + @Override + public boolean isTerminated() { + return terminated; + } + + @Override + public boolean awaitTermination(long timeout, @Nullable TimeUnit unit) throws InterruptedException { + shutdown(); + return terminated; + } + + @Override + public void execute(@Nullable Runnable command) { + if (command != null) { + // execute in the same thread in unit tests + command.run(); + } + } + + @Override + public ScheduledFuture schedule(@Nullable Runnable command, long delay, @Nullable TimeUnit unit) { + // not used in this tests + return new NullScheduledFuture(); + } + + @Override + public ScheduledFuture schedule(@Nullable Callable callable, long delay, @Nullable TimeUnit unit) { + return new NullScheduledFuture(); + } + + @Override + public ScheduledFuture scheduleAtFixedRate(@Nullable Runnable command, long initialDelay, long period, + @Nullable TimeUnit unit) { + if (command != null) { + command.run(); + } + return new NullScheduledFuture(); + } + + @Override + public ScheduledFuture scheduleWithFixedDelay(@Nullable Runnable command, long initialDelay, long delay, + @Nullable TimeUnit unit) { + if (command != null) { + command.run(); + } + return new NullScheduledFuture(); + } + } + + private @NonNullByDefault({}) LongPolling fixture; + + private @NonNullByDefault({}) BoschHttpClient httpClient; + + private @Mock @NonNullByDefault({}) Consumer<@NonNull LongPollResult> longPollHandler; + + private @Mock @NonNullByDefault({}) Consumer<@NonNull Throwable> failureHandler; + + @BeforeEach + void beforeEach() { + fixture = new LongPolling(new SameThreadExecutorService(), longPollHandler, failureHandler); + httpClient = mock(BoschHttpClient.class); + } + + @Test + void start() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + // when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request subscribeRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/subscribe")))).thenReturn(subscribeRequest); + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.start(httpClient); + + ArgumentCaptor completeListener = ArgumentCaptor.forClass(CompleteListener.class); + verify(longPollRequest).send(completeListener.capture()); + + BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue(); + + String longPollResultJSON = "{\"result\":[{\"path\":\"/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch\",\"@type\":\"DeviceServiceData\",\"id\":\"PowerSwitch\",\"state\":{\"@type\":\"powerSwitchState\",\"switchState\":\"ON\"},\"deviceId\":\"hdm:HomeMaticIP:3014F711A0001916D859A8A9\"}],\"jsonrpc\":\"2.0\"}\n"; + Response response = mock(Response.class); + bufferingResponseListener.onContent(response, + ByteBuffer.wrap(longPollResultJSON.getBytes(StandardCharsets.UTF_8))); + + Result result = mock(Result.class); + bufferingResponseListener.onComplete(result); + + ArgumentCaptor longPollResultCaptor = ArgumentCaptor.forClass(LongPollResult.class); + verify(longPollHandler).accept(longPollResultCaptor.capture()); + LongPollResult longPollResult = longPollResultCaptor.getValue(); + assertEquals(1, longPollResult.result.size()); + DeviceServiceData longPollResultItem = longPollResult.result.get(0); + assertEquals("hdm:HomeMaticIP:3014F711A0001916D859A8A9", longPollResultItem.deviceId); + assertEquals("/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch", longPollResultItem.path); + assertEquals("PowerSwitch", longPollResultItem.id); + JsonObject stateObject = (JsonObject) longPollResultItem.state; + assertNotNull(stateObject); + assertEquals("ON", stateObject.get("switchState").getAsString()); + } + + @Test + void startSubscriptionFailure() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())) + .thenThrow(new ExecutionException("Subscription failed.", null)); + + LongPollingFailedException e = assertThrows(LongPollingFailedException.class, () -> fixture.start(httpClient)); + assertTrue(e.getMessage().contains("Subscription failed.")); + } + + @Test + void startLongPollFailure() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), any(JsonRpcRequest.class))) + .thenReturn(request); + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.start(httpClient); + + ArgumentCaptor completeListener = ArgumentCaptor.forClass(CompleteListener.class); + verify(longPollRequest).send(completeListener.capture()); + + BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue(); + + Result result = mock(Result.class); + ExecutionException exception = new ExecutionException("test exception", null); + when(result.getFailure()).thenReturn(exception); + bufferingResponseListener.onComplete(result); + + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(failureHandler).accept(throwableCaptor.capture()); + Throwable t = throwableCaptor.getValue(); + assertEquals("Unexpected exception during long polling request", t.getMessage()); + assertSame(exception, t.getCause()); + } + + @Test + void startSubscriptionInvalid() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request subscribeRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/subscribe")))).thenReturn(subscribeRequest); + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.start(httpClient); + + ArgumentCaptor completeListener = ArgumentCaptor.forClass(CompleteListener.class); + verify(longPollRequest).send(completeListener.capture()); + + BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue(); + + String longPollResultJSON = "{\"jsonrpc\":\"2.0\",\"error\": {\"code\":-32001,\"message\":\"No subscription with id: e8fei62b0-0\"}}\n"; + Response response = mock(Response.class); + bufferingResponseListener.onContent(response, + ByteBuffer.wrap(longPollResultJSON.getBytes(StandardCharsets.UTF_8))); + + Result result = mock(Result.class); + bufferingResponseListener.onComplete(result); + } + + @AfterEach + void afterEach() { + fixture.stop(); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java new file mode 100644 index 0000000000000..cfe653674fe49 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java @@ -0,0 +1,40 @@ +/** + * 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.boschshc.internal.devices.bridge.dto; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link DeviceServiceData}. + * + * @author David Pace - Initial contribution + * + */ +public class DeviceServiceDataTest { + + private DeviceServiceData fixture; + + @BeforeEach + void beforeEach() { + fixture = new DeviceServiceData(); + fixture.deviceId = "64-da-a0-02-14-9b"; + } + + @Test + public void testToString() { + assertEquals("64-da-a0-02-14-9b state: DeviceServiceData", fixture.toString()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java new file mode 100644 index 0000000000000..9ee3ac9edf7aa --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java @@ -0,0 +1,68 @@ +/** + * 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.boschshc.internal.devices.bridge.dto; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link Device}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class DeviceTest { + + public static Device createTestDevice() { + Device device = new Device(); + device.type = "device"; + device.rootDeviceId = "64-da-a0-02-14-9b"; + device.id = "hdm:HomeMaticIP:3014F711A00004953859F31B"; + device.deviceServiceIds = Collections + .unmodifiableList(List.of("PowerMeter", "PowerSwitch", "PowerSwitchProgram", "Routing")); + device.manufacturer = "BOSCH"; + device.roomId = "hz_3"; + device.deviceModel = "PSM"; + device.serial = "3014F711A00004953859F31B"; + device.profile = "GENERIC"; + device.name = "Coffee Machine"; + device.status = "AVAILABLE"; + return device; + } + + private @NonNullByDefault({}) Device fixture; + + @BeforeEach + void beforeEach() { + fixture = createTestDevice(); + } + + @Test + void testIsValid() { + assertTrue(Device.isValid(fixture)); + } + + @Test + public void testToString() { + assertEquals( + "Type device; RootDeviceId: 64-da-a0-02-14-9b; Id: hdm:HomeMaticIP:3014F711A00004953859F31B; Device Service Ids: PowerMeter, PowerSwitch, PowerSwitchProgram, Routing; Manufacturer: BOSCH; Room Id: hz_3; Device Model: PSM; Serial: 3014F711A00004953859F31B; Profile: GENERIC; Name: Coffee Machine; Status: AVAILABLE; Child Device Ids: null ", + fixture.toString()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java index 0cb373d3655f3..fb8a3e3c24817 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java @@ -12,8 +12,7 @@ */ package org.openhab.binding.boschshc.internal.devices.bridge.dto; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; @@ -30,13 +29,11 @@ public class LongPollResultTest { private final Gson gson = new Gson(); @Test - public void noResultsForErrorResult() { + void noResultsForErrorResult() { LongPollResult longPollResult = gson.fromJson( "{\"jsonrpc\":\"2.0\", \"error\": { \"code\":-32001, \"message\":\"No subscription with id: e8fei62b0-0\" } }", LongPollResult.class); - assertNotEquals(null, longPollResult); - if (longPollResult != null) { - assertEquals(null, longPollResult.result); - } + assertNotNull(longPollResult); + assertEquals(null, longPollResult.result); } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java new file mode 100644 index 0000000000000..b2b79190920a6 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java @@ -0,0 +1,134 @@ +/** + * 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.boschshc.internal.devices.camera; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.cameranotification.CameraNotificationState; +import org.openhab.binding.boschshc.internal.services.cameranotification.dto.CameraNotificationServiceState; +import org.openhab.binding.boschshc.internal.services.privacymode.PrivacyModeState; +import org.openhab.binding.boschshc.internal.services.privacymode.dto.PrivacyModeServiceState; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link CameraHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class CameraHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor privacyModeServiceStateCaptor; + + private @Captor @NonNullByDefault({}) ArgumentCaptor cameraNotificationServiceStateCaptor; + + @Override + protected CameraHandler createFixture() { + return new CameraHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_CAMERA_360; + } + + @Override + protected String getDeviceID() { + return "8e28ce2d-e7bf-3e3d-8e3a-a78de61b493e"; + } + + @Test + public void testHandleCommandPrivacyMode() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), + OnOffType.ON); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("PrivacyMode"), + privacyModeServiceStateCaptor.capture()); + PrivacyModeServiceState state = privacyModeServiceStateCaptor.getValue(); + assertSame(PrivacyModeState.ENABLED, state.value); + + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), + OnOffType.OFF); + verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("PrivacyMode"), + privacyModeServiceStateCaptor.capture()); + state = privacyModeServiceStateCaptor.getValue(); + assertSame(PrivacyModeState.DISABLED, state.value); + } + + @Test + public void testHandleCommandCameraNotification() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.ON); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("CameraNotification"), + cameraNotificationServiceStateCaptor.capture()); + CameraNotificationServiceState state = cameraNotificationServiceStateCaptor.getValue(); + assertSame(CameraNotificationState.ENABLED, state.value); + + getFixture().handleCommand( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.OFF); + verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("CameraNotification"), + cameraNotificationServiceStateCaptor.capture()); + state = cameraNotificationServiceStateCaptor.getValue(); + assertSame(CameraNotificationState.DISABLED, state.value); + } + + @Test + public void testUpdateChannelsPrivacyModeState() { + JsonElement jsonObject = JsonParser.parseString("{\"@type\":\"privacyModeState\",\"value\":\"ENABLED\"}"); + getFixture().processUpdate("PrivacyMode", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), OnOffType.ON); + + jsonObject = JsonParser.parseString("{\"@type\":\"privacyModeState\",\"value\":\"DISABLED\"}"); + getFixture().processUpdate("PrivacyMode", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), OnOffType.OFF); + } + + @Test + public void testUpdateChannelsCameraNotificationState() { + JsonElement jsonObject = JsonParser + .parseString("{\"@type\":\"cameraNotificationState\",\"value\":\"ENABLED\"}"); + getFixture().processUpdate("CameraNotification", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.ON); + + jsonObject = JsonParser.parseString("{\"@type\":\"cameraNotificationState\",\"value\":\"DISABLED\"}"); + getFixture().processUpdate("CameraNotification", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.OFF); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java new file mode 100644 index 0000000000000..86dc3120bf0a1 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java @@ -0,0 +1,98 @@ +/** + * 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.boschshc.internal.devices.climatecontrol; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.roomclimatecontrol.dto.RoomClimateControlServiceState; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link ClimateControlHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class ClimateControlHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor roomClimateControlServiceStateCaptor; + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:abcd6fc012ad25b1"; + } + + @Override + protected ClimateControlHandler createFixture() { + return new ClimateControlHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_CLIMATE_CONTROL; + } + + @Test + public void testHandleCommandRoomClimateControlService() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + QuantityType temperature = new QuantityType<>(21.5, SIUnits.CELSIUS); + getFixture().handleCommand( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SETPOINT_TEMPERATURE), + temperature); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("RoomClimateControl"), + roomClimateControlServiceStateCaptor.capture()); + RoomClimateControlServiceState state = roomClimateControlServiceStateCaptor.getValue(); + assertEquals(temperature, state.getSetpointTemperatureState()); + } + + @Test + public void testUpdateChannelsTemperatureLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"temperatureLevelState\",\n" + " \"temperature\": 21.5\n" + " }"); + getFixture().processUpdate("TemperatureLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } + + @Test + public void testUpdateChannelsRoomClimateControlService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"climateControlState\",\n" + " \"setpointTemperature\": 21.5\n" + " }"); + getFixture().processUpdate("RoomClimateControl", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SETPOINT_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java new file mode 100644 index 0000000000000..caf80c43264a6 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java @@ -0,0 +1,139 @@ +/** + * 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.boschshc.internal.devices.intrusion; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit test for {@link IntrusionDetectionHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class IntrusionDetectionHandlerTest extends AbstractBoschSHCHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor armActionRequestCaptor; + + @Override + protected IntrusionDetectionHandler createFixture() { + return new IntrusionDetectionHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_INTRUSION_DETECTION_SYSTEM; + } + + @Test + public void testHandleCommandArmAction() throws InterruptedException, TimeoutException, ExecutionException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARM_ACTION), + new StringType("0")); + verify(getBridgeHandler()).postAction(eq("intrusion/actions/arm"), armActionRequestCaptor.capture()); + ArmActionRequest armRequest = armActionRequestCaptor.getValue(); + assertEquals("0", armRequest.profileId); + } + + @Test + public void testHandleCommandDisarmAction() throws InterruptedException, TimeoutException, ExecutionException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_DISARM_ACTION), + OnOffType.ON); + verify(getBridgeHandler()).postAction("intrusion/actions/disarm"); + } + + @Test + public void testHandleCommandMuteAction() throws InterruptedException, TimeoutException, ExecutionException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_MUTE_ACTION), + OnOffType.ON); + verify(getBridgeHandler()).postAction("intrusion/actions/mute"); + } + + @Test + public void testUpdateChannelsIntrusionDetectionSystemState() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"systemState\",\n" + + " \"systemAvailability\": {\n" + " \"@type\": \"systemAvailabilityState\",\n" + + " \"available\": true,\n" + " \"deleted\": false\n" + " },\n" + + " \"armingState\": {\n" + " \"@type\": \"armingState\",\n" + + " \"state\": \"SYSTEM_DISARMED\",\n" + " \"deleted\": false\n" + " },\n" + + " \"alarmState\": {\n" + " \"@type\": \"alarmState\",\n" + + " \"value\": \"ALARM_OFF\",\n" + " \"incidents\": [],\n" + + " \"deleted\": false\n" + " },\n" + " \"activeConfigurationProfile\": {\n" + + " \"@type\": \"activeConfigurationProfile\",\n" + " \"deleted\": false\n" + + " },\n" + " \"securityGapState\": {\n" + " \"@type\": \"securityGapState\",\n" + + " \"securityGaps\": [],\n" + " \"deleted\": false\n" + " },\n" + + " \"deleted\": false\n" + " }\n"); + getFixture().processUpdate(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SYSTEM_AVAILABILITY), + OnOffType.ON); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARMING_STATE), + new StringType("SYSTEM_DISARMED")); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ALARM_STATE), + new StringType("ALARM_OFF")); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ACTIVE_CONFIGURATION_PROFILE), + new StringType(null)); + } + + @Test + public void testUpdateChannelsIntrusionDetectionControlState() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"intrusionDetectionControlState\",\n" + + " \"activeProfile\": \"0\",\n" + " \"alarmActivationDelayTime\": 30,\n" + " \"actuators\": [\n" + + " {\n" + " \"readonly\": false,\n" + " \"active\": true,\n" + + " \"id\": \"intrusion:video\"\n" + " },\n" + " {\n" + " \"readonly\": false,\n" + + " \"active\": false,\n" + " \"id\": \"intrusion:siren\"\n" + " }\n" + " ],\n" + + " \"remainingTimeUntilArmed\": 29559,\n" + " \"armActivationDelayTime\": 30,\n" + + " \"triggers\": [\n" + " {\n" + " \"readonly\": false,\n" + " \"active\": true,\n" + + " \"id\": \"hdm:ZigBee:000d6f0012f02378\"\n" + " }\n" + " ],\n" + + " \"value\": \"SYSTEM_ARMING\"\n" + " }"); + getFixture().processUpdate("IntrusionDetectionControl", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARMING_STATE), + new StringType("SYSTEM_ARMING")); + } + + @Test + public void testUpdateChannelsSurveillanceAlarmState() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"surveillanceAlarmState\",\n" + + " \"incidents\": [\n" + " {\n" + " \"triggerName\": \"Motion Detector\",\n" + + " \"locationId\": \"hz_5\",\n" + " \"location\": \"Living Room\",\n" + + " \"id\": \"hdm:ZigBee:000d6f0012f02342\",\n" + " \"time\": 1652615755336,\n" + + " \"type\": \"INTRUSION\"\n" + " }\n" + " ],\n" + " \"value\": \"ALARM_ON\"\n" + " }"); + getFixture().processUpdate("SurveillanceAlarm", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ALARM_STATE), + new StringType("ALARM_ON")); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java index f36f78805271a..418218426bf7f 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java @@ -12,11 +12,19 @@ */ package org.openhab.binding.boschshc.internal.devices.motiondetector; +import static org.mockito.Mockito.verify; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link MotionDetectorHandler}. * @@ -40,4 +48,14 @@ protected String getDeviceID() { protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_MOTION_DETECTOR; } + + @Test + public void testUpdateChannelsLatestMotionService() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"latestMotionState\",\n" + + " \"latestMotionDetected\": \"2020-04-03T19:02:19.054Z\"\n" + " }"); + getFixture().processUpdate("LatestMotion", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LATEST_MOTION), + new DateTimeType("2020-04-03T19:02:19.054Z")); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java new file mode 100644 index 0000000000000..e3a9a5a075030 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java @@ -0,0 +1,114 @@ +/** + * 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.boschshc.internal.devices.shuttercontrol; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.shuttercontrol.OperationState; +import org.openhab.binding.boschshc.internal.services.shuttercontrol.dto.ShutterControlServiceState; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link ShutterControlHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class ShutterControlHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor shutterControlServiceStateCaptor; + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:abcd6fc012ad25b1"; + } + + @Override + protected ShutterControlHandler createFixture() { + return new ShutterControlHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL; + } + + @Test + public void testHandleCommandUpDownType() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + UpDownType.UP); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + ShutterControlServiceState state = shutterControlServiceStateCaptor.getValue(); + assertEquals(1d, state.level); + + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + UpDownType.DOWN); + verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + state = shutterControlServiceStateCaptor.getValue(); + assertEquals(0d, state.level); + } + + @Test + public void testHandleCommandStopMoveType() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + StopMoveType.STOP); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + ShutterControlServiceState state = shutterControlServiceStateCaptor.getValue(); + assertEquals(OperationState.STOPPED, state.operationState); + } + + @Test + public void testHandleCommandPercentType() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + new PercentType(42)); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + ShutterControlServiceState state = shutterControlServiceStateCaptor.getValue(); + assertEquals(0.58d, state.level); + } + + @Test + public void testUpdateChannelsShutterControlService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"shutterControlState\",\n" + " \"level\": 0.58\n" + " }"); + getFixture().processUpdate("ShutterControl", jsonObject); + verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + new PercentType(42)); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java index 8662d5da058c7..9f3fd50580001 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java @@ -19,6 +19,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -44,16 +45,14 @@ * @author David Pace - Initial contribution * */ +@NonNullByDefault public class SmartBulbHandlerTest extends AbstractBoschSHCDeviceHandlerTest { - @Captor - private ArgumentCaptor binarySwitchServiceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor binarySwitchServiceStateCaptor; - @Captor - private ArgumentCaptor multiLevelSwitchServiceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor multiLevelSwitchServiceStateCaptor; - @Captor - private ArgumentCaptor hsbColorActuatorServiceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor hsbColorActuatorServiceStateCaptor; @Override protected SmartBulbHandler createFixture() { @@ -71,9 +70,8 @@ protected ThingTypeUID getThingTypeUID() { } @Test - public void testHandleCommand_BinarySwitch() + public void testHandleCommandBinarySwitch() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { - getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("BinarySwitch"), @@ -90,9 +88,8 @@ public void testHandleCommand_BinarySwitch() } @Test - public void testHandleCommand_MultiLevelSwitch() + public void testHandleCommandMultiLevelSwitch() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { - getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BRIGHTNESS), new PercentType(42)); verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("MultiLevelSwitch"), @@ -102,9 +99,8 @@ public void testHandleCommand_MultiLevelSwitch() } @Test - public void testHandleCommand_HSBColorActuator() + public void testHandleCommandHSBColorActuator() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { - getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_COLOR), HSBType.BLUE); verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("HSBColorActuator"), @@ -114,7 +110,7 @@ public void testHandleCommand_HSBColorActuator() } @Test - public void testUpdateChannel_BinarySwitchState() { + public void testUpdateChannelBinarySwitchState() { JsonElement jsonObject = JsonParser.parseString("{\"@type\":\"binarySwitchState\",\"on\":true}"); getFixture().processUpdate("BinarySwitch", jsonObject); verify(getCallback()).stateUpdated( @@ -127,7 +123,7 @@ public void testUpdateChannel_BinarySwitchState() { } @Test - public void testUpdateChannel_MultiLevelSwitchState() { + public void testUpdateChannelMultiLevelSwitchState() { JsonElement jsonObject = JsonParser.parseString("{\"@type\":\"multiLevelSwitchState\",\"level\":16}"); getFixture().processUpdate("MultiLevelSwitch", jsonObject); verify(getCallback()).stateUpdated( @@ -135,7 +131,7 @@ public void testUpdateChannel_MultiLevelSwitchState() { } @Test - public void testUpdateChannel_HSBColorActuatorState() { + public void testUpdateChannelHSBColorActuatorState() { JsonElement jsonObject = JsonParser.parseString("{\"colorTemperatureRange\": {\n" + " \"minCt\": 153,\n" + " \"maxCt\": 526\n" + " },\n" + " \"@type\": \"colorState\",\n" + " \"gamut\": \"LEDVANCE_GAMUT_A\",\n" + " \"rgb\": -12427}"); diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java index 0e650cde0096d..e493c57cc7890 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java @@ -12,10 +12,37 @@ */ package org.openhab.binding.boschshc.internal.devices.thermostat; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.measure.quantity.Temperature; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.childlock.dto.ChildLockServiceState; +import org.openhab.binding.boschshc.internal.services.childlock.dto.ChildLockState; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; /** * Unit Tests for {@link ThermostatHandler}. @@ -26,6 +53,8 @@ @NonNullByDefault public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest { + private @Captor @NonNullByDefault({}) ArgumentCaptor childLockServiceStateCaptor; + @Override protected ThermostatHandler createFixture() { return new ThermostatHandler(getThing()); @@ -40,4 +69,55 @@ protected String getDeviceID() { protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_THERMOSTAT; } + + @Test + public void testHandleCommand() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), + OnOffType.ON); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("Thermostat"), childLockServiceStateCaptor.capture()); + ChildLockServiceState state = childLockServiceStateCaptor.getValue(); + assertSame(ChildLockState.ON, state.childLock); + } + + @Test + public void testHandleCommandUnknownCommand() { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), + new DecimalType(42)); + ThingStatusInfo expectedThingStatusInfo = ThingStatusInfoBuilder + .create(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR) + .withDescription( + "Error when service Thermostat should handle command org.openhab.core.library.types.DecimalType: Thermostat: Can not handle command org.openhab.core.library.types.DecimalType") + .build(); + verify(getCallback()).statusUpdated(getThing(), expectedThingStatusInfo); + } + + @Test + public void testUpdateChannelsTemperatureLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"temperatureLevelState\",\n" + " \"temperature\": 21.5\n" + " }"); + getFixture().processUpdate("TemperatureLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } + + @Test + public void testUpdateChannelsValveTappetService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"valveTappetState\",\n" + " \"position\": 42\n" + " }"); + getFixture().processUpdate("ValveTappet", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_VALVE_TAPPET_POSITION), + new DecimalType(42)); + } + + @Test + public void testUpdateChannelsChildLockService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"childLockState\",\n" + " \"childLock\": \"ON\"\n" + " }"); + getFixture().processUpdate("Thermostat", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), OnOffType.ON); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java index 58dd99efc1c87..2ef534dc6d564 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java @@ -12,11 +12,25 @@ */ package org.openhab.binding.boschshc.internal.devices.twinguard; +import static org.mockito.Mockito.verify; + +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.Temperature; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractSmokeDetectorHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +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.library.unit.Units; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link TwinguardHandler}. * @@ -40,4 +54,43 @@ protected String getDeviceID() { protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_TWINGUARD; } + + @Test + public void testUpdateChannelsAirQualityLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\"temperatureRating\":\"GOOD\",\"humidityRating\":\"MEDIUM\",\"purity\":620,\"@type\":\"airQualityLevelState\",\n" + + " \"purityRating\":\"GOOD\",\"temperature\":23.77,\"description\":\"LITTLE_DRY\",\"humidity\":32.69,\"combinedRating\":\"MEDIUM\"}"); + getFixture().processUpdate("AirQualityLevel", jsonObject); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(23.77, SIUnits.CELSIUS)); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE_RATING), + new StringType("GOOD")); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_HUMIDITY), + new QuantityType(32.69, Units.PERCENT)); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_HUMIDITY_RATING), + new StringType("MEDIUM")); + + verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PURITY), + new QuantityType(620, Units.PARTS_PER_MILLION)); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PURITY_RATING), + new StringType("GOOD")); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_AIR_DESCRIPTION), + new StringType("LITTLE_DRY")); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_COMBINED_RATING), + new StringType("MEDIUM")); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java index 96b1ec31a9144..9f755f696e13c 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java @@ -12,11 +12,24 @@ */ package org.openhab.binding.boschshc.internal.devices.wallthermostat; +import static org.mockito.Mockito.verify; + +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.Temperature; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link WallThermostatHandler}. * @@ -40,4 +53,24 @@ protected String getDeviceID() { protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_WALL_THERMOSTAT; } + + @Test + public void testUpdateChannelsTemperatureLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"temperatureLevelState\",\n" + " \"temperature\": 21.5\n" + " }"); + getFixture().processUpdate("TemperatureLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } + + @Test + public void testUpdateChannelsHumidityLevelService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"humidityLevelState\",\n" + " \"humidity\": 42.5\n" + " }"); + getFixture().processUpdate("HumidityLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_HUMIDITY), + new QuantityType(42.5, Units.PERCENT)); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java index 510ec0d3efccf..9c1b3920a1068 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java @@ -12,11 +12,19 @@ */ package org.openhab.binding.boschshc.internal.devices.windowcontact; +import static org.mockito.Mockito.verify; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link WindowContactHandler}. * @@ -40,4 +48,19 @@ protected String getDeviceID() { protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT; } + + @Test + public void testUpdateChannelsShutterContactService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"OPEN\"\n" + " }"); + getFixture().processUpdate("ShutterContact", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CONTACT), OpenClosedType.OPEN); + + jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"CLOSED\"\n" + " }"); + getFixture().processUpdate("ShutterContact", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CONTACT), OpenClosedType.CLOSED); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java new file mode 100644 index 0000000000000..7eb58b799e633 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java @@ -0,0 +1,37 @@ +/** + * 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.boschshc.internal.exceptions; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link LongPollingFailedException}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class LongPollingFailedExceptionTest { + + @Test + public void testConstructor() { + RuntimeException testException = new RuntimeException("test exception"); + LongPollingFailedException longPollingFailedException = new LongPollingFailedException("message", + testException); + assertEquals("message", longPollingFailedException.getMessage()); + assertSame(testException, longPollingFailedException.getCause()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java new file mode 100644 index 0000000000000..b9bdfae9ec57d --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java @@ -0,0 +1,53 @@ +/** + * 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.boschshc.internal.exceptions; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link PairingFailedException}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class PairingFailedExceptionTest { + + @Test + public void testConstructor() { + PairingFailedException fixture = new PairingFailedException(); + assertNotNull(fixture); + assertNull(fixture.getMessage()); + assertNull(fixture.getCause()); + } + + @Test + public void testConstructorWithMessage() { + PairingFailedException fixture = new PairingFailedException("message"); + assertNotNull(fixture); + assertEquals("message", fixture.getMessage()); + assertNull(fixture.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + RuntimeException testException = new RuntimeException("test exception"); + PairingFailedException fixture = new PairingFailedException("message", testException); + assertNotNull(fixture); + assertEquals("message", fixture.getMessage()); + assertSame(testException, fixture.getCause()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java index d8ac8049da807..04b3c328cd008 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java @@ -16,6 +16,7 @@ import java.util.ArrayList; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; import org.openhab.binding.boschshc.internal.devices.bridge.dto.Fault; @@ -30,6 +31,7 @@ * @author David Pace - Initial contribution * */ +@NonNullByDefault class BatteryLevelTest { @Test diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java index 05a3ae157a676..dd780413596e0 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java @@ -12,8 +12,7 @@ */ package org.openhab.binding.boschshc.internal.services.dto; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; @@ -53,14 +52,14 @@ public class BoschSHCServiceStateTest { private final Gson gson = new Gson(); @Test - public void fromJson_nullStateForDifferentType() { + public void fromJsonNullStateForDifferentType() { var state = BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"differentState\"}", JsonObject.class), TestState.class); assertEquals(null, state); } @Test - public void fromJson_stateObjectForValidJson() { + public void fromJsonStateObjectForValidJson() { var state = BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"testState\"}", JsonObject.class), TestState.class); assertNotEquals(null, state); @@ -70,7 +69,7 @@ public void fromJson_stateObjectForValidJson() { * This checks for a bug we had where the expected type stayed the same for different state classes */ @Test - public void fromJson_stateObjectForValidJsonAfterOtherState() { + public void fromJsonStateObjectForValidJsonAfterOtherState() { BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"testState\"}", JsonObject.class), TestState.class); var state2 = BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"testState2\"}", JsonObject.class), TestState2.class); diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java new file mode 100644 index 0000000000000..86e485acbdef7 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java @@ -0,0 +1,48 @@ +/** + * 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.boschshc.internal.services.dto; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link JsonRestExceptionResponse}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class JsonRestExceptionResponseTest { + + private @NonNullByDefault({}) JsonRestExceptionResponse fixture; + + @BeforeEach + public void setUp() throws Exception { + fixture = new JsonRestExceptionResponse(); + } + + @Test + public void testIsValid() { + assertFalse(JsonRestExceptionResponse.isValid(null)); + assertTrue(JsonRestExceptionResponse.isValid(fixture)); + fixture.errorCode = null; + assertFalse(JsonRestExceptionResponse.isValid(fixture)); + fixture.statusCode = null; + assertFalse(JsonRestExceptionResponse.isValid(fixture)); + fixture.errorCode = ""; + assertFalse(JsonRestExceptionResponse.isValid(fixture)); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java index 390d3ecca422b..3f4fe0c8334e7 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java @@ -12,17 +12,15 @@ */ package org.openhab.binding.boschshc.internal.services.intrusion; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -38,23 +36,21 @@ /** * Unit tests for {@link IntrusionDetectionControlStateService}. - * + * * @author David Pace - Initial contribution * */ +@NonNullByDefault @ExtendWith(MockitoExtension.class) class IntrusionDetectionControlStateServiceTest { - private IntrusionDetectionControlStateService fixture; + private @NonNullByDefault({}) IntrusionDetectionControlStateService fixture; - @Mock - private BridgeHandler bridgeHandler; + private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler; - @Mock - private Consumer consumer; + private @Mock @NonNullByDefault({}) Consumer consumer; - @Mock - private IntrusionDetectionControlState testState; + private @Mock @NonNullByDefault({}) IntrusionDetectionControlState testState; @BeforeEach void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java index 5ac919c8581db..5372efa5c7c46 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java @@ -13,15 +13,14 @@ package org.openhab.binding.boschshc.internal.services.intrusion; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -34,23 +33,21 @@ /** * Unit tests for {@link IntrusionDetectionSystemStateService}. - * + * * @author David Pace - Initial contribution * */ +@NonNullByDefault @ExtendWith(MockitoExtension.class) class IntrusionDetectionSystemStateServiceTest { - private IntrusionDetectionSystemStateService fixture; + private @NonNullByDefault({}) IntrusionDetectionSystemStateService fixture; - @Mock - private BridgeHandler bridgeHandler; + private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler; - @Mock - private Consumer consumer; + private @Mock @NonNullByDefault({}) Consumer consumer; - @Mock - private IntrusionDetectionSystemState testState; + private @Mock @NonNullByDefault({}) IntrusionDetectionSystemState testState; @BeforeEach void beforeEach() { diff --git a/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusCGateHandler.java b/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusCGateHandler.java index 0317ff82befb7..d644a7c248942 100644 --- a/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusCGateHandler.java +++ b/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusCGateHandler.java @@ -76,7 +76,7 @@ public void initialize() { try { this.ipAddress = InetAddress.getByName(configuration.ipAddress); } catch (UnknownHostException e1) { - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "IP Address not resolvable"); return; } diff --git a/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusNetworkHandler.java b/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusNetworkHandler.java index 8699ef857e814..8ebeae84940aa 100644 --- a/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusNetworkHandler.java +++ b/bundles/org.openhab.binding.cbus/src/main/java/org/openhab/binding/cbus/handler/CBusNetworkHandler.java @@ -171,7 +171,7 @@ private void cgateOnline() { } } catch (CGateException e) { logger.warn("Cannot load C-Bus network {}", networkID, e); - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.COMMUNICATION_ERROR); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR); } updateStatus(); } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java index 8bcae1d0ceca6..c4dc931cacbee 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java @@ -30,7 +30,7 @@ @NonNullByDefault public class DaikinBindingConstants { - private static final String BINDING_ID = "daikin"; + public static final String BINDING_ID = "daikin"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_AC_UNIT = new ThingTypeUID(BINDING_ID, "ac_unit"); diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java index a076d8014c9b0..77a9661a5a2d6 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java @@ -16,8 +16,11 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,37 +42,32 @@ public class DaikinHttpClientFactoryImpl implements DaikinHttpClientFactory { private final Logger logger = LoggerFactory.getLogger(DaikinHttpClientFactoryImpl.class); - private @Nullable HttpClient httpClient; + private HttpClient httpClient; + + @Activate + public DaikinHttpClientFactoryImpl(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.createHttpClient(DaikinBindingConstants.BINDING_ID, + new SslContextFactory.Client(true)); + try { + httpClient.start(); + logger.debug("Daikin http client started"); + } catch (Exception e) { + logger.warn("Could not start Daikin http client", e); + } + } @Deactivate protected void deactivate() { - if (httpClient != null) { - try { - httpClient.stop(); - logger.debug("Daikin http client stopped"); - } catch (Exception e) { - logger.debug("error while stopping Daikin http client", e); - } - httpClient = null; + try { + httpClient.stop(); + logger.debug("Daikin http client stopped"); + } catch (Exception e) { + logger.debug("error while stopping Daikin http client", e); } } @Override public @Nullable HttpClient getHttpClient() { - initialize(); return httpClient; } - - private synchronized void initialize() { - if (httpClient == null) { - httpClient = new HttpClient(new SslContextFactory.Client(true)); - try { - httpClient.start(); - logger.debug("Daikin http client started"); - } catch (Exception e) { - logger.warn("Could not start Daikin http client", e); - httpClient = null; - } - } - } } diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/DeconzBridgeHandler.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/DeconzBridgeHandler.java index 79f956be936e2..e5369a30ff8a1 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/DeconzBridgeHandler.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/DeconzBridgeHandler.java @@ -46,6 +46,7 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,12 +87,7 @@ public DeconzBridgeHandler(Bridge thing, WebSocketFactory webSocketFactory, Asyn super(thing); this.http = http; this.gson = gson; - String websocketID = thing.getUID().getAsString().replace(':', '-'); - if (websocketID.length() < 4) { - websocketID = "openHAB-deconz-" + websocketID; - } else if (websocketID.length() > 20) { - websocketID = websocketID.substring(websocketID.length() - 20); - } + String websocketID = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); this.websocket = new WebSocketConnection(this, webSocketFactory.createWebSocketClient(websocketID), gson); } diff --git a/bundles/org.openhab.binding.denonmarantz/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.denonmarantz/src/main/resources/OH-INF/thing/thing-types.xml index 51bf60a87e0f5..0248ebc9a6c62 100644 --- a/bundles/org.openhab.binding.denonmarantz/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.denonmarantz/src/main/resources/OH-INF/thing/thing-types.xml @@ -29,6 +29,8 @@ + serialNumber + diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java index f5aa068609329..762da5f004a4b 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java @@ -26,12 +26,10 @@ import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation; -import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum; import org.openhab.binding.digitalstrom.internal.providers.DsDeviceThingTypeProvider; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; -import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.slf4j.Logger; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DiscoveryServiceManager.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DiscoveryServiceManager.java index ca26c8d7ce520..84f81eda20a8a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DiscoveryServiceManager.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DiscoveryServiceManager.java @@ -37,7 +37,6 @@ import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryService; import org.openhab.core.thing.ThingTypeUID; -import org.openhab.core.thing.type.ThingType; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java index 9db813c01e68f..d275567a0856e 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java @@ -24,7 +24,6 @@ import java.util.Map; import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler; -import org.openhab.binding.digitalstrom.internal.handler.SceneHandler; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ApplicationGroup; import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene; import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java index ef725c8ed5be3..7b57652b7cefc 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java @@ -22,7 +22,6 @@ import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler; -import org.openhab.binding.digitalstrom.internal.handler.ZoneTemperatureControlHandler; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; @@ -38,8 +37,8 @@ * {@link #ZoneTemperatureControlDiscoveryService(BridgeHandler, ThingTypeUID)} as {@link ThingTypeUID}. The supported * {@link ThingTypeUID} can be found at {@link ZoneTemperatureControlHandler#SUPPORTED_THING_TYPES} * - * @author Michael Ochel - * @author Matthias Siegele + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class ZoneTemperatureControlDiscoveryService extends AbstractDiscoveryService { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/ZoneTemperatureControlHandler.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/ZoneTemperatureControlHandler.java index d390f96457811..3fbdf4310ff3d 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/ZoneTemperatureControlHandler.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/ZoneTemperatureControlHandler.java @@ -28,7 +28,6 @@ import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus; import org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener; import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager; -import org.openhab.binding.digitalstrom.internal.lib.manager.impl.TemperatureControlManager; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ApplicationGroup; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputChannelEnum; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/climate/TemperatureControlSensorTransmitter.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/climate/TemperatureControlSensorTransmitter.java index 0d9c32951f7db..98440b4eb6a9a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/climate/TemperatureControlSensorTransmitter.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/climate/TemperatureControlSensorTransmitter.java @@ -16,8 +16,8 @@ * The {@link TemperatureControlSensorTransmitter} can be implement by subclasses to implement a * transmitter which can be used to push the target temperature or control value to a digitalSTROM zone. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public interface TemperatureControlSensorTransmitter { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/EventHandler.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/EventHandler.java index f2d466579d745..0005e50a047dc 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/EventHandler.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/EventHandler.java @@ -28,8 +28,8 @@ *
* To handle the {@link EventItem} the method {@link #handleEvent(EventItem)} has to be implemented. * - * @author Michael Ochel - * @author Matthias Siegele + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public interface EventHandler { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventNames.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventNames.java index 1a3a09b9d6f81..f99cf3f99cdbf 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventNames.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventNames.java @@ -15,8 +15,8 @@ /** * The {@link EventNames} contains all needed event names to subscribe at the digitalSTROM server. * - * @author Michael Ochel - * @author Matthias Siegele + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class EventNames { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventResponseEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventResponseEnum.java index d0fad1779eec9..fe63f792f4fb2 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventResponseEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/constants/EventResponseEnum.java @@ -18,8 +18,8 @@ /** * The {@link EventResponseEnum} contains digitalSTROM-Event properties of the events at {@link EventNames}. * - * @author Michael Ochel - * @author Mathias Siegele + * @author Michael Ochel - Initial contribution + * @author Mathias Siegele - Initial contribution */ public enum EventResponseEnum { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/Event.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/Event.java index e642d583333b4..ee76ef7612b24 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/Event.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/Event.java @@ -17,7 +17,7 @@ /** * The {@link Event} represents a digitalSTROM-Event. * - * @author Alexander Betker + * @author Alexander Betker - Initial contribution */ public interface Event { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItem.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItem.java index 57a2fbf3467b0..dc2c685cb995b 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItem.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItem.java @@ -19,7 +19,7 @@ /** * The {@link EventItem} represents an event item of a digitalSTROM-Event. * - * @author Alexander Betker + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add getSource() * @author Matthias Siegele - add getSource() */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItemImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItemImpl.java index 1331d11fa1328..8606c26bf97df 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItemImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/EventItemImpl.java @@ -26,8 +26,8 @@ /** * The {@link EventItemImpl} is the implementation of the {@link EventItem}. * - * @author Michael Ochel - * @author Mathias Siegele + * @author Michael Ochel - Initial contribution + * @author Mathias Siegele - Initial contribution */ public class EventItemImpl implements EventItem { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/JSONEventImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/JSONEventImpl.java index 6cf3273d2a5ff..e2cc1ec54b86a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/JSONEventImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/event/types/JSONEventImpl.java @@ -21,7 +21,7 @@ /** * The {@link JSONEventImpl} is the implementation of the {@link Event}. * - * @author Alexander Betker + * @author Alexander Betker - Initial contribution */ public class JSONEventImpl implements Event { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/SystemStateChangeListener.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/SystemStateChangeListener.java index 6e2effd5c21df..9a686ce4f736a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/SystemStateChangeListener.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/SystemStateChangeListener.java @@ -16,8 +16,8 @@ * The {@link SystemStateChangeListener} can be implemented to get informed by digitalSTROM system state changes. It * has to be registered by supported classes, e.g. the {@link TemperatureControlManager} or self implemented classes. * - * @author Michael Ochel - * @author Matthias Siegele + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public interface SystemStateChangeListener { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/TemperatureControlStatusListener.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/TemperatureControlStatusListener.java index f0d346196d25f..6bc19c01e7652 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/TemperatureControlStatusListener.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/listener/TemperatureControlStatusListener.java @@ -20,8 +20,8 @@ *
* It also can be implemented as discovery, than the id have to be {@link #DISCOVERY}. * - * @author Michael Ochel - * @author Matthias Siegele + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution * */ public interface TemperatureControlStatusListener { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java index 487247778e3b8..45438b0f5b2c1 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java @@ -39,7 +39,7 @@ public class ConnectionManagerImpl implements ConnectionManager { * Query to get all enabled application tokens. Can be executed with {@link DsAPI#query(String, String)} or * {@link DsAPI#query2(String, String)}. */ - public final String QUERY_GET_ENABLED_APPLICATION_TOKENS = "/system/security/applicationTokens/enabled/*(*)"; + public static final String QUERY_GET_ENABLED_APPLICATION_TOKENS = "/system/security/applicationTokens/enabled/*(*)"; private final Logger logger = LoggerFactory.getLogger(ConnectionManagerImpl.class); private Config config; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java index 2b4eba153361d..35f742d8dc211 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java @@ -230,8 +230,8 @@ private void init(ConnectionManager connMan, StructureManager strucMan, SceneMan /** * Check and updates the {@link Device} structure, configurations and status. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ private class PollingRunnable implements Runnable { private boolean devicesLoaded = false; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/StructureManagerImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/StructureManagerImpl.java index c39df15e6dcaa..7faab1044981a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/StructureManagerImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/StructureManagerImpl.java @@ -21,7 +21,6 @@ import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager; import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager; -import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.AbstractGeneralDeviceInformations; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/TemperatureControlManager.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/TemperatureControlManager.java index b81f89feb1a39..5ead15d8ee46a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/TemperatureControlManager.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/TemperatureControlManager.java @@ -55,12 +55,12 @@ * {@link #GET_HEATING_WATER_SYSTEM_STATE_PATH} to get the current heating water system state through * {@link DsAPI#propertyTreeGetString(String, String)}. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class TemperatureControlManager implements EventHandler, TemperatureControlSensorTransmitter { - private final List SUPPORTED_EVENTS = Arrays.asList(EventNames.HEATING_CONTROL_OPERATION_MODE); + private static final List SUPPORTED_EVENTS = Arrays.asList(EventNames.HEATING_CONTROL_OPERATION_MODE); private final Logger logger = LoggerFactory.getLogger(TemperatureControlManager.class); @@ -294,7 +294,6 @@ public void handleEvent(EventItem eventItem) { Integer zoneID = Integer .parseInt(eventItem.getSource().getOrDefault(EventResponseEnum.ZONEID, "")); if (zoneTemperationControlListenerMap.get(zoneID) != null) { - Float newValue = Float.parseFloat( eventItem.getProperties().getOrDefault(EventResponseEnum.SENSOR_VALUE_FLOAT, "")); if (!isEcho(zoneID, SensorEnum.ROOM_TEMPERATURE_CONTROL_VARIABLE, newValue)) { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/DsAPI.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/DsAPI.java index dfa7fb81ceb0f..7538b98622211 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/DsAPI.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/DsAPI.java @@ -15,8 +15,6 @@ import java.util.List; import java.util.Map; -import org.openhab.binding.digitalstrom.internal.lib.climate.constants.ControlModes; -import org.openhab.binding.digitalstrom.internal.lib.climate.constants.OperationModes; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseSensorValues; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.AssignedSensors; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.SensorValues; @@ -45,7 +43,7 @@ * digitalSTROM-API based on dSS-Version higher then 1.14.5. * digitalSTROM documentation can be found at http://developer.digitalstrom.org/Architecture/v1.1/dss-json.pdf * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * * @author Michael Ochel - add missing java-doc, update digitalSTROM-JSON-API as far as possible to the pdf version from * June 19, 2014 and add checkConnection method and ALL_METERS constant diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/constants/JSONApiResponseKeysEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/constants/JSONApiResponseKeysEnum.java index e21bb3dc091a8..dc5e7277751fb 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/constants/JSONApiResponseKeysEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/constants/JSONApiResponseKeysEnum.java @@ -15,7 +15,7 @@ /** * The {@link JSONApiResponseKeysEnum} contains digitalSTROM-JSON response keys. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel completely changed and updated only methods remained * @author Matthias Siegele completely changed and updated only methods remained */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java index 8c6698f6ad0bd..9bac1066652de 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java @@ -69,8 +69,8 @@ /** * The {@link DsAPIImpl} is the implementation of the {@link DsAPI}. * - * @author Alexander Betker - initial contributer - * @author Alex Maier - initial contributer + * @author Alexander Betker - Initial contribution + * @author Alex Maier - Initial contribution * @author Michael Ochel - implements new methods, API updates and change SimpleJSON to GSON, add helper methods and * requests building with constants to {@link SimpleRequestBuilder} * @author Matthias Siegele - implements new methods, API updates and change SimpleJSON to GSON, add helper methods and @@ -281,12 +281,12 @@ public boolean turnDeviceOff(String token, DSID dSID, String dSUID, String name) @Override public DeviceConfig getDeviceConfig(String token, DSID dSID, String dSUID, String name, - DeviceParameterClassEnum class_, Integer index) { - if (checkRequiredDevice(dSID, dSUID, name) && class_ != null + DeviceParameterClassEnum classEnum, Integer index) { + if (checkRequiredDevice(dSID, dSUID, name) && classEnum != null && SimpleRequestBuilder.objectToString(index) != null) { String response = transport.execute(SimpleRequestBuilder.buildNewJsonRequest(ClassKeys.DEVICE) .addFunction(FunctionKeys.GET_CONFIG).addDefaultDeviceParameter(token, dSID, dSUID, name) - .addParameter(ParameterKeys.CLASS, class_.getClassIndex().toString()) + .addParameter(ParameterKeys.CLASS, classEnum.getClassIndex().toString()) .addParameter(ParameterKeys.INDEX, SimpleRequestBuilder.objectToString(index)) .buildRequestString()); diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java index 20b836fea1f82..1c63bb82b8130 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java @@ -43,7 +43,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import org.apache.commons.lang3.StringUtils; import org.openhab.binding.digitalstrom.internal.lib.config.Config; import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager; import org.openhab.binding.digitalstrom.internal.lib.serverconnection.HttpTransport; @@ -334,7 +333,8 @@ private String checkSessionToken(String request) { } private boolean checkNeededSessionToken(String request) { - String functionName = StringUtils.substringAfterLast(StringUtils.substringBefore(request, "?"), "/"); + String requestFirstPart = request.substring(0, request.indexOf("?")); + String functionName = requestFirstPart.substring(requestFirstPart.lastIndexOf("/") + 1); return !DsAPIImpl.METHODS_MUST_NOT_BE_LOGGED_IN.contains(functionName); } @@ -347,9 +347,10 @@ private String addSessionToken(String request, String sessionToken) { correctedRequest = correctedRequest + "?" + ParameterKeys.TOKEN + "=" + sessionToken; } } else { - correctedRequest = StringUtils.replaceOnce(correctedRequest, StringUtils.substringBefore( - StringUtils.substringAfter(correctedRequest, ParameterKeys.TOKEN + "="), "&"), sessionToken); - + String strippedRequest = correctedRequest + .substring(correctedRequest.indexOf(ParameterKeys.TOKEN + "=") + ParameterKeys.TOKEN.length() + 1); + strippedRequest = strippedRequest.substring(0, strippedRequest.lastIndexOf("&")); + correctedRequest = correctedRequest.replaceFirst(strippedRequest, sessionToken); } return correctedRequest; } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java index 01cbb07fc7234..9eb712e1ac6a5 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java @@ -36,8 +36,8 @@ * {@link #buildRequestString()};
*
* - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class SimpleRequestBuilder { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ClassKeys.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ClassKeys.java index c13bb39cc6d24..4f95574b98883 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ClassKeys.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ClassKeys.java @@ -15,8 +15,8 @@ /** * The {@link ClassKeys} contains digitalSTROM-JSON class keys. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class ClassKeys { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ExeptionConstants.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ExeptionConstants.java index 4aec9715666bc..16267ac6290d6 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ExeptionConstants.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ExeptionConstants.java @@ -15,8 +15,8 @@ /** * The {@link ExeptionConstants} contains the {@link SimpleRequestBuilder} exception strings. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class ExeptionConstants { public static final String NO_CLASS_ADDED = "No class added! Please add a class first!"; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/FunctionKeys.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/FunctionKeys.java index f517152c0e860..baf75588e38ba 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/FunctionKeys.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/FunctionKeys.java @@ -15,8 +15,8 @@ /** * The {@link FunctionKeys} contains digitalSTROM-JSON function keys. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class FunctionKeys { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/InterfaceKeys.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/InterfaceKeys.java index d70d9906b6deb..5205f0867b7e4 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/InterfaceKeys.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/InterfaceKeys.java @@ -15,8 +15,8 @@ /** * The {@link InterfaceKeys} contains digitalSTROM-JSON interface keys. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class InterfaceKeys { public static final String JSON = "json"; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ParameterKeys.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ParameterKeys.java index ffd49e8c02a3e..64998e7472a68 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ParameterKeys.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/constants/ParameterKeys.java @@ -15,8 +15,8 @@ /** * The {@link ParameterKeys} contains digitalSTROM-JSON parameter keys. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class ParameterKeys { public static final String TOKEN = "token"; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Apartment.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Apartment.java index 2dd83e359d12c..036427f99927f 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Apartment.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Apartment.java @@ -17,7 +17,7 @@ /** * The {@link Apartment} represents a digitalSTROM-Apartment. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add java-doc * @author Matthias Siegele - add java-doc */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/DetailedGroupInfo.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/DetailedGroupInfo.java index 39b427ebe736e..01501266046a4 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/DetailedGroupInfo.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/DetailedGroupInfo.java @@ -18,7 +18,7 @@ * The {@link DetailedGroupInfo} represents a digitalSTROM-Group with a list of all dSUID's of the included * digitalSTROM-Devices. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add java-doc * @author Matthias Siegele - add java-doc */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Group.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Group.java index b66bb8efc4348..9675a55860c52 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Group.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Group.java @@ -15,7 +15,7 @@ /** * The {@link Group} represents a digitalSTROM-Group. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add java-doc * @author Matthias Siegele - add java-doc */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Zone.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Zone.java index 3dd09a455f18e..bb90c5ae50502 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Zone.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/Zone.java @@ -19,7 +19,7 @@ /** * The {@link Zone} represents a digitalSTROM-Zone. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add java-doc * @author Matthias Siegele - add java-doc */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/AbstractGeneralDeviceInformations.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/AbstractGeneralDeviceInformations.java index cc94cceceb30a..5e7c222173b78 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/AbstractGeneralDeviceInformations.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/AbstractGeneralDeviceInformations.java @@ -24,8 +24,8 @@ * can be implement by subclasses which contains the same device informations like dSID and/or mechanismen like the * {@link DeviceStatusListener}. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public abstract class AbstractGeneralDeviceInformations implements GeneralDeviceInformation { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Circuit.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Circuit.java index 5408660a74055..eae7516a1e52b 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Circuit.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Circuit.java @@ -24,8 +24,8 @@ * {@link DeviceStatusListener} can be registered. For that and to get the general device informations like the dSID the * {@link Circuit} implements the {@link GeneralDeviceInformations} interface. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public interface Circuit extends GeneralDeviceInformation { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Device.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Device.java index fd064b6be77d3..6ecd3c0502fef 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Device.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/Device.java @@ -16,9 +16,7 @@ import java.util.Map; import org.openhab.binding.digitalstrom.internal.lib.config.Config; -import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventNames; import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem; -import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.DeviceConsumptionSensorJob; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ApplicationGroup; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/GeneralDeviceInformation.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/GeneralDeviceInformation.java index 32f5eef27cdb3..68e8dbe1e8232 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/GeneralDeviceInformation.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/GeneralDeviceInformation.java @@ -20,8 +20,8 @@ * identical for all device types. It also contains the methods to implement the mechanism of the * {@link DeviceStatusListener}. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public interface GeneralDeviceInformation { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceConfig.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceConfig.java index 742d4da528078..a98a26f5b396d 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceConfig.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceConfig.java @@ -15,7 +15,7 @@ /** * The {@link DeviceConfig} saves device configurations. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add missing java-doc * @author Matthias Siegele - add missing java-doc */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceSceneSpec.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceSceneSpec.java index 4591bac5fafd4..3566f4944ad4f 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceSceneSpec.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/DeviceSceneSpec.java @@ -17,7 +17,7 @@ /** * The {@link DeviceSceneSpec} saves a digitalSTROM-Device scene mode. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add missing java-doc * @author Matthias Siegele - add missing java-doc */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceBinarayInputEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceBinarayInputEnum.java index b77eb7d60be42..e1d24cde99842 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceBinarayInputEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceBinarayInputEnum.java @@ -15,8 +15,8 @@ /** * This enum contains all binary inputs with they id.
* - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public enum DeviceBinarayInputEnum { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceParameterClassEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceParameterClassEnum.java index aa6751551d04f..f98f5ebc260a0 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceParameterClassEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/DeviceParameterClassEnum.java @@ -15,7 +15,7 @@ /** * The {@link DeviceParameterClassEnum} lists all digitalSTROM-device parameter classes. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @version digitalSTROM-API 1.14.5 */ public enum DeviceParameterClassEnum { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringTypeEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringTypeEnum.java index a26f97d3cf557..46edadca569f3 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringTypeEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringTypeEnum.java @@ -18,7 +18,7 @@ /** * The {@link MeteringTypeEnum} lists all available digitalSTROM metering types. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - add MeteringUnitEnum list * @author Matthias Siegele - add MeteringUnitEnum list */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringUnitsEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringUnitsEnum.java index 636c2878860ea..db6abf847f53f 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringUnitsEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/constants/MeteringUnitsEnum.java @@ -15,7 +15,7 @@ /** * The {@link MeteringUnitsEnum} lists all available digitalSTROM metering units. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - remove W, because it does not exist any more * @author Matthias Siegele - remove W, because it does not exist any more */ diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSID.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSID.java index 84984f0f55c78..afb279f521d28 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSID.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSID.java @@ -15,15 +15,15 @@ /** * The {@link DSID} represents the digitalSTROM-Device identifier. * - * @author Alexander Betker - initial contributor + * @author Alexander Betker - Initial contribution * @author Alexander Friese - simplified constructor */ public class DSID { private final String dsid; - private final String DEFAULT_DSID = "3504175fe000000000000001"; - private final String PRE = "3504175fe0000000"; - private final String ALL = "ALL"; + private static final String DEFAULT_DSID = "3504175fe000000000000001"; + private static final String PRE = "3504175fe0000000"; + private static final String ALL = "ALL"; /** * Creates a new {@link DSID}. @@ -35,7 +35,7 @@ public DSID(String dsid) { if (trimmedDsid.length() == 24) { this.dsid = trimmedDsid; } else if (trimmedDsid.length() == 8) { - this.dsid = this.PRE + trimmedDsid; + this.dsid = PRE + trimmedDsid; } else if (trimmedDsid.toUpperCase().equals(ALL)) { this.dsid = ALL; } else { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSUID.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSUID.java index 64503a8918bcf..856cf4dc877e8 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSUID.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DSUID.java @@ -15,12 +15,12 @@ /** * The {@link DSUID} represents the digitalSTROM-Device unique identifier. * - * @author Alexander Friese - initial contributor + * @author Alexander Friese - Initial contribution */ public class DSUID { private final String dsuid; - private final String DEFAULT_DSUID = "3504175fe0000000000000000000000001"; + private static final String DEFAULT_DSUID = "3504175fe0000000000000000000000001"; /** * Creates a new {@link DSUID}. diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceBinaryInput.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceBinaryInput.java index b6ab3369c0890..7c61b2b5e68c6 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceBinaryInput.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceBinaryInput.java @@ -20,8 +20,8 @@ * The {@link DeviceBinaryInput} contains all information of a device binary input, e.g. binary input type id (see * {@link DeviceBinarayInputEnum}, state and so on. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution * */ public class DeviceBinaryInput { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceSensorValue.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceSensorValue.java index af2464dc130f5..1cd6f03159bde 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceSensorValue.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/DeviceSensorValue.java @@ -34,8 +34,8 @@ * and * timestamp of the last sensor update. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class DeviceSensorValue { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/JSONDeviceConfigImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/JSONDeviceConfigImpl.java index be8c0df58ed35..da0e1a20b5b34 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/JSONDeviceConfigImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/deviceparameters/impl/JSONDeviceConfigImpl.java @@ -20,13 +20,13 @@ /** * The {@link JSONDeviceConfigImpl} is the implementation of the {@link DeviceConfig}. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @author Michael Ochel - change from SimpleJSON to GSON * @author Matthias Siegele - change from SimpleJSON to GSON */ public class JSONDeviceConfigImpl implements DeviceConfig { - private int class_ = -1; + private int clazz = -1; private int index = -1; private int value = -1; @@ -37,7 +37,7 @@ public class JSONDeviceConfigImpl implements DeviceConfig { */ public JSONDeviceConfigImpl(JsonObject object) { if (object.get(JSONApiResponseKeysEnum.CLASS.getKey()) != null) { - class_ = object.get(JSONApiResponseKeysEnum.CLASS.getKey()).getAsInt(); + clazz = object.get(JSONApiResponseKeysEnum.CLASS.getKey()).getAsInt(); } if (object.get(JSONApiResponseKeysEnum.INDEX.getKey()) != null) { index = object.get(JSONApiResponseKeysEnum.INDEX.getKey()).getAsInt(); @@ -49,7 +49,7 @@ public JSONDeviceConfigImpl(JsonObject object) { @Override public int getConfigurationClass() { - return class_; + return clazz; } @Override @@ -64,6 +64,6 @@ public int getValue() { @Override public String toString() { - return "class: " + this.class_ + ", " + "index: " + this.index + ", " + "value: " + this.value; + return "class: " + this.clazz + ", " + "index: " + this.index + ", " + "value: " + this.value; } } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/CircuitImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/CircuitImpl.java index f367e8e9c2750..7d4e17b5c581b 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/CircuitImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/CircuitImpl.java @@ -29,8 +29,8 @@ /** * The {@link CircuitImpl} is the implementation of the {@link Circuit} and represent a digitalSTROM circuit. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public class CircuitImpl extends AbstractGeneralDeviceInformations implements Circuit { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java index e81ecacbb4fed..856d1b5f51a2a 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java @@ -263,7 +263,6 @@ public void run() { groupIdInter = null; } if (groupID != null) { - if (ApplicationGroup.Color.YELLOW .equals(ApplicationGroup.getGroup(groupID).getColor())) { discoverScene(SceneEnum.AUTO_OFF.getSceneNumber(), groupID); diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/constants/SceneEnum.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/constants/SceneEnum.java index f7bac42c18c27..db3475fac4e4f 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/constants/SceneEnum.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/constants/SceneEnum.java @@ -18,7 +18,7 @@ /** * The {@link SceneEnum} lists all available scenes of digitalSTROM. * - * @author Alexander Betker - initial contributer + * @author Alexander Betker - Initial contribution * @version digitalSTROM-API 1.14.5 * * @author Michael Ochel - add new scenes and missing java-doc diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/util/DSJsonParser.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/util/DSJsonParser.java index cfc04ad9521ba..0cd5bef636eb1 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/util/DSJsonParser.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/util/DSJsonParser.java @@ -19,7 +19,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputChannelEnum; -import org.openhab.binding.digitalstrom.internal.lib.structure.devices.impl.DeviceImpl; import com.google.gson.JsonArray; import com.google.gson.JsonElement; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java index 9f9bc33fcaac2..590ca6d606f91 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java @@ -26,8 +26,8 @@ * digitalSTROM-Bindings. So this class can be implement e.g. by provider implementations like the * {@link org.openhab.core.thing.type.ChannelTypeProvider}. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public abstract class BaseDsI18n { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsChannelTypeProvider.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsChannelTypeProvider.java index 1d48f537c83f5..a38f7e6ec190b 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsChannelTypeProvider.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsChannelTypeProvider.java @@ -33,7 +33,6 @@ import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum; import org.openhab.core.i18n.TranslationProvider; -import org.openhab.core.thing.type.ChannelGroupTypeUID; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeBuilder; import org.openhab.core.thing.type.ChannelTypeProvider; @@ -336,7 +335,7 @@ private StateDescriptionFragment getSensorStateDescription(SensorEnum sensorType // sensor-events and cached values are // shown in °C so we will use this unit for temperature sensors String unitShortCut = sensorType.getUnitShortcut(); - if (unitShortCut.equals("%")) { + if ("%".equals(unitShortCut)) { unitShortCut = "%%"; } if (sensorType.toString().contains("TEMPERATURE")) { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsDeviceThingTypeProvider.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsDeviceThingTypeProvider.java index 8547a537679d2..aef01b50e2218 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsDeviceThingTypeProvider.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/DsDeviceThingTypeProvider.java @@ -48,8 +48,8 @@ * {@link SupportedThingTypes} enum has to be adjusted, if new device types of digitalSTROM should be supported. * Provided the new digitalSTROM devices uses the same mechanism like now. * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ @Component(service = ThingTypeProvider.class) public class DsDeviceThingTypeProvider extends BaseDsI18n implements ThingTypeProvider { @@ -59,8 +59,8 @@ public class DsDeviceThingTypeProvider extends BaseDsI18n implements ThingTypePr * used as thing type id, the first field will set the responsible handler and the last enum field will set the * supporting of the power sensor refresh configurations (config-description with refresh priority setting or not). * - * @author Michael Ochel - initial contributer - * @author Matthias Siegele - initial contributer + * @author Michael Ochel - Initial contribution + * @author Matthias Siegele - Initial contribution */ public static enum SupportedThingTypes { // ThingType, responsible ThingHanlder, Device config-description with power-sensors @@ -82,8 +82,8 @@ private SupportedThingTypes(String handler, boolean havePowerSensors) { private final Logger logger = LoggerFactory.getLogger(DsDeviceThingTypeProvider.class); - private final String DEVICE_WITH_POWER_SENSORS = "thing-type:digitalstrom:deviceWithPowerSensors"; - private final String DEVICE_WITHOUT_POWER_SENSORS = "thing-type:digitalstrom:deviceWithoutPowerSensors"; + private static final String DEVICE_WITH_POWER_SENSORS = "thing-type:digitalstrom:deviceWithPowerSensors"; + private static final String DEVICE_WITHOUT_POWER_SENSORS = "thing-type:digitalstrom:deviceWithoutPowerSensors"; @Activate @Override diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java index 0a72355be9aad..e60a9fa46df5b 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java @@ -47,7 +47,7 @@ public final class DSMRBindingConstants { public static final String CONFIGURATION_DECRYPTION_KEY = "decryptionKey"; public static final String CONFIGURATION_DECRYPTION_KEY_EMPTY = ""; public static final String CONFIGURATION_ADDITIONAL_KEY = "additionalKey"; - public static final String ADDITIONAL_KEY_DEFAULT = "3000112233445566778899AABBCCDDEEFF"; + public static final String CONFIGURATION_ADDITIONAL_KEY_DEFAULT = "3000112233445566778899AABBCCDDEEFF"; private DSMRBindingConstants() { // Constants class diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java index 2462a87282f3a..12edfad94ede2 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java @@ -55,7 +55,7 @@ public class DSMRDeviceConfiguration { /** * Austria smart meter additional decryption key */ - public String additionalKey = DSMRBindingConstants.ADDITIONAL_KEY_DEFAULT; + public String additionalKey = DSMRBindingConstants.CONFIGURATION_ADDITIONAL_KEY_DEFAULT; /** * When no message was received after the configured number of seconds action will be taken. diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java index 979ff02376394..438b3062c4001 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java @@ -12,10 +12,12 @@ */ package org.openhab.binding.dsmr.internal.device; +import java.util.Optional; import java.util.concurrent.Semaphore; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +34,7 @@ public class DSMRDeviceRunnable implements Runnable { private final Logger logger = LoggerFactory.getLogger(DSMRDeviceRunnable.class); private final Semaphore semaphore = new Semaphore(0); private final DSMRDevice device; - private final DSMREventListener portEventListener; + private final P1TelegramListener portEventListener; /** * Keeps state of running. If false run will stop. @@ -45,7 +47,7 @@ public class DSMRDeviceRunnable implements Runnable { * @param device the device to control * @param eventListener listener to used ot report errors. */ - public DSMRDeviceRunnable(DSMRDevice device, DSMREventListener eventListener) { + public DSMRDeviceRunnable(final DSMRDevice device, final P1TelegramListener eventListener) { this.device = device; this.portEventListener = eventListener; } @@ -83,10 +85,11 @@ public void run() { } } logger.trace("Device shutdown"); - } catch (RuntimeException e) { + } catch (final RuntimeException e) { logger.warn("DSMRDeviceRunnable stopped with a RuntimeException", e); - portEventListener.handleErrorEvent(DSMRConnectorErrorEvent.READ_ERROR); - } catch (InterruptedException e) { + portEventListener.onError(DSMRErrorStatus.SERIAL_DATA_READ_ERROR, + Optional.ofNullable(e.getMessage()).orElse("")); + } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } finally { device.stop(); diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java deleted file mode 100644 index 6e93b37655218..0000000000000 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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.dsmr.internal.device; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; - -/** - * Interface for classes handling DSMR connector events. - * - * @author M. Volaart - Initial contribution - * @author Hilbrand Bouwkamp - renamed classes/methods - */ -@NonNullByDefault -public interface DSMREventListener { - /** - * Callback for DSMRPortEvent events - * - * @param connectorErrorEvent {@link DSMRConnectorErrorEvent} that has occurred - */ - public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent); - - /** - * Callback for received P1 telegrams - * - * @param telegram the received P1 telegram - */ - public void handleTelegramReceived(P1Telegram telegram); -} diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java index 099aecc0d5abd..429d3e3462e56 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java @@ -15,6 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialConnector; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialSettings; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.core.io.transport.serial.SerialPortManager; /** @@ -36,14 +37,15 @@ public class DSMRFixedConfigDevice implements DSMRDevice { * @param serialPortManager the manager to get a new serial port connecting from * @param serialPortName the port name (e.g. /dev/ttyUSB0 or COM1) * @param fixedPortSettings The serial port connection settings - * @param listener the parent {@link DSMREventListener} + * @param listener the parent {@link P1TelegramListener} * @param telegramListener listener to report found telegrams or errors */ - public DSMRFixedConfigDevice(SerialPortManager serialPortManager, String serialPortName, - DSMRSerialSettings fixedPortSettings, DSMREventListener listener, DSMRTelegramListener telegramListener) { + public DSMRFixedConfigDevice(final SerialPortManager serialPortManager, final String serialPortName, + final DSMRSerialSettings fixedPortSettings, final P1TelegramListener listener, + final DSMRTelegramListener telegramListener) { this.fixedPortSettings = fixedPortSettings; this.telegramListener = telegramListener; - telegramListener.setDsmrEventListener(listener); + telegramListener.setP1TelegramListener(listener); dsmrPort = new DSMRSerialConnector(serialPortManager, serialPortName, telegramListener); } @@ -64,7 +66,7 @@ public void stop() { } @Override - public void setLenientMode(boolean lenientMode) { + public void setLenientMode(final boolean lenientMode) { telegramListener.setLenientMode(lenientMode); } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java index efdd95c5964cb..cacfc1381fa73 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java @@ -18,10 +18,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialConnector; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialSettings; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.core.io.transport.serial.SerialPortManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +35,7 @@ * settings automatically. */ @NonNullByDefault -public class DSMRSerialAutoDevice implements DSMRDevice, DSMREventListener { +public class DSMRSerialAutoDevice implements DSMRDevice, P1TelegramListener { /** * Enum to keep track of the internal state of {@link DSMRSerialAutoDevice}. @@ -113,7 +114,7 @@ enum DeviceState { /** * The listener of the class handling the connector events */ - private DSMREventListener parentListener; + private final P1TelegramListener parentListener; /** * Time in nanos the last time the baudrate was switched. This is used during discovery ignore errors retrieved @@ -126,20 +127,20 @@ enum DeviceState { * * @param serialPortManager the manager to get a new serial port connecting from * @param serialPortName the port name (e.g. /dev/ttyUSB0 or COM1) - * @param listener the parent {@link DSMREventListener} + * @param listener the parent {@link P1TelegramListener} * @param telegramListener listener to report found telegrams or errors * @param scheduler the scheduler to use with the baudrate switching timers * @param baudrateSwitchTimeoutSeconds timeout period for when to try other baudrate settings and end the discovery * of the baudrate */ - public DSMRSerialAutoDevice(SerialPortManager serialPortManager, String serialPortName, DSMREventListener listener, - DSMRTelegramListener telegramListener, ScheduledExecutorService scheduler, - int baudrateSwitchTimeoutSeconds) { + public DSMRSerialAutoDevice(final SerialPortManager serialPortManager, final String serialPortName, + final P1TelegramListener listener, final DSMRTelegramListener telegramListener, + final ScheduledExecutorService scheduler, final int baudrateSwitchTimeoutSeconds) { this.parentListener = listener; this.scheduler = scheduler; this.baudrateSwitchTimeoutSeconds = baudrateSwitchTimeoutSeconds; this.telegramListener = telegramListener; - telegramListener.setDsmrEventListener(listener); + telegramListener.setP1TelegramListener(listener); dsmrConnector = new DSMRSerialConnector(serialPortManager, serialPortName, telegramListener); logger.debug("Initialized port '{}'", serialPortName); } @@ -148,7 +149,7 @@ public DSMRSerialAutoDevice(SerialPortManager serialPortManager, String serialPo public void start() { stopDiscover(DeviceState.DISCOVER_SETTINGS); portSettings = DEFAULT_PORT_SETTINGS; - telegramListener.setDsmrEventListener(this); + telegramListener.setP1TelegramListener(this); dsmrConnector.open(portSettings); restartHalfTimer(); endTimeTimer = scheduler.schedule(this::endTimeScheduledCall, @@ -178,30 +179,28 @@ public synchronized void stop() { * @param telegram the details of the received telegram */ @Override - public void handleTelegramReceived(P1Telegram telegram) { - if (!telegram.getCosemObjects().isEmpty()) { - stopDiscover(DeviceState.NORMAL); - parentListener.handleTelegramReceived(telegram); - logger.info("Start receiving telegrams on port {} with settings: {}", dsmrConnector.getPortName(), - portSettings); - } + public void telegramReceived(final P1Telegram telegram) { + stopDiscover(DeviceState.NORMAL); + parentListener.telegramReceived(telegram); + logger.info("Start receiving telegrams on port {} with settings: {}", dsmrConnector.getPortName(), + portSettings); } /** * Event handler for DSMR Port events. * - * @param portEvent {@link DSMRConnectorErrorEvent} to handle + * @param portEvent {@link DSMRErrorStatus} to handle */ @Override - public void handleErrorEvent(DSMRConnectorErrorEvent portEvent) { + public void onError(final DSMRErrorStatus portEvent, final String message) { logger.trace("Received portEvent {}", portEvent.getEventDetails()); - if (portEvent == DSMRConnectorErrorEvent.READ_ERROR) { + if (portEvent == DSMRErrorStatus.SERIAL_DATA_READ_ERROR) { switchBaudrate(); } else { logger.debug("Error during discovery of port settings: {}, current state:{}.", portEvent.getEventDetails(), state); stopDiscover(DeviceState.ERROR); - parentListener.handleErrorEvent(portEvent); + parentListener.onError(portEvent, message); } } @@ -209,7 +208,7 @@ public void handleErrorEvent(DSMRConnectorErrorEvent portEvent) { * @param lenientMode the lenientMode to set */ @Override - public void setLenientMode(boolean lenientMode) { + public void setLenientMode(final boolean lenientMode) { telegramListener.setLenientMode(lenientMode); } @@ -248,7 +247,7 @@ private void switchBaudrate() { private void endTimeScheduledCall() { if (state == DeviceState.DISCOVER_SETTINGS) { stopDiscover(DeviceState.ERROR); - parentListener.handleErrorEvent(DSMRConnectorErrorEvent.DONT_EXISTS); + parentListener.onError(DSMRErrorStatus.PORT_DONT_EXISTS, ""); } } @@ -257,8 +256,9 @@ private void endTimeScheduledCall() { * * @param state the state with which the process was stopped. */ - private void stopDiscover(DeviceState state) { - telegramListener.setDsmrEventListener(parentListener); + private void stopDiscover(final DeviceState state) { + this.state = state; + telegramListener.setP1TelegramListener(parentListener); logger.debug("Stop discovery of port settings."); if (halfTimeTimer != null) { halfTimeTimer.cancel(true); @@ -268,7 +268,6 @@ private void stopDiscover(DeviceState state) { endTimeTimer.cancel(true); endTimeTimer = null; } - this.state = state; } /** diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java index e314b74a61361..16a3acc4816c7 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java @@ -13,14 +13,12 @@ package org.openhab.binding.dsmr.internal.device; import java.util.List; -import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorListener; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; @@ -40,7 +38,7 @@ public class DSMRTelegramListener implements P1TelegramListener, DSMRConnectorLi private final Logger logger = LoggerFactory.getLogger(DSMRTelegramListener.class); private final TelegramParser parser; - private @NonNullByDefault({}) DSMREventListener dsmrEventListener; + private @NonNullByDefault({}) P1TelegramListener p1TelegramListener; /** * Constructor. @@ -62,25 +60,29 @@ public DSMRTelegramListener(final String decryptionKey, final String additionalK } /** - * Set the DSMR event listener. + * Set the P1 Telegram listener. * - * @param eventListener the listener to set + * @param p1TelegramListener the listener to set */ - public void setDsmrEventListener(final DSMREventListener eventListener) { - this.dsmrEventListener = eventListener; + public void setP1TelegramListener(final P1TelegramListener p1TelegramListener) { + this.p1TelegramListener = p1TelegramListener; } + // Handle calls from the Connector + @Override public void handleData(final byte[] data, final int length) { parser.parse(data, length); } @Override - public void handleErrorEvent(final DSMRConnectorErrorEvent portEvent) { - dsmrEventListener.handleErrorEvent(portEvent); + public void handleError(final DSMRErrorStatus portEvent, final String message) { + onError(portEvent, message); parser.reset(); } + // Handle calls from the Parser + /** * Handler for cosemObjects received in a P1 telegram * @@ -88,22 +90,23 @@ public void handleErrorEvent(final DSMRConnectorErrorEvent portEvent) { */ @Override public void telegramReceived(final P1Telegram telegram) { - final TelegramState telegramState = telegram.getTelegramState(); final List cosemObjects = telegram.getCosemObjects(); if (logger.isTraceEnabled()) { - logger.trace("Received {} Cosem Objects with state: '{}'", cosemObjects.size(), telegramState); + logger.trace("Received {} Cosem Objects", cosemObjects.size()); } - if (telegramState == TelegramState.OK || telegramState == TelegramState.INVALID_ENCRYPTION_KEY) { - dsmrEventListener.handleTelegramReceived(telegram); + if (cosemObjects.isEmpty()) { + onError(DSMRErrorStatus.TELEGRAM_NO_DATA, ""); } else { - if (logger.isDebugEnabled()) { - logger.debug("Telegram received with error state '{}': {}", telegramState, - cosemObjects.stream().map(CosemObject::toString).collect(Collectors.joining(","))); - } + p1TelegramListener.telegramReceived(telegram); } } + @Override + public void onError(final DSMRErrorStatus state, final String message) { + p1TelegramListener.onError(state, message); + } + /** * @param lenientMode the lenientMode to set */ diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java index a750450fdff1b..bf7ea751e9823 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java @@ -16,7 +16,8 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.Collections; +import java.util.Arrays; +import java.util.Optional; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -28,8 +29,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.dsmr.internal.DSMRBindingConstants; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; import org.openhab.core.util.HexUtils; @@ -53,8 +53,7 @@ private enum State { READ_SEPARATOR_30, READ_FRAME_COUNTER, READ_PAYLOAD, - READ_GCM_TAG, - DONE_READING_TELEGRAM + READ_GCM_TAG } private static final byte START_BYTE = (byte) 0xDB; @@ -95,7 +94,7 @@ public SmartyDecrypter(final TelegramParser parser, final P1TelegramListener tel this.parser = parser; this.telegramListener = telegramListener; secretKeySpec = decryptionKey.isEmpty() ? null : new SecretKeySpec(HexUtils.hexToBytes(decryptionKey), "AES"); - addKey = HexUtils.hexToBytes(additionalKey.isBlank() ? DSMRBindingConstants.ADDITIONAL_KEY_DEFAULT + addKey = HexUtils.hexToBytes(additionalKey.isBlank() ? DSMRBindingConstants.CONFIGURATION_ADDITIONAL_KEY_DEFAULT : ((additionalKey.length() == 32 ? (ADDITIONAL_ADD_PREFIX) : "") + additionalKey)); } @@ -113,6 +112,11 @@ public void parse(final byte[] data, final int length) { } private boolean processStateActions(final byte rawInput) { + // Safeguard against buffer overrun in case corrupt data is received. + if (ivLength == IV_BUFFER_LENGTH) { + reset(); + return false; + } switch (state) { case WAITING_FOR_START_BYTE: if (rawInput == START_BYTE) { @@ -179,26 +183,23 @@ private boolean processStateActions(final byte rawInput) { // All input has been read. cipherText.put(rawInput); if (currentBytePosition >= changeToNextStateAt) { - state = State.DONE_READING_TELEGRAM; + state = State.WAITING_FOR_START_BYTE; + return true; } break; } - if (state == State.DONE_READING_TELEGRAM) { - state = State.WAITING_FOR_START_BYTE; - return true; - } return false; } private void processCompleted() { - final byte[] plainText = decrypt(); - - reset(); - if (plainText == null) { - telegramListener - .telegramReceived(new P1Telegram(Collections.emptyList(), TelegramState.INVALID_ENCRYPTION_KEY)); - } else { - parser.parse(plainText, plainText.length); + try { + final byte[] plainText = decrypt(); + + if (plainText != null) { + parser.parse(plainText, plainText.length); + } + } finally { + reset(); } } @@ -218,7 +219,15 @@ private void processCompleted() { } } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { - logger.warn("Decrypting smarty telegram failed: ", e); + if (lenientMode || logger.isDebugEnabled()) { + // log in lenient mode or when debug is enabled. But log to warn to also work when lenientMode is + // enabled. + logger.warn("Failed encrypted telegram: {}", + HexUtils.bytesToHex(Arrays.copyOf(cipherText.array(), cipherText.position()))); + logger.warn("Exception of failed decryption of telegram: ", e); + } + telegramListener.onError(DSMRErrorStatus.INVALID_DECRYPTION_KEY, + Optional.ofNullable(e.getMessage()).orElse("")); } return null; } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java index 1eebc28fe14e0..7159ea3bff3cf 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java @@ -15,6 +15,7 @@ import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -36,7 +37,7 @@ class DSMRBaseConnector { /** * Listener to send received data and errors to. */ - protected final DSMRConnectorListener dsmrConnectorListener; + private final DSMRConnectorListener dsmrConnectorListener; /** * 1Kbyte buffer for storing received data. @@ -53,7 +54,7 @@ class DSMRBaseConnector { */ private boolean open; - public DSMRBaseConnector(DSMRConnectorListener connectorListener) { + public DSMRBaseConnector(final DSMRConnectorListener connectorListener) { this.dsmrConnectorListener = connectorListener; } @@ -68,7 +69,7 @@ public DSMRBaseConnector(DSMRConnectorListener connectorListener) { * @param inputStream input stream to read data from * @throws IOException throws exception in case input stream is null */ - protected void open(@Nullable InputStream inputStream) throws IOException { + protected void open(@Nullable final InputStream inputStream) throws IOException { if (inputStream == null) { throw new IOException("Inputstream is null"); } @@ -91,7 +92,7 @@ protected void close() { if (inputStream != null) { try { inputStream.close(); - } catch (IOException ioe) { + } catch (final IOException ioe) { logger.debug("Failed to close reader", ioe); } } @@ -104,12 +105,12 @@ protected void close() { protected void handleDataAvailable() { try { synchronized (readLock) { - BufferedInputStream localInputStream = inputStream; + final BufferedInputStream localInputStream = inputStream; if (localInputStream != null) { int bytesAvailable = localInputStream.available(); while (bytesAvailable > 0) { - int bytesAvailableRead = localInputStream.read(buffer, 0, + final int bytesAvailableRead = localInputStream.read(buffer, 0, Math.min(bytesAvailable, buffer.length)); if (open && bytesAvailableRead > 0) { @@ -122,8 +123,9 @@ protected void handleDataAvailable() { } } } - } catch (IOException e) { - dsmrConnectorListener.handleErrorEvent(DSMRConnectorErrorEvent.READ_ERROR); + } catch (final IOException e) { + dsmrConnectorListener.handleError(DSMRErrorStatus.SERIAL_DATA_READ_ERROR, + Optional.ofNullable(e.getMessage()).orElse("")); logger.debug("Exception on read data", e); } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java deleted file mode 100644 index 9d85330bb9a9a..0000000000000 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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.dsmr.internal.device.connector; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * Error events from a connector. - * - * @author M. Volaart - Initial contribution - * @author Hilbrand Bouwkamp - Reduced number of event to only errors - */ -@NonNullByDefault -public enum DSMRConnectorErrorEvent { - DONT_EXISTS, - IN_USE, - INTERNAL_ERROR, - NOT_COMPATIBLE, - READ_ERROR; - - /** - * @return the event details - */ - public String getEventDetails() { - return "@text/error.connector." + name().toLowerCase(); - } -} diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java index 219da50c0bf16..5f71bf63d4bd3 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java @@ -23,11 +23,12 @@ public interface DSMRConnectorListener { /** - * Callback for {@link DSMRConnectorErrorEvent} events. + * Callback for {@link DSMRErrorStatus} events. * - * @param portEvent {@link DSMRConnectorErrorEvent} that has occurred + * @param errorStatus {@link DSMRErrorStatus} that has occurred + * @param message Additional error message */ - public void handleErrorEvent(DSMRConnectorErrorEvent portEvent); + void handleError(DSMRErrorStatus errorStatus, String message); /** * Handle data. diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRErrorStatus.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRErrorStatus.java new file mode 100644 index 0000000000000..5c1c606f44021 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRErrorStatus.java @@ -0,0 +1,84 @@ +/** + * 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.dsmr.internal.device.connector; + +import java.util.Locale; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Error events from a connector. + * + * @author M. Volaart - Initial contribution + * @author Hilbrand Bouwkamp - Refactored all error into one enum + */ +@NonNullByDefault +public enum DSMRErrorStatus { + /** + * The smarty telegram was successfully received but could not be decoded because of an invalid decryption key. + */ + INVALID_DECRYPTION_KEY(false), + /** + * Serial port could not be found. + */ + PORT_DONT_EXISTS(true), + /** + * Serial port is already in use by another application. + */ + PORT_IN_USE(true), + /** + * Internal error in the serial port communication. + */ + PORT_INTERNAL_ERROR(true), + /** + * Serial port doesn't support the configured settings. + */ + PORT_NOT_COMPATIBLE(true), + /** + * Reading data from the serial port failed. + */ + SERIAL_DATA_READ_ERROR(false), + /** + * The telegram CRC16 checksum failed (only DSMR V4 and up). + */ + TELEGRAM_CRC_ERROR(false), + /** + * The P1 telegram has syntax errors. + */ + TELEGRAM_DATA_CORRUPTION(false), + /** + * Received telegram data, but after parsing no data is present. Possibly all data corrupted. + */ + TELEGRAM_NO_DATA(false); + + private final boolean fatal; + + private DSMRErrorStatus(final boolean fatal) { + this.fatal = fatal; + } + + /** + * @return Returns true if this error is not something possible temporary, but something that can't be recovered + * from. + */ + public boolean isFatal() { + return fatal; + } + + /** + * @return the event details + */ + public String getEventDetails() { + return "@text/addon.dsmr.error.status." + name().toLowerCase(Locale.ROOT); + } +} diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java index dd670ded2b981..93f7124b8d742 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Optional; import java.util.TooManyListenersException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -67,7 +68,7 @@ public class DSMRSerialConnector extends DSMRBaseConnector implements SerialPort /** * Serial port instance. */ - private AtomicReference<@Nullable SerialPort> serialPortReference = new AtomicReference<>(); + private final AtomicReference<@Nullable SerialPort> serialPortReference = new AtomicReference<>(); /** * DSMR Connector listener. @@ -89,8 +90,8 @@ public class DSMRSerialConnector extends DSMRBaseConnector implements SerialPort * @param serialPortName Device identifier of the port (e.g. /dev/ttyUSB0) * @param dsmrConnectorListener The listener to send error or received data from the port */ - public DSMRSerialConnector(SerialPortManager portManager, String serialPortName, - DSMRConnectorListener dsmrConnectorListener) { + public DSMRSerialConnector(final SerialPortManager portManager, final String serialPortName, + final DSMRConnectorListener dsmrConnectorListener) { super(dsmrConnectorListener); this.portManager = portManager; this.serialPortName = serialPortName; @@ -106,35 +107,35 @@ public String getPortName() { * * @param portSettings The serial port settings to open the port with */ - public void open(DSMRSerialSettings portSettings) { - DSMRConnectorErrorEvent errorEvent = null; + public void open(final DSMRSerialSettings portSettings) { + DSMRErrorStatus errorStatus = null; synchronized (portLock) { - SerialPortIdentifier portIdentifier = portManager.getIdentifier(serialPortName); + final SerialPortIdentifier portIdentifier = portManager.getIdentifier(serialPortName); if (portIdentifier == null) { logger.debug("Port {} does not exists", serialPortName); - errorEvent = DSMRConnectorErrorEvent.DONT_EXISTS; + errorStatus = DSMRErrorStatus.PORT_DONT_EXISTS; } else { - errorEvent = open(portSettings, portIdentifier); + errorStatus = open(portSettings, portIdentifier); } - if (errorEvent != null) { + if (errorStatus != null) { // handle event within lock - dsmrConnectorListener.handleErrorEvent(errorEvent); + dsmrConnectorListener.handleError(errorStatus, ""); } } } - private @Nullable DSMRConnectorErrorEvent open(DSMRSerialSettings portSettings, - SerialPortIdentifier portIdentifier) { - DSMRConnectorErrorEvent errorEvent = null; + private @Nullable DSMRErrorStatus open(final DSMRSerialSettings portSettings, + final SerialPortIdentifier portIdentifier) { + DSMRErrorStatus errorStatus = null; try { logger.trace("Opening port {}", serialPortName); - SerialPort oldSerialPort = serialPortReference.get(); + final SerialPort oldSerialPort = serialPortReference.get(); // Opening Operating System Serial Port - SerialPort serialPort = portIdentifier.open(DSMRBindingConstants.DSMR_PORT_NAME, + final SerialPort serialPort = portIdentifier.open(DSMRBindingConstants.DSMR_PORT_NAME, SERIAL_PORT_READ_TIMEOUT_MILLISECONDS); // Configure Serial Port based on specified port speed @@ -157,39 +158,39 @@ public void open(DSMRSerialSettings portSettings) { try { serialPort.enableReceiveThreshold(SERIAL_TIMEOUT_MILLISECONDS); - } catch (UnsupportedCommOperationException e) { + } catch (final UnsupportedCommOperationException e) { logger.debug("Enable receive threshold is unsupported"); } try { serialPort.enableReceiveTimeout(SERIAL_TIMEOUT_MILLISECONDS); - } catch (UnsupportedCommOperationException e) { + } catch (final UnsupportedCommOperationException e) { logger.debug("Enable receive timeout is unsupported"); } // The binding is ready, let the meter know we want to receive values serialPort.setRTS(true); if (!serialPortReference.compareAndSet(oldSerialPort, serialPort)) { logger.warn("Possible bug because a new serial port value was set during opening new port."); - errorEvent = DSMRConnectorErrorEvent.INTERNAL_ERROR; + errorStatus = DSMRErrorStatus.PORT_INTERNAL_ERROR; } - } catch (IOException ioe) { + } catch (final IOException ioe) { logger.debug("Failed to get inputstream for serialPort", ioe); - errorEvent = DSMRConnectorErrorEvent.READ_ERROR; - } catch (TooManyListenersException tmle) { + errorStatus = DSMRErrorStatus.SERIAL_DATA_READ_ERROR; + } catch (final TooManyListenersException tmle) { logger.warn("Possible bug because a listener was added while one already set.", tmle); - errorEvent = DSMRConnectorErrorEvent.INTERNAL_ERROR; - } catch (PortInUseException piue) { + errorStatus = DSMRErrorStatus.PORT_INTERNAL_ERROR; + } catch (final PortInUseException piue) { logger.debug("Port already in use: {}", serialPortName, piue); - errorEvent = DSMRConnectorErrorEvent.IN_USE; - } catch (UnsupportedCommOperationException ucoe) { + errorStatus = DSMRErrorStatus.PORT_IN_USE; + } catch (final UnsupportedCommOperationException ucoe) { logger.debug("Port does not support requested port settings (invalid dsmr:portsettings parameter?): {}", serialPortName, ucoe); - errorEvent = DSMRConnectorErrorEvent.NOT_COMPATIBLE; + errorStatus = DSMRErrorStatus.PORT_NOT_COMPATIBLE; } - return errorEvent; + return errorStatus; } /** @@ -208,12 +209,12 @@ public void close() { serialPort.setRTS(false); serialPort.removeEventListener(); try { - InputStream inputStream = serialPort.getInputStream(); + final InputStream inputStream = serialPort.getInputStream(); if (inputStream != null) { inputStream.close(); } - } catch (IOException ioe) { + } catch (final IOException ioe) { logger.debug("Failed to close serial port inputstream", ioe); } serialPort.close(); @@ -228,22 +229,23 @@ public void close() { * * @param portSettings the port settings to set on the serial port */ - public void setSerialPortParams(DSMRSerialSettings portSettings) { + public void setSerialPortParams(final DSMRSerialSettings portSettings) { synchronized (portLock) { if (isOpen()) { logger.debug("Update port {} with settings: {}", this.serialPortName, portSettings); try { - SerialPort serialPort = serialPortReference.get(); + final SerialPort serialPort = serialPortReference.get(); if (serialPort != null) { serialPort.setSerialPortParams(portSettings.getBaudrate(), portSettings.getDataBits(), portSettings.getStopbits(), portSettings.getParity()); } - } catch (UnsupportedCommOperationException e) { + } catch (final UnsupportedCommOperationException e) { logger.debug( "Port does {} not support requested port settings (invalid dsmr:portsettings parameter?): {}", serialPortName, portSettings); - dsmrConnectorListener.handleErrorEvent(DSMRConnectorErrorEvent.NOT_COMPATIBLE); + dsmrConnectorListener.handleError(DSMRErrorStatus.PORT_NOT_COMPATIBLE, + Optional.ofNullable(e.getMessage()).orElse("")); } } else { restart(portSettings); @@ -254,7 +256,7 @@ public void setSerialPortParams(DSMRSerialSettings portSettings) { /** * Switch the Serial Port speed (LOW --> HIGH and vice versa). */ - public void restart(DSMRSerialSettings portSettings) { + public void restart(final DSMRSerialSettings portSettings) { synchronized (portLock) { logger.trace("Restart port {} with settings: {}", this.serialPortName, portSettings); close(); @@ -263,7 +265,7 @@ public void restart(DSMRSerialSettings portSettings) { } @Override - public void serialEvent(@Nullable SerialPortEvent seEvent) { + public void serialEvent(@Nullable final SerialPortEvent seEvent) { if (seEvent == null) { return; } @@ -289,7 +291,7 @@ public void serialEvent(@Nullable SerialPortEvent seEvent) { break; default: // do nothing } - } catch (RuntimeException e) { + } catch (final RuntimeException e) { logger.warn("RuntimeException during handling serial event: {}", seEvent.getEventType(), e); } } @@ -300,10 +302,10 @@ public void serialEvent(@Nullable SerialPortEvent seEvent) { * @param typeName type of the event, used in logging only * @param portEvent Serial port event that triggered the error. */ - private void handleErrorEvent(String typeName, SerialPortEvent portEvent) { + private void handleErrorEvent(final String typeName, final SerialPortEvent portEvent) { if (isOpen() && portEvent.getNewValue()) { logger.trace("New DSMR port {} event", typeName); - dsmrConnectorListener.handleErrorEvent(DSMRConnectorErrorEvent.READ_ERROR); + dsmrConnectorListener.handleError(DSMRErrorStatus.SERIAL_DATA_READ_ERROR, ""); } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java index 5d9394a9930e3..8c7c844351a83 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java @@ -38,6 +38,11 @@ public class OBISIdentifier { */ private static final Pattern OBIS_ID_PATTERN = Pattern.compile(OBISID_REGEX); + /** + * Value to return when an invalid int was read. + */ + private static final int INVALID_INT_READ = -1; + /* the six individual group values of the OBIS ID */ private final int groupA; private final @Nullable Integer channel; @@ -91,25 +96,41 @@ public OBISIdentifier(final String obisIDString) throws ParseException { if (m.matches()) { // Optional value A - this.groupA = m.group(2) == null ? null : Integer.parseInt(m.group(2)); + this.groupA = safeInt(m.group(2)); // Optional value B - this.channel = m.group(4) == null ? null : Integer.valueOf(m.group(4)); + this.channel = safeInteger(m.group(4)); // Required value C & D - this.groupC = Integer.parseInt(m.group(6)); - this.groupD = Integer.parseInt(m.group(7)); + this.groupC = safeInt(m.group(6)); + this.groupD = safeInt(m.group(7)); // Optional value E - this.groupE = m.group(9) == null ? null : Integer.valueOf(m.group(9)); + this.groupE = safeInteger(m.group(9)); // Optional value F - this.groupF = m.group(11) == null ? null : Integer.valueOf(m.group(11)); + this.groupF = safeInteger(m.group(11)); } else { throw new ParseException("Invalid OBIS identifier:" + obisIDString, 0); } } + private static int safeInt(final @Nullable String value) { + try { + return value == null ? INVALID_INT_READ : Integer.parseInt(value); + } catch (final NumberFormatException e) { + return INVALID_INT_READ; + } + } + + private static @Nullable Integer safeInteger(final @Nullable String value) { + try { + return value == null ? null : Integer.valueOf(value); + } catch (final NumberFormatException e) { + return null; + } + } + public boolean isConflict() { return conflict; } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java index b0dc9fbc7cbd0..c9d13a66d2fb8 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java @@ -31,54 +31,18 @@ public class P1Telegram { /** * The TelegramState described the meta data of the P1Telegram */ - public enum TelegramState { - /** - * OK. Telegram was successful received and CRC16 checksum is verified (CRC16 only for DSMR V4 and up) - */ - OK("P1 telegram received OK"), - /** - * CRC_ERROR. CRC16 checksum failed (only DSMR V4 and up) - */ - CRC_ERROR("CRC checksum failed for received P1 telegram"), - /** - * DATA_CORRUPTION. The P1 telegram has syntax errors. - */ - DATA_CORRUPTION("Received P1 telegram is corrupted"), - /** - * P1TelegramListener. The smarty telegram was successful received but could not be decoded because of an - * invalid - * encryption key. - */ - INVALID_ENCRYPTION_KEY("Failed to decrypt P1 telegram due to invalid encryption key"); - - /** - * public accessible state details - */ - public final String stateDetails; - - /** - * Constructs a new TelegramState enum - * - * @param stateDetails String containing the details of this TelegramState - */ - private TelegramState(String stateDetails) { - this.stateDetails = stateDetails; - } - } private final List cosemObjects; - private final TelegramState telegramState; private final String rawTelegram; private final List> unknownCosemObjects; - public P1Telegram(List cosemObjects, TelegramState telegramState) { - this(cosemObjects, telegramState, "", Collections.emptyList()); + public P1Telegram(final List cosemObjects) { + this(cosemObjects, "", Collections.emptyList()); } - public P1Telegram(List cosemObjects, TelegramState telegramState, String rawTelegram, - List> unknownCosemObjects) { + public P1Telegram(final List cosemObjects, final String rawTelegram, + final List> unknownCosemObjects) { this.cosemObjects = cosemObjects; - this.telegramState = telegramState; this.rawTelegram = rawTelegram; this.unknownCosemObjects = unknownCosemObjects; } @@ -97,13 +61,6 @@ public String getRawTelegram() { return rawTelegram; } - /** - * @return The state of the telegram - */ - public TelegramState getTelegramState() { - return telegramState; - } - /** * @return The list of CosemObject found in the telegram but not known to the binding */ diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java index 2b31facd35c59..6b9306d2f6cb3 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java @@ -13,6 +13,7 @@ package org.openhab.binding.dsmr.internal.device.p1telegram; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; /** * Interface for receiving CosemObjects that come from a P1 Telegram @@ -23,10 +24,18 @@ @NonNullByDefault public interface P1TelegramListener { + /** + * Called when reading the telegram failed. Passes the failed state and optional an additional error message. + * + * @param errorStatus error state + * @param message optional additional message + */ + void onError(DSMRErrorStatus errorStatus, String message); + /** * Callback on received telegram. * * @param telegram The received telegram */ - public void telegramReceived(P1Telegram telegram); + void telegramReceived(P1Telegram telegram); } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java index c8e040e9809c2..e7c17071c96de 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java @@ -18,12 +18,13 @@ import java.util.Collections; import java.util.List; import java.util.Map.Entry; +import java.util.Optional; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.cosem.CosemObjectFactory; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,7 +107,7 @@ private enum State { /** * Current telegram state */ - private volatile TelegramState telegramState; + private volatile Optional telegramState = Optional.empty(); /** * CosemObjectFactory helper class @@ -116,7 +117,7 @@ private enum State { /** * Received Cosem Objects in the P1Telegram that is currently received */ - private final List cosemObjects = new ArrayList<>(); + private final List> cosemObjects = new ArrayList<>(); /** * List of Cosem Object values that are not known to this binding. @@ -138,18 +139,18 @@ private enum State { * * @param telegramListener */ - public P1TelegramParser(P1TelegramListener telegramListener) { + public P1TelegramParser(final P1TelegramListener telegramListener) { this(telegramListener, false); } - public P1TelegramParser(P1TelegramListener telegramListener, boolean test) { + public P1TelegramParser(final P1TelegramListener telegramListener, final boolean test) { this.telegramListener = telegramListener; this.test = test; factory = new CosemObjectFactory(); state = State.WAIT_FOR_START; crc = new CRC16(CRC16.Polynom.CRC16_IBM); - telegramState = TelegramState.OK; + telegramState = Optional.empty(); } /** @@ -159,7 +160,7 @@ public P1TelegramParser(P1TelegramListener telegramListener, boolean test) { * @param length number of bytes to parse */ @Override - public void parse(byte[] data, int length) { + public void parse(final byte[] data, final int length) { if (lenientMode || logger.isTraceEnabled()) { final String rawBlock = new String(data, 0, length, StandardCharsets.UTF_8); @@ -254,10 +255,11 @@ public void parse(byte[] data, int length) { if (c == '\r' || c == '/') { logger.trace("telegramState {}, crcValue to check 0x{}", telegramState, crcValue); // Only perform CRC check if telegram is still ok - if (telegramState == TelegramState.OK && crcValue.length() > 0) { - telegramState = checkCRC(telegramState); + + if (telegramState.isEmpty() && crcValue.length() > 0) { + telegramState = checkCRC(); } - telegramListener.telegramReceived(constructTelegram()); + processTelegram(); reset(); if (c == '/') { /* @@ -275,8 +277,8 @@ public void parse(byte[] data, int length) { logger.trace("State after parsing: {}", state); } - private TelegramState checkCRC(TelegramState currentState) { - final TelegramState telegramState; + private Optional checkCRC() { + final Optional telegramState; if (Pattern.matches(CRC_PATTERN, crcValue)) { final int crcP1Telegram = Integer.parseInt(crcValue.toString(), 16); @@ -293,24 +295,45 @@ private TelegramState checkCRC(TelegramState currentState) { } logger.trace("CRC value does not match, p1 Telegram failed"); - telegramState = TelegramState.CRC_ERROR; + telegramState = Optional.of(DSMRErrorStatus.TELEGRAM_CRC_ERROR); } else { - telegramState = currentState; + telegramState = Optional.empty(); } } else { - telegramState = TelegramState.CRC_ERROR; + telegramState = Optional.of(DSMRErrorStatus.TELEGRAM_CRC_ERROR); } return telegramState; } + private void processTelegram() { + telegramState.ifPresentOrElse(error -> telegramListener.onError(error, ""), + () -> telegramListener.telegramReceived(constructTelegram())); + } + private P1Telegram constructTelegram() { - final List cosemObjectsCopy = new ArrayList<>(cosemObjects); + final List cosemObjectsCopy = new ArrayList<>(); + cosemObjects.stream().forEach(e -> addCosemObject(cosemObjectsCopy, e)); if (lenientMode) { - return new P1Telegram(cosemObjectsCopy, telegramState, rawData.toString(), + return new P1Telegram(cosemObjectsCopy, rawData.toString(), unknownCosemObjects.isEmpty() ? Collections.emptyList() : new ArrayList<>(unknownCosemObjects)); } else { - return new P1Telegram(cosemObjectsCopy, telegramState); + return new P1Telegram(cosemObjectsCopy); + } + } + + private void addCosemObject(final List objects, final Entry cosemEntry) { + final String obisIdString = cosemEntry.getKey(); + final String obisValueString = cosemEntry.getValue(); + final CosemObject cosemObject = factory.getCosemObject(obisIdString, obisValueString); + + if (cosemObject == null) { + if (lenientMode) { + unknownCosemObjects.add(new SimpleEntry<>(obisIdString, obisValueString)); + } + } else { + logger.trace("Adding {} to list of Cosem Objects", cosemObject); + objects.add(cosemObject); } } @@ -324,10 +347,11 @@ public void reset() { * * @param c the unexpected character */ - private void handleUnexpectedCharacter(char c) { + private void handleUnexpectedCharacter(final char c) { logger.debug("Unexpected character '{}' in state: {}. This P1 telegram is marked as failed", c, state); - telegramState = TelegramState.DATA_CORRUPTION; + telegramState = Optional.of(DSMRErrorStatus.TELEGRAM_DATA_CORRUPTION); + telegramListener.onError(DSMRErrorStatus.TELEGRAM_DATA_CORRUPTION, ""); } /** @@ -335,7 +359,7 @@ private void handleUnexpectedCharacter(char c) { * * @param c the character to process */ - private void handleCharacter(char c) { + private void handleCharacter(final char c) { switch (state) { case WAIT_FOR_START: // ignore the data @@ -401,17 +425,7 @@ private void storeCurrentCosemObject() { final String obisIdString = obisId.toString(); if (!obisIdString.isEmpty()) { - final String obisValueString = obisValue.toString(); - final CosemObject cosemObject = factory.getCosemObject(obisIdString, obisValueString); - - if (cosemObject == null) { - if (lenientMode) { - unknownCosemObjects.add(new SimpleEntry<>(obisIdString, obisValueString)); - } - } else { - logger.trace("Adding {} to list of Cosem Objects", cosemObject); - cosemObjects.add(cosemObject); - } + cosemObjects.add(new SimpleEntry(obisIdString, obisValue.toString())); } clearObisData(); } @@ -419,7 +433,7 @@ private void storeCurrentCosemObject() { /** * @param newState the new state to set */ - private void setState(State newState) { + private void setState(final State newState) { synchronized (state) { switch (newState) { case HEADER: @@ -429,7 +443,7 @@ private void setState(State newState) { case WAIT_FOR_START: // Clears internal state data and mark current telegram as OK clearInternalData(); - telegramState = TelegramState.OK; + telegramState = Optional.empty(); break; case DATA_OBIS_ID: // If the current state is CRLF we are processing the header and don't have a cosem object yet @@ -448,7 +462,7 @@ private void setState(State newState) { } @Override - public void setLenientMode(boolean lenientMode) { + public void setLenientMode(final boolean lenientMode) { this.lenientMode = lenientMode; } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java index 25674aa095f06..abc422d94be22 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java @@ -13,6 +13,7 @@ package org.openhab.binding.dsmr.internal.discovery; import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_ADDITIONAL_KEY; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_ADDITIONAL_KEY_DEFAULT; import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_DECRYPTION_KEY; import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_DECRYPTION_KEY_EMPTY; import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_SERIAL_PORT; @@ -28,13 +29,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.dsmr.internal.device.DSMRDeviceRunnable; -import org.openhab.binding.dsmr.internal.device.DSMREventListener; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice; import org.openhab.binding.dsmr.internal.device.DSMRTelegramListener; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.DiscoveryService; @@ -69,7 +69,7 @@ */ @NonNullByDefault @Component(service = DiscoveryService.class, configurationPid = "discovery.dsmr") -public class DSMRBridgeDiscoveryService extends DSMRDiscoveryService implements DSMREventListener { +public class DSMRBridgeDiscoveryService extends DSMRDiscoveryService implements P1TelegramListener { /** * The timeout used to switch baudrate if no valid data is received within that time frame. @@ -133,7 +133,7 @@ protected void startScan() { logger.debug("Start discovery on serial port: {}", currentScannedPortName); // final DSMRTelegramListener telegramListener = new DSMRTelegramListener("", - CONFIGURATION_ADDITIONAL_KEY); + CONFIGURATION_ADDITIONAL_KEY_DEFAULT); final DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, portIdentifier.getName(), this, telegramListener, scheduler, BAUDRATE_SWITCH_TIMEOUT_SECONDS); @@ -173,20 +173,25 @@ private void stopSerialPortScan() { * @param telegram the received telegram */ @Override - public void handleTelegramReceived(final P1Telegram telegram) { + public void telegramReceived(final P1Telegram telegram) { final List cosemObjects = telegram.getCosemObjects(); if (logger.isDebugEnabled()) { logger.debug("[{}] Received {} cosemObjects", currentScannedPortName, cosemObjects.size()); } - if (telegram.getTelegramState() == TelegramState.INVALID_ENCRYPTION_KEY) { + final ThingUID bridgeThingUID = bridgeDiscovered(THING_TYPE_DSMR_BRIDGE); + meterDetector.detectMeters(telegram).getKey().forEach(m -> meterDiscovered(m, bridgeThingUID)); + stopSerialPortScan(); + } + + @Override + public void onError(final DSMRErrorStatus portEvent, final String message) { + if (portEvent == DSMRErrorStatus.INVALID_DECRYPTION_KEY) { bridgeDiscovered(THING_TYPE_SMARTY_BRIDGE); - stopSerialPortScan(); - } else if (!cosemObjects.isEmpty()) { - final ThingUID bridgeThingUID = bridgeDiscovered(THING_TYPE_DSMR_BRIDGE); - meterDetector.detectMeters(telegram).getKey().forEach(m -> meterDiscovered(m, bridgeThingUID)); - stopSerialPortScan(); + } else { + logger.debug("[{}] Error on port during discovery: {} - {}", currentScannedPortName, portEvent, message); } + stopSerialPortScan(); } /** @@ -205,7 +210,7 @@ private ThingUID bridgeDiscovered(final ThingTypeUID bridgeThingTypeUID) { properties.put(CONFIGURATION_SERIAL_PORT, currentScannedPortName); if (smarty) { properties.put(CONFIGURATION_DECRYPTION_KEY, CONFIGURATION_DECRYPTION_KEY_EMPTY); - properties.put(CONFIGURATION_ADDITIONAL_KEY, CONFIGURATION_ADDITIONAL_KEY); + properties.put(CONFIGURATION_ADDITIONAL_KEY, CONFIGURATION_ADDITIONAL_KEY_DEFAULT); } final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) .withThingType(bridgeThingTypeUID).withProperties(properties).withLabel(label).build(); @@ -215,10 +220,4 @@ private ThingUID bridgeDiscovered(final ThingTypeUID bridgeThingTypeUID) { thingDiscovered(discoveryResult); return thingUID; } - - @Override - public void handleErrorEvent(final DSMRConnectorErrorEvent portEvent) { - logger.debug("[{}] Error on port during discovery: {}", currentScannedPortName, portEvent); - stopSerialPortScan(); - } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java index 82f5c45bc6714..7717699c0d9e6 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java @@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.cosem.CosemObjectType; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; @@ -74,7 +75,7 @@ public void deactivate() { } @Override - public void setThingHandler(ThingHandler handler) { + public void setThingHandler(final ThingHandler handler) { if (handler instanceof DSMRBridgeHandler) { dsmrBridgeHandler = (DSMRBridgeHandler) handler; } @@ -96,7 +97,7 @@ protected synchronized void stopScan() { } @Override - public void telegramReceived(P1Telegram telegram) { + public void telegramReceived(final P1Telegram telegram) { if (logger.isDebugEnabled()) { logger.debug("Detect meters from #{} objects", telegram.getCosemObjects().size()); } @@ -108,7 +109,12 @@ public void telegramReceived(P1Telegram telegram) { detectedMeters.getKey().forEach(m -> meterDiscovered(m, dsmrBridgeHandler.getThing().getUID())); } - protected void verifyUnregisteredCosemObjects(P1Telegram telegram, List list) { + @Override + public void onError(final DSMRErrorStatus state, final String message) { + logger.info("Telegram could not be parsed correctly, failed with state: {}, {}", state, message); + } + + protected void verifyUnregisteredCosemObjects(final P1Telegram telegram, final List list) { if (!list.isEmpty()) { if (list.stream() .anyMatch(e -> e.getType() == CosemObjectType.METER_EQUIPMENT_IDENTIFIER @@ -138,7 +144,7 @@ protected void verifyUnregisteredCosemObjects(P1Telegram telegram, List list) { + protected void reportUnrecognizedCosemObjects(final List list) { list.forEach(c -> logger.info("Unrecognized cosem object '{}' found in the data: {}", c.getType(), c)); } @@ -158,7 +164,7 @@ protected void reportUnregisteredMeters() { * @param things The list of configured things * @param configuredMeterTypes The set of meters detected in the telegram */ - private void validateConfiguredMeters(List things, Set configuredMeterTypes) { + private void validateConfiguredMeters(final List things, final Set configuredMeterTypes) { // @formatter:off final Set configuredMeters = things.stream() .map(Thing::getHandler) @@ -188,8 +194,8 @@ private void validateConfiguredMeters(List things, Set con * @param invalidConfigured The list of invalid configured meters * @param unconfiguredMeters The list of meters that were detected, but not configured */ - protected void reportConfigurationValidationResults(List invalidConfigured, - List unconfiguredMeters) { + protected void reportConfigurationValidationResults(final List invalidConfigured, + final List unconfiguredMeters) { logger.info( "Possible incorrect meters configured. These are configured: {}." + "But the following unconfigured meters are found in the data received from the meter: {}", diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java index d041684a4cb1c..e4ce9c3562553 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal.handler; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_ADDITIONAL_KEY; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.CONFIGURATION_DECRYPTION_KEY; import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.THING_TYPE_SMARTY_BRIDGE; import java.util.ArrayList; @@ -21,14 +23,14 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.dsmr.internal.device.DSMRDevice; import org.openhab.binding.dsmr.internal.device.DSMRDeviceConfiguration; import org.openhab.binding.dsmr.internal.device.DSMRDeviceRunnable; -import org.openhab.binding.dsmr.internal.device.DSMREventListener; import org.openhab.binding.dsmr.internal.device.DSMRFixedConfigDevice; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice; import org.openhab.binding.dsmr.internal.device.DSMRTelegramListener; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialSettings; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; @@ -41,6 +43,7 @@ import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; +import org.openhab.core.util.HexUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +55,7 @@ * @author Hilbrand Bouwkamp - Refactored way messages are forwarded to meters. Removed availableMeters dependency. */ @NonNullByDefault -public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventListener { +public class DSMRBridgeHandler extends BaseBridgeHandler implements P1TelegramListener { /** * Factor that will be multiplied with {@link #receivedTimeoutNanos} to get the timeout factor after which the @@ -105,6 +108,8 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis private final boolean smartyMeter; + private @Nullable String lastKnownReadErrorMessage; + /** * Constructor * @@ -143,9 +148,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { public void initialize() { final DSMRDeviceConfiguration deviceConfig = getConfigAs(DSMRDeviceConfiguration.class); - if (smartyMeter && (deviceConfig.decryptionKey == null || deviceConfig.decryptionKey.length() != 32)) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/error.configuration.invalidsmartykey"); + if (smartyMeter && !validateSmartyMeterConfiguration(deviceConfig)) { return; } @@ -164,6 +167,32 @@ public void initialize() { TimeUnit.NANOSECONDS); } + private boolean validateSmartyMeterConfiguration(final DSMRDeviceConfiguration deviceConfig) { + final boolean valid; + if (deviceConfig.decryptionKey == null || deviceConfig.decryptionKey.length() != 32) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/addon.dsmr.error.configuration.invalidsmartykey"); + valid = false; + } else if (!validDecryptionKey(deviceConfig.decryptionKey, CONFIGURATION_DECRYPTION_KEY) + || !validDecryptionKey(deviceConfig.additionalKey, CONFIGURATION_ADDITIONAL_KEY)) { + valid = false; + } else { + valid = true; + } + return valid; + } + + private boolean validDecryptionKey(final String key, final String message) { + try { + HexUtils.hexToBytes(key); + return true; + } catch (final IllegalArgumentException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/addon.dsmr.error.configuration.invalid." + message + " [" + e.getMessage() + "]"); + } + return false; + } + /** * Creates the {@link DSMRDevice} that corresponds with the user specified configuration. * @@ -224,18 +253,21 @@ private void alive() { final long deltaLastReceived = System.nanoTime() - telegramReceivedTimeNanos; if (deltaLastReceived > receivedTimeoutNanos) { - logger.debug("No data received for {} seconds, restarting port if possible.", + logger.debug("No valid data received for {} seconds, restarting port if possible.", TimeUnit.NANOSECONDS.toSeconds(deltaLastReceived)); - if (dsmrDeviceRunnable != null) { - dsmrDeviceRunnable.restart(); - } if (deltaLastReceived > receivedTimeoutNanos * OFFLINE_TIMEOUT_FACTOR) { logger.trace("Setting device offline if not yet done, and reset last received time."); - if (getThing().getStatus() == ThingStatus.ONLINE) { - deviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.bridge.nodata"); + if (isInitialized() && getThing().getStatus() != ThingStatus.OFFLINE) { + final String lkm = lastKnownReadErrorMessage; + final String message = lkm == null ? "@text/addon.dsmr.error.bridge.nodata" : lkm; + + deviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, message); } resetLastReceivedState(); } + if (dsmrDeviceRunnable != null) { + dsmrDeviceRunnable.restart(); + } } } @@ -243,26 +275,29 @@ private void alive() { * Sets the last received time of messages to the current time. */ private void resetLastReceivedState() { + lastKnownReadErrorMessage = null; telegramReceivedTimeNanos = System.nanoTime(); logger.trace("Telegram received time set: {}", telegramReceivedTimeNanos); } @Override - public synchronized void handleTelegramReceived(final P1Telegram telegram) { - if (telegram.getCosemObjects().isEmpty()) { - logger.debug("Parsing worked but something went wrong, so there were no CosemObjects:{}", - telegram.getTelegramState().stateDetails); - deviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, telegram.getTelegramState().stateDetails); - } else { - resetLastReceivedState(); - meterValueReceived(telegram); - } + public synchronized void telegramReceived(final P1Telegram telegram) { + resetLastReceivedState(); + meterValueReceived(telegram); } @Override - public void handleErrorEvent(final DSMRConnectorErrorEvent portEvent) { - if (portEvent != DSMRConnectorErrorEvent.READ_ERROR) { - deviceOffline(ThingStatusDetail.CONFIGURATION_ERROR, portEvent.getEventDetails()); + public void onError(final DSMRErrorStatus errorStatus, final String message) { + if (errorStatus == DSMRErrorStatus.TELEGRAM_NO_DATA) { + logger.debug("Parsing worked but something went wrong, so there were no CosemObjects:{}", message); + lastKnownReadErrorMessage = errorStatus.getEventDetails(); + } else { + final String errorMessage = errorStatus.getEventDetails() + ' ' + message; + lastKnownReadErrorMessage = errorMessage; + // if fatal set directly offline. + if (errorStatus.isFatal()) { + deviceOffline(ThingStatusDetail.CONFIGURATION_ERROR, errorMessage); + } } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java index 47c21c9560d3d..a4c834a4c8695 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java @@ -20,6 +20,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; @@ -34,6 +35,7 @@ import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.util.ThingHandlerHelper; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -77,7 +79,7 @@ public class DSMRMeterHandler extends BaseThingHandler implements P1TelegramList * * @param thing {@link Thing} to create the MeterHandler for */ - public DSMRMeterHandler(Thing thing) { + public DSMRMeterHandler(final Thing thing) { super(thing); } @@ -85,7 +87,7 @@ public DSMRMeterHandler(Thing thing) { * DSMR Meter don't support handling commands */ @Override - public void handleCommand(ChannelUID channelUID, Command command) { + public void handleCommand(final ChannelUID channelUID, final Command command) { if (command == RefreshType.REFRESH) { updateState(); } @@ -103,17 +105,17 @@ public void initialize() { try { meterType = DSMRMeterType.valueOf(getThing().getThingTypeUID().getId().toUpperCase()); - } catch (IllegalArgumentException iae) { + } catch (final IllegalArgumentException iae) { logger.warn( "{} could not be initialized due to an invalid meterType {}. Delete this Thing if the problem persists.", getThing(), getThing().getThingTypeUID().getId().toUpperCase()); updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/error.configuration.invalidmetertype"); + "@text/addon.dsmr.error.configuration.invalidmetertype"); return; } - DSMRMeterConfiguration meterConfig = getConfigAs(DSMRMeterConfiguration.class); + final DSMRMeterConfiguration meterConfig = getConfigAs(DSMRMeterConfiguration.class); channel = meterType.meterKind.isChannelRelevant() ? meterConfig.channel : DSMRMeterConstants.UNKNOWN_CHANNEL; - DSMRMeterDescriptor meterDescriptor = new DSMRMeterDescriptor(meterType, channel); + final DSMRMeterDescriptor meterDescriptor = new DSMRMeterDescriptor(meterType, channel); meter = new DSMRMeter(meterDescriptor); meterWatchdog = scheduler.scheduleWithFixedDelay(this::updateState, meterConfig.refresh, meterConfig.refresh, TimeUnit.SECONDS); @@ -147,7 +149,7 @@ private synchronized void updateState() { updateState(channel, newState); } } - if (getThing().getStatus() != ThingStatus.ONLINE) { + if (ThingHandlerHelper.isHandlerInitialized(getThing()) && getThing().getStatus() != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); } lastReceivedValues = Collections.emptyList(); @@ -161,18 +163,18 @@ private synchronized void updateState() { * @param telegram The received telegram */ @Override - public void telegramReceived(P1Telegram telegram) { + public void telegramReceived(final P1Telegram telegram) { lastReceivedValues = Collections.emptyList(); final DSMRMeter localMeter = meter; if (localMeter == null) { return; } - List filteredValues = localMeter.filterMeterValues(telegram.getCosemObjects(), channel); + final List filteredValues = localMeter.filterMeterValues(telegram.getCosemObjects(), channel); if (filteredValues.isEmpty()) { if (getThing().getStatus() == ThingStatus.ONLINE) { - setDeviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.thing.nodata"); + setDeviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, "@text/addon.dsmr.error.thing.nodata"); } } else { if (logger.isTraceEnabled()) { @@ -186,7 +188,12 @@ public void telegramReceived(P1Telegram telegram) { } @Override - public synchronized void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + public void onError(final DSMRErrorStatus state, final String message) { + // Error is handled in other places. + } + + @Override + public synchronized void bridgeStatusChanged(final ThingStatusInfo bridgeStatusInfo) { if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE && getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) { // Set status to offline --> Thing will become online after receiving meter values @@ -209,7 +216,7 @@ && getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFL * @param status off line status * @param details off line detailed message */ - private void setDeviceOffline(ThingStatusDetail status, @Nullable String details) { + private void setDeviceOffline(final ThingStatusDetail status, @Nullable final String details) { updateStatus(ThingStatus.OFFLINE, status, details); getThing().getChannels().forEach(c -> updateState(c.getUID(), UnDefType.NULL)); } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java index 9a34868badc74..fbe5acce07a70 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java @@ -52,6 +52,6 @@ public boolean isChannelRelevant() { * @return Returns the i18n label key for this meter. */ public String getLabelKey() { - return "@text/meterKind." + name().toLowerCase() + ".label"; + return "@text/addon.dsmr.meterKind." + name().toLowerCase() + ".label"; } } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java index 32a2955c0fd9e..1ee60b7936209 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java @@ -388,8 +388,8 @@ public enum DSMRMeterType { * @param cosemObjectTypeMeterId identifier cosem object * @param requiredCosemObjects list of objects that are present in this meter type */ - DSMRMeterType(DSMRMeterKind meterKind, CosemObjectType cosemObjectTypeMeterId, - CosemObjectType... requiredCosemObjects) { + DSMRMeterType(final DSMRMeterKind meterKind, final CosemObjectType cosemObjectTypeMeterId, + final CosemObjectType... requiredCosemObjects) { this(meterKind, cosemObjectTypeMeterId, requiredCosemObjects, new CosemObjectType[0]); } @@ -401,8 +401,8 @@ public enum DSMRMeterType { * @param requiredCosemObjects list of objects that are present in this meter type * @param optionalCosemObjects list of objects that are optional present in this meter type */ - DSMRMeterType(DSMRMeterKind meterKind, CosemObjectType cosemObjectTypeMeterId, - CosemObjectType[] requiredCosemObjects, CosemObjectType[] optionalCosemObjects) { + DSMRMeterType(final DSMRMeterKind meterKind, final CosemObjectType cosemObjectTypeMeterId, + final CosemObjectType[] requiredCosemObjects, final CosemObjectType[] optionalCosemObjects) { this.meterKind = meterKind; this.cosemObjectTypeMeterId = cosemObjectTypeMeterId; this.requiredCosemObjects = requiredCosemObjects; @@ -427,15 +427,18 @@ public enum DSMRMeterType { * @param availableCosemObjects the Cosem Objects to detect if the current meter compatible * @return {@link DSMRMeterDescriptor} containing the identification of the compatible meter */ - public @Nullable DSMRMeterDescriptor findCompatible(List availableCosemObjects) { + public @Nullable DSMRMeterDescriptor findCompatible(final List availableCosemObjects) { final Map<@Nullable Integer, AtomicInteger> channelCounter = new HashMap<>(3); for (final CosemObjectType objectType : requiredCosemObjects) { final AtomicBoolean match = new AtomicBoolean(); availableCosemObjects.stream().filter(a -> a.getType() == objectType).forEach(b -> { match.set(true); - channelCounter.computeIfAbsent(b.getObisIdentifier().getChannel(), t -> new AtomicInteger()) - .incrementAndGet(); + final Integer channel = b.getObisIdentifier().getChannel(); + + if (channel != null) { + channelCounter.computeIfAbsent(channel, t -> new AtomicInteger()).incrementAndGet(); + } }); if (!match.get()) { logger.trace("Required objectType {} not found for meter: {}", objectType, this); diff --git a/bundles/org.openhab.binding.dsmr/src/main/resources/OH-INF/i18n/dsmr.properties b/bundles/org.openhab.binding.dsmr/src/main/resources/OH-INF/i18n/dsmr.properties index 56d4c4da55b16..e8f0da1f397e5 100644 --- a/bundles/org.openhab.binding.dsmr/src/main/resources/OH-INF/i18n/dsmr.properties +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/OH-INF/i18n/dsmr.properties @@ -300,27 +300,36 @@ channel-type.dsmr.waterValvePositionType.description = The water valve switch po # meter kind names -meterKind.invalid.label = Invalid Meter -meterKind.device.label = Generic DSMR Device -meterKind.main_electricity.label = Main Electricity Meter -meterKind.gas.label = Gas Meter -meterKind.heating.label = Heating Meter -meterKind.cooling.label = Cooling Meter -meterKind.water.label = Water Meter -meterKind.generic.label = Generic Meter -meterKind.gj.label = GJ Meter -meterKind.m3.label = M3 Meter -meterKind.slave_electricity1.label = Slave Electricity Meter -meterKind.slave_electricity2.label = Slave Electricity Meter 2 +addon.dsmr.meterKind.invalid.label = Invalid Meter +addon.dsmr.meterKind.device.label = Generic DSMR Device +addon.dsmr.meterKind.main_electricity.label = Main Electricity Meter +addon.dsmr.meterKind.gas.label = Gas Meter +addon.dsmr.meterKind.heating.label = Heating Meter +addon.dsmr.meterKind.cooling.label = Cooling Meter +addon.dsmr.meterKind.water.label = Water Meter +addon.dsmr.meterKind.generic.label = Generic Meter +addon.dsmr.meterKind.gj.label = GJ Meter +addon.dsmr.meterKind.m3.label = M3 Meter +addon.dsmr.meterKind.slave_electricity1.label = Slave Electricity Meter +addon.dsmr.meterKind.slave_electricity2.label = Slave Electricity Meter 2 # connector error messages -error.bridge.nodata = Not receiving data from meter. -error.configuration.invalidmetertype = The thing could not be initialized. Delete and re-add thing if the problem persists. -error.configuration.invalidsmartykey = The given Smarty decyption key is to short. The decyption key must be 32 characters long. -error.thing.nodata = Not receiving data from meter. -error.connector.dont_exists = Serial port does not exist. -error.connector.in_use = Serial port is already in use. -error.connector.internal_error = Unexpected error, possible bug. Please report. -error.connector.not_compatible = Serial port is not compatible. -error.connector.read_error = Read error. +addon.dsmr.error.bridge.nodata = Not receiving data from meter. +addon.dsmr.error.configuration.invalidmetertype = The thing could not be initialized. Delete and re-add thing if the problem persists. +addon.dsmr.error.configuration.invalidsmartykey = The given Smarty decyption key is to short. The decyption key must be 32 characters long. +addon.dsmr.error.configuration.invalid.decryptionKey = The given Smarty decyption key is invalid. It contains an illegal character: {0} +addon.dsmr.error.configuration.invalid.additionalKey = The given Smarty additional key is invalid. It contains an illegal character: {0} +addon.dsmr.error.thing.nodata = Not receiving data from meter. + +addon.dsmr.error.status.invalid_decryption_key = Failed to decrypt P1 telegram due to invalid encryption key +addon.dsmr.error.status.port_dont_exists = Serial port does not exist. +addon.dsmr.error.status.port_in_use = Serial port is already in use. +addon.dsmr.error.status.port_internal_error = Unexpected error, possible bug. Please report. +addon.dsmr.error.status.port_not_compatible = Serial port is not compatible. +addon.dsmr.error.status.parse_error = Telegram received, but parsing failed due to parse errors. +addon.dsmr.error.status.serial_data_read_error = Reading data from the serial port failed. +addon.dsmr.error.status.telegram_crc_error = CRC checksum failed for received P1 telegram. +addon.dsmr.error.status.telegram_data_corruption = Received P1 telegram is corrupted. Possible bad/wrong P1 data cable? +addon.dsmr.error.status.telegram_no_data = Received telegram data, but after parsing no data is present. Possible all data corrupted. + diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java index 593e1f86bcdf0..c84dca2472be5 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java @@ -12,17 +12,18 @@ */ package org.openhab.binding.dsmr.internal; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; /** @@ -44,7 +45,7 @@ private TelegramReaderUtil() { * @param telegramName name of the telegram file to read * @return The raw bytes of a telegram */ - public static byte[] readRawTelegram(String telegramName) { + public static byte[] readRawTelegram(final String telegramName) { try (InputStream is = TelegramReaderUtil.class.getResourceAsStream(telegramName + TELEGRAM_EXT)) { if (is == null) { fail("Could not find telegram file with name:" + telegramName + TELEGRAM_EXT); @@ -62,16 +63,32 @@ public static byte[] readRawTelegram(String telegramName) { * @param expectedTelegramState expected state of the telegram read * @return a P1Telegram object */ - public static P1Telegram readTelegram(String telegramName, TelegramState expectedTelegramState) { - final AtomicReference p1Telegram = new AtomicReference<>(); + public static P1Telegram readTelegram(final String telegramName) { final byte[] telegram = readRawTelegram(telegramName); - final P1TelegramParser parser = new P1TelegramParser(p1Telegram::set, true); + final P1TelegramListenerImpl listener = new P1TelegramListenerImpl(); + final P1TelegramParser parser = new P1TelegramParser(listener, true); parser.setLenientMode(true); parser.parse(telegram, telegram.length); - assertNotNull(p1Telegram.get(), "Telegram state should have been set. (Missing newline at end of message?)"); - assertEquals(expectedTelegramState, p1Telegram.get().getTelegramState(), - "Expected TelegramState should be as expected"); - return p1Telegram.get(); + final P1Telegram p1Telegram = listener.telegram; + + assertNotNull(p1Telegram, "Telegram state should have been set. (Missing newline at end of message?)"); + assertNull(listener.state, "Expected TelegramState should not be set"); + return p1Telegram; + } + + public static class P1TelegramListenerImpl implements P1TelegramListener { + public @Nullable P1Telegram telegram; + public @Nullable DSMRErrorStatus state; + + @Override + public void telegramReceived(final P1Telegram telegram) { + this.telegram = telegram; + } + + @Override + public void onError(final DSMRErrorStatus state, final String error) { + this.state = state; + } } } diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java index 4a49fcae7e7c0..cc49fe1416d4f 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java @@ -12,9 +12,18 @@ */ package org.openhab.binding.dsmr.internal.device; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -34,8 +43,9 @@ import org.openhab.binding.dsmr.internal.DSMRBindingConstants; import org.openhab.binding.dsmr.internal.TelegramReaderUtil; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice.DeviceState; -import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; +import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPortEvent; @@ -61,7 +71,7 @@ public class DSMRSerialAutoDeviceTest { private final SerialPortManager serialPortManager = new SerialPortManager() { @Override - public @Nullable SerialPortIdentifier getIdentifier(String name) { + public @Nullable SerialPortIdentifier getIdentifier(final String name) { assertEquals(DUMMY_PORTNAME, name, "Expect the passed serial port name"); return mockIdentifier; } @@ -84,21 +94,21 @@ public void setUp() throws PortInUseException, TooManyListenersException { @Test public void testHandlingDataAndRestart() throws IOException, PortInUseException { mockValidSerialPort(); - AtomicReference<@Nullable P1Telegram> telegramRef = new AtomicReference<>(null); - DSMREventListener listener = new DSMREventListener() { + final AtomicReference<@Nullable P1Telegram> telegramRef = new AtomicReference<>(null); + final P1TelegramListener listener = new P1TelegramListener() { @Override - public void handleTelegramReceived(P1Telegram telegram) { + public void telegramReceived(final P1Telegram telegram) { telegramRef.set(telegram); } @Override - public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) { - fail("No handleErrorEvent Expected" + connectorErrorEvent); + public void onError(final DSMRErrorStatus errorStatus, final String message) { + fail("No error status expected" + errorStatus); } }; try (InputStream inputStream = new ByteArrayInputStream(TelegramReaderUtil.readRawTelegram(TELEGRAM_NAME))) { when(mockSerialPort.getInputStream()).thenReturn(inputStream); - DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, + final DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, new DSMRTelegramListener(), scheduler, 1); device.start(); assertSame(DeviceState.DISCOVER_SETTINGS, device.getState(), "Expect to be starting discovery state"); @@ -117,16 +127,16 @@ public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) { @Test public void testHandleError() throws IOException, PortInUseException { - AtomicReference<@Nullable DSMRConnectorErrorEvent> eventRef = new AtomicReference<>(null); - DSMREventListener listener = new DSMREventListener() { + final AtomicReference<@Nullable DSMRErrorStatus> eventRef = new AtomicReference<>(null); + final P1TelegramListener listener = new P1TelegramListener() { @Override - public void handleTelegramReceived(P1Telegram telegram) { + public void telegramReceived(final P1Telegram telegram) { fail("No telegram expected:" + telegram); } @Override - public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) { - eventRef.set(connectorErrorEvent); + public void onError(final DSMRErrorStatus errorStatus, final String message) { + eventRef.set(errorStatus); } }; try (InputStream inputStream = new ByteArrayInputStream(new byte[] {})) { @@ -137,7 +147,7 @@ public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) { DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, new DSMRTelegramListener(), scheduler, 1); device.start(); - assertSame(DSMRConnectorErrorEvent.IN_USE, eventRef.get(), "Expected an error"); + assertSame(DSMRErrorStatus.PORT_IN_USE, eventRef.get(), "Expected an error"); assertSame(DeviceState.ERROR, device.getState(), "Expect to be in error state"); // Trigger device to restart mockValidSerialPort(); @@ -148,7 +158,7 @@ public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) { device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, new DSMRTelegramListener(), scheduler, 1); device.start(); - assertSame(DSMRConnectorErrorEvent.DONT_EXISTS, eventRef.get(), "Expected an error"); + assertSame(DSMRErrorStatus.PORT_DONT_EXISTS, eventRef.get(), "Expected an error"); assertSame(DeviceState.ERROR, device.getState(), "Expect to be in error state"); } } @@ -164,7 +174,8 @@ private static class MockSerialPortEvent implements SerialPortEvent { private final int eventType; private final boolean newValue; - public MockSerialPortEvent(SerialPort mockSerialPort, int eventType, boolean oldValue, boolean newValue) { + public MockSerialPortEvent(final SerialPort mockSerialPort, final int eventType, final boolean oldValue, + final boolean newValue) { this.eventType = eventType; this.newValue = newValue; } diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java index e01542cb7dace..213ab67c4402c 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java @@ -22,8 +22,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.dsmr.internal.TelegramReaderUtil; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; +import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; /** * Test class for the {@link SmartyDecrypter}. @@ -96,10 +95,13 @@ public class SmartyDecrypterTest { */ @Test public void testSmartyDecrypter() { - final AtomicReference telegramResult = new AtomicReference<>(""); - final P1TelegramListener telegramListener = telegram -> telegramResult.set(telegram.getRawTelegram()); - final SmartyDecrypter decoder = new SmartyDecrypter(new P1TelegramParser(telegramListener), - new DSMRTelegramListener(KEY, ""), KEY, ""); + final AtomicReference dataRead = new AtomicReference<>(); + final SmartyDecrypter decoder = new SmartyDecrypter(new TelegramParser() { + @Override + public void parse(final byte[] data, final int length) { + dataRead.set(new String(data, StandardCharsets.UTF_8)); + } + }, new DSMRTelegramListener(KEY, ""), KEY, ""); decoder.setLenientMode(true); final byte[] data = new byte[TELEGRAM.length]; @@ -110,6 +112,6 @@ public void testSmartyDecrypter() { decoder.parse(data, data.length); final String expected = new String(TelegramReaderUtil.readRawTelegram("smarty_long"), StandardCharsets.UTF_8); - assertThat("Should have correctly decrypted the telegram", telegramResult.get(), is(equalTo(expected))); + assertThat("Should have correctly decrypted the telegram", dataRead.get(), is(equalTo(expected))); } } diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java index 9c852d2317c4a..818fcab569946 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java @@ -21,7 +21,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.openhab.binding.dsmr.internal.TelegramReaderUtil; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; /** * Test class for {@link P1TelegramParser}. @@ -55,7 +54,7 @@ public static final List data() { @ParameterizedTest @MethodSource("data") public void testParsing(final String telegramName, final int numberOfCosemObjects, final int unknownObjects) { - final P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName, TelegramState.OK); + final P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName); assertEquals(unknownObjects, telegram.getUnknownCosemObjects().size(), "Should not have other than " + unknownObjects + " unknown cosem objects"); assertEquals(numberOfCosemObjects, diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java index 94d75ca785db6..558ac6c533ea3 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java @@ -42,7 +42,6 @@ import org.openhab.binding.dsmr.internal.TelegramReaderUtil; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.openhab.binding.dsmr.internal.meter.DSMRMeterDescriptor; import org.openhab.binding.dsmr.internal.meter.DSMRMeterType; @@ -77,7 +76,7 @@ public static final List data() { @ParameterizedTest @MethodSource("data") public void testDetectMeters(final String telegramName, final Set expectedMeters) { - final P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName, TelegramState.OK); + final P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName); final DSMRMeterDetector detector = new DSMRMeterDetector(); final Entry, List> entry = detector.detectMeters(telegram); final Collection detectMeters = entry.getKey(); diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java index 0be22cfda71d8..859f5810d6790 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java @@ -12,9 +12,12 @@ */ package org.openhab.binding.dsmr.internal.discovery; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; -import static org.openhab.binding.dsmr.internal.meter.DSMRMeterType.*; +import static org.openhab.binding.dsmr.internal.meter.DSMRMeterType.DEVICE_V5; +import static org.openhab.binding.dsmr.internal.meter.DSMRMeterType.ELECTRICITY_V4_2; +import static org.openhab.binding.dsmr.internal.meter.DSMRMeterType.M3_V5_0; import java.util.Collections; import java.util.EnumSet; @@ -32,7 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.openhab.binding.dsmr.internal.TelegramReaderUtil; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.openhab.binding.dsmr.internal.handler.DSMRBridgeHandler; import org.openhab.binding.dsmr.internal.handler.DSMRMeterHandler; import org.openhab.binding.dsmr.internal.meter.DSMRMeterDescriptor; @@ -63,13 +65,13 @@ public class DSMRMeterDiscoveryServiceTest { */ @Test public void testInvalidConfiguredMeters() { - P1Telegram expected = TelegramReaderUtil.readTelegram(EXPECTED_CONFIGURED_TELEGRAM, TelegramState.OK); - AtomicReference> invalidConfiguredRef = new AtomicReference<>(); - AtomicReference> unconfiguredRef = new AtomicReference<>(); - DSMRMeterDiscoveryService service = new DSMRMeterDiscoveryService() { + final P1Telegram expected = TelegramReaderUtil.readTelegram(EXPECTED_CONFIGURED_TELEGRAM); + final AtomicReference> invalidConfiguredRef = new AtomicReference<>(); + final AtomicReference> unconfiguredRef = new AtomicReference<>(); + final DSMRMeterDiscoveryService service = new DSMRMeterDiscoveryService() { @Override - protected void reportConfigurationValidationResults(List invalidConfigured, - List unconfiguredMeters) { + protected void reportConfigurationValidationResults(final List invalidConfigured, + final List unconfiguredMeters) { super.reportConfigurationValidationResults(invalidConfigured, unconfiguredMeters); invalidConfiguredRef.set(invalidConfigured); unconfiguredRef.set(unconfiguredMeters); @@ -79,10 +81,13 @@ protected void reportConfigurationValidationResults(List invalidC // Mock the invalid configuration by reading a telegram that is valid for a meter that is a subset of the // expected meter. - List invalidConfiguredMeterDescriptors = EnumSet.of(DEVICE_V5, ELECTRICITY_V4_2, M3_V5_0) - .stream().map(mt -> new DSMRMeterDescriptor(mt, 0)).collect(Collectors.toList()); - List things = invalidConfiguredMeterDescriptors.stream().map(m -> thing).collect(Collectors.toList()); - AtomicReference> detectMetersRef = new AtomicReference<>(); + final List invalidConfiguredMeterDescriptors = EnumSet + .of(DEVICE_V5, ELECTRICITY_V4_2, M3_V5_0).stream().map(mt -> new DSMRMeterDescriptor(mt, 0)) + .collect(Collectors.toList()); + final List things = invalidConfiguredMeterDescriptors.stream().map(m -> thing) + .collect(Collectors.toList()); + final AtomicReference> detectMetersRef = new AtomicReference<>(); + when((meterHandler).getMeterDescriptor()).then(a -> { if (detectMetersRef.get() == null || !detectMetersRef.get().hasNext()) { detectMetersRef.set(invalidConfiguredMeterDescriptors.iterator()); @@ -109,9 +114,9 @@ protected void reportConfigurationValidationResults(List invalidC */ @Test public void testUnregisteredMeters() { - P1Telegram telegram = TelegramReaderUtil.readTelegram(UNREGISTERED_METER_TELEGRAM, TelegramState.OK); - AtomicBoolean unregisteredMeter = new AtomicBoolean(false); - DSMRMeterDiscoveryService service = new DSMRMeterDiscoveryService() { + final P1Telegram telegram = TelegramReaderUtil.readTelegram(UNREGISTERED_METER_TELEGRAM); + final AtomicBoolean unregisteredMeter = new AtomicBoolean(false); + final DSMRMeterDiscoveryService service = new DSMRMeterDiscoveryService() { @Override protected void reportUnregisteredMeters() { super.reportUnregisteredMeters(); diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java index 7f9d27443a6e3..3630854fa9b37 100644 --- a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.dsmr.internal.TelegramReaderUtil; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; -import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; /** * Test class for {@link DSMRMeter}. @@ -35,15 +34,15 @@ public class DSMRMeterTest { */ @Test public void testFilterMeterValues() { - final List cosemObjects = TelegramReaderUtil.readTelegram("dsmr_50", TelegramState.OK) - .getCosemObjects(); + final List cosemObjects = TelegramReaderUtil.readTelegram("dsmr_50").getCosemObjects(); assertMeterValues(cosemObjects, DSMRMeterType.DEVICE_V5, DSMRMeterConstants.UNKNOWN_CHANNEL, 3); assertMeterValues(cosemObjects, DSMRMeterType.ELECTRICITY_V5_0, 0, 29); assertMeterValues(cosemObjects, DSMRMeterType.M3_V5_0, 1, 3); } - private void assertMeterValues(List cosemObjects, DSMRMeterType type, int channel, int expected) { + private void assertMeterValues(final List cosemObjects, final DSMRMeterType type, final int channel, + final int expected) { final DSMRMeterDescriptor descriptor = new DSMRMeterDescriptor(type, channel); final DSMRMeter meter = new DSMRMeter(descriptor); final List filterMeterValues = meter.filterMeterValues(cosemObjects, channel); diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java index b245a4c83f90b..e2e6f6396350d 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java @@ -26,7 +26,6 @@ import java.util.Set; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -46,6 +45,7 @@ import org.openhab.binding.ecobee.internal.dto.thermostat.summary.SummaryResponseDTO; import org.openhab.binding.ecobee.internal.function.FunctionRequest; import org.openhab.binding.ecobee.internal.handler.EcobeeAccountBridgeHandler; +import org.openhab.binding.ecobee.internal.util.ExceptionUtils; import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener; import org.openhab.core.auth.client.oauth2.AccessTokenResponse; import org.openhab.core.auth.client.oauth2.OAuthClientService; @@ -315,7 +315,7 @@ private boolean executePost(String url, String json) { } private void logIOException(Exception e) { - Throwable rootCause = ExceptionUtils.getRootCause(e); + Throwable rootCause = ExceptionUtils.getRootThrowable(e); if (rootCause instanceof TimeoutException || rootCause instanceof EOFException) { // These are "normal" errors and should be logged as DEBUG logger.debug("API: Call to Ecobee API failed with exception: {}: {}", rootCause.getClass().getSimpleName(), diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java index 06713ce002568..a974d2978b03a 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java @@ -18,7 +18,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -29,6 +28,7 @@ import org.openhab.binding.ecobee.internal.dto.oauth.AuthorizeResponseDTO; import org.openhab.binding.ecobee.internal.dto.oauth.TokenResponseDTO; import org.openhab.binding.ecobee.internal.handler.EcobeeAccountBridgeHandler; +import org.openhab.binding.ecobee.internal.util.ExceptionUtils; import org.openhab.core.auth.client.oauth2.AccessTokenResponse; import org.openhab.core.auth.client.oauth2.OAuthClientService; import org.openhab.core.auth.client.oauth2.OAuthException; @@ -254,7 +254,7 @@ private boolean isPinExpired() { } catch (TimeoutException e) { logger.debug("TimeoutException: Call to Ecobee API timed out"); } catch (ExecutionException e) { - if (ExceptionUtils.getRootCause(e) instanceof HttpResponseException) { + if (ExceptionUtils.getRootThrowable(e) instanceof HttpResponseException) { logger.info("Awaiting entry of PIN in Ecobee portal. Expires in {} minutes", getMinutesUntilPinExpiration()); } else { diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java index 5559b02ea2728..c3353276d1a55 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java @@ -17,11 +17,11 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.lang3.text.WordUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.ecobee.internal.config.EcobeeSensorConfiguration; import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorCapabilityDTO; import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorDTO; +import org.openhab.binding.ecobee.internal.util.StringUtils; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -126,7 +126,7 @@ private void updateCapabilityChannels(RemoteSensorCapabilityDTO capability) { ThingBuilder thingBuilder; thingBuilder = editThing(); channel = ChannelBuilder.create(uid, getAcceptedItemType(capability.type)) - .withLabel("Sensor " + WordUtils.capitalize(capability.type)) + .withLabel("Sensor " + StringUtils.capitalizeWords(capability.type)) .withType(getChannelTypeUID(capability.type)).build(); thingBuilder.withChannel(channel); updateThing(thingBuilder.build()); diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java index 1f20011f3d5fa..20867209653c6 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java @@ -25,7 +25,6 @@ import javax.measure.Unit; -import org.apache.commons.lang3.text.WordUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.ecobee.internal.action.EcobeeActions; @@ -50,6 +49,7 @@ import org.openhab.binding.ecobee.internal.dto.thermostat.WeatherForecastDTO; import org.openhab.binding.ecobee.internal.function.AbstractFunction; import org.openhab.binding.ecobee.internal.function.FunctionRequest; +import org.openhab.binding.ecobee.internal.util.StringUtils; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -179,7 +179,7 @@ public SelectionDTO getSelection() { for (Channel channel : thing.getChannelsOfGroup(group)) { if (isLinked(channel.getUID())) { try { - Field field = selection.getClass().getField("include" + WordUtils.capitalize(group)); + Field field = selection.getClass().getField("include" + StringUtils.capitalizeWords(group)); logger.trace("ThermostatBridge: Thermostat thing '{}' including object '{}' in selection", thing.getUID(), field.getName()); field.set(selection, Boolean.TRUE); diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/util/ExceptionUtils.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/util/ExceptionUtils.java new file mode 100644 index 0000000000000..860917e2ba64a --- /dev/null +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/util/ExceptionUtils.java @@ -0,0 +1,39 @@ +/** + * 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.ecobee.internal.util; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ExceptionUtils} class defines static Exception related methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class ExceptionUtils { + + public static Throwable getRootThrowable(Throwable throwable) { + List list = new ArrayList<>(); + while (!list.contains(throwable)) { + list.add(throwable); + Throwable throwableLocal = throwable.getCause(); + if (throwableLocal != null) { + throwable = throwableLocal; + } + } + return throwable; + } +} diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/util/StringUtils.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/util/StringUtils.java new file mode 100644 index 0000000000000..10460da6167c1 --- /dev/null +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/util/StringUtils.java @@ -0,0 +1,43 @@ +/** + * 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.ecobee.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link StringUtils} class defines static string related methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class StringUtils { + + public static String capitalizeWords(@Nullable String input) { + String output = ""; + if (input != null) { + String[] splitted = input.split("\\s+"); + String[] processed = new String[splitted.length]; + for (int wordIndex = 0; wordIndex < splitted.length; wordIndex++) { + if (splitted[wordIndex].length() > 1) { + processed[wordIndex] = splitted[wordIndex].substring(0, 1).toUpperCase() + + splitted[wordIndex].substring(1); + } else { + processed[wordIndex] = splitted[wordIndex].toUpperCase(); + } + } + output = String.join(" ", processed); + } + return output; + } +} diff --git a/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/CommandUtil.java b/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/CommandUtil.java index 876761ee9d739..1f04a75347e7f 100644 --- a/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/CommandUtil.java +++ b/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/CommandUtil.java @@ -77,7 +77,6 @@ private static byte getCommandByte(CommandType command) { return (byte) 0x24; default: throw new IllegalArgumentException("Unhandled command type " + command); - } } } diff --git a/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java b/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java index 06a6dfe78e9ea..702c757c67b7f 100644 --- a/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java +++ b/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.TooManyListenersException; -import org.apache.commons.lang3.ArrayUtils; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPortEvent; @@ -155,8 +154,13 @@ private void analyzeBuffer() { } if (logger.isTraceEnabled()) { - logger.trace("buffer contains bytes: {}", - HexUtils.bytesToHex(ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()])))); + int j = 0; + byte[] primeBytes = new byte[bytes.size()]; + for (Byte b : bytes.toArray(new Byte[bytes.size()])) { + primeBytes[j++] = b.byteValue(); + } + + logger.trace("buffer contains bytes: {}", HexUtils.bytesToHex(primeBytes)); } if (bytes.size() > 1) { diff --git a/bundles/org.openhab.binding.evohome/README.md b/bundles/org.openhab.binding.evohome/README.md index 4df9171563821..65744fd222980 100644 --- a/bundles/org.openhab.binding.evohome/README.md +++ b/bundles/org.openhab.binding.evohome/README.md @@ -64,11 +64,12 @@ None ### Zone -| Channel Type ID | Item Type | Description | -|-------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Temperature | Number | Allows for viewing the current actual temperature of the zone. | -| SetPointStatus | String | Allows for viewing the current set point mode of the zone. | -| SetPoint | Number | Allows for viewing and permanently overriding the temperature set point of the zone. Sending 0 cancels any active set point overrides. | +| Channel Type ID | Item Type | Description | +|-----------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| Temperature | Number:Temperature | Allows for viewing the current actual temperature of the zone. | +| SetPointStatus | String | Allows for viewing the current set point mode of the zone. | +| SetPoint | Number:Temperature | Allows for viewing and permanently overriding the temperature set point of the zone. Sending 0 cancels any active set point overrides. | + | ## Full Example @@ -89,9 +90,9 @@ Bridge evohome:account:your_account_alias [ username="your_user_name", password= String DemoMode { channel="evohome:display:your_account_alias:your_display_alias:SystemMode" } // evohome Heatingzone -Number DemoZoneTemperature { channel="evohome:heatingzone:your_account_alias:your_zone_alias:Temperature" } -String DemoZoneSetPointStatus { channel="evohome:heatingzone:your_account_alias:your_zone_alias:SetPointStatus" } -Number DemoZoneSetPoint { channel="evohome:heatingzone:your_account_alias:your_zone_alias:SetPoint" } +Number:Temperature DemoZoneTemperature { channel="evohome:heatingzone:your_account_alias:your_zone_alias:Temperature" } +String DemoZoneSetPointStatus { channel="evohome:heatingzone:your_account_alias:your_zone_alias:SetPointStatus" } +Number:Temperature DemoZoneSetPoint { channel="evohome:heatingzone:your_account_alias:your_zone_alias:SetPoint" } ``` ### demo.sitemap diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/EvohomeBindingConstants.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/EvohomeBindingConstants.java index 2c07a7773721a..d416730cd5d4e 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/EvohomeBindingConstants.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/EvohomeBindingConstants.java @@ -17,6 +17,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; /** @@ -26,6 +27,7 @@ * @author Jasper van Zuijlen - Initial contribution * @author Neil Renaud - Heating Zones */ +@NonNullByDefault public class EvohomeBindingConstants { private static final String BINDING_ID = "evohome"; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/RunnableWithTimeout.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/RunnableWithTimeout.java index 0cb741839f848..ea7b90a15828d 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/RunnableWithTimeout.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/RunnableWithTimeout.java @@ -14,12 +14,15 @@ import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Provides an interface for a delegate that can throw a timeout * * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public interface RunnableWithTimeout { public abstract void run() throws TimeoutException; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/ApiAccess.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/ApiAccess.java index cb2335e7473b9..60c8cbac3ed99 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/ApiAccess.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/ApiAccess.java @@ -18,13 +18,15 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.Authentication; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Authentication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,17 +39,17 @@ * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class ApiAccess { private static final int REQUEST_TIMEOUT_SECONDS = 5; private final Logger logger = LoggerFactory.getLogger(ApiAccess.class); private final HttpClient httpClient; - private final Gson gson; + private final Gson gson = new GsonBuilder().create(); - private Authentication authenticationData; - private String applicationId; + private @Nullable Authentication authenticationData; + private @Nullable String applicationId; public ApiAccess(HttpClient httpClient) { - this.gson = new GsonBuilder().create(); this.httpClient = httpClient; } @@ -56,7 +58,7 @@ public ApiAccess(HttpClient httpClient) { * * @param authentication The authentication details to apply */ - public void setAuthentication(Authentication authentication) { + public void setAuthentication(@Nullable Authentication authentication) { authenticationData = authentication; } @@ -65,7 +67,7 @@ public void setAuthentication(Authentication authentication) { * * @return The current authentication details */ - public Authentication getAuthentication() { + public @Nullable Authentication getAuthentication() { return authenticationData; } @@ -74,7 +76,7 @@ public Authentication getAuthentication() { * * @param applicationId The application id to apply */ - public void setApplicationId(String applicationId) { + public void setApplicationId(@Nullable String applicationId) { this.applicationId = applicationId; } @@ -89,18 +91,16 @@ public void setApplicationId(String applicationId) { * @return The result of the request or null * @throws TimeoutException Thrown when a request times out */ - public TOut doRequest(HttpMethod method, String url, Map headers, String requestData, - String contentType, Class outClass) throws TimeoutException { - TOut retVal = null; + public @Nullable TOut doRequest(HttpMethod method, String url, Map headers, + @Nullable String requestData, String contentType, @Nullable Class outClass) throws TimeoutException { logger.debug("Requesting: [{}]", url); - + @Nullable + TOut retVal = null; try { Request request = httpClient.newRequest(url).method(method); - if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - request.header(header.getKey(), header.getValue()); - } + for (Map.Entry header : headers.entrySet()) { + request.header(header.getKey(), header.getValue()); } if (requestData != null) { @@ -109,8 +109,8 @@ public TOut doRequest(HttpMethod method, String url, Map ContentResponse response = request.timeout(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS).send(); - logger.debug("Response: {}", response); - logger.debug("\n{}\n{}", response.getHeaders(), response.getContentAsString()); + logger.trace("Response: {}", response); + logger.trace("\n{}\n{}", response.getHeaders(), response.getContentAsString()); if ((response.getStatus() == HttpStatus.OK_200) || (response.getStatus() == HttpStatus.ACCEPTED_202)) { String reply = response.getContentAsString(); @@ -118,6 +118,10 @@ public TOut doRequest(HttpMethod method, String url, Map if (outClass != null) { retVal = new Gson().fromJson(reply, outClass); } + } else if ((response.getStatus() == HttpStatus.CREATED_201)) { + // success nothing to return ignore + } else { + logger.debug("Request failed with unexpected response code {}", response.getStatus()); } } catch (ExecutionException e) { logger.debug("Error in handling request: ", e); @@ -125,7 +129,6 @@ public TOut doRequest(HttpMethod method, String url, Map logger.debug("Handling request interrupted: ", e); Thread.currentThread().interrupt(); } - return retVal; } @@ -138,7 +141,7 @@ public TOut doRequest(HttpMethod method, String url, Map * @return The result of the request or null * @throws TimeoutException Thrown when a request times out */ - public TOut doAuthenticatedGet(String url, Class outClass) throws TimeoutException { + public @Nullable TOut doAuthenticatedGet(String url, Class outClass) throws TimeoutException { return doAuthenticatedRequest(HttpMethod.GET, url, null, outClass); } @@ -161,16 +164,16 @@ public void doAuthenticatedPut(String url, Object requestContainer) throws Timeo * @param method The HTTP method to use (POST, GET, ...) * @param url The URL to query * @param headers The optional additional headers to apply, can be null - * @param requestContainer The object to use as JSON data for the request - * @param outClass The type of the requested result + * @param requestContainer The object to use as JSON data for the request, can be null + * @param outClass The type of the requested result, can be null * @return The result of the request or null * @throws TimeoutException Thrown when a request times out */ - private TOut doRequest(HttpMethod method, String url, Map headers, Object requestContainer, - Class outClass) throws TimeoutException { + private @Nullable TOut doRequest(HttpMethod method, String url, Map headers, + @Nullable Object requestContainer, @Nullable Class outClass) throws TimeoutException { String json = null; if (requestContainer != null) { - json = this.gson.toJson(requestContainer); + json = gson.toJson(requestContainer); } return doRequest(method, url, headers, json, "application/json", outClass); @@ -188,17 +191,19 @@ private TOut doRequest(HttpMethod method, String url, Map * @return The result of the request or null * @throws TimeoutException Thrown when a request times out */ - private TOut doAuthenticatedRequest(HttpMethod method, String url, Object requestContainer, - Class outClass) throws TimeoutException { - Map headers = null; - if (authenticationData != null) { - headers = new HashMap<>(); - - headers.put("Authorization", "Bearer " + authenticationData.getAccessToken()); - headers.put("applicationId", applicationId); - headers.put("Accept", - "application/json, application/xml, text/json, text/x-json, text/javascript, text/xml"); + private @Nullable TOut doAuthenticatedRequest(HttpMethod method, String url, + @Nullable Object requestContainer, @Nullable Class outClass) throws TimeoutException { + Map headers = new HashMap<>(); + Authentication localAuthenticationData = authenticationData; + String localApplicationId = applicationId; + + if (localAuthenticationData != null) { + headers.put("Authorization", "Bearer " + localAuthenticationData.getAccessToken()); + } + if (localApplicationId != null) { + headers.put("applicationId", localApplicationId); } + headers.put("Accept", "application/json, application/xml, text/json, text/x-json, text/javascript, text/xml"); return doRequest(method, url, headers, requestContainer, outClass); } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClient.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClient.java index 477d68a79459b..eaceccf87a1f6 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClient.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClient.java @@ -19,18 +19,20 @@ import java.util.Map; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.evohome.internal.api.models.v2.request.HeatSetPoint; -import org.openhab.binding.evohome.internal.api.models.v2.request.HeatSetPointBuilder; -import org.openhab.binding.evohome.internal.api.models.v2.request.Mode; -import org.openhab.binding.evohome.internal.api.models.v2.request.ModeBuilder; -import org.openhab.binding.evohome.internal.api.models.v2.response.Authentication; -import org.openhab.binding.evohome.internal.api.models.v2.response.Location; -import org.openhab.binding.evohome.internal.api.models.v2.response.LocationStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.Locations; -import org.openhab.binding.evohome.internal.api.models.v2.response.LocationsStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.UserAccount; +import org.openhab.binding.evohome.internal.api.models.v2.dto.request.HeatSetPoint; +import org.openhab.binding.evohome.internal.api.models.v2.dto.request.HeatSetPointBuilder; +import org.openhab.binding.evohome.internal.api.models.v2.dto.request.Mode; +import org.openhab.binding.evohome.internal.api.models.v2.dto.request.ModeBuilder; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Authentication; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Location; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.LocationStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Locations; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.LocationsStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.UserAccount; import org.openhab.binding.evohome.internal.configuration.EvohomeAccountConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +43,7 @@ * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeApiClient { private static final String APPLICATION_ID = "b013aa26-9724-4dbd-8897-048b9aada249"; @@ -52,8 +55,8 @@ public class EvohomeApiClient { private final ApiAccess apiAccess; private Locations locations = new Locations(); - private UserAccount useraccount; - private LocationsStatus locationsStatus; + private @Nullable UserAccount useraccount; + private @Nullable LocationsStatus locationsStatus; /** * Creates a new API client based on the V2 API interface @@ -72,7 +75,7 @@ public EvohomeApiClient(EvohomeAccountConfiguration configuration, HttpClient ht public void close() { apiAccess.setAuthentication(null); useraccount = null; - locations = null; + locations = new Locations(); locationsStatus = null; } @@ -113,7 +116,7 @@ public Locations getInstallationInfo() { return locations; } - public LocationsStatus getInstallationStatus() { + public @Nullable LocationsStatus getInstallationStatus() { return locationsStatus; } @@ -139,33 +142,33 @@ private void setHeatingZoneOverride(String zoneId, HeatSetPoint heatSetPoint) th apiAccess.doAuthenticatedPut(url, heatSetPoint); } - private UserAccount requestUserAccount() throws TimeoutException { + private @Nullable UserAccount requestUserAccount() throws TimeoutException { String url = EvohomeApiConstants.URL_V2_BASE + EvohomeApiConstants.URL_V2_ACCOUNT; return apiAccess.doAuthenticatedGet(url, UserAccount.class); } private Locations requestLocations() throws TimeoutException { - Locations locations = new Locations(); - if (useraccount != null) { + Locations locations = null; + UserAccount localAccount = useraccount; + if (localAccount != null) { String url = EvohomeApiConstants.URL_V2_BASE + EvohomeApiConstants.URL_V2_INSTALLATION_INFO; - url = String.format(url, useraccount.getUserId()); + url = String.format(url, localAccount.getUserId()); locations = apiAccess.doAuthenticatedGet(url, Locations.class); } - return locations; + return locations != null ? locations : new Locations(); } private LocationsStatus requestLocationsStatus() throws TimeoutException { LocationsStatus locationsStatus = new LocationsStatus(); - if (locations != null) { - for (Location location : locations) { - String url = EvohomeApiConstants.URL_V2_BASE + EvohomeApiConstants.URL_V2_LOCATION_STATUS; - url = String.format(url, location.getLocationInfo().getLocationId()); - LocationStatus status = apiAccess.doAuthenticatedGet(url, LocationStatus.class); - locationsStatus.add(status); - } + for (Location location : locations) { + String url = EvohomeApiConstants.URL_V2_BASE + EvohomeApiConstants.URL_V2_LOCATION_STATUS; + url = String.format(url, location.getLocationInfo().getLocationId()); + LocationStatus status = apiAccess.doAuthenticatedGet(url, LocationStatus.class); + locationsStatus.add(status); } + return locationsStatus; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClientException.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClientException.java index a622e00c16bdb..850879f8aaedb 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClientException.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiClientException.java @@ -12,13 +12,17 @@ */ package org.openhab.binding.evohome.internal.api; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Exception for errors from the API Client. * * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeApiClientException extends Exception { + private static final long serialVersionUID = 1L; public EvohomeApiClientException() { } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiConstants.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiConstants.java index c0237c7e71551..d35e3cd82adbb 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiConstants.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/EvohomeApiConstants.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.evohome.internal.api; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * List of evohome API constants * * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeApiConstants { public static final String URL_V2_AUTH = "https://tccna.honeywell.com/Auth/OAuth/Token"; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/HeatSetPoint.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/HeatSetPoint.java similarity index 88% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/HeatSetPoint.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/HeatSetPoint.java index 127841b80aa71..7a4168c08f5f1 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/HeatSetPoint.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/HeatSetPoint.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.request; +package org.openhab.binding.evohome.internal.api.models.v2.dto.request; import com.google.gson.annotations.SerializedName; @@ -43,12 +43,15 @@ public class HeatSetPoint { timeUntil = null; } + @SuppressWarnings("unused") @SerializedName("heatSetpointValue") private double heatSetpointValue; + @SuppressWarnings("unused") @SerializedName("setpointMode") private String setpointMode; + @SuppressWarnings("unused") @SerializedName("timeUntil") private String timeUntil; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/HeatSetPointBuilder.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/HeatSetPointBuilder.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/HeatSetPointBuilder.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/HeatSetPointBuilder.java index abc90ed7820b9..5eb458f2d744f 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/HeatSetPointBuilder.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/HeatSetPointBuilder.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.request; +package org.openhab.binding.evohome.internal.api.models.v2.dto.request; /** * Builder for heat set point API requests diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/Mode.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/Mode.java similarity index 86% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/Mode.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/Mode.java index 0a58be085e428..125147f98929b 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/Mode.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/Mode.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.request; +package org.openhab.binding.evohome.internal.api.models.v2.dto.request; import com.google.gson.annotations.SerializedName; @@ -34,12 +34,15 @@ public class Mode { permanent = false; } + @SuppressWarnings("unused") @SerializedName("systemMode") private String systemMode; + @SuppressWarnings("unused") @SerializedName("timeUntil") private String timeUntil; + @SuppressWarnings("unused") @SerializedName("permanent") private boolean permanent; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/ModeBuilder.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/ModeBuilder.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/ModeBuilder.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/ModeBuilder.java index d6821b6985353..92bee418c78f3 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/ModeBuilder.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/ModeBuilder.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.request; +package org.openhab.binding.evohome.internal.api.models.v2.dto.request; /** * Builder for mode API requests diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/RequestBuilder.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/RequestBuilder.java similarity index 88% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/RequestBuilder.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/RequestBuilder.java index 301c4b855c18b..048b3ae9e7837 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/RequestBuilder.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/RequestBuilder.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.request; +package org.openhab.binding.evohome.internal.api.models.v2.dto.request; /** * Builder for API requests diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/TimedRequestBuilder.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/TimedRequestBuilder.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/TimedRequestBuilder.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/TimedRequestBuilder.java index 8b6fa675519b2..829ea946e333a 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/request/TimedRequestBuilder.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/request/TimedRequestBuilder.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.request; +package org.openhab.binding.evohome.internal.api.models.v2.dto.request; /** * Builder for timed API requests diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ActiveFault.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ActiveFault.java similarity index 91% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ActiveFault.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ActiveFault.java index ef9ede0cd2a63..790054d75fe60 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ActiveFault.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ActiveFault.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Authentication.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Authentication.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Authentication.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Authentication.java index e91e6c39c1bcc..ac90b23b62e8a 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Authentication.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Authentication.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Gateway.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Gateway.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Gateway.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Gateway.java index 057053a7164a7..154d85562f841 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Gateway.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Gateway.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/GatewayInfo.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/GatewayInfo.java similarity index 92% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/GatewayInfo.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/GatewayInfo.java index 586afae161905..c6a572a9104c0 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/GatewayInfo.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/GatewayInfo.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/GatewayStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/GatewayStatus.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/GatewayStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/GatewayStatus.java index 1e57385cf397c..0a91eb58fffcd 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/GatewayStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/GatewayStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/HeatSetpointCapabilities.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/HeatSetpointCapabilities.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/HeatSetpointCapabilities.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/HeatSetpointCapabilities.java index 83247db6e93c7..ffc222a920891 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/HeatSetpointCapabilities.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/HeatSetpointCapabilities.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/HeatSetpointStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/HeatSetpointStatus.java similarity index 92% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/HeatSetpointStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/HeatSetpointStatus.java index 99114ea9ea827..924b407bc8953 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/HeatSetpointStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/HeatSetpointStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Location.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Location.java similarity index 92% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Location.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Location.java index 20dbeb9d1bb8b..6a7d978077251 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Location.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Location.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationInfo.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationInfo.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationInfo.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationInfo.java index 40b81c11a89c6..66575c742a113 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationInfo.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationInfo.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationOwner.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationOwner.java similarity index 91% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationOwner.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationOwner.java index 286848a82908a..1952f43923e05 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationOwner.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationOwner.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationStatus.java similarity index 92% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationStatus.java index 3446023048e4f..22caadc843fc9 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.ArrayList; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Locations.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Locations.java similarity index 81% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Locations.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Locations.java index d0864b407866d..f0a18bc73abdf 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Locations.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Locations.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.ArrayList; @@ -21,5 +21,5 @@ * */ public class Locations extends ArrayList { - + private static final long serialVersionUID = 1L; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationsStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationsStatus.java similarity index 82% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationsStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationsStatus.java index 3b6956457e5d3..a40bcb3e542e1 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/LocationsStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/LocationsStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.ArrayList; @@ -21,5 +21,5 @@ * */ public class LocationsStatus extends ArrayList { - + private static final long serialVersionUID = 1L; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Mode.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Mode.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Mode.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Mode.java index 707b4163475e2..cd091123085d2 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Mode.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Mode.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ScheduleCapabilities.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ScheduleCapabilities.java similarity index 92% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ScheduleCapabilities.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ScheduleCapabilities.java index e1aec29b8ce6a..9e86e512c6b93 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ScheduleCapabilities.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ScheduleCapabilities.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/SystemModeStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/SystemModeStatus.java similarity index 91% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/SystemModeStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/SystemModeStatus.java index ce2af909eaa28..93e79582077d1 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/SystemModeStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/SystemModeStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureControlSystem.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureControlSystem.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureControlSystem.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureControlSystem.java index b5c8d8a59a8a6..3eecdfcf2844e 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureControlSystem.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureControlSystem.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureControlSystemStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureControlSystemStatus.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureControlSystemStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureControlSystemStatus.java index 019e6ec5ddfd0..555e7bdf70a2d 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureControlSystemStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureControlSystemStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureStatus.java similarity index 91% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureStatus.java index c0374cfde8535..c064c1b4ef9e2 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TemperatureStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TemperatureStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TimeZone.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TimeZone.java similarity index 92% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TimeZone.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TimeZone.java index eef0cff64763b..e91f5275d4e52 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/TimeZone.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/TimeZone.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/UserAccount.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/UserAccount.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/UserAccount.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/UserAccount.java index 6967cf9ec6d1a..386638bb9f745 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/UserAccount.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/UserAccount.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Zone.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Zone.java similarity index 93% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Zone.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Zone.java index 726c988f4b3dc..933fd9a88d3e4 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/Zone.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/Zone.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ZoneStatus.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ZoneStatus.java similarity index 94% rename from bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ZoneStatus.java rename to bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ZoneStatus.java index 3a063ceecf166..053bc11bba974 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/response/ZoneStatus.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/api/models/v2/dto/response/ZoneStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.evohome.internal.api.models.v2.response; +package org.openhab.binding.evohome.internal.api.models.v2.dto.response; import java.util.List; diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeAccountConfiguration.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeAccountConfiguration.java index 2fd30a516e01b..ce750c26c9a48 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeAccountConfiguration.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeAccountConfiguration.java @@ -12,15 +12,18 @@ */ package org.openhab.binding.evohome.internal.configuration; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Contains the configuration of the binding. * * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeAccountConfiguration { - public String username; - public String password; - public String applicationId; + public String username = ""; + public String password = ""; + public String applicationId = ""; public int refreshInterval; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeTemperatureControlSystemConfiguration.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeTemperatureControlSystemConfiguration.java index af56d9f0f5d1c..2c8727f991116 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeTemperatureControlSystemConfiguration.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeTemperatureControlSystemConfiguration.java @@ -12,13 +12,16 @@ */ package org.openhab.binding.evohome.internal.configuration; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Contains the configuration of the binding. * * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeTemperatureControlSystemConfiguration { - public String id; - public String name; + public String id = ""; + public String name = ""; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeThingConfiguration.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeThingConfiguration.java index 961bed76bcb90..42b7949981d6f 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeThingConfiguration.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/configuration/EvohomeThingConfiguration.java @@ -12,13 +12,16 @@ */ package org.openhab.binding.evohome.internal.configuration; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Contains the common configuration definition of an evohome Thing * * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeThingConfiguration { - public String id; - public String name; + public String id = ""; + public String name = ""; } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/discovery/EvohomeDiscoveryService.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/discovery/EvohomeDiscoveryService.java index cf9cd92216d06..f5b197e6908db 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/discovery/EvohomeDiscoveryService.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/discovery/EvohomeDiscoveryService.java @@ -15,11 +15,13 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.evohome.internal.EvohomeBindingConstants; -import org.openhab.binding.evohome.internal.api.models.v2.response.Gateway; -import org.openhab.binding.evohome.internal.api.models.v2.response.Location; -import org.openhab.binding.evohome.internal.api.models.v2.response.TemperatureControlSystem; -import org.openhab.binding.evohome.internal.api.models.v2.response.Zone; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Gateway; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Location; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Locations; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.TemperatureControlSystem; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Zone; import org.openhab.binding.evohome.internal.handler.AccountStatusListener; import org.openhab.binding.evohome.internal.handler.EvohomeAccountBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; @@ -37,6 +39,7 @@ * @author Jasper van Zuijlen - Background discovery * */ +@NonNullByDefault public class EvohomeDiscoveryService extends AbstractDiscoveryService implements AccountStatusListener { private final Logger logger = LoggerFactory.getLogger(EvohomeDiscoveryService.class); private static final int TIMEOUT = 5; @@ -86,18 +89,29 @@ private void discoverDevices() { logger.debug("Evohome Gateway not online, scanning postponed"); return; } + Locations localEvohomeConfig = bridge.getEvohomeConfig(); - for (Location location : bridge.getEvohomeConfig()) { + if (localEvohomeConfig == null) { + return; + } + for (Location location : localEvohomeConfig) { + if (location == null) { + continue; + } for (Gateway gateway : location.getGateways()) { for (TemperatureControlSystem tcs : gateway.getTemperatureControlSystems()) { + if (tcs == null) { + continue; + } addDisplayDiscoveryResult(location, tcs); for (Zone zone : tcs.getZones()) { - addZoneDiscoveryResult(location, zone); + if (zone != null) { + addZoneDiscoveryResult(location, zone); + } } } } } - stopScan(); } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/AccountStatusListener.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/AccountStatusListener.java index 7253312601bef..4341688a7b99b 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/AccountStatusListener.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/AccountStatusListener.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.evohome.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingStatus; /** @@ -20,6 +21,7 @@ * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public interface AccountStatusListener { /** diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java index ac5ad2378fb00..11048291cde0e 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java @@ -12,7 +12,9 @@ */ package org.openhab.binding.evohome.internal.handler; -import org.openhab.binding.evohome.internal.api.models.v2.response.Locations; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Locations; import org.openhab.binding.evohome.internal.configuration.EvohomeThingConfiguration; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; @@ -25,8 +27,9 @@ * * @author Jasper van Zuijlen - Initial contribution */ +@NonNullByDefault public abstract class BaseEvohomeHandler extends BaseThingHandler { - private EvohomeThingConfiguration configuration; + private EvohomeThingConfiguration configuration = new EvohomeThingConfiguration(); public BaseEvohomeHandler(Thing thing) { super(thing); @@ -40,14 +43,10 @@ public void initialize() { @Override public void dispose() { - configuration = null; } public String getId() { - if (configuration != null) { - return configuration.id; - } - return null; + return configuration.id; } /** @@ -64,7 +63,7 @@ protected EvohomeThingConfiguration getEvohomeThingConfig() { * * @return The evohome brdige */ - protected EvohomeAccountBridgeHandler getEvohomeBridge() { + protected @Nullable EvohomeAccountBridgeHandler getEvohomeBridge() { Bridge bridge = getBridge(); if (bridge != null) { return (EvohomeAccountBridgeHandler) bridge.getHandler(); @@ -78,10 +77,10 @@ protected EvohomeAccountBridgeHandler getEvohomeBridge() { * * @return The current evohome configuration */ - protected Locations getEvohomeConfig() { - EvohomeAccountBridgeHandler bridge = getEvohomeBridge(); - if (bridge != null) { - return bridge.getEvohomeConfig(); + protected @Nullable Locations getEvohomeConfig() { + EvohomeAccountBridgeHandler bridgeAccountHandler = getEvohomeBridge(); + if (bridgeAccountHandler != null) { + return bridgeAccountHandler.getEvohomeConfig(); } return null; @@ -115,7 +114,7 @@ protected void updateEvohomeThingStatus(ThingStatus newStatus) { * @param detail The status detail value * @param message The message to show with the status */ - protected void updateEvohomeThingStatus(ThingStatus newStatus, ThingStatusDetail detail, String message) { + protected void updateEvohomeThingStatus(ThingStatus newStatus, ThingStatusDetail detail, @Nullable String message) { // Prevent spamming the log file if (!newStatus.equals(getThing().getStatus())) { updateStatus(newStatus, detail, message); @@ -128,10 +127,7 @@ protected void updateEvohomeThingStatus(ThingStatus newStatus, ThingStatusDetail * @param configuration The configuration to check */ private void checkConfig() { - if (configuration == null) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Configuration is missing or corrupted"); - } else if (configuration.id == null || configuration.id.isEmpty()) { + if (configuration.id.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Id not configured"); } } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java index 22054a4e2a58f..e472ce968065c 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java @@ -22,19 +22,21 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.evohome.internal.RunnableWithTimeout; import org.openhab.binding.evohome.internal.api.EvohomeApiClient; -import org.openhab.binding.evohome.internal.api.models.v2.response.Gateway; -import org.openhab.binding.evohome.internal.api.models.v2.response.GatewayStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.Location; -import org.openhab.binding.evohome.internal.api.models.v2.response.LocationStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.Locations; -import org.openhab.binding.evohome.internal.api.models.v2.response.LocationsStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.TemperatureControlSystem; -import org.openhab.binding.evohome.internal.api.models.v2.response.TemperatureControlSystemStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.Zone; -import org.openhab.binding.evohome.internal.api.models.v2.response.ZoneStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Gateway; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.GatewayStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Location; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.LocationStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Locations; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.LocationsStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.TemperatureControlSystem; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.TemperatureControlSystemStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.Zone; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.ZoneStatus; import org.openhab.binding.evohome.internal.configuration.EvohomeAccountConfiguration; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -54,15 +56,16 @@ * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeAccountBridgeHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(EvohomeAccountBridgeHandler.class); private final HttpClient httpClient; - private EvohomeAccountConfiguration configuration; - private EvohomeApiClient apiClient; + private EvohomeAccountConfiguration configuration = new EvohomeAccountConfiguration(); + private @Nullable EvohomeApiClient apiClient; private List listeners = new CopyOnWriteArrayList<>(); - protected ScheduledFuture refreshTask; + protected @Nullable ScheduledFuture refreshTask; public EvohomeAccountBridgeHandler(Bridge thing, HttpClient httpClient) { super(thing); @@ -73,13 +76,14 @@ public EvohomeAccountBridgeHandler(Bridge thing, HttpClient httpClient) { public void initialize() { configuration = getConfigAs(EvohomeAccountConfiguration.class); - if (checkConfig()) { + if (checkConfig(configuration)) { apiClient = new EvohomeApiClient(configuration, this.httpClient); // Initialization can take a while, so kick it off on a separate thread scheduler.schedule(() -> { - if (apiClient.login()) { - if (checkInstallationInfoHasDuplicateIds(apiClient.getInstallationInfo())) { + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null && localApiCLient.login()) { + if (checkInstallationInfoHasDuplicateIds(localApiCLient.getInstallationInfo())) { startRefreshTask(); } else { updateAccountStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, @@ -104,24 +108,41 @@ public void dispose() { public void handleCommand(ChannelUID channelUID, Command command) { } - public Locations getEvohomeConfig() { - return apiClient.getInstallationInfo(); + public @Nullable Locations getEvohomeConfig() { + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null) { + return localApiCLient.getInstallationInfo(); + } + return null; } - public LocationsStatus getEvohomeStatus() { - return apiClient.getInstallationStatus(); + public @Nullable LocationsStatus getEvohomeStatus() { + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null) { + return localApiCLient.getInstallationStatus(); + } + return null; } public void setTcsMode(String tcsId, String mode) { - tryToCall(() -> apiClient.setTcsMode(tcsId, mode)); + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null) { + tryToCall(() -> localApiCLient.setTcsMode(tcsId, mode)); + } } public void setPermanentSetPoint(String zoneId, double doubleValue) { - tryToCall(() -> apiClient.setHeatingZoneOverride(zoneId, doubleValue)); + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null) { + tryToCall(() -> localApiCLient.setHeatingZoneOverride(zoneId, doubleValue)); + } } public void cancelSetPointOverride(String zoneId) { - tryToCall(() -> apiClient.cancelHeatingZoneOverride(zoneId)); + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null) { + tryToCall(() -> localApiCLient.cancelHeatingZoneOverride(zoneId)); + } } public void addAccountStatusListener(AccountStatusListener listener) { @@ -164,26 +185,27 @@ private boolean checkInstallationInfoHasDuplicateIds(Locations locations) { } private void disposeApiClient() { - if (apiClient != null) { - apiClient.logout(); + EvohomeApiClient localApiClient = apiClient; + if (localApiClient != null) { + localApiClient.logout(); + this.apiClient = null; } - apiClient = null; } private void disposeRefreshTask() { - if (refreshTask != null) { - refreshTask.cancel(true); + ScheduledFuture localRefreshTask = refreshTask; + if (localRefreshTask != null) { + localRefreshTask.cancel(true); + this.refreshTask = null; } } - private boolean checkConfig() { + private boolean checkConfig(EvohomeAccountConfiguration configuration) { String errorMessage = ""; - if (configuration == null) { - errorMessage = "Configuration is missing or corrupted"; - } else if (configuration.username == null || configuration.username.isEmpty()) { + if (configuration.username.isBlank()) { errorMessage = "Username not configured"; - } else if (configuration.password == null || configuration.password.isEmpty()) { + } else if (configuration.password.isBlank()) { errorMessage = "Password not configured"; } else { return true; @@ -202,7 +224,10 @@ private void startRefreshTask() { private void update() { try { - apiClient.update(); + EvohomeApiClient localApiCLient = apiClient; + if (localApiCLient != null) { + localApiCLient.update(); + } updateAccountStatus(ThingStatus.ONLINE); updateThings(); } catch (Exception e) { @@ -215,7 +240,7 @@ private void updateAccountStatus(ThingStatus newStatus) { updateAccountStatus(newStatus, ThingStatusDetail.NONE, null); } - private void updateAccountStatus(ThingStatus newStatus, ThingStatusDetail detail, String message) { + private void updateAccountStatus(ThingStatus newStatus, ThingStatusDetail detail, @Nullable String message) { // Prevent spamming the log file if (!newStatus.equals(getThing().getStatus())) { updateStatus(newStatus, detail, message); @@ -236,15 +261,32 @@ private void updateThings() { Map zoneIdToTcsIdMap = new HashMap<>(); Map idToTcsThingsStatusMap = new HashMap<>(); - // First, create a lookup table - for (LocationStatus location : apiClient.getInstallationStatus()) { - for (GatewayStatus gateway : location.getGateways()) { - for (TemperatureControlSystemStatus tcs : gateway.getTemperatureControlSystems()) { - idToTcsMap.put(tcs.getSystemId(), tcs); - tcsIdToGatewayMap.put(tcs.getSystemId(), gateway); - for (ZoneStatus zone : tcs.getZones()) { - idToZoneMap.put(zone.getZoneId(), zone); - zoneIdToTcsIdMap.put(zone.getZoneId(), tcs.getSystemId()); + EvohomeApiClient localApiClient = apiClient; + if (localApiClient != null) { + // First, create a lookup table + LocationsStatus localLocationsStatus = localApiClient.getInstallationStatus(); + if (localLocationsStatus != null) { + for (LocationStatus location : localLocationsStatus) { + for (GatewayStatus gateway : location.getGateways()) { + if (gateway == null) { + continue; + } + for (TemperatureControlSystemStatus tcs : gateway.getTemperatureControlSystems()) { + String systemId = tcs.getSystemId(); + if (systemId != null) { + idToTcsMap.put(systemId, tcs); + tcsIdToGatewayMap.put(systemId, gateway); + } + for (ZoneStatus zone : tcs.getZones()) { + String zoneId = zone.getZoneId(); + if (zoneId != null) { + idToZoneMap.put(zoneId, zone); + if (systemId != null) { + zoneIdToTcsIdMap.put(zoneId, systemId); + } + } + } + } } } } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeHeatingZoneHandler.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeHeatingZoneHandler.java index 1fb6d43456ccb..8dc6cbb3915ce 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeHeatingZoneHandler.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeHeatingZoneHandler.java @@ -12,10 +12,15 @@ */ package org.openhab.binding.evohome.internal.handler; +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.evohome.internal.EvohomeBindingConstants; -import org.openhab.binding.evohome.internal.api.models.v2.response.ZoneStatus; -import org.openhab.core.library.types.DecimalType; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.ZoneStatus; +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.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -30,12 +35,14 @@ * @author Jasper van Zuijlen - Initial contribution * @author Neil Renaud - Working implementation * @author Jasper van Zuijlen - Refactor + Permanent Zone temperature setting + * @author Leo Siepel - Add UoM */ +@NonNullByDefault public class EvohomeHeatingZoneHandler extends BaseEvohomeHandler { private static final int CANCEL_SET_POINT_OVERRIDE = 0; - private ThingStatus tcsStatus; - private ZoneStatus zoneStatus; + private @Nullable ThingStatus tcsStatus; + private @Nullable ZoneStatus zoneStatus; public EvohomeHeatingZoneHandler(Thing thing) { super(thing); @@ -46,7 +53,7 @@ public void initialize() { super.initialize(); } - public void update(ThingStatus tcsStatus, ZoneStatus zoneStatus) { + public void update(@Nullable ThingStatus tcsStatus, @Nullable ZoneStatus zoneStatus) { this.tcsStatus = tcsStatus; this.zoneStatus = zoneStatus; @@ -62,11 +69,11 @@ public void update(ThingStatus tcsStatus, ZoneStatus zoneStatus) { updateEvohomeThingStatus(ThingStatus.ONLINE); updateState(EvohomeBindingConstants.ZONE_TEMPERATURE_CHANNEL, - new DecimalType(zoneStatus.getTemperature().getTemperature())); + new QuantityType(zoneStatus.getTemperature().getTemperature(), SIUnits.CELSIUS)); updateState(EvohomeBindingConstants.ZONE_SET_POINT_STATUS_CHANNEL, new StringType(zoneStatus.getHeatSetpoint().getSetpointMode())); - updateState(EvohomeBindingConstants.ZONE_SET_POINT_CHANNEL, - new DecimalType(zoneStatus.getHeatSetpoint().getTargetTemperature())); + updateState(EvohomeBindingConstants.ZONE_SET_POINT_CHANNEL, new QuantityType( + zoneStatus.getHeatSetpoint().getTargetTemperature(), SIUnits.CELSIUS)); } } @@ -78,16 +85,19 @@ public void handleCommand(ChannelUID channelUID, Command command) { EvohomeAccountBridgeHandler bridge = getEvohomeBridge(); if (bridge != null) { String channelId = channelUID.getId(); - if (EvohomeBindingConstants.ZONE_SET_POINT_CHANNEL.equals(channelId) - && command instanceof DecimalType) { - double newTemp = ((DecimalType) command).doubleValue(); - if (newTemp == CANCEL_SET_POINT_OVERRIDE) { - bridge.cancelSetPointOverride(getEvohomeThingConfig().id); - } else if (newTemp < 5) { - newTemp = 5; - } - if (newTemp >= 5 && newTemp <= 35) { - bridge.setPermanentSetPoint(getEvohomeThingConfig().id, newTemp); + if (EvohomeBindingConstants.ZONE_SET_POINT_CHANNEL.equals(channelId)) { + if (command instanceof QuantityType) { + QuantityType state = ((QuantityType) command).toUnit(SIUnits.CELSIUS); + double newTempInCelsius = state.doubleValue(); + + if (newTempInCelsius == CANCEL_SET_POINT_OVERRIDE) { + bridge.cancelSetPointOverride(getEvohomeThingConfig().id); + } else if (newTempInCelsius < 5) { + newTempInCelsius = 5; + } + if (newTempInCelsius >= 5 && newTempInCelsius <= 35) { + bridge.setPermanentSetPoint(getEvohomeThingConfig().id, newTempInCelsius); + } } } } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeTemperatureControlSystemHandler.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeTemperatureControlSystemHandler.java index 6781e22b3548e..5d07539cd8d41 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeTemperatureControlSystemHandler.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeTemperatureControlSystemHandler.java @@ -12,9 +12,11 @@ */ package org.openhab.binding.evohome.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.evohome.internal.EvohomeBindingConstants; -import org.openhab.binding.evohome.internal.api.models.v2.response.GatewayStatus; -import org.openhab.binding.evohome.internal.api.models.v2.response.TemperatureControlSystemStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.GatewayStatus; +import org.openhab.binding.evohome.internal.api.models.v2.dto.response.TemperatureControlSystemStatus; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -29,9 +31,10 @@ * @author Jasper van Zuijlen - Initial contribution * */ +@NonNullByDefault public class EvohomeTemperatureControlSystemHandler extends BaseEvohomeHandler { - private GatewayStatus gatewayStatus; - private TemperatureControlSystemStatus tcsStatus; + private @Nullable GatewayStatus gatewayStatus; + private @Nullable TemperatureControlSystemStatus tcsStatus; public EvohomeTemperatureControlSystemHandler(Thing thing) { super(thing); @@ -42,7 +45,7 @@ public void initialize() { super.initialize(); } - public void update(GatewayStatus gatewayStatus, TemperatureControlSystemStatus tcsStatus) { + public void update(@Nullable GatewayStatus gatewayStatus, @Nullable TemperatureControlSystemStatus tcsStatus) { this.gatewayStatus = gatewayStatus; this.tcsStatus = tcsStatus; diff --git a/bundles/org.openhab.binding.evohome/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.evohome/src/main/resources/OH-INF/thing/channels.xml index c7b8c7e2538f3..0d8978b5ffeee 100644 --- a/bundles/org.openhab.binding.evohome/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.evohome/src/main/resources/OH-INF/thing/channels.xml @@ -20,19 +20,27 @@ - Number + Number:Temperature Current zone temperature temperature - + + Measurement + Temperature + + - Number + Number:Temperature Gets or sets the set point of this zone (0 cancels the override). heating - + + Setpoint + Temperature + + String diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java index 31f2102ff65e3..1f2e3d6ea503e 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java @@ -56,6 +56,8 @@ import org.openhab.binding.gardena.internal.model.dto.command.GardenaCommandRequest; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.io.net.http.WebSocketFactory; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,23 +103,24 @@ public class GardenaSmartImpl implements GardenaSmart, GardenaSmartWebSocketList private final Object newDeviceTasksLock = new Object(); private final List> newDeviceTasks = new ArrayList<>(); - public GardenaSmartImpl(String id, GardenaConfig config, GardenaSmartEventListener eventListener, + public GardenaSmartImpl(ThingUID uid, GardenaConfig config, GardenaSmartEventListener eventListener, ScheduledExecutorService scheduler, HttpClientFactory httpClientFactory, WebSocketFactory webSocketFactory) throws GardenaException { - this.id = id; + this.id = uid.getId(); this.config = config; this.eventListener = eventListener; this.scheduler = scheduler; logger.debug("Starting GardenaSmart"); try { - httpClient = httpClientFactory.createHttpClient(id); + String name = ThingWebClientUtil.buildWebClientConsumerName(uid, null); + httpClient = httpClientFactory.createHttpClient(name); httpClient.setConnectTimeout(config.getConnectionTimeout() * 1000L); httpClient.setIdleTimeout(httpClient.getConnectTimeout()); httpClient.start(); - String webSocketId = String.valueOf(hashCode()); - webSocketClient = webSocketFactory.createWebSocketClient(webSocketId); + name = ThingWebClientUtil.buildWebClientConsumerName(uid, "ws-"); + webSocketClient = webSocketFactory.createWebSocketClient(name); webSocketClient.setConnectTimeout(config.getConnectionTimeout() * 1000L); webSocketClient.setStopTimeout(3000); webSocketClient.setMaxIdleTimeout(150000); diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java index 5b80ec1c1c5da..adbf9d7d91379 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java @@ -173,8 +173,7 @@ private synchronized void initializeGardena() { GardenaConfig gardenaConfig = getThing().getConfiguration().as(GardenaConfig.class); logger.debug("{}", gardenaConfig); - String id = getThing().getUID().getId(); - gardenaSmart = new GardenaSmartImpl(id, gardenaConfig, this, scheduler, httpClientFactory, + gardenaSmart = new GardenaSmartImpl(getThing().getUID(), gardenaConfig, this, scheduler, httpClientFactory, webSocketFactory); final GardenaDeviceDiscoveryService discoveryService = this.discoveryService; if (discoveryService != null) { diff --git a/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerV2Handler.java b/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerV2Handler.java index a853a8a14bacb..027af1eaffe3f 100644 --- a/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerV2Handler.java +++ b/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerV2Handler.java @@ -275,11 +275,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof DecimalType) { value = String.valueOf(((DecimalType) command).intValue()); } + break; case TRANSACTION: key = "trx"; if (command instanceof DecimalType) { value = String.valueOf(((DecimalType) command).intValue()); } + break; } if (key != null && value != null) { diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpBindingConstants.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpBindingConstants.java index a499eb953a48d..79ed4b87ee05a 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpBindingConstants.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpBindingConstants.java @@ -24,7 +24,7 @@ @NonNullByDefault public class HttpBindingConstants { - private static final String BINDING_ID = "http"; + public static final String BINDING_ID = "http"; public static final ThingTypeUID THING_TYPE_URL = new ThingTypeUID(BINDING_ID, "url"); } diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java index 0b864da97a8ea..617e21c4d72d0 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.http.internal; -import static org.openhab.binding.http.internal.HttpBindingConstants.THING_TYPE_URL; +import static org.openhab.binding.http.internal.HttpBindingConstants.*; import java.util.Set; @@ -59,8 +59,9 @@ public class HttpHandlerFactory extends BaseThingHandlerFactory @Activate public HttpHandlerFactory(@Reference HttpClientFactory httpClientFactory, @Reference HttpDynamicStateDescriptionProvider httpDynamicStateDescriptionProvider) { - this.secureClient = new HttpClient(new SslContextFactory.Client()); - this.insecureClient = new HttpClient(new SslContextFactory.Client(true)); + this.secureClient = httpClientFactory.createHttpClient(BINDING_ID + "-secure", new SslContextFactory.Client()); + this.insecureClient = httpClientFactory.createHttpClient(BINDING_ID + "-insecure", + new SslContextFactory.Client(true)); try { this.secureClient.start(); this.insecureClient.start(); diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ShutterAction.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ShutterAction.java index 62c24320ae3a7..351db4db727f8 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ShutterAction.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ShutterAction.java @@ -15,7 +15,7 @@ /** * Special {@link Action} needed to control shutters. * - * @author Marco Mans + * @author Marco Mans - Initial contribution */ public class ShutterAction extends Action { diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java index 528bfb8831caa..c82b8ae7ffc0c 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java @@ -32,7 +32,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -58,6 +57,7 @@ import org.openhab.binding.innogysmarthome.internal.listener.EventListener; import org.openhab.binding.innogysmarthome.internal.manager.DeviceStructureManager; import org.openhab.binding.innogysmarthome.internal.manager.FullDeviceManager; +import org.openhab.binding.innogysmarthome.internal.util.ExceptionUtils; import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener; import org.openhab.core.auth.client.oauth2.AccessTokenResponse; import org.openhab.core.auth.client.oauth2.OAuthClientService; @@ -954,7 +954,7 @@ private boolean handleClientException(final Exception e) { isReinitialize = false; Thread.currentThread().interrupt(); } else if (e instanceof ExecutionException) { - logger.debug("ExecutionException: {}", ExceptionUtils.getRootCauseMessage(e)); + logger.debug("ExecutionException: {}", ExceptionUtils.getRootThrowable(e).getMessage()); updateStatus(ThingStatus.OFFLINE); } else { logger.debug("Unknown exception", e); diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java index 4628c311cc6d6..a50e428257ede 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java @@ -56,7 +56,6 @@ public FullDeviceManager(InnogyClient client) { * states. Calling this may take a while... */ public List getFullDevices() throws IOException, ApiException, AuthenticationException { - final Map locationMap = createLocationMap(client); final Map capabilityMap = createCapabilityMap(client); final Map deviceStateMap = createDeviceStateMap(client); @@ -91,7 +90,6 @@ public Device getFullDeviceById(final String deviceId) throws IOException, ApiEx private void initializeDevice(Device device, @Nullable DeviceState deviceState, Map locationMap, Map capabilityMap, List messageList) { - device.setDeviceState(deviceState); if (isBatteryPowered(device)) { @@ -135,7 +133,6 @@ private static Map createCapabilityStateMap(InnogyClien private static Map createCapabilityMap(InnogyClient client) throws IOException, ApiException, AuthenticationException { - final Map capabilityStateMap = createCapabilityStateMap(client); final List capabilityList = client.getCapabilities(); @@ -144,7 +141,6 @@ private static Map createCapabilityMap(InnogyClient client) private static Map createCapabilityMap(String deviceId, InnogyClient client) throws IOException, ApiException, AuthenticationException { - final Map capabilityStateMap = createCapabilityStateMap(client); final List capabilityList = client.getCapabilitiesForDevice(deviceId); @@ -167,7 +163,6 @@ private static Map initializeCapabilities(Map createDeviceCapabilityMap(Device device, Map capabilityMap) { - final HashMap deviceCapabilityMap = new HashMap<>(); for (final String capabilityValue : device.getCapabilities()) { final Capability capability = capabilityMap.get(Link.getId(capabilityValue)); diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/util/ExceptionUtils.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/util/ExceptionUtils.java new file mode 100644 index 0000000000000..6761f5b02959f --- /dev/null +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/util/ExceptionUtils.java @@ -0,0 +1,39 @@ +/** + * 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.innogysmarthome.internal.util; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ExceptionUtils} class defines static Exception related methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class ExceptionUtils { + + public static Throwable getRootThrowable(Throwable throwable) { + List list = new ArrayList<>(); + while (!list.contains(throwable)) { + list.add(throwable); + Throwable throwableLocal = throwable.getCause(); + if (throwableLocal != null) { + throwable = throwableLocal; + } + } + return throwable; + } +} diff --git a/bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandlerTest.java b/bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandlerTest.java index a07030a04110d..83847ae0da14f 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandlerTest.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandlerTest.java @@ -36,6 +36,11 @@ import org.openhab.core.config.core.Configuration; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; /** * @author Sven Strohschein - Initial contribution @@ -50,6 +55,12 @@ public class InnogyBridgeHandlerTest { @BeforeEach public void before() throws Exception { + final Logger loggerBridge = (Logger) LoggerFactory.getLogger(InnogyBridgeHandler.class); + loggerBridge.setLevel(Level.OFF); + + final Logger logerBaseHandler = (Logger) LoggerFactory.getLogger(BaseThingHandler.class); + logerBaseHandler.setLevel(Level.OFF); + bridgeMock = mock(Bridge.class); when(bridgeMock.getUID()).thenReturn(new ThingUID("innogysmarthome", "bridge")); diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200BindingConstants.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200BindingConstants.java index ffe3333a43750..591221fe88d05 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200BindingConstants.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200BindingConstants.java @@ -25,8 +25,6 @@ public class KM200BindingConstants { public static final String BINDING_ID = "km200"; - public static final String CONFIG_DESCRIPTION_URI_CHANNEL = "channel-type:km200:config"; - public static final String CONFIG_DESCRIPTION_URI_THING = "thing-type:km200:config"; // Bridge UID public static final ThingTypeUID THING_TYPE_KMDEVICE = new ThingTypeUID(BINDING_ID, "kmdevice"); diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java index cf5f42248d777..19b2a1325a532 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java @@ -19,7 +19,6 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -56,11 +55,11 @@ public class KM200Device { protected String charSet = ""; /* Needed keys for the communication */ - protected byte[] cryptKeyInit = ArrayUtils.EMPTY_BYTE_ARRAY; - protected byte[] cryptKeyPriv = ArrayUtils.EMPTY_BYTE_ARRAY; + protected byte[] cryptKeyInit = new byte[0]; + protected byte[] cryptKeyPriv = new byte[0]; /* Buderus_MD5Salt */ - protected byte[] md5Salt = ArrayUtils.EMPTY_BYTE_ARRAY; + protected byte[] md5Salt = new byte[0]; /* Device services */ public Map serviceTreeMap; diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java index 95d6826aab5e1..1f2da882e8f51 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java @@ -92,7 +92,6 @@ public KM200GatewayHandler(Bridge bridge, HttpClient httpClient) { super(bridge); refreshInterval = 120; readDelay = 100; - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.CONFIGURATION_PENDING); remoteDevice = new KM200Device(httpClient); dataHandler = new KM200DataHandler(remoteDevice); executor = Executors.newScheduledThreadPool(2, new NamedThreadFactory("org.openhab.binding.km200", true)); diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200ThingHandler.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200ThingHandler.java index 9842eebf4ca2a..99bff1063efc7 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200ThingHandler.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200ThingHandler.java @@ -16,8 +16,6 @@ import java.math.BigDecimal; import java.math.RoundingMode; -import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -149,7 +147,6 @@ String checkCategory(String unitOfMeasure, String topCategory, @Nullable Boolean Channel createChannel(ChannelTypeUID channelTypeUID, ChannelUID channelUID, String root, String type, @Nullable String currentPathName, String description, String label, boolean addProperties, boolean switchProgram, StateDescriptionFragment state, String unitOfMeasure) { - URI configDescriptionUriChannel = null; Channel newChannel = null; ChannelType channelType = null; Map chProperties = new HashMap<>(); @@ -162,20 +159,11 @@ Channel createChannel(ChannelTypeUID channelTypeUID, ChannelUID channelUID, Stri logger.info("Channeltype {} not supported", type); return null; } - try { - configDescriptionUriChannel = new URI(CONFIG_DESCRIPTION_URI_CHANNEL); - channelType = ChannelTypeBuilder.state(channelTypeUID, label, type) // - .withDescription(description) // - .withCategory(checkCategory(unitOfMeasure, category, state.isReadOnly())) // - .withStateDescriptionFragment(state) // - .withConfigDescriptionURI(configDescriptionUriChannel).build(); - } catch (URISyntaxException ex) { - logger.warn("Can't create ConfigDescription URI '{}', ConfigDescription for channels not avilable!", - CONFIG_DESCRIPTION_URI_CHANNEL); - channelType = ChannelTypeBuilder.state(channelTypeUID, label, type) // - .withDescription(description) // - .withCategory(checkCategory(unitOfMeasure, category, state.isReadOnly())).build(); - } + channelType = ChannelTypeBuilder.state(channelTypeUID, label, type) // + .withDescription(description) // + .withCategory(checkCategory(unitOfMeasure, category, state.isReadOnly())) // + .withStateDescriptionFragment(state).build(); + channelTypeProvider.addChannelType(channelType); chProperties.put("root", KM200Utils.translatesPathToName(root)); diff --git a/bundles/org.openhab.binding.knx/README.md b/bundles/org.openhab.binding.knx/README.md index db9a7a671362a..21b62a106ad0d 100644 --- a/bundles/org.openhab.binding.knx/README.md +++ b/bundles/org.openhab.binding.knx/README.md @@ -14,10 +14,6 @@ Since the protocol is identical, the KNX binding can also communicate with it tr The KNX binding supports two types of bridges, and one type of things to access the KNX bus. There is an _ip_ bridge to connect to KNX IP Gateways, and a _serial_ bridge for connection over a serial port to a host-attached gateway. -## Binding Configuration - -The binding itself does not require any special configuration. - ## Bridges The following two bridge types are supported. Bridges don't have channels on their own. @@ -124,6 +120,9 @@ Note: After changing the DPT of already existing Channels, openHAB needs to be r |-----------|---------------|-------------| | ga | Group address | 1.009 | +*Attention:* Due to a bug in the original implementation, the states for DPT 1.009 are inverted (i.e. `1` is mapped to `OPEN` instead of `CLOSE`). +A change would break all existing installations and is therefore not implemented. + ##### Channel Type "number" | Parameter | Description | Default DPT | @@ -189,6 +188,9 @@ If from the KNX bus a `GroupValueRead` telegram is sent to a *-control Channel, |-----------|---------------|-------------| | ga | Group address | 1.009 | +*Attention:* Due to a bug in the original implementation, the states for DPT 1.009 are inverted (i.e. `1` is mapped to `OPEN` instead of `CLOSE`). +A change would break all existing installations and is therefore not implemented. + ##### Channel Type "number-control" | Parameter | Description | Default DPT | diff --git a/bundles/org.openhab.binding.knx/pom.xml b/bundles/org.openhab.binding.knx/pom.xml index 65d508c0e5f1e..acab25a9550da 100644 --- a/bundles/org.openhab.binding.knx/pom.xml +++ b/bundles/org.openhab.binding.knx/pom.xml @@ -15,25 +15,19 @@ openHAB Add-ons :: Bundles :: KNX Binding - gnu.io;version="[3.12,6)",javax.microedition.io.*;resolution:="optional",javax.usb.*;resolution:="optional",org.usb4java.*;resolution:="optional" + javax.microedition.io.*;resolution:="optional",javax.usb.*;resolution:="optional",org.usb4java.*;resolution:="optional" - com.github.calimero - calimero-core - 2.5.1 - compile - - - org.slf4j - slf4j-api - - + biz.aQute.bnd + biz.aQute.bnd.annotation + ${bnd.version} + provided com.github.calimero - calimero-device + calimero-core 2.5.1 compile @@ -45,7 +39,7 @@ com.github.calimero - calimero-rxtx + calimero-device 2.5.1 compile @@ -60,6 +54,23 @@ + + biz.aQute.bnd + bnd-maven-plugin + + + + + org.apache.maven.plugins maven-javadoc-plugin diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java index 6e20fa676ff72..20e269f8300f6 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java @@ -12,20 +12,26 @@ */ package org.openhab.binding.knx.internal.client; -import java.util.Enumeration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.io.transport.serial.SerialPortIdentifier; +import org.openhab.core.io.transport.serial.SerialPortManager; import org.openhab.core.thing.ThingUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gnu.io.CommPortIdentifier; -import gnu.io.RXTXVersion; +import tuwien.auto.calimero.Connection.BlockingMode; import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.link.KNXNetworkLink; import tuwien.auto.calimero.link.KNXNetworkLinkFT12; import tuwien.auto.calimero.link.medium.TPSettings; +import tuwien.auto.calimero.serial.FT12Connection; /** * Serial specific {@link AbstractKNXClient} implementation. @@ -40,50 +46,98 @@ public class SerialClient extends AbstractKNXClient { private final Logger logger = LoggerFactory.getLogger(SerialClient.class); + private final SerialPortManager serialPortManager; private final String serialPort; private final boolean useCemi; public SerialClient(int autoReconnectPeriod, ThingUID thingUID, int responseTimeout, int readingPause, int readRetriesLimit, ScheduledExecutorService knxScheduler, String serialPort, boolean useCemi, - StatusUpdateCallback statusUpdateCallback) { + SerialPortManager serialPortManager, StatusUpdateCallback statusUpdateCallback) { super(autoReconnectPeriod, thingUID, responseTimeout, readingPause, readRetriesLimit, knxScheduler, statusUpdateCallback); + this.serialPortManager = serialPortManager; this.serialPort = serialPort; this.useCemi = useCemi; } + /** + * try autodetection of cEMI devices via the PEI identification frame + * + * @implNote This is based on an vendor specific extension and may not work for other devices. + */ + protected boolean detectCemi() throws InterruptedException { + final byte[] peiIdentifyReqFrame = { (byte) 0xa7 }; + final byte peiIdentifyCon = (byte) 0xa8; + final byte peiWzIdentFrameLength = 11; + + logger.trace("Checking for cEMI support"); + + try (FT12Connection serialConnection = new FT12Connection(serialPort)) { + final CompletableFuture frameListener = new CompletableFuture(); + serialConnection.addConnectionListener(frameReceived -> { + final byte[] content = frameReceived.getFrameBytes(); + if ((content.length > 0) && (content[0] == peiIdentifyCon)) { + logger.trace("Received PEI confirmation of {} bytes", content.length); + frameListener.complete(content); + } + }); + + serialConnection.send(peiIdentifyReqFrame, BlockingMode.NonBlocking); + byte[] content = frameListener.get(1, TimeUnit.SECONDS); + + if (peiWzIdentFrameLength == content.length) { + // standard emi2 frame contain 9 bytes, + // content[1..2] physical address + // content[3..8] serial no + // + // Weinzierl adds 2 extra bytes, 0x0004 for capablity cEMI, + // see "Weinzierl KNX BAOS Starter Kit, User Guide" + if (0 == content[9] && 4 == content[10]) { + logger.debug("Detected device with cEMI support"); + return true; + } + } + } catch (final ExecutionException | TimeoutException | KNXException na) { + if (logger.isTraceEnabled()) { + logger.trace("Exception detecting cEMI: ", na); + } + } + + logger.trace("Did not detect device with cEMI support"); + return false; + } + @Override protected KNXNetworkLink establishConnection() throws KNXException, InterruptedException { try { - RXTXVersion.getVersion(); - logger.debug("Establishing connection to KNX bus through FT1.2 on serial port {}{}.", serialPort, - (useCemi ? " using CEMI" : "")); + boolean useCemiL = useCemi; + if (!useCemiL) { + useCemiL = detectCemi(); + } + logger.debug("Establishing connection to KNX bus through FT1.2 on serial port {}{}{}", serialPort, + (useCemiL ? " using cEMI" : ""), ((useCemiL != useCemi) ? " (autodetected)" : "")); // CEMI support by Calimero library, userful for newer serial devices like KNX RF sticks, kBerry, // etc.; default is still old EMI frame format - if (useCemi) { + if (useCemiL) { return KNXNetworkLinkFT12.newCemiLink(serialPort, new TPSettings()); } + return new KNXNetworkLinkFT12(serialPort, new TPSettings()); } catch (NoClassDefFoundError e) { throw new KNXException( - "The serial FT1.2 KNX connection requires the RXTX libraries to be available, but they could not be found!", + "The serial FT1.2 KNX connection requires the serial libraries to be available, but they could not be found!", e); } catch (KNXException e) { final String msg = e.getMessage(); // TODO add a test for this string match; error message might change in later version of Calimero library if ((msg != null) && (msg.startsWith(CALIMERO_ERROR_CANNOT_OPEN_PORT))) { - StringBuilder sb = new StringBuilder("Available ports are:\n"); - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - while (portList.hasMoreElements()) { - CommPortIdentifier id = (CommPortIdentifier) portList.nextElement(); - if ((id != null) && (id.getPortType() == CommPortIdentifier.PORT_SERIAL)) { - sb.append(id.getName()); - sb.append("\n"); - } + String availablePorts = serialPortManager.getIdentifiers().map(SerialPortIdentifier::getName) + .collect(Collectors.joining("\n")); + if (!availablePorts.isEmpty()) { + availablePorts = " Available ports are:\n" + availablePorts; } - sb.deleteCharAt(sb.length() - 1); - throw new KNXException("Serial port '" + serialPort + "' could not be opened. " + sb.toString()); + throw new KNXException("Serial port '" + serialPort + "' could not be opened." + availablePorts); } else { throw e; } diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java new file mode 100644 index 0000000000000..125c2e2ea9f53 --- /dev/null +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java @@ -0,0 +1,208 @@ +/** + * 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.knx.internal.client; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.io.transport.serial.PortInUseException; +import org.openhab.core.io.transport.serial.SerialPort; +import org.openhab.core.io.transport.serial.SerialPortIdentifier; +import org.openhab.core.io.transport.serial.SerialPortManager; +import org.openhab.core.io.transport.serial.UnsupportedCommOperationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import aQute.bnd.annotation.spi.ServiceProvider; +import tuwien.auto.calimero.KNXException; +import tuwien.auto.calimero.serial.spi.SerialCom; + +/** + * The {@link SerialTransportAdapter} provides org.openhab.core.io.transport.serial + * services to the Calimero library. + * + * @ServiceProvider annotation (biz.aQute.bnd.annotation) automatically creates the file + * /META-INF/services/tuwien.auto.calimero.serial.spi.SerialCom + * to register SerialTransportAdapter to the service loader. + * Additional attributes for SerialTansportAdapter can be specified as well, e.g. + * attribute = { "position=1" } + * and will be part of MANIFEST.MF + * + * @author Holger Friedrich - Initial contribution + */ +@ServiceProvider(value = SerialCom.class) +@NonNullByDefault +public class SerialTransportAdapter implements SerialCom { + private static final int OPEN_TIMEOUT_MS = 200; + private static final int RECEIVE_TIMEOUT_MS = 5; + private static final int RECEIVE_THRESHOLD = 1024; + private static final int BAUDRATE = 19200; + + private Logger logger = LoggerFactory.getLogger(SerialTransportAdapter.class); + @Nullable + private static SerialPortManager serialPortManager = null; + @Nullable + private SerialPort serialPort = null; + + public static void setSerialPortManager(SerialPortManager serialPortManager) { + SerialTransportAdapter.serialPortManager = serialPortManager; + } + + public SerialTransportAdapter() { + } + + @Override + public void open(@Nullable String portId) throws IOException, KNXException { + if (portId == null) { + throw new IOException("Port not available"); + } + logger = LoggerFactory.getLogger("SerialTransportAdapter:" + portId); + + final @Nullable SerialPortManager tmpSerialPortManager = serialPortManager; + if (tmpSerialPortManager == null) { + throw new IOException("PortManager not available"); + } + try { + SerialPortIdentifier portIdentifier = tmpSerialPortManager.getIdentifier(portId); + if (portIdentifier != null) { + logger.trace("Trying to open port {}", portId); + SerialPort serialPort = portIdentifier.open(this.getClass().getName(), OPEN_TIMEOUT_MS); + // apply default settings for com port, may be overwritten by caller + serialPort.setSerialPortParams(BAUDRATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, + SerialPort.PARITY_EVEN); + serialPort.enableReceiveThreshold(RECEIVE_THRESHOLD); + serialPort.enableReceiveTimeout(RECEIVE_TIMEOUT_MS); + this.serialPort = serialPort; + + // Notification / event listeners are available and may be used to log/trace com failures + // serialPort.notifyOnDataAvailable(true); + logger.trace("Port opened successfully"); + } else { + logger.info("Port {} not available", portId); + throw new IOException("Port " + portId + " not available"); + } + } catch (PortInUseException e) { + logger.info("Port {} already in use", portId); + throw new IOException("Port " + portId + " already in use", e); + } catch (UnsupportedCommOperationException e) { + logger.info("Port {} unsupported com operation", portId); + throw new IOException("Port " + portId + " unsupported com operation", e); + } + } + + // SerialCom extends AutoCloseable, close() throws Exception + @Override + public void close() { + logger.trace("Closing serial port"); + final @Nullable SerialPort tmpSerialPort = serialPort; + if (tmpSerialPort != null) { + tmpSerialPort.close(); + serialPort = null; + } + } + + @Override + public @Nullable InputStream inputStream() { + final @Nullable SerialPort tmpSerialPort = serialPort; + if (tmpSerialPort != null) { + try { + return tmpSerialPort.getInputStream(); + } catch (IOException e) { + logger.info("Cannot open input stream"); + } + } + // should not throw, create a dummy return value + byte buf[] = {}; + return new ByteArrayInputStream(buf); + } + + @Override + public @Nullable OutputStream outputStream() { + final @Nullable SerialPort tmpSerialPort = serialPort; + if (tmpSerialPort != null) { + try { + return tmpSerialPort.getOutputStream(); + } catch (IOException e) { + logger.info("Cannot open output stream"); + } + } + // should not throw, create a dummy return value + return new ByteArrayOutputStream(0); + } + + // disable NonNullByDefault for this function, legacy interface List + @NonNullByDefault({}) + @Override + public List portIdentifiers() { + final @Nullable SerialPortManager tmpSerialPortManager = serialPortManager; + if (tmpSerialPortManager == null) { + return Collections.emptyList(); + } + return tmpSerialPortManager.getIdentifiers().map(SerialPortIdentifier::getName).collect(Collectors.toList()); + } + + @Override + public int baudRate() throws IOException { + final @Nullable SerialPort tmpSerialPort = serialPort; + if (tmpSerialPort == null) { + throw new IOException("Port not available"); + } + return tmpSerialPort.getBaudRate(); + } + + @Override + public void setSerialPortParams(final int baudrate, final int databits, @Nullable StopBits stopbits, + @Nullable Parity parity) throws IOException { + final @Nullable SerialPort tmpSerialPort = serialPort; + if (tmpSerialPort == null) { + throw new IOException("Port not available"); + } + if ((stopbits == null) || (parity == null)) { + throw new IOException("Invalid parameters"); + } + try { + tmpSerialPort.setSerialPortParams(baudrate, databits, stopbits.value(), parity.value()); + } catch (final UnsupportedCommOperationException e) { + throw new IOException("Setting serial port parameters for " + tmpSerialPort.getName() + " failed", e); + } + } + + @Override + public void setFlowControlMode(@Nullable FlowControl mode) throws IOException { + final @Nullable SerialPort tmpSerialPort = serialPort; + if (tmpSerialPort == null) { + throw new IOException("Port not available"); + } + if (mode == null) { + throw new IOException("Invalid parameters"); + } + if (mode == FlowControl.None) { + try { + tmpSerialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); + } catch (final UnsupportedCommOperationException e) { + throw new IOException("Setting flow control parameters for " + tmpSerialPort.getName() + " failed", e); + } + } else { + logger.warn("Unknown FlowControl mode {}", mode); + throw new IOException("Invalid flow mode"); + } + } +} diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java index bc2b18e31512a..db902650a10a9 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java @@ -14,8 +14,6 @@ import java.math.BigDecimal; import java.math.RoundingMode; -import java.text.DecimalFormat; -import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -890,7 +888,7 @@ private String quantityTypeToDPTValue(QuantityType qt, int mainNumber, int su DPTXlator3BitControlled translator3BitControlled = (DPTXlator3BitControlled) translator; if (translator3BitControlled.getStepCode() == 0) { logger.debug("toType: KNX DPT_Control_Dimming: break received."); - return UnDefType.UNDEF; + return UnDefType.NULL; } switch (subNumber) { case 7: @@ -899,27 +897,6 @@ private String quantityTypeToDPTValue(QuantityType qt, int mainNumber, int su case 8: return translator3BitControlled.getControlBit() ? UpDownType.DOWN : UpDownType.UP; } - case 14: - /* - * FIXME: Workaround for a bug in Calimero / Openhab DPTXlator4ByteFloat.makeString(): is using a - * locale when - * translating a Float to String. It could happen the a ',' is used as separator, such as - * 3,14159E20. - * Openhab's DecimalType expects this to be in US format and expects '.': 3.14159E20. - * There is no issue with DPTXlator2ByteFloat since calimero is using a non-localized translation - * there. - */ - DPTXlator4ByteFloat translator4ByteFloat = (DPTXlator4ByteFloat) translator; - Float f = translator4ByteFloat.getValueFloat(); - if (Math.abs(f) < 100000) { - value = String.valueOf(f); - } else { - NumberFormat dcf = NumberFormat.getInstance(Locale.US); - if (dcf instanceof DecimalFormat) { - ((DecimalFormat) dcf).applyPattern("0.#####E0"); - } - value = dcf.format(f); - } break; case 18: DPTXlatorSceneControl translatorSceneControl = (DPTXlatorSceneControl) translator; diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java index a510355f2bd3f..460d236206710 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java @@ -19,6 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.knx.internal.client.SerialTransportAdapter; import org.openhab.binding.knx.internal.handler.DeviceThingHandler; import org.openhab.binding.knx.internal.handler.IPBridgeThingHandler; import org.openhab.binding.knx.internal.handler.SerialBridgeThingHandler; @@ -26,6 +27,7 @@ import org.openhab.core.config.core.Configuration; import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.io.transport.serial.SerialPortManager; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; @@ -53,11 +55,14 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory { @Nullable private NetworkAddressService networkAddressService; + private final SerialPortManager serialPortManager; @Activate public KNXHandlerFactory(final @Reference TranslationProvider translationProvider, - final @Reference LocaleProvider localeProvider) { + final @Reference LocaleProvider localeProvider, final @Reference SerialPortManager serialPortManager) { KNXTranslationProvider.I18N.setProvider(localeProvider, translationProvider); + this.serialPortManager = serialPortManager; + SerialTransportAdapter.setSerialPortManager(serialPortManager); } @Override @@ -87,7 +92,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (thing.getThingTypeUID().equals(THING_TYPE_IP_BRIDGE)) { return new IPBridgeThingHandler((Bridge) thing, networkAddressService); } else if (thing.getThingTypeUID().equals(THING_TYPE_SERIAL_BRIDGE)) { - return new SerialBridgeThingHandler((Bridge) thing); + return new SerialBridgeThingHandler((Bridge) thing, serialPortManager); } else if (thing.getThingTypeUID().equals(THING_TYPE_DEVICE)) { return new DeviceThingHandler(thing); } diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/DeviceThingHandler.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/DeviceThingHandler.java index aecce6bce168c..68d5c74b72e4f 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/DeviceThingHandler.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/DeviceThingHandler.java @@ -413,7 +413,7 @@ private void processDataReceived(GroupAddress destination, byte[] asdu, InboundS } } } else { - if (type instanceof State) { + if (type instanceof State && !(type instanceof UnDefType)) { updateState(channelUID, (State) type); } } diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java index 4c598f1fe7d36..a97538165891d 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java @@ -20,6 +20,7 @@ import org.openhab.binding.knx.internal.client.NoOpClient; import org.openhab.binding.knx.internal.client.SerialClient; import org.openhab.binding.knx.internal.config.SerialBridgeConfiguration; +import org.openhab.core.io.transport.serial.SerialPortManager; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ThingStatus; import org.slf4j.Logger; @@ -42,8 +43,11 @@ public class SerialBridgeThingHandler extends KNXBridgeBaseThingHandler { private final Logger logger = LoggerFactory.getLogger(SerialBridgeThingHandler.class); - public SerialBridgeThingHandler(Bridge bridge) { + private final SerialPortManager serialPortManager; + + public SerialBridgeThingHandler(Bridge bridge, final SerialPortManager serialPortManager) { super(bridge); + this.serialPortManager = serialPortManager; } @Override @@ -53,7 +57,7 @@ public void initialize() { SerialBridgeConfiguration config = getConfigAs(SerialBridgeConfiguration.class); client = new SerialClient(config.getAutoReconnectPeriod(), thing.getUID(), config.getResponseTimeout(), config.getReadingPause(), config.getReadRetriesLimit(), getScheduler(), config.getSerialPort(), - config.useCemi(), this); + config.useCemi(), serialPortManager, this); updateStatus(ThingStatus.UNKNOWN); // delay actual initialization, allow for longer runtime of actual initialization diff --git a/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/thirdgeneration/ThirdGenerationHandler.java b/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/thirdgeneration/ThirdGenerationHandler.java index 7d66418e8a252..039b7bd06ff2f 100644 --- a/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/thirdgeneration/ThirdGenerationHandler.java +++ b/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/thirdgeneration/ThirdGenerationHandler.java @@ -327,7 +327,7 @@ private final void authenticate() { if (statusCode == 503) { // internal communication error // This can happen if the device is not ready yet for communication - updateStatus(ThingStatus.UNINITIALIZED); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR); return; } } catch (InterruptedException | TimeoutException | ExecutionException e) { diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java index 2da48f9353774..5ceaeb7b34aac 100644 --- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java +++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.linky.internal.handler.LinkyHandler; import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.io.net.http.HttpClientFactory; @@ -68,25 +69,27 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory { public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider, final @Reference HttpClientFactory httpClientFactory) { this.localeProvider = localeProvider; - this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID); - } - - @Override - protected void activate(ComponentContext componentContext) { - super.activate(componentContext); - httpClient.setFollowRedirects(false); - httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE); - + SslContextFactory sslContextFactory = new SslContextFactory.Client(); try { SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null); - httpClient.getSslContextFactory().setSslContext(sslContext); - httpClient.start(); + sslContextFactory.setSslContext(sslContext); } catch (NoSuchAlgorithmException e) { logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(), e); } catch (KeyManagementException e) { logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e); + } + this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory); + httpClient.setFollowRedirects(false); + httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE); + } + + @Override + protected void activate(ComponentContext componentContext) { + super.activate(componentContext); + try { + httpClient.start(); } catch (Exception e) { logger.warn("Unable to start Jetty HttpClient {}", e.getMessage()); } diff --git a/bundles/org.openhab.binding.mycroft/src/main/java/org/openhab/binding/mycroft/internal/MycroftHandler.java b/bundles/org.openhab.binding.mycroft/src/main/java/org/openhab/binding/mycroft/internal/MycroftHandler.java index bb132c37e823c..7b00fa0318632 100644 --- a/bundles/org.openhab.binding.mycroft/src/main/java/org/openhab/binding/mycroft/internal/MycroftHandler.java +++ b/bundles/org.openhab.binding.mycroft/src/main/java/org/openhab/binding/mycroft/internal/MycroftHandler.java @@ -43,6 +43,7 @@ import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.slf4j.Logger; @@ -126,13 +127,7 @@ public void initialize() { logger.debug("Start initializing Mycroft {}", thing.getUID()); - String websocketID = thing.getUID().getAsString().replace(':', '-'); - if (websocketID.length() < 4) { - websocketID = "mycroft-" + websocketID; - } - if (websocketID.length() > 20) { - websocketID = websocketID.substring(websocketID.length() - 20); - } + String websocketID = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); this.connection = new MycroftConnection(this, webSocketFactory.createWebSocketClient(websocketID)); config = getConfigAs(MycroftConfiguration.class); diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java index 49953a787f672..304f9f62ab65b 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java @@ -91,6 +91,7 @@ import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.slf4j.Logger; @@ -147,7 +148,7 @@ public NanoleafControllerHandler(Bridge bridge, HttpClientFactory httpClientFact } private void initializeTouchHttpClient() { - String httpClientName = thing.getUID().getId(); + String httpClientName = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); try { httpClientSSETouchEvent = httpClientFactory.createHttpClient(httpClientName); @@ -290,6 +291,14 @@ public void handleRemoval() { @Override public void dispose() { stopAllJobs(); + HttpClient localHttpClientSSETouchEvent = this.httpClientSSETouchEvent; + if (localHttpClientSSETouchEvent != null) { + try { + localHttpClientSSETouchEvent.stop(); + } catch (Exception e) { + } + this.httpClientSSETouchEvent = null; + } super.dispose(); logger.debug("Disposing handler for Nanoleaf controller {}", getThing().getUID()); } diff --git a/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java b/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java index cf8a7f3779142..49166b6319790 100644 --- a/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java +++ b/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java @@ -17,7 +17,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.ObjectUtils; import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.neato.internal.CouldNotFindRobotException; import org.openhab.binding.neato.internal.NeatoBindingConstants; @@ -146,7 +145,9 @@ private void publishChannels() { updateProperty(Thing.PROPERTY_MODEL_ID, neatoState.getMeta().getModelName()); updateState(CHANNEL_STATE, new StringType(neatoState.getRobotState().name())); - updateState(CHANNEL_ERROR, new StringType((String) ObjectUtils.defaultIfNull(neatoState.getError(), ""))); + + String error = neatoState.getError() != null ? neatoState.getError() : ""; + updateState(CHANNEL_ERROR, new StringType(error)); updateState(CHANNEL_ACTION, new StringType(neatoState.getRobotAction().name())); Details details = neatoState.getDetails(); diff --git a/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandler.java b/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandler.java index 3e22cddf44e3b..40715180f4409 100644 --- a/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandler.java +++ b/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandler.java @@ -30,6 +30,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord; import org.openhab.binding.neohub.internal.NeoHubBindingConstants.NeoHubReturnResult; +import org.openhab.core.io.net.http.WebSocketFactory; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; @@ -65,6 +66,8 @@ public class NeoHubHandler extends BaseBridgeHandler { private final Map connectionStates = new HashMap<>(); + private WebSocketFactory webSocketFactory; + private @Nullable NeoHubConfiguration config; private @Nullable NeoHubSocketBase socket; private @Nullable ScheduledFuture lazyPollingScheduler; @@ -89,8 +92,9 @@ private ApiVersion(String label) { private boolean isApiOnline = false; private int failedSendAttempts = 0; - public NeoHubHandler(Bridge bridge) { + public NeoHubHandler(Bridge bridge, WebSocketFactory webSocketFactory) { super(bridge); + this.webSocketFactory = webSocketFactory; } @Override @@ -148,7 +152,7 @@ public void initialize() { NeoHubSocketBase socket; try { if (config.useWebSocket) { - socket = new NeoHubWebSocket(config, thing.getUID().getAsString()); + socket = new NeoHubWebSocket(config, webSocketFactory, thing.getUID()); } else { socket = new NeoHubSocket(config, thing.getUID().getAsString()); } diff --git a/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandlerFactory.java b/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandlerFactory.java index 729868a20f9b4..cc90d6fc85c05 100644 --- a/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandlerFactory.java +++ b/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubHandlerFactory.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.io.net.http.WebSocketFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -33,7 +34,9 @@ import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerFactory; import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * The {@link NeoHubHandlerFactory} creates things and thing handlers @@ -48,8 +51,14 @@ public class NeoHubHandlerFactory extends BaseThingHandlerFactory { .unmodifiableSet(new HashSet<>(Arrays.asList(THING_TYPE_NEOHUB, THING_TYPE_NEOSTAT, THING_TYPE_NEOPLUG, THING_TYPE_NEOCONTACT, THING_TYPE_NEOTEMPERATURESENSOR))); + private final WebSocketFactory webSocketFactory; private final Map> discoServices = new HashMap<>(); + @Activate + public NeoHubHandlerFactory(final @Reference WebSocketFactory webSocketFactory) { + this.webSocketFactory = webSocketFactory; + } + @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); @@ -60,7 +69,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if ((thingTypeUID.equals(THING_TYPE_NEOHUB)) && (thing instanceof Bridge)) { - NeoHubHandler handler = new NeoHubHandler((Bridge) thing); + NeoHubHandler handler = new NeoHubHandler((Bridge) thing, webSocketFactory); createDiscoveryService(handler); return handler; } diff --git a/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubWebSocket.java b/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubWebSocket.java index fddfd06e0c03f..7d24d9b807880 100644 --- a/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubWebSocket.java +++ b/bundles/org.openhab.binding.neohub/src/main/java/org/openhab/binding/neohub/internal/NeoHubWebSocket.java @@ -19,7 +19,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; @@ -28,6 +27,9 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.openhab.core.io.net.http.WebSocketFactory; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,19 +73,14 @@ private static class Response { public @Nullable String response; } - public NeoHubWebSocket(NeoHubConfiguration config, String hubId) throws IOException { - super(config, hubId); + public NeoHubWebSocket(NeoHubConfiguration config, WebSocketFactory webSocketFactory, ThingUID bridgeUID) + throws IOException { + super(config, bridgeUID.getAsString()); - // initialise and start ssl context factory, http client, web socket client SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); sslContextFactory.setTrustAll(true); - HttpClient httpClient = new HttpClient(sslContextFactory); - try { - httpClient.start(); - } catch (Exception e) { - throw new IOException("Error starting HTTP client", e); - } - webSocketClient = new WebSocketClient(httpClient); + String name = ThingWebClientUtil.buildWebClientConsumerName(bridgeUID, null); + webSocketClient = webSocketFactory.createWebSocketClient(name, sslContextFactory); webSocketClient.setConnectTimeout(config.socketTimeout * 1000); try { webSocketClient.start(); diff --git a/bundles/org.openhab.binding.neohub/src/test/java/org/openhab/binding/neohub/test/NeoHubProtocolTests.java b/bundles/org.openhab.binding.neohub/src/test/java/org/openhab/binding/neohub/test/NeoHubProtocolTests.java index 81ff9b7b48785..8fd11d71ec9f4 100644 --- a/bundles/org.openhab.binding.neohub/src/test/java/org/openhab/binding/neohub/test/NeoHubProtocolTests.java +++ b/bundles/org.openhab.binding.neohub/src/test/java/org/openhab/binding/neohub/test/NeoHubProtocolTests.java @@ -13,16 +13,23 @@ package org.openhab.binding.neohub.test; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.io.IOException; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.websocket.client.WebSocketClient; import org.junit.jupiter.api.Test; import org.openhab.binding.neohub.internal.NeoHubBindingConstants; import org.openhab.binding.neohub.internal.NeoHubConfiguration; import org.openhab.binding.neohub.internal.NeoHubException; import org.openhab.binding.neohub.internal.NeoHubSocket; import org.openhab.binding.neohub.internal.NeoHubWebSocket; +import org.openhab.core.io.net.http.WebSocketFactory; +import org.openhab.core.thing.ThingUID; /** * JUnit for testing WSS and TCP socket protocols. @@ -70,7 +77,15 @@ void testWssConnection() throws NeoHubException, IOException { config.socketTimeout = SOCKET_TIMEOUT; config.apiToken = HUB_API_TOKEN; - NeoHubWebSocket socket = new NeoHubWebSocket(config, "test"); + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setTrustAll(true); + HttpClient httpClient = new HttpClient(sslContextFactory); + WebSocketClient webSocketClient = new WebSocketClient(httpClient); + + WebSocketFactory webSocketFactory = mock(WebSocketFactory.class); + when(webSocketFactory.createWebSocketClient(anyString(), any())).thenReturn(webSocketClient); + + NeoHubWebSocket socket = new NeoHubWebSocket(config, webSocketFactory, new ThingUID("neohub:account:test")); String requestJson = NeoHubBindingConstants.CMD_CODE_FIRMWARE; String responseJson = socket.sendMessage(requestJson); assertNotEquals(0, responseJson.length()); diff --git a/bundles/org.openhab.binding.netatmo/README.md b/bundles/org.openhab.binding.netatmo/README.md index 7ec8be86b0c6b..f0def84b734b4 100644 --- a/bundles/org.openhab.binding.netatmo/README.md +++ b/bundles/org.openhab.binding.netatmo/README.md @@ -537,7 +537,7 @@ Warnings: | live | vpn-stream-url (*) | String | Read-only | Url of the live stream for this camera through Netatmo VPN. | | signal | strength | Number | Read-only | Signal strength (0 for no signal, 1 for weak...) | | signal | value | Number:Power | Read-only | Signal strength in dBm | -| presence | floodlight | Switch | Read-write | Sets the floodlight to ON/OFF/AUTO | +| presence | floodlight | String | Read-write | Sets the floodlight to ON/OFF/AUTO | | last-event | type | String | Read-only | Type of event | | last-event | subtype | String | Read-only | Sub-type of event | | last-event | time | DateTime | Read-only | Time of occurrence of event | diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java index 869f33b637066..91cc30c208e2c 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java @@ -43,9 +43,8 @@ */ @NonNullByDefault public class AuthenticationApi extends RestManager { - private static final UriBuilder OAUTH_BUILDER = getApiBaseBuilder().path(PATH_OAUTH); - private static final UriBuilder AUTH_BUILDER = OAUTH_BUILDER.clone().path(SUB_PATH_AUTHORIZE); - private static final URI TOKEN_URI = OAUTH_BUILDER.clone().path(SUB_PATH_TOKEN).build(); + private static final UriBuilder AUTH_BUILDER = getApiBaseBuilder(PATH_OAUTH, SUB_PATH_AUTHORIZE); + private static final URI TOKEN_URI = getApiBaseBuilder(PATH_OAUTH, SUB_PATH_TOKEN).build(); private final Logger logger = LoggerFactory.getLogger(AuthenticationApi.class); private final ScheduledExecutorService scheduler; diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java index 95f2301792694..23d580678839e 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java @@ -45,7 +45,7 @@ public EnergyApi(ApiBridgeHandler apiClient) { * response body */ public void switchSchedule(String homeId, String scheduleId) throws NetatmoException { - UriBuilder uriBuilder = getAppUriBuilder(SUB_PATH_SWITCH_SCHEDULE, PARAM_HOME_ID, homeId, PARAM_SCHEDULE_ID, + UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_SWITCH_SCHEDULE, PARAM_HOME_ID, homeId, PARAM_SCHEDULE_ID, scheduleId); post(uriBuilder, ApiResponse.Ok.class, null); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java index aacbdedfa43dd..398265f412d88 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.ws.rs.core.UriBuilder; @@ -36,9 +37,7 @@ */ @NonNullByDefault public abstract class RestManager { - private static final UriBuilder API_BASE_BUILDER = UriBuilder.fromUri(URL_API); - private static final UriBuilder APP_URI_BUILDER = UriBuilder.fromUri(URL_APP).path(PATH_API); - private static final UriBuilder API_URI_BUILDER = getApiBaseBuilder().path(PATH_API); + private static final UriBuilder API_URI_BUILDER = getApiBaseBuilder(PATH_API); private final Set requiredScopes; private final ApiBridgeHandler apiBridge; @@ -76,11 +75,11 @@ private static UriBuilder appendParams(UriBuilder builder, @Nullable Object... p throw new IllegalArgumentException("appendParams : params count must be even"); } for (int i = 0; i < params.length; i += 2) { - Object query = params[i]; - if (query instanceof String) { - Object param = params[i + 1]; - if (param != null) { - builder.queryParam((String) query, param); + Object param1 = params[i]; + Object param2 = params[i + 1]; + if (param1 instanceof String query) { + if (param2 != null) { // or else just ignore this query element + builder.queryParam(query, param2); } } else { throw new IllegalArgumentException("appendParams : even parameters must be Strings"); @@ -89,18 +88,16 @@ private static UriBuilder appendParams(UriBuilder builder, @Nullable Object... p return builder; } - protected static UriBuilder getApiBaseBuilder() { - return API_BASE_BUILDER.clone(); + protected static UriBuilder getApiBaseBuilder(String... paths) { + UriBuilder builder = UriBuilder.fromUri(URL_API); + Stream.of(paths).forEach(path -> builder.path(path)); + return builder; } public static UriBuilder getApiUriBuilder(String path, @Nullable Object... params) { return appendParams(API_URI_BUILDER.clone().path(path), params); } - protected static UriBuilder getAppUriBuilder(String path, @Nullable Object... params) { - return appendParams(APP_URI_BUILDER.clone().path(path), params); - } - private String toRequest(Map entries) { return entries.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java index cb3c4f4246fab..8acaef8c72e08 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java @@ -122,13 +122,13 @@ public void changeStatus(String localCameraURL, boolean setOn) throws NetatmoExc } public void changeFloodLightMode(String homeId, String cameraId, FloodLightMode mode) throws NetatmoException { - UriBuilder uriBuilder = getAppUriBuilder(PATH_STATE); - String payload = String.format(PAYLOAD_FLOODLIGHT, homeId, cameraId, mode.name().toLowerCase()); + UriBuilder uriBuilder = getApiUriBuilder(PATH_STATE); + String payload = PAYLOAD_FLOODLIGHT.formatted(homeId, cameraId, mode.name().toLowerCase()); post(uriBuilder, ApiResponse.Ok.class, payload); } public void setPersonAwayStatus(String homeId, String personId, boolean away) throws NetatmoException { - UriBuilder uriBuilder = getAppUriBuilder(away ? SUB_PATH_PERSON_AWAY : SUB_PATH_PERSON_HOME); + UriBuilder uriBuilder = getApiUriBuilder(away ? SUB_PATH_PERSON_AWAY : SUB_PATH_PERSON_HOME); String payload = String.format(away ? PAYLOAD_PERSON_AWAY : PAYLOAD_PERSON_HOME, homeId, personId); post(uriBuilder, ApiResponse.Ok.class, payload); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java index 2066d0adb915f..c805180223b1d 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java @@ -120,7 +120,6 @@ public enum MeasureClass { // Netatmo API urls public static final String URL_API = "https://api.netatmo.com/"; - public static final String URL_APP = "https://app.netatmo.net/"; public static final String PATH_OAUTH = "oauth2"; public static final String SUB_PATH_TOKEN = "token"; public static final String SUB_PATH_AUTHORIZE = "authorize"; diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml index 7599a195835e2..97efdce8269c5 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml @@ -152,6 +152,7 @@ String State of the floodlight (On/Off/Auto) + Lightbulb diff --git a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino index 664144c3a4451..da52e3d40ab22 100644 --- a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino +++ b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino @@ -147,7 +147,7 @@ void setupStaticConfigMode() { #ifdef ENABLE_NIBE_DEBUG nibegw.setDebugCallback(nibeDebugCallback); - nibegw.setVerboseLevel(config.debug.level); + nibegw.setVerboseLevel(config.debug.verboseLevel); #endif targetIp.fromString(config.nibe.targetIp); diff --git a/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java b/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java index 28934c05b3acc..9e4ed2b3c0f21 100644 --- a/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java +++ b/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.nibeuplink.internal.config; -import org.apache.commons.lang3.builder.ToStringBuilder; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -91,9 +90,9 @@ public void setHouseKeepingInterval(Integer houseKeepingInterval) { @Override public String toString() { - return new ToStringBuilder(this).append("user", getUser()).append("password", getPassword()) - .append("nibeId", getNibeId()).append("pollingInterval", getPollingInterval()) - .append("houseKeepingInterval", getHouseKeepingInterval()).append("asyncTimeout", getAsyncTimeout()) - .append("syncTimeout", getSyncTimeout()).toString(); + return getClass().getSimpleName() + "{ user=" + getUser() + ", password=" + getPassword() + ", nibeId=" + + getNibeId() + ", pollingInterval=" + getPollingInterval() + ", houseKeepingInterval=" + + getHouseKeepingInterval() + ", asyncTimeout=" + getAsyncTimeout() + ", syncTimeout=" + + getSyncTimeout() + "}"; } } diff --git a/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java b/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java index 0a1dc725f4318..c36f52070aa7e 100644 --- a/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java +++ b/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java @@ -16,7 +16,6 @@ import java.net.Socket; import java.nio.ByteBuffer; -import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -55,7 +54,13 @@ public OmnikInverterMessage pullCurrentStats() throws IOException { private byte[] generateMagicPacket() { ByteBuffer serialByteBuffer = ByteBuffer.allocate(8).putInt(serialNumber).putInt(serialNumber); byte[] serialBytes = serialByteBuffer.array(); - ArrayUtils.reverse(serialBytes); + + // reverse array + for (int i = 0; i < serialBytes.length / 2; i++) { + byte temp = serialBytes[i]; + serialBytes[i] = serialBytes[serialBytes.length - i - 1]; + serialBytes[serialBytes.length - i - 1] = temp; + } byte checksumCount = 115; for (byte b : serialBytes) { diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java index ba364e238dfb5..2cabb96fad120 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java @@ -14,8 +14,6 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*; -import org.apache.commons.lang3.builder.ToStringBuilder; - /** * The {@link ApiConfiguration} defines the model for an API bridge configuration. * @@ -55,7 +53,7 @@ public void setApiServer(String apiServer) { @Override public String toString() { - return new ToStringBuilder(this).append(API_CONFIG_API_KEY, this.getApiKey()) - .append(API_CONFIG_API_SERVER, this.getApiServer()).toString(); + return getClass().getSimpleName() + "{ " + API_CONFIG_API_KEY + "=" + this.getApiKey() + ", " + + API_CONFIG_API_SERVER + "=" + this.getApiServer() + "}"; } } diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java index 24a22183aca78..6125e9e3d7caa 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java @@ -14,8 +14,6 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.CHANNEL_CONFIG_OFFSET; -import org.apache.commons.lang3.builder.ToStringBuilder; - /** * The {@link ChannelConfig} defines the model for a channel configuration. * @@ -40,6 +38,6 @@ public void setOffset(Integer offset) { @Override public String toString() { - return new ToStringBuilder(this).append(CHANNEL_CONFIG_OFFSET, this.getOffset()).toString(); + return getClass().getSimpleName() + "{ " + CHANNEL_CONFIG_OFFSET + "=" + this.getOffset() + "}"; } } diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java index 0e674ab1c8adb..1d7ee188212c8 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java @@ -14,8 +14,6 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.ROUTE_CONFIG_ROUTE_ID; -import org.apache.commons.lang3.builder.ToStringBuilder; - /** * The {@link RouteConfiguration} defines the model for a route stop configuration. * @@ -41,6 +39,6 @@ public void setRouteId(String routeId) { @Override public String toString() { - return new ToStringBuilder(this).append(ROUTE_CONFIG_ROUTE_ID, this.getRouteId()).toString(); + return getClass().getSimpleName() + "{ " + ROUTE_CONFIG_ROUTE_ID + "=" + this.getRouteId() + "}"; } } diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java index 75c5978fdf733..42b38e07f8b10 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java @@ -14,8 +14,6 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*; -import org.apache.commons.lang3.builder.ToStringBuilder; - /** * The {@link StopConfiguration} defines the model for a stop bridge configuration. * @@ -56,7 +54,7 @@ public void setStopId(String stopId) { @Override public String toString() { - return new ToStringBuilder(this).append(STOP_CONFIG_INTERVAL, this.getInterval()) - .append(STOP_CONFIG_ID, this.getStopId()).toString(); + return getClass().getSimpleName() + "{ " + STOP_CONFIG_INTERVAL + "=" + this.getInterval() + ", " + + STOP_CONFIG_ID + "=" + this.getStopId() + "}"; } } diff --git a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java index d3f36111768ff..7bae092054a17 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java @@ -20,7 +20,6 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.jupnp.model.meta.RemoteDevice; @@ -143,6 +142,6 @@ private ThingTypeUID findThingType(@Nullable String deviceModel) { */ private boolean isSupportedDeviceModel(final @Nullable String deviceModel) { return deviceModel != null && !deviceModel.isBlank() && Arrays.stream(OnkyoModel.values()) - .anyMatch(model -> StringUtils.startsWithIgnoreCase(deviceModel, model.getId())); + .anyMatch(model -> deviceModel.toLowerCase().startsWith(model.getId().toLowerCase())); } } diff --git a/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairIPBridgeConfig.java b/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairIPBridgeConfig.java index 88e57ceaa23df..6c3d4aad92d96 100644 --- a/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairIPBridgeConfig.java +++ b/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairIPBridgeConfig.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.pentair.internal.config; -import org.apache.commons.lang3.builder.ToStringBuilder; - /** * Configuration parameters for IP Bridge * @@ -31,6 +29,6 @@ public class PentairIPBridgeConfig { @Override public String toString() { - return new ToStringBuilder(this).append("address", address).append("port", port).append("id", id).toString(); + return getClass().getSimpleName() + "{ address=" + address + ", port=" + port + ", id=" + id + "}"; } } diff --git a/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairSerialBridgeConfig.java b/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairSerialBridgeConfig.java index 9c2e36ba09a9c..13028160bd9c1 100644 --- a/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairSerialBridgeConfig.java +++ b/bundles/org.openhab.binding.pentair/src/main/java/org/openhab/binding/pentair/internal/config/PentairSerialBridgeConfig.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.pentair.internal.config; -import org.apache.commons.lang3.builder.ToStringBuilder; - /** * Configuration parameters for Serial Bridge * @@ -28,6 +26,6 @@ public class PentairSerialBridgeConfig { @Override public String toString() { - return new ToStringBuilder(this).append("serialPort", serialPort).append("id", id).toString(); + return getClass().getSimpleName() + "{ serialPort=" + serialPort + ", id=" + id + "}"; } } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java index a3034a1628545..23058f6139925 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java @@ -12,8 +12,8 @@ */ package org.openhab.binding.phc.internal; -import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.phc.internal.util.StringUtils; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; @@ -35,7 +35,7 @@ public class PHCHelper { */ public static ThingUID getThingUIDreverse(ThingTypeUID thingTypeUID, byte moduleAddr) { // convert to 5-bit binary string and reverse in second step - String thingID = StringUtils.leftPad(StringUtils.trim(Integer.toBinaryString(moduleAddr & 0xFF)), 5, '0'); + String thingID = StringUtils.padLeft(Integer.toBinaryString(moduleAddr & 0xFF).trim(), 5, "0"); thingID = new StringBuilder(thingID).reverse().toString(); ThingUID thingUID = new ThingUID(thingTypeUID, thingID); @@ -52,7 +52,7 @@ public static ThingUID getThingUIDreverse(ThingTypeUID thingTypeUID, byte module public static Object bytesToBinaryString(byte[] bytes) { StringBuilder bin = new StringBuilder(); for (byte b : bytes) { - bin.append(StringUtils.leftPad(StringUtils.trim(Integer.toBinaryString(b & 0xFF)), 8, '0')); + bin.append(StringUtils.padLeft(Integer.toBinaryString(b & 0xFF).trim(), 8, "0")); bin.append(' '); } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java index 8e4e244ae0076..f3b279aaf86c4 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java @@ -24,11 +24,11 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledThreadPoolExecutor; -import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.phc.internal.PHCBindingConstants; import org.openhab.binding.phc.internal.PHCHelper; +import org.openhab.binding.phc.internal.util.StringUtils; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPortEvent; @@ -712,7 +712,7 @@ private short crc16Update(short crc, byte messagePart) { private void handleIncomingCommand(byte moduleAddress, int channel, OnOffType onOff) { ThingUID uid = PHCHelper.getThingUIDreverse(PHCBindingConstants.THING_TYPE_EM, moduleAddress); Thing thing = getThing().getThing(uid); - String channelId = "em#" + StringUtils.leftPad(Integer.toString(channel), 2, '0'); + String channelId = "em#" + StringUtils.padLeft(Integer.toString(channel), 2, "0"); if (thing != null && thing.getHandler() != null) { logger.debug("Input: {}, {}, {}", thing.getUID(), channelId, onOff); diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/util/StringUtils.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/util/StringUtils.java new file mode 100644 index 0000000000000..69b768bcdb8f3 --- /dev/null +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/util/StringUtils.java @@ -0,0 +1,32 @@ +/** + * 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.phc.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link StringUtils} class defines some static string utility methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class StringUtils { + + public static String padLeft(@Nullable String input, int minSize, String padString) { + if (input == null) { + input = ""; + } + return String.format("%" + minSize + "s", input).replace(" ", padString); + } +} diff --git a/bundles/org.openhab.binding.phc/src/test/java/org/openhab/binding/phc/internal/util/StringUtilsTest.java b/bundles/org.openhab.binding.phc/src/test/java/org/openhab/binding/phc/internal/util/StringUtilsTest.java new file mode 100644 index 0000000000000..e8000a8a76b2c --- /dev/null +++ b/bundles/org.openhab.binding.phc/src/test/java/org/openhab/binding/phc/internal/util/StringUtilsTest.java @@ -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.phc.internal.util; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * The {@link StringUtils} class defines some static string utility methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class StringUtilsTest { + + @Test + public void padLeft() { + assertEquals("000000", StringUtils.padLeft("", 6, "0")); + assertEquals("000000", StringUtils.padLeft(null, 6, "0")); + assertEquals("000teststr", StringUtils.padLeft("teststr", 10, "0")); + assertEquals("AAAAAAp3RF@CT", StringUtils.padLeft("p3RF@CT", 13, "A")); + assertEquals("nopaddingshouldhappen", StringUtils.padLeft("nopaddingshouldhappen", 21, "x")); + assertEquals("LongerStringThenMinSize", StringUtils.padLeft("LongerStringThenMinSize", 10, "x")); + } +} diff --git a/bundles/org.openhab.binding.remoteopenhab/src/main/java/org/openhab/binding/remoteopenhab/internal/RemoteopenhabHandlerFactory.java b/bundles/org.openhab.binding.remoteopenhab/src/main/java/org/openhab/binding/remoteopenhab/internal/RemoteopenhabHandlerFactory.java index fcb60e45021a2..9d012df1b526f 100644 --- a/bundles/org.openhab.binding.remoteopenhab/src/main/java/org/openhab/binding/remoteopenhab/internal/RemoteopenhabHandlerFactory.java +++ b/bundles/org.openhab.binding.remoteopenhab/src/main/java/org/openhab/binding/remoteopenhab/internal/RemoteopenhabHandlerFactory.java @@ -30,6 +30,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.remoteopenhab.internal.handler.RemoteopenhabBridgeHandler; import org.openhab.binding.remoteopenhab.internal.handler.RemoteopenhabThingHandler; import org.openhab.core.config.core.Configuration; @@ -92,7 +93,6 @@ public RemoteopenhabHandlerFactory(final @Reference HttpClientFactory httpClient final @Reference RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider, final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider) { this.httpClient = httpClientFactory.getCommonHttpClient(); - this.httpClientTrustingCert = httpClientFactory.createHttpClient(RemoteopenhabBindingConstants.BINDING_ID); this.clientBuilder = clientBuilder; this.eventSourceFactory = eventSourceFactory; this.channelTypeProvider = channelTypeProvider; @@ -102,6 +102,7 @@ public RemoteopenhabHandlerFactory(final @Reference HttpClientFactory httpClient this.i18nProvider = i18nProvider; this.localeProvider = localeProvider; + SslContextFactory sslContextFactory = new SslContextFactory.Client(); try { SSLContext sslContext = SSLContext.getInstance("SSL"); @@ -142,14 +143,15 @@ public void checkServerTrusted(X509Certificate @Nullable [] chain, @Nullable Str } } }; sslContext.init(null, trustAllCerts, null); - - this.httpClientTrustingCert.getSslContextFactory().setSslContext(sslContext); + sslContextFactory.setSslContext(sslContext); } catch (NoSuchAlgorithmException e) { logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(), e); } catch (KeyManagementException e) { logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e); } + this.httpClientTrustingCert = httpClientFactory.createHttpClient(RemoteopenhabBindingConstants.BINDING_ID, + sslContextFactory); } @Override diff --git a/bundles/org.openhab.binding.semsportal/src/main/java/org/openhab/binding/semsportal/internal/PortalHandler.java b/bundles/org.openhab.binding.semsportal/src/main/java/org/openhab/binding/semsportal/internal/PortalHandler.java index 4f1190a20c311..853fd94f63c82 100644 --- a/bundles/org.openhab.binding.semsportal/src/main/java/org/openhab/binding/semsportal/internal/PortalHandler.java +++ b/bundles/org.openhab.binding.semsportal/src/main/java/org/openhab/binding/semsportal/internal/PortalHandler.java @@ -140,7 +140,8 @@ private void login() { loggedIn = true; updateStatus(ThingStatus.ONLINE); } else { - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.CONFIGURATION_ERROR, "Check username / password"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Check username / password"); } } diff --git a/bundles/org.openhab.binding.sleepiq/README.md b/bundles/org.openhab.binding.sleepiq/README.md index 4e47a9f47a140..c01f408be61d4 100644 --- a/bundles/org.openhab.binding.sleepiq/README.md +++ b/bundles/org.openhab.binding.sleepiq/README.md @@ -31,13 +31,11 @@ The binding requires no special configuration. The bridge requires a username and a password. Optionally, you can also specify a polling interval. -To enable verbose logging of HTTP requests and responses regarding the cloud service, enable DEBUG level logging on ```SleepIQCloudHandler```. - | Configuration Parameter | Type | Description | Default | |-------------------------|---------|--------------------------------------------------------|---------| | username | text | Username of a registered SleepIQ account owner | | | password | text | Password of a registered SleepIQ account owner | | -| pollingInterval | integer | Seconds between fetching values from the cloud service | 60 | +| pollingInterval | integer | Seconds between fetching values from the cloud service | 120 | ### Dual-Chamber Bed (Thing ID: "dualBed") @@ -50,7 +48,7 @@ Each bed requires a bed ID as defined by the SleepIQ service. ### Sample Thing Configuration ```java -Bridge sleepiq:cloud:1 [ username="mail@example.com", password="password", pollingInterval=60, logging=false ] +Bridge sleepiq:cloud:1 [ username="mail@example.com", password="password", pollingInterval=120 ] { Thing dualBed master [ bedId="-9999999999999999999" ] Thing dualBed guest [ bedId="-8888888888888888888" ] @@ -68,33 +66,77 @@ Bridge sleepiq:cloud:1 [ username="mail@example.com", password="password", polli ### Chamber Channel Group -All channels within this group are read-only. - -| Channel ID | Item Type | Description | -|----------------------|-----------|---------------------------------------------------------------------------------------------------------------------| -| inBed | Switch | The presence of a person or object on the chamber | -| sleepNumber | Number | The Sleep Number setting of the chamber | -| pressure | Number | The current pressure inside the chamber | -| lastLink | String | The amount of time that has passed since a connection was made from the chamber to the cloud service (D d HH:MM:SS) | -| alertId | Number | Identifier for an alert condition with the chamber | -| alertDetailedMessage | String | A detailed message describing an alert condition with the chamber | +All channels within this group are read-only, except for the sleepNumber and privacyMode channels. + +| Channel ID | Item Type | Description | +|-----------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------| +| inBed | Switch | The presence of a person or object on the chamber | +| sleepNumber | Number | The Sleep Number setting of the chamber. Set the sleep number of the chamber by sending a command to the sleepNumber channel with a value between 5 and 100. The value must be a multiple of 5 | + +| sleepGoalMinutes | Number:Time | The person's sleep goal in minutes | +| pressure | Number | The current pressure inside the chamber | +| privacyMode | Switch | Enable or disable privacy mode | +| lastLink | String | The amount of time that has passed since a connection was made from the chamber to the cloud service (D d HH:MM:SS) | +| alertId | Number | Identifier for an alert condition with the chamber | +| alertDetailedMessage | String | A detailed message describing an alert condition with the chamber | +| todaySleepIQ | Number | The Sleep IQ score for the current day | +| todayAverageHeartRate | Number | The average heart rate for the current day | +| todayAverageRespirationRate | Number | The average respiration rate for the current day | +| todayMessage | String | A description of the sleep quality for the current day | +| todaySleepDurationSeconds | Number:Time | The duration of sleep for the current day | +| todaySleepInBedSeconds | Number:Time | The duration of time in bed for the current day | +| todaySleepOutOfBedSeconds | Number:Time | The duration of time out of bed for the current day | +| todaySleepRestfulSeconds | Number:Time | The duration of restful sleep for the current day | +| todaySleepRestlessSeconds | Number:Time | The duration of restless sleep for the current day | +| monthlySleepIQ | Number | The average Sleep IQ score for the current month | +| monthlyAverageHeartRate | Number | The average heart rate for the current month | +| monthlyAverageRespirationRate | Number | The average respiration rate for the current month | ## Items Here is a sample item configuration: ```java -Switch MasterBedroom_SleepIQ_InBed_Alice "In Bed [%s]" { channel="sleepiq:dualBed:1:master:left#inBed" } -Number MasterBedroom_SleepIQ_SleepNumber_Alice "Sleep Number [%s]" { channel="sleepiq:dualBed:1:master:left#sleepNumber" } -Number MasterBedroom_SleepIQ_Pressure_Alice "Pressure [%s]" { channel="sleepiq:dualBed:1:master:left#pressure" } -String MasterBedroom_SleepIQ_LastLink_Alice "Last Update [%s]" { channel="sleepiq:dualBed:1:master:left#lastLink" } -Number MasterBedroom_SleepIQ_AlertId_Alice "Alert ID [%s]" { channel="sleepiq:dualBed:1:master:left#alertId" } -String MasterBedroom_SleepIQ_AlertMessage_Alice "Alert Message [%s]" { channel="sleepiq:dualBed:1:master:left#alertDetailedMessage" } - -Switch MasterBedroom_SleepIQ_InBed_Bob "In Bed [%s]" { channel="sleepiq:dualBed:1:master:right#inBed" } -Number MasterBedroom_SleepIQ_SleepNumber_Bob "Sleep Number [%s]" { channel="sleepiq:dualBed:1:master:right#sleepNumber" } -Number MasterBedroom_SleepIQ_Pressure_Bob "Pressure [%s]" { channel="sleepiq:dualBed:1:master:right#pressure" } -String MasterBedroom_SleepIQ_LastLink_Bob "Last Update [%s]" { channel="sleepiq:dualBed:1:master:right#lastLink" } -Number MasterBedroom_SleepIQ_AlertId_Bob "Alert ID [%s]" { channel="sleepiq:dualBed:1:master:right#alertId" } -String MasterBedroom_SleepIQ_AlertMessage_Bob "Alert Message [%s]" { channel="sleepiq:dualBed:1:master:right#alertDetailedMessage" } +Switch MasterBR_SleepIQ_InBed_Alice "In Bed [%s]" { channel="sleepiq:dualBed:1:master:left#inBed" } +Number MasterBR_SleepIQ_SleepNumber_Alice "Sleep Number [%s]" { channel="sleepiq:dualBed:1:master:left#sleepNumber" } +Number:Time MasterBR_SleepIQ_SleepGoal_Alice "Sleep Goal [%d min]" { channel="sleepiq:dualBed:1:master:left#sleepGoalMinutes" +Number MasterBR_SleepIQ_Pressure_Alice "Pressure [%s]" { channel="sleepiq:dualBed:1:master:left#pressure" } +Switch MasterBR_SleepIQ_PrivacyMode_Alice "Privacy Mode [%s]" { channel="sleepiq:dualBed:1:master:left#privacyMode" } +String MasterBR_SleepIQ_LastLink_Alice "Last Update [%s]" { channel="sleepiq:dualBed:1:master:left#lastLink" } +Number MasterBR_SleepIQ_AlertId_Alice "Alert ID [%s]" { channel="sleepiq:dualBed:1:master:left#alertId" } +String MasterBR_SleepIQ_AlertMessage_Alice "Alert Message [%s]" { channel="sleepiq:dualBed:1:master:left#alertDetailedMessage" } +Number MasterBR_SleepIQ_DailySleepIQ_Alice "Daily Sleep IQ [%.0f]" { channel="sleepiq:dualBed:1:master:left#todaySleepIQ" } +Number MasterBR_SleepIQ_DailyHeartRate_Alice "Daily Heart Rate [%.0f]" { channel="sleepiq:dualBed:1:master:left#todayAverageHeartRate" } +Number MasterBR_SleepIQ_DailyRespRate_Alice "Daily Respiration Rate [%.0f]" { channel="sleepiq:dualBed:1:master:left#todayAverageRespirationRate"} +String MasterBR_SleepIQ_DailyMessage_Alice "Daily Message [%s]" { channel="sleepiq:dualBed:1:master:left#todayMessage"} +Number:Time MasterBR_SleepIQ_DailyDuration_Alice "Daily Sleep Duration [%.0f]" { channel="sleepiq:dualBed:1:master:left#todaySleepDurationSeconds"} +Number:Time MasterBR_SleepIQ_DailyInBed_Alice "Daily Sleep In Bed [%.0f]" { channel="sleepiq:dualBed:1:master:left#todaySleepInBedSeconds"} +Number:Time MasterBR_SleepIQ_DailyOutOfBed_Alice "Daily Sleep Out Of Bed [%.0f]" { channel="sleepiq:dualBed:1:master:left#todaySleepOutOfBedSeconds"} +Number:Time MasterBR_SleepIQ_DailyRestful_Alice "Daily Sleep Restful [%.0f]" { channel="sleepiq:dualBed:1:master:left#todaySleepRestfulSeconds"} +Number:Time MasterBR_SleepIQ_DailyRestless_Alice "Daily Sleep Restless [%.0f]" { channel="sleepiq:dualBed:1:master:left#todaySleepRestlessSeconds"} +Number MasterBR_SleepIQ_MonthlySleepIQ_Alice "Monthly Sleep IQ [%d s]" { channel="sleepiq:dualBed:1:master:left#monthlySleepIQ"} +Number MasterBR_SleepIQ_MonthlyHeartRate_Alice "Monthly Heart Rate [%.0f]" { channel="sleepiq:dualBed:1:master:left#monthlyAverageHeartRate"} +Number MasterBR_SleepIQ_MonthlyRespRate_Alice "Monthly Respiration Rate [%.0f]" { channel="sleepiq:dualBed:1:master:left#monthlyAverageRespirationRate"} + + +Switch MasterBR_SleepIQ_InBed_Bob "In Bed [%s]" { channel="sleepiq:dualBed:1:master:right#inBed" } +Number MasterBR_SleepIQ_SleepNumber_Bob "Sleep Number [%s]" { channel="sleepiq:dualBed:1:master:right#sleepNumber" } +Number MasterBR_SleepIQ_SleepGoal_Alice "Sleep Goal [%d min]" { channel="sleepiq:dualBed:1:master:left#sleepGoalMinutes" +Number:Time MasterBR_SleepIQ_Pressure_Bob "Pressure [%s]" { channel="sleepiq:dualBed:1:master:right#pressure" } +Switch MasterBR_SleepIQ_PrivacyMode_Bob "Privacy Mode [%s]" { channel="sleepiq:dualBed:1:master:right#privacyMode" } +String MasterBR_SleepIQ_LastLink_Bob "Last Update [%s]" { channel="sleepiq:dualBed:1:master:right#lastLink" } +Number MasterBR_SleepIQ_AlertId_Bob "Alert ID [%s]" { channel="sleepiq:dualBed:1:master:right#alertId" } +String MasterBR_SleepIQ_AlertMessage_Bob "Alert Message [%s]" { channel="sleepiq:dualBed:1:master:right#alertDetailedMessage" } +Number MasterBR_SleepIQ_DailySleepIQ_Bob "Daily Sleep IQ [%.0f]" { channel="sleepiq:dualBed:1:master:right#todaySleepIQ" } +Number MasterBR_SleepIQ_DailyHeartRate_Bob "Daily Heart Rate [%.0f]" { channel="sleepiq:dualBed:1:master:right#todayAverageHeartRate" } +Number MasterBR_SleepIQ_DailyRespRate_Bob "Daily Respiration Rate [%.0f]" { channel="sleepiq:dualBed:1:master:right#todayAverageRespirationRate"} +String MasterBR_SleepIQ_DailyMessage_Bob "Daily Message [%s]" { channel="sleepiq:dualBed:1:master:right#todayMessage"} +Number:Time MasterBR_SleepIQ_DailyDuration_Bob "Daily Sleep Duration [%d s]" { channel="sleepiq:dualBed:1:master:right#todaySleepDurationSeconds"} +Number:Time MasterBR_SleepIQ_DailyInBed_Bob "Daily Sleep In Bed [%.0f]" { channel="sleepiq:dualBed:1:master:right#todaySleepInBedSeconds"} +Number:Time MasterBR_SleepIQ_DailyOutOfBed_Bob "Daily Sleep Out Of Bed [%.0f]" { channel="sleepiq:dualBed:1:master:right#todaySleepOutOfBedSeconds"} +Number:Time MasterBR_SleepIQ_DailyRestful_Bob "Daily Sleep Restful [%.0f]" { channel="sleepiq:dualBed:1:master:right#todaySleepRestfulSeconds"} +Number:Time MasterBR_SleepIQ_DailyRestless_Bob "Daily Sleep Restless [%.0f]" { channel="sleepiq:dualBed:1:master:right#todaySleepRestlessSeconds"} +Number MasterBR_SleepIQ_MonthlySleepIQ_Bob "Monthly Sleep IQ [%.0f]" { channel="sleepiq:dualBed:1:master:right#monthlySleepIQ"} +Number MasterBR_SleepIQ_MonthlyHeartRate_Bob "Monthly Heart Rate [%.0f]" { channel="sleepiq:dualBed:1:master:right#monthlyAverageHeartRate"} +Number MasterBR_SleepIQ_MonthlyRespRate_Bob "Monthly Respiration Rate [%.0f]" { channel="sleepiq:dualBed:1:master:right#monthlyAverageRespirationRate"} ``` diff --git a/bundles/org.openhab.binding.sleepiq/pom.xml b/bundles/org.openhab.binding.sleepiq/pom.xml index 754bf4078aba8..842ada6428b6f 100644 --- a/bundles/org.openhab.binding.sleepiq/pom.xml +++ b/bundles/org.openhab.binding.sleepiq/pom.xml @@ -14,51 +14,4 @@ openHAB Add-ons :: Bundles :: SleepIQ Binding - - provider-gson - - - - - junit - junit - 4.12 - test - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - - add-source - - generate-sources - - - src/3rdparty/java - - - - - add-test-source - generate-sources - - add-test-source - - - - src/3rdparty/test - - - - - - - - diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/BedNotFoundException.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/BedNotFoundException.java deleted file mode 100644 index 3eba856849511..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/BedNotFoundException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api; - -import org.openhab.binding.sleepiq.api.model.Failure; - -public class BedNotFoundException extends SleepIQException -{ - private static final long serialVersionUID = 1L; - - public BedNotFoundException(Failure failure) - { - super(failure); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/LoginException.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/LoginException.java deleted file mode 100644 index 6480ae4ffd083..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/LoginException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api; - -import org.openhab.binding.sleepiq.api.model.Failure; - -public class LoginException extends SleepIQException -{ - private static final long serialVersionUID = 1L; - - public LoginException(Failure failure) - { - super(failure); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/SleepIQ.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/SleepIQ.java deleted file mode 100644 index 5785b24f791e9..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/SleepIQ.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api; - -import java.util.List; - -import javax.ws.rs.client.ClientBuilder; - -import org.openhab.binding.sleepiq.api.impl.SleepIQImpl; -import org.openhab.binding.sleepiq.api.model.Bed; -import org.openhab.binding.sleepiq.api.model.FamilyStatus; -import org.openhab.binding.sleepiq.api.model.LoginInfo; -import org.openhab.binding.sleepiq.api.model.PauseMode; -import org.openhab.binding.sleepiq.api.model.Sleeper; - -/** - * This interface is the main API to access the SleepIQ system. - * - * @author Gregory Moyer - */ -public interface SleepIQ { - /** - * Login to the {@link Configuration configured} account. This method is not - * required to be called before other methods because all methods must - * ensure login before acting. However, when the only desired action is to - * login and not retrieve other data, this method is the most efficient - * option. - * - * @return basic information about the logged in user - * @throws UnauthorizedException - * if the credentials provided are not valid - * @throws LoginException - * if the login request fails for any reason other than bad - * credentials (including missing credentials) - */ - public LoginInfo login() throws LoginException; - - /** - * Get a list of beds connected to the account. - * - * @return the list of beds - */ - public List getBeds(); - - /** - * Get a list of people registered to this account for beds or bed positions - * (left or right side). - * - * @return the list of sleepers - */ - public List getSleepers(); - - /** - * Get the status of all beds and all air chambers registered to this - * account. - * - * @return the complete status of beds on the account - */ - public FamilyStatus getFamilyStatus(); - - /** - * Get the status of "pause mode" (disabling SleepIQ data upload) for a - * specific bed. A bed in pause mode will send no information to the SleepIQ - * cloud services. For example, if a sleeper is in bed and disables SleepIQ - * (enables pause mode), the service will continue to report that the bed is - * occupied even after the sleeper exits the bed until pause mode is - * disabled. - * - * @param bedId - * the unique identifier of the bed to query - * @return the status of pause mode for the specified bed - * @throws BedNotFoundException - * if the bed identifier was not found on the account - */ - public PauseMode getPauseMode(String bedId) throws BedNotFoundException; - - /** - * Create a default implementation instance of this interface. Each call to - * this method will create a new object. - * - * @param config - * the configuration to use for the new instance - * @return a concrete implementation of this interface - */ - public static SleepIQ create(Configuration config, ClientBuilder clientBuilder) { - return new SleepIQImpl(config, clientBuilder); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/SleepIQException.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/SleepIQException.java deleted file mode 100644 index 55935c1f7d2e5..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/SleepIQException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api; - -import org.openhab.binding.sleepiq.api.model.Failure; - -public class SleepIQException extends Exception -{ - private static final long serialVersionUID = 1L; - - private final Failure failure; - - public SleepIQException(Failure failure) - { - super(failure.getError().getMessage()); - this.failure = failure; - } - - public Failure getFailure() - { - return failure; - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/UnauthorizedException.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/UnauthorizedException.java deleted file mode 100644 index 5a809005fa785..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/UnauthorizedException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api; - -import org.openhab.binding.sleepiq.api.model.Failure; - -public class UnauthorizedException extends LoginException -{ - private static final long serialVersionUID = 1L; - - public UnauthorizedException(Failure failure) - { - super(failure); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/filter/LoggingFilter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/filter/LoggingFilter.java deleted file mode 100644 index 5ce38e5ef3903..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/filter/LoggingFilter.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * http://glassfish.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ -package org.openhab.binding.sleepiq.api.filter; - -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.nio.charset.Charset; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Logger; - -import javax.annotation.Priority; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.client.ClientResponseContext; -import javax.ws.rs.client.ClientResponseFilter; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.container.PreMatching; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.WriterInterceptor; -import javax.ws.rs.ext.WriterInterceptorContext; - -/** - * Universal logging filter. - *

- * Can be used on client or server side. Has the highest priority. - * - * @author Pavel Bucek (pavel.bucek at oracle.com) - * @author Martin Matula - */ -@PreMatching -@Priority(Integer.MIN_VALUE) -public final class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, - ClientResponseFilter, WriterInterceptor { - - public static final Charset UTF8 = Charset.forName("UTF-8"); - - private static final Logger LOGGER = Logger.getLogger(LoggingFilter.class.getName()); - private static final String NOTIFICATION_PREFIX = "* "; - private static final String REQUEST_PREFIX = "> "; - private static final String RESPONSE_PREFIX = "< "; - private static final String ENTITY_LOGGER_PROPERTY = LoggingFilter.class.getName() + ".entityLogger"; - private static final String LOGGING_ID_PROPERTY = LoggingFilter.class.getName() + ".id"; - - private static final Comparator>> COMPARATOR = new Comparator>>() { - - @Override - public int compare(final Map.Entry> o1, final Map.Entry> o2) { - return o1.getKey().compareToIgnoreCase(o2.getKey()); - } - }; - - private static final int DEFAULT_MAX_ENTITY_SIZE = 8 * 1024; - - // - private final Logger logger; - private final AtomicLong _id = new AtomicLong(0); - private final boolean printEntity; - private final int maxEntitySize; - - /** - * Create a logging filter logging the request and response to a default JDK - * logger, named as the fully qualified class name of this class. Entity - * logging is turned off by default. - */ - public LoggingFilter() { - this(LOGGER, false); - } - - /** - * Create a logging filter with custom logger and custom settings of entity - * logging. - * - * @param logger the logger to log requests and responses. - * @param printEntity if true, entity will be logged as well up to the default maxEntitySize, which is 8KB - */ - public LoggingFilter(final Logger logger, final boolean printEntity) { - this.logger = logger; - this.printEntity = printEntity; - this.maxEntitySize = DEFAULT_MAX_ENTITY_SIZE; - } - - /** - * Creates a logging filter with custom logger and entity logging turned on, but potentially limiting the size - * of entity to be buffered and logged. - * - * @param logger the logger to log requests and responses. - * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger, - * logging filter will print (and buffer in memory) only the specified number of bytes - * and print "...more..." string at the end. Negative values are interpreted as zero. - */ - public LoggingFilter(final Logger logger, final int maxEntitySize) { - this.logger = logger; - this.printEntity = true; - this.maxEntitySize = Math.max(0, maxEntitySize); - } - - private void log(final StringBuilder b) { - if (logger != null) { - logger.info(b.toString()); - } - } - - private StringBuilder prefixId(final StringBuilder b, final long id) { - b.append(Long.toString(id)).append(" "); - return b; - } - - private void printRequestLine(final StringBuilder b, final String note, final long id, final String method, - final URI uri) { - prefixId(b, id).append(NOTIFICATION_PREFIX).append(note).append(" on thread ") - .append(Thread.currentThread().getName()).append("\n"); - prefixId(b, id).append(REQUEST_PREFIX).append(method).append(" ").append(uri.toASCIIString()).append("\n"); - } - - private void printResponseLine(final StringBuilder b, final String note, final long id, final int status) { - prefixId(b, id).append(NOTIFICATION_PREFIX).append(note).append(" on thread ") - .append(Thread.currentThread().getName()).append("\n"); - prefixId(b, id).append(RESPONSE_PREFIX).append(Integer.toString(status)).append("\n"); - } - - private void printPrefixedHeaders(final StringBuilder b, final long id, final String prefix, - final MultivaluedMap headers) { - for (final Map.Entry> headerEntry : getSortedHeaders(headers.entrySet())) { - final List val = headerEntry.getValue(); - final String header = headerEntry.getKey(); - - if (val.size() == 1) { - prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append("\n"); - } else { - final StringBuilder sb = new StringBuilder(); - boolean add = false; - for (final Object s : val) { - if (add) { - sb.append(','); - } - add = true; - sb.append(s); - } - prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append("\n"); - } - } - } - - private Set>> getSortedHeaders(final Set>> headers) { - final TreeSet>> sortedHeaders = new TreeSet>>( - COMPARATOR); - sortedHeaders.addAll(headers); - return sortedHeaders; - } - - private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) - throws IOException { - if (!stream.markSupported()) { - stream = new BufferedInputStream(stream); - } - stream.mark(maxEntitySize + 1); - final byte[] entity = new byte[maxEntitySize + 1]; - final int entitySize = stream.read(entity); - b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset)); - if (entitySize > maxEntitySize) { - b.append("...more..."); - } - b.append('\n'); - stream.reset(); - return stream; - } - - @Override - public void filter(final ClientRequestContext context) throws IOException { - final long id = _id.incrementAndGet(); - context.setProperty(LOGGING_ID_PROPERTY, id); - - final StringBuilder b = new StringBuilder(); - - printRequestLine(b, "Sending client request", id, context.getMethod(), context.getUri()); - printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getStringHeaders()); - - if (printEntity && context.hasEntity()) { - final OutputStream stream = new LoggingStream(b, context.getEntityStream()); - context.setEntityStream(stream); - context.setProperty(ENTITY_LOGGER_PROPERTY, stream); - // not calling log(b) here - it will be called by the interceptor - } else { - log(b); - } - } - - @Override - public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) - throws IOException { - final Object requestId = requestContext.getProperty(LOGGING_ID_PROPERTY); - final long id = requestId != null ? (Long) requestId : _id.incrementAndGet(); - - final StringBuilder b = new StringBuilder(); - - printResponseLine(b, "Client response received", id, responseContext.getStatus()); - printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getHeaders()); - - if (printEntity && responseContext.hasEntity()) { - responseContext.setEntityStream( - logInboundEntity(b, responseContext.getEntityStream(), getCharset(responseContext.getMediaType()))); - } - - log(b); - } - - @Override - public void filter(final ContainerRequestContext context) throws IOException { - final long id = _id.incrementAndGet(); - context.setProperty(LOGGING_ID_PROPERTY, id); - - final StringBuilder b = new StringBuilder(); - - printRequestLine(b, "Server has received a request", id, context.getMethod(), - context.getUriInfo().getRequestUri()); - printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getHeaders()); - - if (printEntity && context.hasEntity()) { - context.setEntityStream(logInboundEntity(b, context.getEntityStream(), getCharset(context.getMediaType()))); - } - - log(b); - } - - @Override - public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) - throws IOException { - final Object requestId = requestContext.getProperty(LOGGING_ID_PROPERTY); - final long id = requestId != null ? (Long) requestId : _id.incrementAndGet(); - - final StringBuilder b = new StringBuilder(); - - printResponseLine(b, "Server responded with a response", id, responseContext.getStatus()); - printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getStringHeaders()); - - if (printEntity && responseContext.hasEntity()) { - final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream()); - responseContext.setEntityStream(stream); - requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream); - // not calling log(b) here - it will be called by the interceptor - } else { - log(b); - } - } - - @Override - public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) - throws IOException, WebApplicationException { - final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY); - writerInterceptorContext.proceed(); - if (stream != null) { - log(stream.getStringBuilder(getCharset(writerInterceptorContext.getMediaType()))); - } - } - - private class LoggingStream extends FilterOutputStream { - - private final StringBuilder b; - private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - LoggingStream(final StringBuilder b, final OutputStream inner) { - super(inner); - - this.b = b; - } - - StringBuilder getStringBuilder(final Charset charset) { - // write entity to the builder - final byte[] entity = baos.toByteArray(); - - b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset)); - if (entity.length > maxEntitySize) { - b.append("...more..."); - } - b.append('\n'); - - return b; - } - - @Override - public void write(final int i) throws IOException { - if (baos.size() <= maxEntitySize) { - baos.write(i); - } - out.write(i); - } - } - - /** - * Get the character set from a media type. - *

- * The character set is obtained from the media type parameter "charset". - * If the parameter is not present the {@link #UTF8} charset is utilized. - * - * @param m the media type. - * @return the character set. - */ - public static Charset getCharset(MediaType m) { - String name = (m == null) ? null : m.getParameters().get(MediaType.CHARSET_PARAMETER); - return (name == null) ? UTF8 : Charset.forName(name); - } - -} \ No newline at end of file diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/AbstractClient.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/AbstractClient.java deleted file mode 100644 index 7f5e3cbce948b..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/AbstractClient.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.impl; - -import javax.ws.rs.client.Client; - -import com.google.gson.Gson; - -public abstract class AbstractClient -{ - private volatile Client client; - private volatile Gson gson; - - protected Client getClient() - { - if (client == null) - { - synchronized (this) - { - if (client == null) - { - client = createClient(); - } - } - } - - return client; - } - - protected Gson getGson() - { - if (gson == null) - { - synchronized (this) - { - if (gson == null) - { - gson = createGson(); - } - } - } - - return gson; - } - - protected abstract Client createClient(); - - protected Gson createGson() - { - return GsonGenerator.create(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/Endpoints.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/Endpoints.java deleted file mode 100644 index a1e8f348e4c3b..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/Endpoints.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.impl; - -public class Endpoints -{ - private static final String LOGIN = "login"; - private static final String BED = "bed"; - private static final String SLEEPER = "sleeper"; - private static final String FAMILY_STATUS = "familyStatus"; - private static final String PAUSE_MODE = "pauseMode"; - - public static String login() - { - return LOGIN; - } - - public static String bed() - { - return BED; - } - - public static String sleeper() - { - return SLEEPER; - } - - public static String familyStatus() - { - return FAMILY_STATUS; - } - - public static String pauseMode() - { - return PAUSE_MODE; - } - - // @formatter:off - private Endpoints() {} - // @formatter:on -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/GsonGenerator.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/GsonGenerator.java deleted file mode 100644 index fb67e9b0ac83f..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/GsonGenerator.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.impl; - -import org.openhab.binding.sleepiq.api.impl.typeadapters.JSR310TypeAdapters; -import org.openhab.binding.sleepiq.api.impl.typeadapters.TimeSinceTypeAdapter; -import org.openhab.binding.sleepiq.api.model.TimeSince; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -public class GsonGenerator -{ - public static Gson create() - { - return create(false); - } - - public static Gson create(boolean prettyPrint) - { - GsonBuilder builder = new GsonBuilder(); - - // add Java 8 Time API support - JSR310TypeAdapters.registerJSR310TypeAdapters(builder); - - builder.registerTypeAdapter(TimeSince.class, new TimeSinceTypeAdapter()); - - if (prettyPrint) - { - builder.setPrettyPrinting(); - } - - return builder.create(); - } - - // @formatter:off - private GsonGenerator() {} - // @formatter:on -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/SleepIQImpl.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/SleepIQImpl.java deleted file mode 100644 index df1b85a387bd7..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/SleepIQImpl.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.impl; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.client.Entity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -import org.openhab.binding.sleepiq.api.BedNotFoundException; -import org.openhab.binding.sleepiq.api.Configuration; -import org.openhab.binding.sleepiq.api.LoginException; -import org.openhab.binding.sleepiq.api.SleepIQ; -import org.openhab.binding.sleepiq.api.UnauthorizedException; -import org.openhab.binding.sleepiq.api.filter.LoggingFilter; -import org.openhab.binding.sleepiq.api.model.Bed; -import org.openhab.binding.sleepiq.api.model.BedsResponse; -import org.openhab.binding.sleepiq.api.model.Failure; -import org.openhab.binding.sleepiq.api.model.FamilyStatus; -import org.openhab.binding.sleepiq.api.model.LoginInfo; -import org.openhab.binding.sleepiq.api.model.LoginRequest; -import org.openhab.binding.sleepiq.api.model.PauseMode; -import org.openhab.binding.sleepiq.api.model.Sleeper; -import org.openhab.binding.sleepiq.api.model.SleepersResponse; -import org.openhab.binding.sleepiq.internal.GsonProvider; - -public class SleepIQImpl extends AbstractClient implements SleepIQ { - protected static final String PARAM_KEY = "_k"; - - protected static final String DATA_BED_ID = "bedId"; - - protected final Configuration config; - - private volatile LoginInfo loginInfo; - - private final ClientBuilder clientBuilder; - - public SleepIQImpl(Configuration config, ClientBuilder clientBuilder) { - this.config = config; - this.clientBuilder = clientBuilder; - } - - @Override - public LoginInfo login() throws LoginException { - if (loginInfo == null) { - synchronized (this) { - if (loginInfo == null) { - Response response = getClient().target(config.getBaseUri()).path(Endpoints.login()) - .request(MediaType.APPLICATION_JSON_TYPE).put(Entity.json(new LoginRequest() - .withLogin(config.getUsername()).withPassword(config.getPassword()))); - - if (isUnauthorized(response)) { - throw new UnauthorizedException(response.readEntity(Failure.class)); - } - - if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) { - throw new LoginException(response.readEntity(Failure.class)); - } - - // add the received cookies to all future requests - getClient().register(new ClientRequestFilter() { - @Override - public void filter(ClientRequestContext requestContext) throws IOException { - List cookies = response.getCookies().values().stream() - .map(newCookie -> newCookie.toCookie()).collect(Collectors.toList()); - requestContext.getHeaders().put("Cookie", cookies); - } - }); - - loginInfo = response.readEntity(LoginInfo.class); - } - } - } - - return loginInfo; - } - - @Override - public List getBeds() { - return getSessionResponse(this::getBedsResponse).readEntity(BedsResponse.class).getBeds(); - } - - protected Response getBedsResponse(Map data) throws LoginException { - LoginInfo login = login(); - return getClient().target(config.getBaseUri()).path(Endpoints.bed()).queryParam(PARAM_KEY, login.getKey()) - .request(MediaType.APPLICATION_JSON_TYPE).get(); - } - - @Override - public List getSleepers() { - return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class).getSleepers(); - } - - protected Response getSleepersResponse(Map data) throws LoginException { - LoginInfo login = login(); - return getClient().target(config.getBaseUri()).path(Endpoints.sleeper()).queryParam(PARAM_KEY, login.getKey()) - .request(MediaType.APPLICATION_JSON_TYPE).get(); - } - - @Override - public FamilyStatus getFamilyStatus() { - return getSessionResponse(this::getFamilyStatusResponse).readEntity(FamilyStatus.class); - } - - protected Response getFamilyStatusResponse(Map data) throws LoginException { - LoginInfo login = login(); - return getClient().target(config.getBaseUri()).path(Endpoints.bed()).path(Endpoints.familyStatus()) - .queryParam(PARAM_KEY, login.getKey()).request(MediaType.APPLICATION_JSON_TYPE).get(); - } - - @Override - public PauseMode getPauseMode(String bedId) throws BedNotFoundException { - Map data = new HashMap<>(); - data.put(DATA_BED_ID, bedId); - - Response response = getSessionResponse(this::getPauseModeResponse, data); - - if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) { - throw new BedNotFoundException(response.readEntity(Failure.class)); - } - - return response.readEntity(PauseMode.class); - } - - protected Response getPauseModeResponse(Map data) throws LoginException { - LoginInfo login = login(); - return getClient().target(config.getBaseUri()).path(Endpoints.bed()).path(data.get(DATA_BED_ID).toString()) - .path(Endpoints.pauseMode()).queryParam(PARAM_KEY, login.getKey()) - .request(MediaType.APPLICATION_JSON_TYPE).get(); - } - - protected boolean isUnauthorized(Response response) { - return Status.UNAUTHORIZED.getStatusCode() == response.getStatusInfo().getStatusCode(); - } - - protected synchronized void resetLogin() { - loginInfo = null; - } - - protected Response getSessionResponse(Request request) { - return getSessionResponse(request, Collections.emptyMap()); - } - - protected Response getSessionResponse(Request request, Map data) { - try { - Response response = request.execute(data); - - if (isUnauthorized(response)) { - // session timed out - response.close(); - resetLogin(); - response = request.execute(data); - } - - return response; - } catch (LoginException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - @Override - protected Client createClient() { - // setup Gson (de)serialization - GsonProvider gsonProvider = new GsonProvider<>(getGson()); - clientBuilder.register(gsonProvider); - - // turn on logging if requested - if (config.isLogging()) { - clientBuilder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()), true)); - } - - return clientBuilder.build(); - } - - @FunctionalInterface - public static interface Request { - public Response execute(Map data) throws LoginException; - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/DateTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/DateTimeTypeAdapter.java deleted file mode 100644 index e148736f36537..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/DateTimeTypeAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.util.function.Function; - -/** - * Abstract type adapter for jsr310 date-time types. - * - * @author Christophe Bornet - */ -abstract class DateTimeTypeAdapter extends TemporalTypeAdapter { - - DateTimeTypeAdapter(Function parseFunction) { - super(parseFunction); - } - - @Override - public String preProcess(String in) { - if (in.endsWith("+0000")) { - return in.substring(0, in.length()-5) + "Z"; - } - return in; - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/DurationTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/DurationTypeAdapter.java deleted file mode 100644 index 7651064f6a66a..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/DurationTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.Duration; - -/** - * Type adapter for jsr310 {@link Duration} class. - * - * @author Christophe Bornet - */ -public class DurationTypeAdapter extends TemporalTypeAdapter { - - public DurationTypeAdapter() { - super(Duration::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/InstantTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/InstantTypeAdapter.java deleted file mode 100644 index 5f4b4f4d0c377..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/InstantTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.Instant; - -/** - * Type adapter for jsr310 {@link Instant} class. - * - * @author Christophe Bornet - */ -public class InstantTypeAdapter extends DateTimeTypeAdapter { - - public InstantTypeAdapter() { - super(Instant::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/JSR310TypeAdapters.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/JSR310TypeAdapters.java deleted file mode 100644 index 4de838cf870c1..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/JSR310TypeAdapters.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import com.google.gson.GsonBuilder; - -import java.time.*; - -/** - * Helper methods to register JSR310 type adapters. - * - * @author Christophe Bornet - */ -public class JSR310TypeAdapters { - - private JSR310TypeAdapters() { - } - - public static GsonBuilder registerDurationTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(Duration.class, new DurationTypeAdapter()); - } - - public static GsonBuilder registerInstantTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(Instant.class, new InstantTypeAdapter()); - } - - public static GsonBuilder registerLocalDateTimeTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter()); - } - - public static GsonBuilder registerLocalDateTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter()); - } - - public static GsonBuilder registerLocalTimeTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(LocalTime.class, new LocalTimeTypeAdapter()); - } - - public static GsonBuilder registerMonthDayTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(MonthDay.class, new MonthDayTypeAdapter()); - } - - public static GsonBuilder registerOffsetDateTimeTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter()); - } - - public static GsonBuilder registerOffsetTimeTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(OffsetTime.class, new OffsetTimeTypeAdapter()); - } - - public static GsonBuilder registerPeriodTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(Period.class, new PeriodTypeAdapter()); - } - - public static GsonBuilder registerYearMonthTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(YearMonth.class, new YearMonthTypeAdapter()); - } - - public static GsonBuilder registerYearTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(Year.class, new YearTypeAdapter()); - } - - public static GsonBuilder registerZonedDateTimeTypeAdapter(GsonBuilder gsonBuilder) { - return gsonBuilder.registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeTypeAdapter()); - } - - - /** - * Helper method to register all the available JSR310 adapters at once. - * @param gsonBuilder the gsonBuilder on which all the JSR310 adapters must be registered. - * @return the gsonBuilder with the JSR310 adapters registered. - */ - public static GsonBuilder registerJSR310TypeAdapters(GsonBuilder gsonBuilder) { - registerDurationTypeAdapter(gsonBuilder); - registerInstantTypeAdapter(gsonBuilder); - registerLocalDateTimeTypeAdapter(gsonBuilder); - registerLocalDateTypeAdapter(gsonBuilder); - registerLocalTimeTypeAdapter(gsonBuilder); - registerMonthDayTypeAdapter(gsonBuilder); - registerOffsetDateTimeTypeAdapter(gsonBuilder); - registerOffsetTimeTypeAdapter(gsonBuilder); - registerPeriodTypeAdapter(gsonBuilder); - registerYearMonthTypeAdapter(gsonBuilder); - registerYearTypeAdapter(gsonBuilder); - registerZonedDateTimeTypeAdapter(gsonBuilder); - - return gsonBuilder; - } - - -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalDateTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalDateTimeTypeAdapter.java deleted file mode 100644 index 10360c9ae6373..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalDateTimeTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.LocalDateTime; - -/** - * Type adapter for jsr310 {@link LocalDateTime} class. - * - * @author Christophe Bornet - */ -public class LocalDateTimeTypeAdapter extends DateTimeTypeAdapter { - - public LocalDateTimeTypeAdapter() { - super(LocalDateTime::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalDateTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalDateTypeAdapter.java deleted file mode 100644 index 3ac2762dbad0e..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalDateTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.LocalDate; - -/** - * Type adapter for jsr310 {@link LocalDate} class. - * - * @author Christophe Bornet - */ -public class LocalDateTypeAdapter extends TemporalTypeAdapter { - - public LocalDateTypeAdapter() { - super(LocalDate::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalTimeTypeAdapter.java deleted file mode 100644 index 597301e2f342f..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/LocalTimeTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.LocalTime; - -/** - * Type adapter for jsr310 {@link LocalTime} class. - * - * @author Christophe Bornet - */ -public class LocalTimeTypeAdapter extends TemporalTypeAdapter { - - public LocalTimeTypeAdapter() { - super(LocalTime::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/MonthDayTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/MonthDayTypeAdapter.java deleted file mode 100644 index 5a1ab7fe71aa8..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/MonthDayTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.MonthDay; - -/** - * Type adapter for jsr310 {@link MonthDay} class. - * - * @author Christophe Bornet - */ -public class MonthDayTypeAdapter extends TemporalTypeAdapter { - - public MonthDayTypeAdapter() { - super(MonthDay::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/OffsetDateTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/OffsetDateTimeTypeAdapter.java deleted file mode 100644 index ded37fd975c9d..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/OffsetDateTimeTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.OffsetDateTime; - -/** - * Type adapter for jsr310 {@link OffsetDateTime} class. - * - * @author Christophe Bornet - */ -public class OffsetDateTimeTypeAdapter extends DateTimeTypeAdapter { - - public OffsetDateTimeTypeAdapter() { - super(OffsetDateTime::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/OffsetTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/OffsetTimeTypeAdapter.java deleted file mode 100644 index e6f49e783786c..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/OffsetTimeTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.OffsetTime; - -/** - * Type adapter for jsr310 {@link OffsetTime} class. - * - * @author Christophe Bornet - */ -public class OffsetTimeTypeAdapter extends TemporalTypeAdapter { - - public OffsetTimeTypeAdapter() { - super(OffsetTime::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/PeriodTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/PeriodTypeAdapter.java deleted file mode 100644 index 74ade1885ab86..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/PeriodTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.Period; - -/** - * Type adapter for jsr310 {@link Period} class. - * - * @author Christophe Bornet - */ -public class PeriodTypeAdapter extends TemporalTypeAdapter { - - public PeriodTypeAdapter() { - super(Period::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/TimeSinceTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/TimeSinceTypeAdapter.java deleted file mode 100644 index 3504e1bfba468..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/TimeSinceTypeAdapter.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import org.openhab.binding.sleepiq.api.model.TimeSince; - -public class TimeSinceTypeAdapter extends TemporalTypeAdapter -{ - public TimeSinceTypeAdapter() - { - super(TimeSince::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/YearMonthTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/YearMonthTypeAdapter.java deleted file mode 100644 index 5fe0a6e97a384..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/YearMonthTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.YearMonth; - -/** - * Type adapter for jsr310 {@link YearMonth} class. - * - * @author Christophe Bornet - */ -public class YearMonthTypeAdapter extends TemporalTypeAdapter { - - public YearMonthTypeAdapter() { - super(YearMonth::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/YearTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/YearTypeAdapter.java deleted file mode 100644 index 37f2153a1e99d..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/YearTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.Year; - -/** - * Type adapter for jsr310 {@link Year} class. - * - * @author Christophe Bornet - */ -public class YearTypeAdapter extends TemporalTypeAdapter { - - public YearTypeAdapter() { - super(Year::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/ZonedDateTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/ZonedDateTimeTypeAdapter.java deleted file mode 100644 index 0d31cad1a3d03..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/ZonedDateTimeTypeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. - */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; - -import java.time.ZonedDateTime; - -/** - * Type adapter for jsr310 {@link ZonedDateTime} class. - * - * @author Christophe Bornet - */ -public class ZonedDateTimeTypeAdapter extends DateTimeTypeAdapter { - - public ZonedDateTimeTypeAdapter() { - super(ZonedDateTime::parse); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedStatus.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedStatus.java deleted file mode 100644 index c07fbd8d88ef8..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedStatus.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -public class BedStatus -{ - private Long status; - private String bedId; - private BedSideStatus leftSide; - private BedSideStatus rightSide; - - public Long getStatus() - { - return status; - } - - public void setStatus(Long status) - { - this.status = status; - } - - public BedStatus withStatus(Long status) - { - setStatus(status); - return this; - } - - public String getBedId() - { - return bedId; - } - - public void setBedId(String bedId) - { - this.bedId = bedId; - } - - public BedStatus withBedId(String bedId) - { - setBedId(bedId); - return this; - } - - public BedSideStatus getLeftSide() - { - return leftSide; - } - - public void setLeftSide(BedSideStatus leftSide) - { - this.leftSide = leftSide; - } - - public BedStatus withLeftSide(BedSideStatus leftSide) - { - setLeftSide(leftSide); - return this; - } - - public BedSideStatus getRightSide() - { - return rightSide; - } - - public void setRightSide(BedSideStatus rightSide) - { - this.rightSide = rightSide; - } - - public BedStatus withRightSide(BedSideStatus rightSide) - { - setRightSide(rightSide); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((bedId == null) ? 0 : bedId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof BedStatus)) - { - return false; - } - BedStatus other = (BedStatus)obj; - if (bedId == null) - { - if (other.bedId != null) - { - return false; - } - } - else if (!bedId.equals(other.bedId)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("BedStatus [status="); - builder.append(status); - builder.append(", bedId="); - builder.append(bedId); - builder.append(", leftSide="); - builder.append(leftSide); - builder.append(", rightSide="); - builder.append(rightSide); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedsResponse.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedsResponse.java deleted file mode 100644 index 3b3deeea0c7f5..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedsResponse.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import java.util.List; - -public class BedsResponse -{ - private List beds; - - public List getBeds() - { - return beds; - } - - public void setBeds(List beds) - { - this.beds = beds; - } - - public BedsResponse withBeds(List beds) - { - setBeds(beds); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((beds == null) ? 0 : beds.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof BedsResponse)) - { - return false; - } - BedsResponse other = (BedsResponse)obj; - if (beds == null) - { - if (other.beds != null) - { - return false; - } - } - else if (!beds.equals(other.beds)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("BedsResponse [beds="); - builder.append(beds); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Error.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Error.java deleted file mode 100644 index 5593b59fe42d1..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Error.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import com.google.gson.annotations.SerializedName; - -public class Error -{ - @SerializedName("Code") - private Long code; - @SerializedName("Message") - private String message; - - public Long getCode() - { - return code; - } - - public void setCode(Long code) - { - this.code = code; - } - - public Error withCode(Long code) - { - setCode(code); - return this; - } - - public String getMessage() - { - return message; - } - - public void setMessage(String message) - { - this.message = message; - } - - public Error withMessage(String message) - { - setMessage(message); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((code == null) ? 0 : code.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof Error)) - { - return false; - } - Error other = (Error)obj; - if (code == null) - { - if (other.code != null) - { - return false; - } - } - else if (!code.equals(other.code)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("Error [code="); - builder.append(code); - builder.append(", message="); - builder.append(message); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Failure.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Failure.java deleted file mode 100644 index 4ab0481e8a4ac..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Failure.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import com.google.gson.annotations.SerializedName; - -public class Failure -{ - @SerializedName("Error") - private Error error; - - public Error getError() - { - return error; - } - - public void setError(Error error) - { - this.error = error; - } - - public Failure withError(Error error) - { - setError(error); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((error == null) ? 0 : error.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof Failure)) - { - return false; - } - Failure other = (Failure)obj; - if (error == null) - { - if (other.error != null) - { - return false; - } - } - else if (!error.equals(other.error)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("Failure [error="); - builder.append(error); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/FamilyStatus.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/FamilyStatus.java deleted file mode 100644 index cddea5a606fae..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/FamilyStatus.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import java.util.List; - -public class FamilyStatus -{ - private List beds; - - public List getBeds() - { - return beds; - } - - public void setBeds(List beds) - { - this.beds = beds; - } - - public FamilyStatus withBeds(List beds) - { - setBeds(beds); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((beds == null) ? 0 : beds.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof FamilyStatus)) - { - return false; - } - FamilyStatus other = (FamilyStatus)obj; - if (beds == null) - { - if (other.beds != null) - { - return false; - } - } - else if (!beds.equals(other.beds)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("FamilyStatus [beds="); - builder.append(beds); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/LoginRequest.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/LoginRequest.java deleted file mode 100644 index 07835f054ebe9..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/LoginRequest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -public class LoginRequest -{ - private String login; - private String password; - - public String getLogin() - { - return login; - } - - public void setLogin(String login) - { - this.login = login; - } - - public LoginRequest withLogin(String login) - { - setLogin(login); - return this; - } - - public String getPassword() - { - return password; - } - - public void setPassword(String password) - { - this.password = password; - } - - public LoginRequest withPassword(String password) - { - setPassword(password); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((login == null) ? 0 : login.hashCode()); - result = prime * result + ((password == null) ? 0 : password.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof LoginRequest)) - { - return false; - } - LoginRequest other = (LoginRequest)obj; - if (login == null) - { - if (other.login != null) - { - return false; - } - } - else if (!login.equals(other.login)) - { - return false; - } - if (password == null) - { - if (other.password != null) - { - return false; - } - } - else if (!password.equals(other.password)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("LoginRequest [login="); - builder.append(login); - builder.append(", password="); - builder.append(password); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/PauseMode.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/PauseMode.java deleted file mode 100644 index 56278864655a9..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/PauseMode.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -public class PauseMode -{ - private String accountId; - private String bedId; - private String pauseMode; - - public String getAccountId() - { - return accountId; - } - - public void setAccountId(String accountId) - { - this.accountId = accountId; - } - - public PauseMode withAccountId(String accountId) - { - setAccountId(accountId); - return this; - } - - public String getBedId() - { - return bedId; - } - - public void setBedId(String bedId) - { - this.bedId = bedId; - } - - public PauseMode withBedId(String bedId) - { - setBedId(bedId); - return this; - } - - public String getPauseMode() - { - return pauseMode; - } - - public void setPauseMode(String pauseMode) - { - this.pauseMode = pauseMode; - } - - public PauseMode withPauseMode(String pauseMode) - { - setPauseMode(pauseMode); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); - result = prime * result + ((bedId == null) ? 0 : bedId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof PauseMode)) - { - return false; - } - PauseMode other = (PauseMode)obj; - if (accountId == null) - { - if (other.accountId != null) - { - return false; - } - } - else if (!accountId.equals(other.accountId)) - { - return false; - } - if (bedId == null) - { - if (other.bedId != null) - { - return false; - } - } - else if (!bedId.equals(other.bedId)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("PauseMode [accountId="); - builder.append(accountId); - builder.append(", bedId="); - builder.append(bedId); - builder.append(", pauseMode="); - builder.append(pauseMode); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/SleepersResponse.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/SleepersResponse.java deleted file mode 100644 index 4c793ef1a05a9..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/SleepersResponse.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import java.util.List; - -public class SleepersResponse -{ - private List sleepers; - - public List getSleepers() - { - return sleepers; - } - - public void setSleepers(List sleepers) - { - this.sleepers = sleepers; - } - - public SleepersResponse withSleepers(List sleepers) - { - setSleepers(sleepers); - return this; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((sleepers == null) ? 0 : sleepers.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (!(obj instanceof SleepersResponse)) - { - return false; - } - SleepersResponse other = (SleepersResponse)obj; - if (sleepers == null) - { - if (other.sleepers != null) - { - return false; - } - } - else if (!sleepers.equals(other.sleepers)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("SleepersResponse [sleepers="); - builder.append(sleepers); - builder.append("]"); - return builder.toString(); - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/FamilyStatusTest.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/FamilyStatusTest.java deleted file mode 100644 index 2fa2016938b8a..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/FamilyStatusTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.FileReader; -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; -import org.openhab.binding.sleepiq.api.test.AbstractTest; - -import com.google.gson.Gson; - -public class FamilyStatusTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } - - @Test - public void testSerializeAllFields() throws Exception { - FamilyStatus familyStatus = new FamilyStatus().withBeds(Arrays.asList(new BedStatus().withStatus(1L))); - assertEquals(readJson("family-status.json"), gson.toJson(familyStatus)); - } - - @Test - public void testDeserializeAllFields() throws Exception { - try (FileReader reader = new FileReader(getTestDataFile("family-status.json"))) { - FamilyStatus familyStatus = gson.fromJson(reader, FamilyStatus.class); - - List beds = familyStatus.getBeds(); - assertNotNull(beds); - assertEquals(1, beds.size()); - assertEquals(Long.valueOf(1L), beds.get(0).getStatus()); - } - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/PauseModeTest.java b/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/PauseModeTest.java deleted file mode 100644 index d36bd3acc8b3c..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/PauseModeTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Gregory Moyer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openhab.binding.sleepiq.api.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.FileReader; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; -import org.openhab.binding.sleepiq.api.test.AbstractTest; - -import com.google.gson.Gson; - -public class PauseModeTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } - - @Test - public void testSerializeAllFields() throws Exception { - PauseMode pauseMode = new PauseMode().withAccountId("-8888888888888888888").withBedId("-9999999999999999999") - .withPauseMode("off"); - assertEquals(readJson("pause-mode.json"), gson.toJson(pauseMode)); - } - - @Test - public void testDeserializeAllFields() throws Exception { - try (FileReader reader = new FileReader(getTestDataFile("pause-mode.json"))) { - PauseMode pauseMode = gson.fromJson(reader, PauseMode.class); - assertEquals("-8888888888888888888", pauseMode.getAccountId()); - assertEquals("-9999999999999999999", pauseMode.getBedId()); - assertEquals("off", pauseMode.getPauseMode()); - } - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/GsonProvider.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/GsonProvider.java deleted file mode 100644 index 1438a5e2c9348..0000000000000 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/GsonProvider.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * 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.sleepiq.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; - -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyReader; -import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.Provider; - -import com.google.gson.Gson; - -/** - * JSON reader/writer for Jersey using GSON. - * - * @author Simon Kaufmann - Initial contribution - * - * @param - */ -@Provider -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class GsonProvider implements MessageBodyReader, MessageBodyWriter { - - private final Gson gson; - - public GsonProvider(Gson gson) { - this.gson = gson; - } - - @Override - public long getSize(T t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return -1; - } - - @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return true; - } - - @Override - public void writeTo(T object, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, OutputStream entityStream) - throws IOException, WebApplicationException { - try (OutputStream stream = entityStream) { - entityStream.write(gson.toJson(object).getBytes(StandardCharsets.UTF_8)); - entityStream.flush(); - } - } - - @Override - public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return true; - } - - @Override - public T readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, InputStream entityStream) - throws IOException, WebApplicationException { - try (InputStreamReader reader = new InputStreamReader(entityStream, StandardCharsets.UTF_8)) { - return gson.fromJson(reader, type); - } - } -} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQBindingConstants.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQBindingConstants.java index f5eb4642a87e1..dc1191c97cef3 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQBindingConstants.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQBindingConstants.java @@ -31,34 +31,72 @@ public class SleepIQBindingConstants { public static final ThingTypeUID THING_TYPE_CLOUD = new ThingTypeUID(BINDING_ID, "cloud"); // generic thing types - // public static final ThingTypeUID THING_TYPE_SINGLE_BED = new ThingTypeUID(BINDING_ID, "singleBed"); public static final ThingTypeUID THING_TYPE_DUAL_BED = new ThingTypeUID(BINDING_ID, "dualBed"); // List of all Channel ids - // public static final String CHANNEL_BED_IN_BED = "bed#inBed"; public static final String CHANNEL_LEFT_IN_BED = "left#inBed"; public static final String CHANNEL_RIGHT_IN_BED = "right#inBed"; - // public static final String CHANNEL_BED_SLEEP_NUMBER = "bed#sleepNumber"; public static final String CHANNEL_LEFT_SLEEP_NUMBER = "left#sleepNumber"; public static final String CHANNEL_RIGHT_SLEEP_NUMBER = "right#sleepNumber"; - // public static final String CHANNEL_BED_PRESSURE = "bed#pressure"; public static final String CHANNEL_LEFT_PRESSURE = "left#pressure"; public static final String CHANNEL_RIGHT_PRESSURE = "right#pressure"; - // public static final String CHANNEL_BED_LAST_LINK = "bed#lastLink"; public static final String CHANNEL_LEFT_LAST_LINK = "left#lastLink"; public static final String CHANNEL_RIGHT_LAST_LINK = "right#lastLink"; - // public static final String CHANNEL_BED_ALERT_ID = "bed#alertId"; public static final String CHANNEL_LEFT_ALERT_ID = "left#alertId"; public static final String CHANNEL_RIGHT_ALERT_ID = "right#alertId"; - // public static final String CHANNEL_BED_ALERT_DETAILED_MESSAGE = "bed#alertDetailedMessage"; public static final String CHANNEL_LEFT_ALERT_DETAILED_MESSAGE = "left#alertDetailedMessage"; public static final String CHANNEL_RIGHT_ALERT_DETAILED_MESSAGE = "right#alertDetailedMessage"; + public static final String CHANNEL_LEFT_FIRST_NAME = "left#firstName"; + public static final String CHANNEL_RIGHT_FIRST_NAME = "right#firstName"; + + public static final String CHANNEL_LEFT_SLEEP_GOAL_MINUTES = "left#sleepGoalMinutes"; + public static final String CHANNEL_RIGHT_SLEEP_GOAL_MINUTES = "right#sleepGoalMinutes"; + + public static final String CHANNEL_LEFT_PRIVACY_MODE = "left#privacyMode"; + public static final String CHANNEL_RIGHT_PRIVACY_MODE = "right#privacyMode"; + + public static final String CHANNEL_LEFT_TODAY_SLEEP_IQ = "left#todaySleepIQ"; + public static final String CHANNEL_RIGHT_TODAY_SLEEP_IQ = "right#todaySleepIQ"; + + public static final String CHANNEL_LEFT_TODAY_AVG_HEART_RATE = "left#todayAverageHeartRate"; + public static final String CHANNEL_RIGHT_TODAY_AVG_HEART_RATE = "right#todayAverageHeartRate"; + + public static final String CHANNEL_LEFT_TODAY_AVG_RESPIRATION_RATE = "left#todayAverageRespirationRate"; + public static final String CHANNEL_RIGHT_TODAY_AVG_RESPIRATION_RATE = "right#todayAverageRespirationRate"; + + public static final String CHANNEL_LEFT_TODAY_MESSAGE = "left#todayMessage"; + public static final String CHANNEL_RIGHT_TODAY_MESSAGE = "right#todayMessage"; + + public static final String CHANNEL_LEFT_TODAY_SLEEP_DURATION_SECONDS = "left#todaySleepDurationSeconds"; + public static final String CHANNEL_RIGHT_TODAY_SLEEP_DURATION_SECONDS = "right#todaySleepDurationSeconds"; + + public static final String CHANNEL_LEFT_TODAY_SLEEP_IN_BED_SECONDS = "left#todaySleepInBedSeconds"; + public static final String CHANNEL_RIGHT_TODAY_SLEEP_IN_BED_SECONDS = "right#todaySleepInBedSeconds"; + + public static final String CHANNEL_LEFT_TODAY_SLEEP_OUT_OF_BED_SECONDS = "left#todaySleepOutOfBedSeconds"; + public static final String CHANNEL_RIGHT_TODAY_SLEEP_OUT_OF_BED_SECONDS = "right#todaySleepOutOfBedSeconds"; + + public static final String CHANNEL_LEFT_TODAY_SLEEP_RESTFUL_SECONDS = "left#todaySleepRestfulSeconds"; + public static final String CHANNEL_RIGHT_TODAY_SLEEP_RESTFUL_SECONDS = "right#todaySleepRestfulSeconds"; + + public static final String CHANNEL_LEFT_TODAY_SLEEP_RESTLESS_SECONDS = "left#todaySleepRestlessSeconds"; + public static final String CHANNEL_RIGHT_TODAY_SLEEP_RESTLESS_SECONDS = "right#todaySleepRestlessSeconds"; + + public static final String CHANNEL_LEFT_MONTHLY_SLEEP_IQ = "left#monthlySleepIQ"; + public static final String CHANNEL_RIGHT_MONTHLY_SLEEP_IQ = "right#monthlySleepIQ"; + + public static final String CHANNEL_LEFT_MONTHLY_AVG_HEART_RATE = "left#monthlyAverageHeartRate"; + public static final String CHANNEL_RIGHT_MONTHLY_AVG_HEART_RATE = "right#monthlyAverageHeartRate"; + + public static final String CHANNEL_LEFT_MONTHLY_AVG_RESPIRATION_RATE = "left#monthlyAverageRespirationRate"; + public static final String CHANNEL_RIGHT_MONTHLY_AVG_RESPIRATION_RATE = "right#monthlyAverageRespirationRate"; + // List of non-standard Properties public static final String PROPERTY_BASE = "base"; public static final String PROPERTY_KIDS_BED = "kidsBed"; @@ -67,8 +105,4 @@ public class SleepIQBindingConstants { public static final String PROPERTY_PURCHASE_DATE = "purchaseDate"; public static final String PROPERTY_SIZE = "size"; public static final String PROPERTY_SKU = "sku"; - - private SleepIQBindingConstants() { - // utility class - } } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQConfigStatusMessage.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQConfigStatusMessage.java index e4fc45aed01d4..a599b1a7b813a 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQConfigStatusMessage.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQConfigStatusMessage.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.sleepiq.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link SleepIQConfigStatusMessage} defines the keys to be used for configuration status messages. * * @author Gregory Moyer - Initial contribution * */ +@NonNullByDefault public interface SleepIQConfigStatusMessage { public static final String USERNAME_MISSING = "missing-username-configuration"; public static final String PASSWORD_MISSING = "missing-password-configuration"; diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQHandlerFactory.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQHandlerFactory.java index 318974b299069..7bda6c0002ba6 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQHandlerFactory.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/SleepIQHandlerFactory.java @@ -20,12 +20,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.ws.rs.client.ClientBuilder; - +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.sleepiq.internal.discovery.SleepIQBedDiscoveryService; import org.openhab.binding.sleepiq.internal.handler.SleepIQCloudHandler; import org.openhab.binding.sleepiq.internal.handler.SleepIQDualBedHandler; import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -45,6 +47,7 @@ * * @author Gregory Moyer - Initial contribution */ +@NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.sleepiq") public class SleepIQHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPE_UIDS = Collections @@ -53,13 +56,13 @@ public class SleepIQHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(SleepIQHandlerFactory.class); - private final ClientBuilder clientBuilder; + private final HttpClient httpClient; private final Map> discoveryServiceReg = new HashMap<>(); @Activate - public SleepIQHandlerFactory(@Reference ClientBuilder clientBuilder) { - this.clientBuilder = clientBuilder; + public SleepIQHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); } @Override @@ -68,26 +71,23 @@ public boolean supportsThingType(final ThingTypeUID thingTypeUID) { } @Override - protected ThingHandler createHandler(final Thing thing) { + protected @Nullable ThingHandler createHandler(final Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - if (SleepIQCloudHandler.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID)) { logger.debug("Creating SleepIQ cloud thing handler"); - SleepIQCloudHandler cloudHandler = new SleepIQCloudHandler((Bridge) thing, clientBuilder); + SleepIQCloudHandler cloudHandler = new SleepIQCloudHandler((Bridge) thing, httpClient); registerBedDiscoveryService(cloudHandler); return cloudHandler; } else if (SleepIQDualBedHandler.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID)) { logger.debug("Creating SleepIQ dual bed thing handler"); return new SleepIQDualBedHandler(thing); } - return null; } @Override protected void removeHandler(final ThingHandler thingHandler) { logger.debug("Removing SleepIQ thing handler"); - if (thingHandler instanceof SleepIQCloudHandler) { unregisterBedDiscoveryService((SleepIQCloudHandler) thingHandler); } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/BedNotFoundException.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/BedNotFoundException.java new file mode 100644 index 0000000000000..de653f6b9ac1f --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/BedNotFoundException.java @@ -0,0 +1,29 @@ +/** + * 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.sleepiq.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link BedNotFoundException} is thrown when a bedId is missing or invalid. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public class BedNotFoundException extends SleepIQException { + private static final long serialVersionUID = 1L; + + public BedNotFoundException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/CommunicationException.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/CommunicationException.java new file mode 100644 index 0000000000000..f25f94a9a0f3f --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/CommunicationException.java @@ -0,0 +1,30 @@ +/** + * 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.sleepiq.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link CommunicationException} is thrown when there's an error communicating with + * the sleepiq service. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class CommunicationException extends SleepIQException { + private static final long serialVersionUID = 1L; + + public CommunicationException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/Configuration.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/Configuration.java similarity index 68% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/Configuration.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/Configuration.java index cb0fd28ba4a7d..861b4a11b7a4b 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/Configuration.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/Configuration.java @@ -1,34 +1,32 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api; +package org.openhab.binding.sleepiq.internal.api; import java.net.URI; -import java.util.logging.Level; + +import org.eclipse.jdt.annotation.NonNullByDefault; /** * This class represents configuration parameters for using {@link SleepIQ}. * - * @author Gregory Moyer + * @author Gregory Moyer - Initial contribution */ -public class Configuration -{ - private String username; - private String password; +@NonNullByDefault +public class Configuration { + private String username = ""; + private String password = ""; - private URI baseUri = URI.create("https://api.sleepiq.sleepnumber.com/rest"); + private URI baseUri = URI.create("https://api.sleepiq.sleepnumber.com"); private boolean logging = false; @@ -37,8 +35,7 @@ public class Configuration * * @return the username */ - public String getUsername() - { + public String getUsername() { return username; } @@ -49,8 +46,7 @@ public String getUsername() * @param username * the value to set */ - public void setUsername(String username) - { + public void setUsername(String username) { this.username = username; } @@ -62,8 +58,7 @@ public void setUsername(String username) * the value to set * @return this configuration instance */ - public Configuration withUsername(String username) - { + public Configuration withUsername(String username) { setUsername(username); return this; } @@ -73,8 +68,7 @@ public Configuration withUsername(String username) * * @return the password */ - public String getPassword() - { + public String getPassword() { return password; } @@ -85,8 +79,7 @@ public String getPassword() * @param password * the value to set */ - public void setPassword(String password) - { + public void setPassword(String password) { this.password = password; } @@ -98,8 +91,7 @@ public void setPassword(String password) * the value to set * @return this configuration instance */ - public Configuration withPassword(String password) - { + public Configuration withPassword(String password) { setPassword(password); return this; } @@ -109,8 +101,7 @@ public Configuration withPassword(String password) * * @return the base URI */ - public URI getBaseUri() - { + public URI getBaseUri() { return baseUri; } @@ -121,8 +112,7 @@ public URI getBaseUri() * @param baseUri * the value to set */ - public void setBaseUri(URI baseUri) - { + public void setBaseUri(URI baseUri) { this.baseUri = baseUri; } @@ -134,8 +124,7 @@ public void setBaseUri(URI baseUri) * the value to set * @return this configuration instance */ - public Configuration withBaseUri(URI baseUri) - { + public Configuration withBaseUri(URI baseUri) { setBaseUri(baseUri); return this; } @@ -145,8 +134,7 @@ public Configuration withBaseUri(URI baseUri) * * @return the logging flag */ - public boolean isLogging() - { + public boolean isLogging() { return logging; } @@ -158,8 +146,7 @@ public boolean isLogging() * @param logging * the value to set */ - public void setLogging(boolean logging) - { + public void setLogging(boolean logging) { this.logging = logging; } @@ -172,8 +159,7 @@ public void setLogging(boolean logging) * the value to set * @return this configuration instance */ - public Configuration withLogging(boolean logging) - { + public Configuration withLogging(boolean logging) { setLogging(logging); return this; } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/LoginException.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/LoginException.java new file mode 100644 index 0000000000000..fcd957c1dea6b --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/LoginException.java @@ -0,0 +1,30 @@ +/** + * 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.sleepiq.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link LoginException} is thrown when there's a failure to log + * into the sleepiq service. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class LoginException extends SleepIQException { + private static final long serialVersionUID = 1L; + + public LoginException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/ResponseFormatException.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/ResponseFormatException.java new file mode 100644 index 0000000000000..07a18b8c21171 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/ResponseFormatException.java @@ -0,0 +1,30 @@ +/** + * 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.sleepiq.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link LoginException} is thrown when a badly formatted response is + * received from the sleepiq service. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class ResponseFormatException extends SleepIQException { + private static final long serialVersionUID = 1L; + + public ResponseFormatException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/SleepIQ.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/SleepIQ.java new file mode 100644 index 0000000000000..ca6a9bf74d874 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/SleepIQ.java @@ -0,0 +1,152 @@ +/** + * 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.sleepiq.internal.api; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; +import org.openhab.binding.sleepiq.internal.api.dto.FamilyStatusResponse; +import org.openhab.binding.sleepiq.internal.api.dto.LoginInfo; +import org.openhab.binding.sleepiq.internal.api.dto.PauseModeResponse; +import org.openhab.binding.sleepiq.internal.api.dto.SleepDataResponse; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; +import org.openhab.binding.sleepiq.internal.api.enums.Side; +import org.openhab.binding.sleepiq.internal.api.enums.SleepDataInterval; +import org.openhab.binding.sleepiq.internal.api.impl.SleepIQImpl; + +/** + * This interface is the main API to access the SleepIQ system. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public interface SleepIQ { + /** + * Login to the {@link Configuration configured} account. This method is not + * required to be called before other methods because all methods must + * ensure login before acting. However, when the only desired action is to + * login and not retrieve other data, this method is the most efficient + * option. + * + * @return basic information about the logged in user + * @throws UnauthorizedException + * if the credentials provided are not valid + * @throws LoginException + * if the login request fails for any reason other than bad + * credentials (including missing credentials) + */ + public @Nullable LoginInfo login() throws LoginException, UnauthorizedException; + + /** + * Get a list of beds connected to the account. + * + * @return the list of beds + * @throws LoginException + * if the login request fails for any reason other than bad + * credentials (including missing credentials) + * @throws SleepIQException + */ + public List getBeds() throws LoginException, SleepIQException, BedNotFoundException; + + /** + * Get a list of sleepers registered to this account for beds or bed positions + * (left or right side). + * + * @return the list of sleepers + * @throws LoginException + * @throws SleepIQException + */ + public List getSleepers() throws LoginException, SleepIQException; + + /** + * Get the status of all beds and all air chambers registered to this + * account. + * + * @return the complete status of beds on the account + * @throws LoginException + * @throws SleepIQException + */ + public FamilyStatusResponse getFamilyStatus() throws LoginException, SleepIQException; + + /** + * Get the Sleep Data for a sleeper registered to this account. + * + * @param sleeperId the sleeper Id to query + * @param interval The time period for which data is to be queried + * @return the Sleep Data + * @throws BedNotFoundException + * if the bed identifier was not found on the account + * @throws LoginException + * @throws SleepIQException + */ + public SleepDataResponse getSleepData(String sleeperId, SleepDataInterval interval) + throws BedNotFoundException, LoginException, SleepIQException; + + /** + * Get the status of "pause mode" (disabling SleepIQ data upload) for a + * specific bed. A bed in pause mode will send no information to the SleepIQ + * cloud services. For example, if a sleeper is in bed and disables SleepIQ + * (enables pause mode), the service will continue to report that the bed is + * occupied even after the sleeper exits the bed until pause mode is + * disabled. + * + * @return the status of pause mode for the specified bed + * @throws BedNotFoundException + * if the bed identifier was not found on the account + * @throws LoginException + * @throws SleepIQException + */ + public PauseModeResponse getPauseMode(String bedId) throws BedNotFoundException, LoginException, SleepIQException; + + /** + * Set the sleep number for a chamber of a bed + * + * @param bedId the unique identifier of the bed + * @param side thethe chamber of the bed + * @param sleepNumber the new sleep number + * + * @throws LoginException + * @throws SleepIQException + */ + public void setSleepNumber(String bedId, Side side, int sleepNumber) throws LoginException, SleepIQException; + + /** + * Set the pause (privacy) mode for a bed + * + * @param bedId the unique identifier of the bed + * + * @throws LoginException + * @throws SleepIQException + */ + public void setPauseMode(String bedId, boolean command) throws LoginException, SleepIQException; + + /** + * Create a default implementation instance of this interface. Each call to + * this method will create a new object. + * + * @param config the configuration to use for the new instance + * @param httpClient handle to the Jetty http client + * @return a concrete implementation of this interface + */ + public static SleepIQ create(Configuration config, HttpClient httpClient) { + return new SleepIQImpl(config, httpClient); + } + + /** + * Close down the cloud service + */ + public void shutdown(); +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/SleepIQException.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/SleepIQException.java new file mode 100644 index 0000000000000..bf463e46f4ae7 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/SleepIQException.java @@ -0,0 +1,30 @@ +/** + * 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.sleepiq.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link SleepIQException} is the base exception class from which other + * sleepiq exceptions are derived. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class SleepIQException extends Exception { + private static final long serialVersionUID = 1L; + + public SleepIQException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/UnauthorizedException.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/UnauthorizedException.java new file mode 100644 index 0000000000000..98a47781d54c6 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/UnauthorizedException.java @@ -0,0 +1,30 @@ +/** + * 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.sleepiq.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link UnauthorizedException} is thrown when a login to the sleepiq service + * is unauthorized to use the API. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class UnauthorizedException extends LoginException { + private static final long serialVersionUID = 1L; + + public UnauthorizedException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Bed.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Bed.java similarity index 60% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Bed.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Bed.java index 6bfa86db0ecaa..c8342cc9e5bec 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Bed.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Bed.java @@ -1,26 +1,27 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api.model; +package org.openhab.binding.sleepiq.internal.api.dto; import java.time.ZonedDateTime; import com.google.gson.annotations.SerializedName; -public class Bed -{ +/** + * The {@link Bed} holds the bed response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class Bed { private ZonedDateTime registrationDate; private String sleeperRightId; private String base; @@ -45,345 +46,281 @@ public class Bed private String zipCode; private String reference; - public ZonedDateTime getRegistrationDate() - { + public ZonedDateTime getRegistrationDate() { return registrationDate; } - public void setRegistrationDate(ZonedDateTime registrationDate) - { + public void setRegistrationDate(ZonedDateTime registrationDate) { this.registrationDate = registrationDate; } - public Bed withRegistrationDate(ZonedDateTime registrationDate) - { + public Bed withRegistrationDate(ZonedDateTime registrationDate) { setRegistrationDate(registrationDate); return this; } - public String getSleeperRightId() - { + public String getSleeperRightId() { return sleeperRightId; } - public void setSleeperRightId(String sleeperRightId) - { + public void setSleeperRightId(String sleeperRightId) { this.sleeperRightId = sleeperRightId; } - public Bed withSleeperRightId(String sleeperRightId) - { + public Bed withSleeperRightId(String sleeperRightId) { setSleeperRightId(sleeperRightId); return this; } - public String getBase() - { + public String getBase() { return base; } - public void setBase(String base) - { + public void setBase(String base) { this.base = base; } - public Bed withBase(String base) - { + public Bed withBase(String base) { setBase(base); return this; } - public Long getReturnRequestStatus() - { + public Long getReturnRequestStatus() { return returnRequestStatus; } - public void setReturnRequestStatus(Long returnRequestStatus) - { + public void setReturnRequestStatus(Long returnRequestStatus) { this.returnRequestStatus = returnRequestStatus; } - public Bed withReturnRequestStatus(Long returnRequestStatus) - { + public Bed withReturnRequestStatus(Long returnRequestStatus) { setReturnRequestStatus(returnRequestStatus); return this; } - public String getSize() - { + public String getSize() { return size; } - public void setSize(String size) - { + public void setSize(String size) { this.size = size; } - public Bed withSize(String size) - { + public Bed withSize(String size) { setSize(size); return this; } - public String getName() - { + public String getName() { return name; } - public void setName(String name) - { + public void setName(String name) { this.name = name; } - public Bed withName(String name) - { + public Bed withName(String name) { setName(name); return this; } - public String getSerial() - { + public String getSerial() { return serial; } - public void setSerial(String serial) - { + public void setSerial(String serial) { this.serial = serial; } - public Bed withSerial(String serial) - { + public Bed withSerial(String serial) { setSerial(serial); return this; } - public Boolean isKidsBed() - { + public Boolean isKidsBed() { return kidsBed; } - public void setKidsBed(Boolean kidsBed) - { + public void setKidsBed(Boolean kidsBed) { this.kidsBed = kidsBed; } - public Bed withKidsBed(Boolean kidsBed) - { + public Bed withKidsBed(Boolean kidsBed) { setKidsBed(kidsBed); return this; } - public Boolean isDualSleep() - { + public Boolean isDualSleep() { return dualSleep; } - public void setDualSleep(Boolean dualSleep) - { + public void setDualSleep(Boolean dualSleep) { this.dualSleep = dualSleep; } - public Bed withDualSleep(Boolean dualSleep) - { + public Bed withDualSleep(Boolean dualSleep) { setDualSleep(dualSleep); return this; } - public String getBedId() - { + public String getBedId() { return bedId; } - public void setBedId(String bedId) - { + public void setBedId(String bedId) { this.bedId = bedId; } - public Bed withBedId(String bedId) - { + public Bed withBedId(String bedId) { setBedId(bedId); return this; } - public Long getStatus() - { + public Long getStatus() { return status; } - public void setStatus(Long status) - { + public void setStatus(Long status) { this.status = status; } - public Bed withStatus(Long status) - { + public Bed withStatus(Long status) { setStatus(status); return this; } - public String getSleeperLeftId() - { + public String getSleeperLeftId() { return sleeperLeftId; } - public void setSleeperLeftId(String sleeperLeftId) - { + public void setSleeperLeftId(String sleeperLeftId) { this.sleeperLeftId = sleeperLeftId; } - public Bed withSleeperLeftId(String sleeperLeftId) - { + public Bed withSleeperLeftId(String sleeperLeftId) { setSleeperLeftId(sleeperLeftId); return this; } - public String getVersion() - { + public String getVersion() { return version; } - public void setVersion(String version) - { + public void setVersion(String version) { this.version = version; } - public Bed withVersion(String version) - { + public Bed withVersion(String version) { setVersion(version); return this; } - public String getAccountId() - { + public String getAccountId() { return accountId; } - public void setAccountId(String accountId) - { + public void setAccountId(String accountId) { this.accountId = accountId; } - public Bed withAccountId(String accountId) - { + public Bed withAccountId(String accountId) { setAccountId(accountId); return this; } - public String getTimezone() - { + public String getTimezone() { return timezone; } - public void setTimezone(String timezone) - { + public void setTimezone(String timezone) { this.timezone = timezone; } - public Bed withTimezone(String timezone) - { + public Bed withTimezone(String timezone) { setTimezone(timezone); return this; } - public String getModel() - { + public String getModel() { return model; } - public void setModel(String model) - { + public void setModel(String model) { this.model = model; } - public Bed withModel(String model) - { + public Bed withModel(String model) { setModel(model); return this; } - public ZonedDateTime getPurchaseDate() - { + public ZonedDateTime getPurchaseDate() { return purchaseDate; } - public void setPurchaseDate(ZonedDateTime purchaseDate) - { + public void setPurchaseDate(ZonedDateTime purchaseDate) { this.purchaseDate = purchaseDate; } - public Bed withPurchaseDate(ZonedDateTime purchaseDate) - { + public Bed withPurchaseDate(ZonedDateTime purchaseDate) { setPurchaseDate(purchaseDate); return this; } - public String getMacAddress() - { + public String getMacAddress() { return macAddress; } - public void setMacAddress(String macAddress) - { + public void setMacAddress(String macAddress) { this.macAddress = macAddress; } - public Bed withMacAddress(String macAddress) - { + public Bed withMacAddress(String macAddress) { setMacAddress(macAddress); return this; } - public String getSku() - { + public String getSku() { return sku; } - public void setSku(String sku) - { + public void setSku(String sku) { this.sku = sku; } - public Bed withSku(String sku) - { + public Bed withSku(String sku) { setSku(sku); return this; } - public String getZipCode() - { + public String getZipCode() { return zipCode; } - public void setZipCode(String zipCode) - { + public void setZipCode(String zipCode) { this.zipCode = zipCode; } - public Bed withZipCode(String zipCode) - { + public Bed withZipCode(String zipCode) { setZipCode(zipCode); return this; } - public String getReference() - { + public String getReference() { return reference; } - public void setReference(String reference) - { + public void setReference(String reference) { this.reference = reference; } - public Bed withReference(String reference) - { + public Bed withReference(String reference) { setReference(reference); return this; } @Override - public int hashCode() - { + public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((bedId == null) ? 0 : bedId.hashCode()); @@ -391,38 +328,29 @@ public int hashCode() } @Override - public boolean equals(Object obj) - { - if (this == obj) - { + public boolean equals(Object obj) { + if (this == obj) { return true; } - if (obj == null) - { + if (obj == null) { return false; } - if (!(obj instanceof Bed)) - { + if (!(obj instanceof Bed)) { return false; } - Bed other = (Bed)obj; - if (bedId == null) - { - if (other.bedId != null) - { + Bed other = (Bed) obj; + if (bedId == null) { + if (other.bedId != null) { return false; } - } - else if (!bedId.equals(other.bedId)) - { + } else if (!bedId.equals(other.bedId)) { return false; } return true; } @Override - public String toString() - { + public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Bed [registrationDate="); builder.append(registrationDate); diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedSideStatus.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedSideStatus.java similarity index 50% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedSideStatus.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedSideStatus.java index b7e994b2bd6bb..72844ffa90026 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/BedSideStatus.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedSideStatus.java @@ -1,24 +1,25 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api.model; +package org.openhab.binding.sleepiq.internal.api.dto; import com.google.gson.annotations.SerializedName; -public class BedSideStatus -{ +/** + * The {@link BedSideStatus} holds the BedSideStatus response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class BedSideStatus { @SerializedName("isInBed") private Boolean inBed; private String alertDetailedMessage; @@ -27,109 +28,89 @@ public class BedSideStatus private TimeSince lastLink; private Integer pressure; // appears to be in kPa - public Boolean isInBed() - { + public Boolean isInBed() { return inBed; } - public void setInBed(Boolean inBed) - { + public void setInBed(Boolean inBed) { this.inBed = inBed; } - public BedSideStatus withInBed(Boolean inBed) - { + public BedSideStatus withInBed(Boolean inBed) { setInBed(inBed); return this; } - public String getAlertDetailedMessage() - { + public String getAlertDetailedMessage() { return alertDetailedMessage; } - public void setAlertDetailedMessage(String alertDetailedMessage) - { + public void setAlertDetailedMessage(String alertDetailedMessage) { this.alertDetailedMessage = alertDetailedMessage; } - public BedSideStatus withAlertDetailedMessage(String alertDetailedMessage) - { + public BedSideStatus withAlertDetailedMessage(String alertDetailedMessage) { setAlertDetailedMessage(alertDetailedMessage); return this; } - public Integer getSleepNumber() - { + public Integer getSleepNumber() { return sleepNumber; } - public void setSleepNumber(Integer sleepNumber) - { + public void setSleepNumber(Integer sleepNumber) { this.sleepNumber = sleepNumber; } - public BedSideStatus withSleepNumber(Integer sleepNumber) - { + public BedSideStatus withSleepNumber(Integer sleepNumber) { setSleepNumber(sleepNumber); return this; } - public Long getAlertId() - { + public Long getAlertId() { return alertId; } - public void setAlertId(Long alertId) - { + public void setAlertId(Long alertId) { this.alertId = alertId; } - public BedSideStatus withAlertId(Long alertId) - { + public BedSideStatus withAlertId(Long alertId) { setAlertId(alertId); return this; } - public TimeSince getLastLink() - { + public TimeSince getLastLink() { return lastLink; } - public void setLastLink(TimeSince lastLink) - { + public void setLastLink(TimeSince lastLink) { this.lastLink = lastLink; } - public BedSideStatus withLastLink(TimeSince lastLink) - { + public BedSideStatus withLastLink(TimeSince lastLink) { setLastLink(lastLink); return this; } - public Integer getPressure() - { + public Integer getPressure() { return pressure; } - public void setPressure(Integer pressure) - { + public void setPressure(Integer pressure) { this.pressure = pressure; } - public BedSideStatus withPressure(Integer pressure) - { + public BedSideStatus withPressure(Integer pressure) { setPressure(pressure); return this; } @Override - public int hashCode() - { + public int hashCode() { final int prime = 31; int result = 1; - result = prime * result - + ((alertDetailedMessage == null) ? 0 : alertDetailedMessage.hashCode()); + result = prime * result + ((alertDetailedMessage == null) ? 0 : alertDetailedMessage.hashCode()); result = prime * result + ((alertId == null) ? 0 : alertId.hashCode()); result = prime * result + ((inBed == null) ? 0 : inBed.hashCode()); result = prime * result + ((lastLink == null) ? 0 : lastLink.hashCode()); @@ -139,93 +120,64 @@ public int hashCode() } @Override - public boolean equals(Object obj) - { - if (this == obj) - { + public boolean equals(Object obj) { + if (this == obj) { return true; } - if (obj == null) - { + if (obj == null) { return false; } - if (!(obj instanceof BedSideStatus)) - { + if (!(obj instanceof BedSideStatus)) { return false; } - BedSideStatus other = (BedSideStatus)obj; - if (alertDetailedMessage == null) - { - if (other.alertDetailedMessage != null) - { + BedSideStatus other = (BedSideStatus) obj; + if (alertDetailedMessage == null) { + if (other.alertDetailedMessage != null) { return false; } - } - else if (!alertDetailedMessage.equals(other.alertDetailedMessage)) - { + } else if (!alertDetailedMessage.equals(other.alertDetailedMessage)) { return false; } - if (alertId == null) - { - if (other.alertId != null) - { + if (alertId == null) { + if (other.alertId != null) { return false; } - } - else if (!alertId.equals(other.alertId)) - { + } else if (!alertId.equals(other.alertId)) { return false; } - if (inBed == null) - { - if (other.inBed != null) - { + if (inBed == null) { + if (other.inBed != null) { return false; } - } - else if (!inBed.equals(other.inBed)) - { + } else if (!inBed.equals(other.inBed)) { return false; } - if (lastLink == null) - { - if (other.lastLink != null) - { + if (lastLink == null) { + if (other.lastLink != null) { return false; } - } - else if (!lastLink.equals(other.lastLink)) - { + } else if (!lastLink.equals(other.lastLink)) { return false; } - if (pressure == null) - { - if (other.pressure != null) - { + if (pressure == null) { + if (other.pressure != null) { return false; } - } - else if (!pressure.equals(other.pressure)) - { + } else if (!pressure.equals(other.pressure)) { return false; } - if (sleepNumber == null) - { - if (other.sleepNumber != null) - { + if (sleepNumber == null) { + if (other.sleepNumber != null) { return false; } - } - else if (!sleepNumber.equals(other.sleepNumber)) - { + } else if (!sleepNumber.equals(other.sleepNumber)) { return false; } return true; } @Override - public String toString() - { + public String toString() { StringBuilder builder = new StringBuilder(); builder.append("BedSideStatus [inBed="); builder.append(inBed); diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedStatus.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedStatus.java new file mode 100644 index 0000000000000..c5f25ab119bcf --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedStatus.java @@ -0,0 +1,122 @@ +/** + * 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.sleepiq.internal.api.dto; + +/** + * The {@link BedStatus} holds the BedStatus response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class BedStatus { + private Long status; + private String bedId; + private BedSideStatus leftSide; + private BedSideStatus rightSide; + + public Long getStatus() { + return status; + } + + public void setStatus(Long status) { + this.status = status; + } + + public BedStatus withStatus(Long status) { + setStatus(status); + return this; + } + + public String getBedId() { + return bedId; + } + + public void setBedId(String bedId) { + this.bedId = bedId; + } + + public BedStatus withBedId(String bedId) { + setBedId(bedId); + return this; + } + + public BedSideStatus getLeftSide() { + return leftSide; + } + + public void setLeftSide(BedSideStatus leftSide) { + this.leftSide = leftSide; + } + + public BedStatus withLeftSide(BedSideStatus leftSide) { + setLeftSide(leftSide); + return this; + } + + public BedSideStatus getRightSide() { + return rightSide; + } + + public void setRightSide(BedSideStatus rightSide) { + this.rightSide = rightSide; + } + + public BedStatus withRightSide(BedSideStatus rightSide) { + setRightSide(rightSide); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bedId == null) ? 0 : bedId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof BedStatus)) { + return false; + } + BedStatus other = (BedStatus) obj; + if (bedId == null) { + if (other.bedId != null) { + return false; + } + } else if (!bedId.equals(other.bedId)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("BedStatus [status="); + builder.append(status); + builder.append(", bedId="); + builder.append(bedId); + builder.append(", leftSide="); + builder.append(leftSide); + builder.append(", rightSide="); + builder.append(rightSide); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedsResponse.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedsResponse.java new file mode 100644 index 0000000000000..9cdaa4dab7ede --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/BedsResponse.java @@ -0,0 +1,76 @@ +/** + * 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.sleepiq.internal.api.dto; + +import java.util.List; + +/** + * The {@link BedsResponse} holds the BedsResponse response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class BedsResponse { + private List beds; + + public List getBeds() { + return beds; + } + + public void setBeds(List beds) { + this.beds = beds; + } + + public BedsResponse withBeds(List beds) { + setBeds(beds); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((beds == null) ? 0 : beds.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof BedsResponse)) { + return false; + } + BedsResponse other = (BedsResponse) obj; + if (beds == null) { + if (other.beds != null) { + return false; + } + } else if (!beds.equals(other.beds)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("BedsResponse [beds="); + builder.append(beds); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Error.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Error.java new file mode 100644 index 0000000000000..6865067ae1b29 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Error.java @@ -0,0 +1,94 @@ +/** + * 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.sleepiq.internal.api.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link Error} holds the Error response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class Error { + @SerializedName("Code") + private Long code; + @SerializedName("Message") + private String message; + + public Long getCode() { + return code; + } + + public void setCode(Long code) { + this.code = code; + } + + public Error withCode(Long code) { + setCode(code); + return this; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Error withMessage(String message) { + setMessage(message); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((code == null) ? 0 : code.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Error)) { + return false; + } + Error other = (Error) obj; + if (code == null) { + if (other.code != null) { + return false; + } + } else if (!code.equals(other.code)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Error [code="); + builder.append(code); + builder.append(", message="); + builder.append(message); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Failure.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Failure.java new file mode 100644 index 0000000000000..028055c4b3cbe --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Failure.java @@ -0,0 +1,77 @@ +/** + * 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.sleepiq.internal.api.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link Failure} holds the Failure response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class Failure { + @SerializedName("Error") + private Error error; + + public Error getError() { + return error; + } + + public void setError(Error error) { + this.error = error; + } + + public Failure withError(Error error) { + setError(error); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((error == null) ? 0 : error.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Failure)) { + return false; + } + Failure other = (Failure) obj; + if (error == null) { + if (other.error != null) { + return false; + } + } else if (!error.equals(other.error)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Failure [error="); + builder.append(error); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/FamilyStatusResponse.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/FamilyStatusResponse.java new file mode 100644 index 0000000000000..b0f90c12dd5ac --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/FamilyStatusResponse.java @@ -0,0 +1,76 @@ +/** + * 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.sleepiq.internal.api.dto; + +import java.util.List; + +/** + * The {@link FamilyStatusResponse} holds the FamilyStatusResponse response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class FamilyStatusResponse { + private List beds; + + public List getBeds() { + return beds; + } + + public void setBeds(List beds) { + this.beds = beds; + } + + public FamilyStatusResponse withBeds(List beds) { + setBeds(beds); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((beds == null) ? 0 : beds.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof FamilyStatusResponse)) { + return false; + } + FamilyStatusResponse other = (FamilyStatusResponse) obj; + if (beds == null) { + if (other.beds != null) { + return false; + } + } else if (!beds.equals(other.beds)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("FamilyStatus [beds="); + builder.append(beds); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/LoginInfo.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/LoginInfo.java similarity index 51% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/LoginInfo.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/LoginInfo.java index 7cc35096fb6e0..9f51fc92f7c8a 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/LoginInfo.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/LoginInfo.java @@ -1,167 +1,138 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api.model; +package org.openhab.binding.sleepiq.internal.api.dto; -public class LoginInfo -{ +/** + * The {@link LoginInfo} holds the LoginInfo response from the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class LoginInfo { private String userId; private String key; private Long registrationState; private Long edpLoginStatus; private String edpLoginMessage; - public String getUserId() - { + public String getUserId() { return userId; } - public void setUserId(String userId) - { + public void setUserId(String userId) { this.userId = userId; } - public LoginInfo withUserId(String userId) - { + public LoginInfo withUserId(String userId) { setUserId(userId); return this; } - public String getKey() - { + public String getKey() { return key; } - public void setKey(String key) - { + public void setKey(String key) { this.key = key; } - public LoginInfo withKey(String key) - { + public LoginInfo withKey(String key) { setKey(key); return this; } - public Long getRegistrationState() - { + public Long getRegistrationState() { return registrationState; } - public void setRegistrationState(Long registrationState) - { + public void setRegistrationState(Long registrationState) { this.registrationState = registrationState; } - public LoginInfo withRegistrationState(Long registrationState) - { + public LoginInfo withRegistrationState(Long registrationState) { setRegistrationState(registrationState); return this; } - public Long getEdpLoginStatus() - { + public Long getEdpLoginStatus() { return edpLoginStatus; } - public void setEdpLoginStatus(Long edpLoginStatus) - { + public void setEdpLoginStatus(Long edpLoginStatus) { this.edpLoginStatus = edpLoginStatus; } - public LoginInfo withEdpLoginStatus(Long edpLoginStatus) - { + public LoginInfo withEdpLoginStatus(Long edpLoginStatus) { setEdpLoginStatus(edpLoginStatus); return this; } - public String getEdpLoginMessage() - { + public String getEdpLoginMessage() { return edpLoginMessage; } - public void setEdpLoginMessage(String edpLoginMessage) - { + public void setEdpLoginMessage(String edpLoginMessage) { this.edpLoginMessage = edpLoginMessage; } - public LoginInfo withEdpLoginMessage(String edpLoginMessage) - { + public LoginInfo withEdpLoginMessage(String edpLoginMessage) { setEdpLoginMessage(edpLoginMessage); return this; } @Override - public int hashCode() - { + public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + (int)(registrationState ^ (registrationState >>> 32)); + result = prime * result + (int) (registrationState ^ (registrationState >>> 32)); result = prime * result + ((userId == null) ? 0 : userId.hashCode()); return result; } @Override - public boolean equals(Object obj) - { - if (this == obj) - { + public boolean equals(Object obj) { + if (this == obj) { return true; } - if (obj == null) - { + if (obj == null) { return false; } - if (!(obj instanceof LoginInfo)) - { + if (!(obj instanceof LoginInfo)) { return false; } - LoginInfo other = (LoginInfo)obj; - if (key == null) - { - if (other.key != null) - { + LoginInfo other = (LoginInfo) obj; + if (key == null) { + if (other.key != null) { return false; } - } - else if (!key.equals(other.key)) - { + } else if (!key.equals(other.key)) { return false; } - if (!registrationState.equals(other.registrationState)) - { + if (!registrationState.equals(other.registrationState)) { return false; } - if (userId == null) - { - if (other.userId != null) - { + if (userId == null) { + if (other.userId != null) { return false; } - } - else if (!userId.equals(other.userId)) - { + } else if (!userId.equals(other.userId)) { return false; } return true; } @Override - public String toString() - { + public String toString() { StringBuilder builder = new StringBuilder(); builder.append("LoginInfo [userId="); builder.append(userId); diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/LoginRequest.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/LoginRequest.java new file mode 100644 index 0000000000000..6cbe4d1121902 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/LoginRequest.java @@ -0,0 +1,98 @@ +/** + * 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.sleepiq.internal.api.dto; + +/** + * The {@link LoginRequest} holds the Login request sent to the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +public class LoginRequest { + private String login; + private String password; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public LoginRequest withLogin(String login) { + setLogin(login); + return this; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public LoginRequest withPassword(String password) { + setPassword(password); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((login == null) ? 0 : login.hashCode()); + result = prime * result + ((password == null) ? 0 : password.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof LoginRequest)) { + return false; + } + LoginRequest other = (LoginRequest) obj; + if (login == null) { + if (other.login != null) { + return false; + } + } else if (!login.equals(other.login)) { + return false; + } + if (password == null) { + if (other.password != null) { + return false; + } + } else if (!password.equals(other.password)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("LoginRequest [login="); + builder.append(login); + builder.append(", password="); + builder.append(password); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/PauseModeRequest.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/PauseModeRequest.java new file mode 100644 index 0000000000000..f536d800c1693 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/PauseModeRequest.java @@ -0,0 +1,74 @@ +/** + * 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.sleepiq.internal.api.dto; + +/** + * The {@link PauseModeRequest} holds the request to pause the bed data collection. + * + * @author Mark Hilbush - Initial contribution + */ +public class PauseModeRequest { + private String mode; + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public PauseModeRequest withMode(String mode) { + setMode(mode); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mode == null) ? 0 : mode.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof PauseModeRequest)) { + return false; + } + PauseModeRequest other = (PauseModeRequest) obj; + if (mode == null) { + if (other.mode != null) { + return false; + } + } else if (!mode.equals(other.mode)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("SleepNumberRequest [mode="); + builder.append(mode); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/PauseModeResponse.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/PauseModeResponse.java new file mode 100644 index 0000000000000..966093bde93f6 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/PauseModeResponse.java @@ -0,0 +1,114 @@ +/** + * 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.sleepiq.internal.api.dto; + +/** + * The {@link PauseModeResponse} holds the response to the request to pause the bed data collection. + * + * @author Mark Hilbush - Initial contribution + */ +public class PauseModeResponse { + private String accountId; + private String bedId; + private String pauseMode; + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public PauseModeResponse withAccountId(String accountId) { + setAccountId(accountId); + return this; + } + + public String getBedId() { + return bedId; + } + + public void setBedId(String bedId) { + this.bedId = bedId; + } + + public PauseModeResponse withBedId(String bedId) { + setBedId(bedId); + return this; + } + + public String getPauseMode() { + return pauseMode; + } + + public void setPauseMode(String pauseMode) { + this.pauseMode = pauseMode; + } + + public PauseModeResponse withPauseMode(String pauseMode) { + setPauseMode(pauseMode); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((bedId == null) ? 0 : bedId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof PauseModeResponse)) { + return false; + } + PauseModeResponse other = (PauseModeResponse) obj; + if (accountId == null) { + if (other.accountId != null) { + return false; + } + } else if (!accountId.equals(other.accountId)) { + return false; + } + if (bedId == null) { + if (other.bedId != null) { + return false; + } + } else if (!bedId.equals(other.bedId)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("PauseMode [accountId="); + builder.append(accountId); + builder.append(", bedId="); + builder.append(bedId); + builder.append(", pauseMode="); + builder.append(pauseMode); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataDay.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataDay.java new file mode 100644 index 0000000000000..72ee26f13f322 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataDay.java @@ -0,0 +1,47 @@ +/** + * 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.sleepiq.internal.api.dto; + +import java.time.LocalDate; +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link SleepDataDay} holds the information for a daily sleep session. + * + * @author Mark Hilbush - Initial contribution + */ +public class SleepDataDay { + + @SerializedName("date") + private LocalDate date; + + @SerializedName("message") + private String dailyMessage; + + @SerializedName("sessions") + private List dailySessions; + + public LocalDate getDate() { + return date; + } + + public String getMessage() { + return dailyMessage; + } + + public List getDailySessions() { + return dailySessions; + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataResponse.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataResponse.java new file mode 100644 index 0000000000000..c8a36e72d5d80 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataResponse.java @@ -0,0 +1,115 @@ +/** + * 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.sleepiq.internal.api.dto; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link SleepDataResponse} holds the response to a request for sleep data. + * + * @author Mark Hilbush - Initial contribution + */ +public class SleepDataResponse { + @SerializedName("sleeperId") + private String sleeperId; + + @SerializedName("avgSleepIQ") + private Integer averageSleepIQ; + + @SerializedName("avgHeartRate") + private Integer averageHeartRate; + + @SerializedName("avgRespirationRate") + private Integer averageResperationRate; + + @SerializedName("inBed") + private Integer totalInBedSeconds; + + @SerializedName("outOfBed") + private Integer totalOutOfBedSeconds; + + @SerializedName("restful") + private Integer totalRestfulSeconds; + + @SerializedName("restless") + private Integer totalRestlessSeconds; + + @SerializedName("sleepData") + private List sleepDataDays; + + public String getSleeperId() { + return sleeperId; + } + + public Integer getAverageSleepIQ() { + return averageSleepIQ; + } + + public Integer getAverageHeartRate() { + return averageHeartRate; + } + + public Integer getAverageRespirationRate() { + return averageResperationRate; + } + + public Integer getTotalSleepSessionTime() { + return totalInBedSeconds + totalOutOfBedSeconds; + } + + public Integer getTotalInBedSeconds() { + return totalInBedSeconds; + } + + public Integer getTotalOutOfBedSeconds() { + return totalOutOfBedSeconds; + } + + public Integer getTotalRestfulSeconds() { + return totalRestfulSeconds; + } + + public Integer getTotalRestlessSeconds() { + return totalRestlessSeconds; + } + + public List getSleepDataDays() { + return sleepDataDays; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("SleepDataResponse [sleepData="); + builder.append("averageSleepIQ="); + builder.append(averageSleepIQ); + builder.append(", averageHeartRate="); + builder.append(averageHeartRate); + builder.append(", averageResperationRate="); + builder.append(averageResperationRate); + builder.append(", totalInBedSeconds="); + builder.append(totalInBedSeconds); + builder.append(", totalOutOfBedSeconds="); + builder.append(totalOutOfBedSeconds); + builder.append(", totalRestfulSeconds="); + builder.append(totalRestfulSeconds); + builder.append(", totalRestlessSeconds="); + builder.append(totalRestlessSeconds); + builder.append(", totalSleepSessionTime="); + builder.append(getTotalSleepSessionTime()); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataSession.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataSession.java new file mode 100644 index 0000000000000..be6cd94de7f8b --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepDataSession.java @@ -0,0 +1,86 @@ +/** + * 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.sleepiq.internal.api.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link SleepDataSession} holds the data for a sleep session. + * + * @author Mark Hilbush - Initial contribution + */ +public class SleepDataSession { + + @SerializedName("avgSleepIQ") + private Integer sessionAverageSleepIQ; + + @SerializedName("avgHeartRate") + private Integer sessionAverageHeartRate; + + @SerializedName("avgRespirationRate") + private Integer sessionAverageRespirationRate; + + @SerializedName("isFinalized") + private boolean sessionIsFinalized; + + @SerializedName("totalSleepSessionTime") + private Integer sessionTotalSeconds; + + @SerializedName("inBed") + private Integer sessionInBedSeconds; + + @SerializedName("outOfBed") + private Integer sessionOutOfBedSeconds; + + @SerializedName("restful") + private Integer sessionRestfulSeconds; + + @SerializedName("restless") + private Integer sessionRestlessSeconds; + + public Integer getSessionAverageSleepIQ() { + return sessionAverageSleepIQ; + } + + public Integer getSessionAverageHeartRate() { + return sessionAverageHeartRate; + } + + public Integer getSessionAverageRespirationRate() { + return sessionAverageRespirationRate; + } + + public Boolean sessionIsFinalized() { + return sessionIsFinalized; + } + + public Integer getSessionTotalSeconds() { + return sessionTotalSeconds; + } + + public Integer getSessionInBedSeconds() { + return sessionInBedSeconds; + } + + public Integer getSessionOutOfBedSeconds() { + return sessionOutOfBedSeconds; + } + + public Integer getSessionRestfulSeconds() { + return sessionRestfulSeconds; + } + + public Integer getSessionRestlessSeconds() { + return sessionRestlessSeconds; + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepNumberRequest.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepNumberRequest.java new file mode 100644 index 0000000000000..b07a2ffe53f55 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepNumberRequest.java @@ -0,0 +1,125 @@ +/** + * 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.sleepiq.internal.api.dto; + +import org.openhab.binding.sleepiq.internal.api.enums.Side; + +/** + * The {@link SleepNumberRequest} holds the information used to set the sleep number + * for a bed side. + * + * @author Gregory Moyer - Initial contribution + */ +public class SleepNumberRequest { + private String bedId; + private Integer sleepNumber; + private Side side; + + public String getBedId() { + return bedId; + } + + public void setBedId(String bedId) { + this.bedId = bedId; + } + + public SleepNumberRequest withBedId(String bedId) { + setBedId(bedId); + return this; + } + + public Integer getSleepNumber() { + return sleepNumber; + } + + public void setSleepNumber(Integer sleepNumber) { + this.sleepNumber = sleepNumber; + } + + public SleepNumberRequest withSleepNumber(Integer sleepNumber) { + setSleepNumber(sleepNumber); + return this; + } + + public Side getSide() { + return side; + } + + public void setSide(Side side) { + this.side = side; + } + + public SleepNumberRequest withSide(Side side) { + setSide(side); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bedId == null) ? 0 : bedId.hashCode()); + result = prime * result + ((sleepNumber == null) ? 0 : sleepNumber.hashCode()); + result = prime * result + ((side == null) ? 0 : side.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SleepNumberRequest)) { + return false; + } + SleepNumberRequest other = (SleepNumberRequest) obj; + if (bedId == null) { + if (other.bedId != null) { + return false; + } + } else if (!bedId.equals(other.bedId)) { + return false; + } + if (sleepNumber == null) { + if (other.sleepNumber != null) { + return false; + } + } else if (sleepNumber.equals(other.sleepNumber)) { + return false; + } + if (side == null) { + if (other.side != null) { + return false; + } + } else if (!side.equals(other.side)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("SleepNumberRequest [bedId="); + builder.append(bedId); + builder.append(", sleepNumber="); + builder.append(sleepNumber); + builder.append(", side="); + builder.append(side); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Sleeper.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Sleeper.java similarity index 58% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Sleeper.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Sleeper.java index 8a9ff0adfce52..d10b6c4e636ff 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/Sleeper.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/Sleeper.java @@ -1,24 +1,28 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api.model; +package org.openhab.binding.sleepiq.internal.api.dto; + +import org.openhab.binding.sleepiq.internal.api.enums.Side; import com.google.gson.annotations.SerializedName; -public class Sleeper -{ +/** + * The {@link Sleeper} holds the information about the sleeper that's + * associated with a bed side. + * + * @author Gregory Moyer - Initial contribution + */ +public class Sleeper { private String firstName; private Boolean active; private Boolean emailValidated; @@ -44,379 +48,309 @@ public class Sleeper private String email; private String avatar; private String lastLogin; // should be ZonedDateTime but provider passes string "null" when missing - private Integer side; // 0=left; 1=right + private Side side; // 0=left; 1=right - public String getFirstName() - { + public String getFirstName() { return firstName; } - public void setFirstName(String firstName) - { + public void setFirstName(String firstName) { this.firstName = firstName; } - public Sleeper withFirstName(String firstName) - { + public Sleeper withFirstName(String firstName) { setFirstName(firstName); return this; } - public Boolean isActive() - { + public Boolean isActive() { return active; } - public void setActive(Boolean active) - { + public void setActive(Boolean active) { this.active = active; } - public Sleeper withActive(Boolean active) - { + public Sleeper withActive(Boolean active) { setActive(active); return this; } - public Boolean isEmailValidated() - { + public Boolean isEmailValidated() { return emailValidated; } - public void setEmailValidated(Boolean emailValidated) - { + public void setEmailValidated(Boolean emailValidated) { this.emailValidated = emailValidated; } - public Sleeper withEmailValidated(Boolean emailValidated) - { + public Sleeper withEmailValidated(Boolean emailValidated) { setEmailValidated(emailValidated); return this; } - public Boolean isChild() - { + public Boolean isChild() { return child; } - public void setChild(Boolean child) - { + public void setChild(Boolean child) { this.child = child; } - public Sleeper withChild(Boolean child) - { + public Sleeper withChild(Boolean child) { setChild(child); return this; } - public String getBedId() - { + public String getBedId() { return bedId; } - public void setBedId(String bedId) - { + public void setBedId(String bedId) { this.bedId = bedId; } - public Sleeper withBedId(String bedId) - { + public Sleeper withBedId(String bedId) { setBedId(bedId); return this; } - public String getBirthYear() - { + public String getBirthYear() { return birthYear; } - public void setBirthYear(String birthYear) - { + public void setBirthYear(String birthYear) { this.birthYear = birthYear; } - public Sleeper withBirthYear(String birthYear) - { + public Sleeper withBirthYear(String birthYear) { setBirthYear(birthYear); return this; } - public String getZipCode() - { + public String getZipCode() { return zipCode; } - public void setZipCode(String zipCode) - { + public void setZipCode(String zipCode) { this.zipCode = zipCode; } - public Sleeper withZipCode(String zipCode) - { + public Sleeper withZipCode(String zipCode) { setZipCode(zipCode); return this; } - public String getTimezone() - { + public String getTimezone() { return timezone; } - public void setTimezone(String timezone) - { + public void setTimezone(String timezone) { this.timezone = timezone; } - public Sleeper withTimezone(String timezone) - { + public Sleeper withTimezone(String timezone) { setTimezone(timezone); return this; } - public Boolean isMale() - { + public Boolean isMale() { return male; } - public void setMale(Boolean male) - { + public void setMale(Boolean male) { this.male = male; } - public Sleeper withMale(Boolean male) - { + public Sleeper withMale(Boolean male) { setMale(male); return this; } - public Integer getWeight() - { + public Integer getWeight() { return weight; } - public void setWeight(Integer weight) - { + public void setWeight(Integer weight) { this.weight = weight; } - public Sleeper withWeight(Integer weight) - { + public Sleeper withWeight(Integer weight) { setWeight(weight); return this; } - public String getDuration() - { + public String getDuration() { return duration; } - public void setDuration(String duration) - { + public void setDuration(String duration) { this.duration = duration; } - public Sleeper withDuration(String duration) - { + public Sleeper withDuration(String duration) { setDuration(duration); return this; } - public String getSleeperId() - { + public String getSleeperId() { return sleeperId; } - public void setSleeperId(String sleeperId) - { + public void setSleeperId(String sleeperId) { this.sleeperId = sleeperId; } - public Sleeper withSleeperId(String sleeperId) - { + public Sleeper withSleeperId(String sleeperId) { setSleeperId(sleeperId); return this; } - public Integer getHeight() - { + public Integer getHeight() { return height; } - public void setHeight(Integer height) - { + public void setHeight(Integer height) { this.height = height; } - public Sleeper withHeight(Integer height) - { + public Sleeper withHeight(Integer height) { setHeight(height); return this; } - public Long getLicenseVersion() - { + public Long getLicenseVersion() { return licenseVersion; } - public void setLicenseVersion(Long licenseVersion) - { + public void setLicenseVersion(Long licenseVersion) { this.licenseVersion = licenseVersion; } - public Sleeper withLicenseVersion(Long licenseVersion) - { + public Sleeper withLicenseVersion(Long licenseVersion) { setLicenseVersion(licenseVersion); return this; } - public String getUsername() - { + public String getUsername() { return username; } - public void setUsername(String username) - { + public void setUsername(String username) { this.username = username; } - public Sleeper withUsername(String username) - { + public Sleeper withUsername(String username) { setUsername(username); return this; } - public Integer getBirthMonth() - { + public Integer getBirthMonth() { return birthMonth; } - public void setBirthMonth(Integer birthMonth) - { + public void setBirthMonth(Integer birthMonth) { this.birthMonth = birthMonth; } - public Sleeper withBirthMonth(Integer birthMonth) - { + public Sleeper withBirthMonth(Integer birthMonth) { setBirthMonth(birthMonth); return this; } - public Integer getSleepGoal() - { + public Integer getSleepGoal() { return sleepGoal; } - public void setSleepGoal(Integer sleepGoal) - { + public void setSleepGoal(Integer sleepGoal) { this.sleepGoal = sleepGoal; } - public Sleeper withSleepGoal(Integer sleepGoal) - { + public Sleeper withSleepGoal(Integer sleepGoal) { setSleepGoal(sleepGoal); return this; } - public Boolean isAccountOwner() - { + public Boolean isAccountOwner() { return accountOwner; } - public void setAccountOwner(Boolean accountOwner) - { + public void setAccountOwner(Boolean accountOwner) { this.accountOwner = accountOwner; } - public Sleeper withAccountOwner(Boolean accountOwner) - { + public Sleeper withAccountOwner(Boolean accountOwner) { setAccountOwner(accountOwner); return this; } - public String getAccountId() - { + public String getAccountId() { return accountId; } - public void setAccountId(String accountId) - { + public void setAccountId(String accountId) { this.accountId = accountId; } - public Sleeper withAccountId(String accountId) - { + public Sleeper withAccountId(String accountId) { setAccountId(accountId); return this; } - public String getEmail() - { + public String getEmail() { return email; } - public void setEmail(String email) - { + public void setEmail(String email) { this.email = email; } - public Sleeper withEmail(String email) - { + public Sleeper withEmail(String email) { setEmail(email); return this; } - public String getAvatar() - { + public String getAvatar() { return avatar; } - public void setAvatar(String avatar) - { + public void setAvatar(String avatar) { this.avatar = avatar; } - public Sleeper withAvatar(String avatar) - { + public Sleeper withAvatar(String avatar) { setAvatar(avatar); return this; } - public String getLastLogin() - { + public String getLastLogin() { return lastLogin; } - public void setLastLogin(String lastLogin) - { + public void setLastLogin(String lastLogin) { this.lastLogin = lastLogin; } - public Sleeper withLastLogin(String lastLogin) - { + public Sleeper withLastLogin(String lastLogin) { setLastLogin(lastLogin); return this; } - public Integer getSide() - { + public Side getSide() { return side; } - public void setSide(Integer side) - { + public void setSide(Side side) { this.side = side; } - public Sleeper withSide(Integer side) - { + public Sleeper withSide(Side side) { setSide(side); return this; } @Override - public int hashCode() - { + public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((sleeperId == null) ? 0 : sleeperId.hashCode()); @@ -424,38 +358,29 @@ public int hashCode() } @Override - public boolean equals(Object obj) - { - if (this == obj) - { + public boolean equals(Object obj) { + if (this == obj) { return true; } - if (obj == null) - { + if (obj == null) { return false; } - if (!(obj instanceof Sleeper)) - { + if (!(obj instanceof Sleeper)) { return false; } - Sleeper other = (Sleeper)obj; - if (sleeperId == null) - { - if (other.sleeperId != null) - { + Sleeper other = (Sleeper) obj; + if (sleeperId == null) { + if (other.sleeperId != null) { return false; } - } - else if (!sleeperId.equals(other.sleeperId)) - { + } else if (!sleeperId.equals(other.sleeperId)) { return false; } return true; } @Override - public String toString() - { + public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Sleeper [firstName="); builder.append(firstName); diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepersResponse.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepersResponse.java new file mode 100644 index 0000000000000..d482d4712be88 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/SleepersResponse.java @@ -0,0 +1,76 @@ +/** + * 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.sleepiq.internal.api.dto; + +import java.util.List; + +/** + * The {@link SleeperResponse} holds the information about the sleepers assigned to the bed sides. + * + * @author Gregory Moyer - Initial contribution + */ +public class SleepersResponse { + private List sleepers; + + public List getSleepers() { + return sleepers; + } + + public void setSleepers(List sleepers) { + this.sleepers = sleepers; + } + + public SleepersResponse withSleepers(List sleepers) { + setSleepers(sleepers); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((sleepers == null) ? 0 : sleepers.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SleepersResponse)) { + return false; + } + SleepersResponse other = (SleepersResponse) obj; + if (sleepers == null) { + if (other.sleepers != null) { + return false; + } + } else if (!sleepers.equals(other.sleepers)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("SleepersResponse [sleepers="); + builder.append(sleepers); + builder.append("]"); + return builder.toString(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/TimeSince.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/TimeSince.java similarity index 83% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/TimeSince.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/TimeSince.java index c6c8704861863..3b83451cfc278 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/model/TimeSince.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/dto/TimeSince.java @@ -1,19 +1,16 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api.model; +package org.openhab.binding.sleepiq.internal.api.dto; import java.time.Duration; import java.util.Objects; @@ -21,6 +18,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * The {@link TimeSince} converts from "nn d hh:mm:ss" to Duration + * + * @author Gregory Moyer - Initial contribution + */ public class TimeSince { private static final Pattern PATTERN = Pattern.compile("(([0-9]+) d )?([0-9]{2}):([0-9]{2}):([0-9]{2})", Pattern.CASE_INSENSITIVE); diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/enums/Side.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/enums/Side.java new file mode 100644 index 0000000000000..51763626bd750 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/enums/Side.java @@ -0,0 +1,55 @@ +/** + * 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.sleepiq.internal.api.enums; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link Side} represents the possible sides of the bed (i.e. left and right). + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public enum Side { + LEFT(0), + RIGHT(1); + + private final int side; + + Side(final int side) { + this.side = side; + } + + public int value() { + return side; + } + + public static Side forValue(int value) { + for (Side s : Side.values()) { + if (s.side == value) { + return s; + } + } + throw new IllegalArgumentException("Invalid side: " + value); + } + + public static Side convertFromGroup(@Nullable String channelGroup) { + return "left".equalsIgnoreCase(channelGroup) ? Side.LEFT : Side.RIGHT; + } + + @Override + public String toString() { + return side == 0 ? LEFT.name() : RIGHT.name(); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/enums/SleepDataInterval.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/enums/SleepDataInterval.java new file mode 100644 index 0000000000000..a73f0f6969e18 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/enums/SleepDataInterval.java @@ -0,0 +1,52 @@ +/** + * 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.sleepiq.internal.api.enums; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link SleepDataInterval} represents the time periods that can be + * used for sleep data requests. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public enum SleepDataInterval { + DAY("D1"), + WEEK("W1"), + MONTH("M1"); + + private final String interval; + + SleepDataInterval(final String interval) { + this.interval = interval; + } + + public String value() { + return interval; + } + + public static SleepDataInterval forValue(String value) { + for (SleepDataInterval s : SleepDataInterval.values()) { + if (s.interval.equals(value)) { + return s; + } + } + throw new IllegalArgumentException("Invalid sleep data interval: " + value); + } + + @Override + public String toString() { + return interval; + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/Endpoints.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/Endpoints.java new file mode 100644 index 0000000000000..c806e3ec644aa --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/Endpoints.java @@ -0,0 +1,67 @@ +/** + * 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.sleepiq.internal.api.impl; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link Endpoints} class contains all endpoints for the sleepiq API. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public class Endpoints { + private static final String LOGIN = "/rest/login"; + private static final String BED = "/rest/bed"; + private static final String SLEEPER = "/rest/sleeper"; + private static final String FAMILY_STATUS = "/rest/bed/familyStatus"; + private static final String PAUSE_MODE = "/rest/bed/%s/pauseMode"; + private static final String SLEEP_DATA = "/rest/sleepData"; + private static final String SET_SLEEP_NUMBER = "/rest/bed/%s/sleepNumber"; + private static final String SET_PAUSE_MODE = "/rest/bed/%s/pauseMode"; + + public static String login() { + return LOGIN; + } + + public static String bed() { + return BED; + } + + public static String sleeper() { + return SLEEPER; + } + + public static String familyStatus() { + return FAMILY_STATUS; + } + + public static String pauseMode(String bedId) { + return String.format(PAUSE_MODE, bedId); + } + + public static String sleepData() { + return SLEEP_DATA; + } + + public static String setSleepNumber(String bedId) { + return String.format(SET_SLEEP_NUMBER, bedId); + } + + public static String setPauseMode(String bedId) { + return String.format(SET_PAUSE_MODE, bedId); + } + + private Endpoints() { + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/GsonGenerator.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/GsonGenerator.java new file mode 100644 index 0000000000000..64e99adb24bb2 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/GsonGenerator.java @@ -0,0 +1,55 @@ +/** + * 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.sleepiq.internal.api.impl; + +import java.time.ZonedDateTime; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.sleepiq.internal.api.dto.SleepNumberRequest; +import org.openhab.binding.sleepiq.internal.api.dto.TimeSince; +import org.openhab.binding.sleepiq.internal.api.enums.Side; +import org.openhab.binding.sleepiq.internal.api.impl.typeadapters.SideTypeAdapter; +import org.openhab.binding.sleepiq.internal.api.impl.typeadapters.SleepNumberRequestAdapter; +import org.openhab.binding.sleepiq.internal.api.impl.typeadapters.TimeSinceTypeAdapter; +import org.openhab.binding.sleepiq.internal.api.impl.typeadapters.ZonedDateTimeTypeAdapter; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * The {@link GsonGenerator} class creates the GSON generator object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public class GsonGenerator { + public static Gson create() { + return create(false); + } + + public static Gson create(boolean prettyPrint) { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeTypeAdapter()); + builder.registerTypeAdapter(TimeSince.class, new TimeSinceTypeAdapter()); + builder.registerTypeAdapter(SleepNumberRequest.class, new SleepNumberRequestAdapter()); + builder.registerTypeAdapter(Side.class, new SideTypeAdapter()); + + if (prettyPrint) { + builder.setPrettyPrinting(); + } + return builder.create(); + } + + private GsonGenerator() { + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/SleepIQImpl.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/SleepIQImpl.java new file mode 100644 index 0000000000000..587ee30854356 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/SleepIQImpl.java @@ -0,0 +1,322 @@ +/** + * 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.sleepiq.internal.api.impl; + +import java.net.CookieStore; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.sleepiq.internal.api.CommunicationException; +import org.openhab.binding.sleepiq.internal.api.Configuration; +import org.openhab.binding.sleepiq.internal.api.LoginException; +import org.openhab.binding.sleepiq.internal.api.ResponseFormatException; +import org.openhab.binding.sleepiq.internal.api.SleepIQ; +import org.openhab.binding.sleepiq.internal.api.UnauthorizedException; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; +import org.openhab.binding.sleepiq.internal.api.dto.BedsResponse; +import org.openhab.binding.sleepiq.internal.api.dto.Failure; +import org.openhab.binding.sleepiq.internal.api.dto.FamilyStatusResponse; +import org.openhab.binding.sleepiq.internal.api.dto.LoginInfo; +import org.openhab.binding.sleepiq.internal.api.dto.LoginRequest; +import org.openhab.binding.sleepiq.internal.api.dto.PauseModeResponse; +import org.openhab.binding.sleepiq.internal.api.dto.SleepDataResponse; +import org.openhab.binding.sleepiq.internal.api.dto.SleepNumberRequest; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; +import org.openhab.binding.sleepiq.internal.api.dto.SleepersResponse; +import org.openhab.binding.sleepiq.internal.api.enums.Side; +import org.openhab.binding.sleepiq.internal.api.enums.SleepDataInterval; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link SleepIQImpl} class handles all interactions with the sleepiq service. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public class SleepIQImpl implements SleepIQ { + private static final String PARAM_KEY = "_k"; + private static final String USER_AGENT = "SleepIQ/1593766370 CFNetwork/1185.2 Darwin/20.0.0"; + + private static final Gson GSON = GsonGenerator.create(false); + + private final Logger logger = LoggerFactory.getLogger(SleepIQImpl.class); + + private final HttpClient httpClient; + private final CookieStore cookieStore; + + protected final Configuration config; + + private final LoginRequest loginRequest = new LoginRequest(); + private volatile @Nullable LoginInfo loginInfo; + + public SleepIQImpl(Configuration config, HttpClient httpClient) { + this.config = config; + this.httpClient = httpClient; + cookieStore = httpClient.getCookieStore(); + loginRequest.setLogin(config.getUsername()); + loginRequest.setPassword(config.getPassword()); + } + + @Override + public void shutdown() { + cookieStore.removeAll(); + } + + @Override + public @Nullable LoginInfo login() throws LoginException, UnauthorizedException { + logger.trace("SleepIQ: login: loginInfo={}", loginInfo); + if (loginInfo == null) { + synchronized (this) { + if (loginInfo == null) { + Request request = httpClient.newRequest(config.getBaseUri()).path(Endpoints.login()) + .agent(USER_AGENT).header(HttpHeader.CONTENT_TYPE, "application/json") + .timeout(10, TimeUnit.SECONDS).method(HttpMethod.PUT) + .content(new StringContentProvider(GSON.toJson(loginRequest)), "application/json"); + logger.trace("SleepIQ: login: request url={}", request.getURI()); + + try { + ContentResponse response = request.send(); + logger.debug("SleepIQ: login: status={}, content={}", response.getStatus(), + response.getContentAsString()); + if (isUnauthorized(response)) { + Failure failure = GSON.fromJson(response.getContentAsString(), Failure.class); + String message = failure != null ? failure.getError().getMessage() : "Login unauthorized"; + throw new UnauthorizedException(message); + } + if (isNotOk(response)) { + Failure failure = GSON.fromJson(response.getContentAsString(), Failure.class); + String message = failure != null ? failure.getError().getMessage() : "Login failed"; + throw new LoginException(message); + } + try { + loginInfo = GSON.fromJson(response.getContentAsString(), LoginInfo.class); + } catch (JsonSyntaxException e) { + throw new LoginException("Failed to parse 'login' response"); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + logger.info("SleepIQ: login: Login failed message={}", e.getMessage(), e); + throw new LoginException("Problem communicating with SleepIQ cloud service"); + } + } + } + } + return loginInfo; + } + + @Override + public List getBeds() throws LoginException, CommunicationException, ResponseFormatException { + try { + String contentResponse = cloudRequest(Endpoints.bed()); + BedsResponse response = GSON.fromJson(contentResponse, BedsResponse.class); + if (response != null) { + return response.getBeds(); + } else { + throw new ResponseFormatException("Failed to get a valid 'beds' response from cloud"); + } + } catch (JsonSyntaxException e) { + throw new ResponseFormatException("Failed to parse 'beds' response"); + } + } + + @Override + public FamilyStatusResponse getFamilyStatus() + throws LoginException, ResponseFormatException, CommunicationException { + try { + String contentResponse = cloudRequest(Endpoints.familyStatus()); + FamilyStatusResponse response = GSON.fromJson(contentResponse, FamilyStatusResponse.class); + if (response != null) { + return response; + } else { + throw new ResponseFormatException("Failed to get a valid 'familyStatus' response from cloud"); + } + } catch (JsonSyntaxException e) { + throw new ResponseFormatException("Failed to parse 'familyStatus' response"); + } + } + + @Override + public List getSleepers() throws LoginException, ResponseFormatException, CommunicationException { + try { + String contentResponse = cloudRequest(Endpoints.sleeper()); + SleepersResponse response = GSON.fromJson(contentResponse, SleepersResponse.class); + if (response != null) { + return response.getSleepers(); + } else { + throw new ResponseFormatException("Failed to get a valid 'sleepers' response from cloud"); + } + } catch (JsonSyntaxException e) { + throw new ResponseFormatException("Failed to parse 'sleepers' response"); + } + } + + @Override + public PauseModeResponse getPauseMode(String bedId) + throws LoginException, ResponseFormatException, CommunicationException { + try { + String contentResponse = cloudRequest(Endpoints.pauseMode(bedId)); + PauseModeResponse response = GSON.fromJson(contentResponse, PauseModeResponse.class); + if (response != null) { + return response; + } else { + throw new ResponseFormatException("Failed to get a valid 'pauseMode' response from cloud"); + } + } catch (JsonSyntaxException e) { + throw new ResponseFormatException("Failed to parse 'pauseMode' response"); + } + } + + @Override + public SleepDataResponse getSleepData(String sleeperId, SleepDataInterval interval) + throws LoginException, ResponseFormatException, CommunicationException { + try { + Map parameters = new HashMap<>(); + parameters.put("interval", interval.value()); + parameters.put("sleeper", sleeperId); + parameters.put("includeSlices", "false"); + parameters.put("date", formatSleepDataDate(ZonedDateTime.now())); + String contentResponse = cloudRequest(Endpoints.sleepData(), parameters); + SleepDataResponse response = GSON.fromJson(contentResponse, SleepDataResponse.class); + if (response != null) { + return response; + } else { + throw new ResponseFormatException("Failed to get a valid 'sleepData' response from cloud"); + } + } catch (JsonSyntaxException e) { + throw new ResponseFormatException("Failed to parse 'sleepData' response"); + } + } + + private String formatSleepDataDate(ZonedDateTime zonedDateTime) { + return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(zonedDateTime); + } + + @Override + public void setSleepNumber(String bedId, Side side, int sleepNumber) + throws LoginException, ResponseFormatException, CommunicationException { + String body = GSON.toJson(new SleepNumberRequest().withBedId(bedId).withSleepNumber(sleepNumber).withSide(side), + SleepNumberRequest.class); + logger.debug("SleepIQ: setSleepNumber: Request body={}", body); + cloudRequest(Endpoints.setSleepNumber(bedId), null, body); + } + + @Override + public void setPauseMode(String bedId, boolean pauseMode) + throws LoginException, ResponseFormatException, CommunicationException { + logger.debug("SleepIQ: setPauseMode: command={}", pauseMode); + Map requestParameters = new HashMap<>(); + requestParameters.put("mode", pauseMode ? "on" : "off"); + cloudRequest(Endpoints.setPauseMode(bedId), requestParameters, ""); + } + + private String cloudRequest(String endpoint) + throws LoginException, ResponseFormatException, CommunicationException { + return cloudRequest(endpoint, null, null); + } + + private String cloudRequest(String endpoint, Map parameters) + throws LoginException, ResponseFormatException, CommunicationException { + return cloudRequest(endpoint, parameters, null); + } + + private String cloudRequest(String endpoint, @Nullable Map parameters, @Nullable String body) + throws LoginException, ResponseFormatException, CommunicationException { + logger.debug("SleepIQ: cloudGetRequest: Invoke endpoint={}", endpoint); + ContentResponse response = (body == null ? doGet(endpoint, parameters) : doPut(endpoint, parameters, body)); + if (isUnauthorized(response)) { + logger.debug("SleepIQ: cloudGetRequest: UNAUTHORIZED, reset login"); + // Force new login and try again + resetLogin(); + response = (body == null ? doGet(endpoint, parameters) : doPut(endpoint, parameters, body)); + } + if (isNotOk(response)) { + throw new ResponseFormatException(String.format("Cloud API returned error: status=%d, message=%s", + response.getStatus(), HttpStatus.getCode(response.getStatus()).getMessage())); + } + return response.getContentAsString(); + } + + private ContentResponse doGet(String endpoint, @Nullable Map parameters) + throws CommunicationException, LoginException { + LoginInfo login = login(); + Request request = httpClient.newRequest(config.getBaseUri()).path(endpoint).param(PARAM_KEY, login.getKey()) + .agent(USER_AGENT).header(HttpHeader.CONTENT_TYPE, "application/json").timeout(10, TimeUnit.SECONDS) + .method(HttpMethod.GET); + return doRequest(request, parameters); + } + + private ContentResponse doPut(String endpoint, @Nullable Map parameters, String body) + throws CommunicationException, LoginException { + LoginInfo login = login(); + Request request = httpClient.newRequest(config.getBaseUri()).path(endpoint).param(PARAM_KEY, login.getKey()) + .agent(USER_AGENT).header(HttpHeader.CONTENT_TYPE, "application/json").timeout(10, TimeUnit.SECONDS) + .method(HttpMethod.PUT).content(new StringContentProvider(body), "application/json"); + return doRequest(request, parameters); + } + + private ContentResponse doRequest(Request request, @Nullable Map parameters) + throws CommunicationException { + try { + if (parameters != null) { + for (String key : parameters.keySet()) { + request.param(key, parameters.get(key)); + } + } + addCookiesToRequest(request); + logger.debug("SleepIQ: doPut: request url={}", request.getURI()); + ContentResponse response = request.send(); + logger.trace("SleepIQ: doPut: status={} response={}", response.getStatus(), response.getContentAsString()); + return response; + } catch (InterruptedException | TimeoutException | ExecutionException e) { + logger.debug("SleepIQ: doPut: Exception message={}", e.getMessage(), e); + throw new CommunicationException("Communication error while accessing API: " + e.getMessage()); + } + } + + private void addCookiesToRequest(Request request) { + cookieStore.get(config.getBaseUri()).forEach(cookie -> { + request.cookie(cookie); + }); + } + + private boolean isUnauthorized(ContentResponse response) { + return response.getStatus() == HttpStatus.Code.UNAUTHORIZED.getCode(); + } + + private boolean isNotOk(ContentResponse response) { + return response.getStatus() != HttpStatus.Code.OK.getCode(); + } + + private synchronized void resetLogin() { + logger.debug("SleepIQ: resetLogin: Set loginInfo=null to force login on next transaction"); + loginInfo = null; + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/DateTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/DateTimeTypeAdapter.java new file mode 100644 index 0000000000000..811618358b193 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/DateTimeTypeAdapter.java @@ -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.sleepiq.internal.api.impl.typeadapters; + +import java.util.function.Function; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Abstract type adapter for jsr310 date-time types. + * + * @author Christophe Bornet - Initial contribution + */ +@NonNullByDefault +abstract class DateTimeTypeAdapter extends TemporalTypeAdapter { + + DateTimeTypeAdapter(Function parseFunction) { + super(parseFunction); + } + + @Override + public String preProcess(String in) { + if (in.endsWith("+0000")) { + return in.substring(0, in.length() - 5) + "Z"; + } + return in; + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/SideTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/SideTypeAdapter.java new file mode 100644 index 0000000000000..655a3d5c40aa9 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/SideTypeAdapter.java @@ -0,0 +1,48 @@ +/** + * 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.sleepiq.internal.api.impl.typeadapters; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.sleepiq.internal.api.enums.Side; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * The {@link SideTypeAdapter} converts the Side enum to the format expected by the sleepiq API. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class SideTypeAdapter implements JsonDeserializer, JsonSerializer { + + @Override + public JsonElement serialize(Side side, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(side.value()); + } + + @Override + public @Nullable Side deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2) + throws JsonParseException { + int key = element.getAsInt(); + return Side.forValue(key); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/SleepNumberRequestAdapter.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/SleepNumberRequestAdapter.java new file mode 100644 index 0000000000000..5019bbf94aee1 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/SleepNumberRequestAdapter.java @@ -0,0 +1,48 @@ +/** + * 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.sleepiq.internal.api.impl.typeadapters; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.sleepiq.internal.api.dto.SleepNumberRequest; +import org.openhab.binding.sleepiq.internal.api.enums.Side; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * The {@link SleepNumberRequestAdapter} serializes the request to set the sleep number. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class SleepNumberRequestAdapter implements JsonSerializer { + + @Override + public JsonElement serialize(SleepNumberRequest src, Type typeOfSrc, JsonSerializationContext context) { + // Sleep number must be >= 5 and <= 100, and be a multiple of 5 + int sleepNumber = src.getSleepNumber(); + sleepNumber = Math.min(100, sleepNumber); + sleepNumber = Math.max(5, sleepNumber); + sleepNumber = 5 * (int) (Math.round(sleepNumber / 5.0)); + + JsonObject obj = new JsonObject(); + obj.addProperty("bedId", src.getBedId()); + obj.addProperty("sleepNumber", sleepNumber); + obj.addProperty("side", src.getSide().equals(Side.LEFT) ? "L" : "R"); + return obj; + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/TemporalTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/TemporalTypeAdapter.java similarity index 52% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/TemporalTypeAdapter.java rename to bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/TemporalTypeAdapter.java index dcbde82e6fccc..2503eb964e075 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/java/org/openhab/binding/sleepiq/api/impl/typeadapters/TemporalTypeAdapter.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/TemporalTypeAdapter.java @@ -1,29 +1,24 @@ -/* - * Copyright (C) 2016 Gson Type Adapter Authors. +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src - * and repackaged to avoid the default package. + * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.sleepiq.api.impl.typeadapters; +package org.openhab.binding.sleepiq.internal.api.impl.typeadapters; import java.io.IOException; import java.util.Objects; import java.util.function.Function; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -32,8 +27,9 @@ /** * Abstract type adapter for jsr310 date-time types. * - * @author Christophe Bornet + * @author Christophe Bornet - Initial contribution */ +@NonNullByDefault abstract class TemporalTypeAdapter extends TypeAdapter { Function parseFunction; @@ -44,7 +40,7 @@ abstract class TemporalTypeAdapter extends TypeAdapter { } @Override - public void write(JsonWriter out, T value) throws IOException { + public void write(JsonWriter out, @Nullable T value) throws IOException { if (value == null) { out.nullValue(); } else { @@ -53,7 +49,7 @@ public void write(JsonWriter out, T value) throws IOException { } @Override - public T read(JsonReader in) throws IOException { + public @Nullable T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/TimeSinceTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/TimeSinceTypeAdapter.java new file mode 100644 index 0000000000000..6377feca6e4d8 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/TimeSinceTypeAdapter.java @@ -0,0 +1,28 @@ +/** + * 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.sleepiq.internal.api.impl.typeadapters; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.sleepiq.internal.api.dto.TimeSince; + +/** + * The {@link TimeSinceTypeAdapter} handles the parsing of the TimeSince. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public class TimeSinceTypeAdapter extends TemporalTypeAdapter { + public TimeSinceTypeAdapter() { + super(TimeSince::parse); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/ZonedDateTimeTypeAdapter.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/ZonedDateTimeTypeAdapter.java new file mode 100644 index 0000000000000..bb3e64b755f35 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/api/impl/typeadapters/ZonedDateTimeTypeAdapter.java @@ -0,0 +1,30 @@ +/** + * 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.sleepiq.internal.api.impl.typeadapters; + +import java.time.ZonedDateTime; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Type adapter for jsr310 {@link ZonedDateTime} class. + * + * @author Christophe Bornet - Initial contribution + */ +@NonNullByDefault +public class ZonedDateTimeTypeAdapter extends DateTimeTypeAdapter { + + public ZonedDateTimeTypeAdapter() { + super(ZonedDateTime::parse); + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/config/SleepIQBedConfiguration.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/config/SleepIQBedConfiguration.java index 3d8fcd30deb36..96e09ea316104 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/config/SleepIQBedConfiguration.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/config/SleepIQBedConfiguration.java @@ -12,13 +12,17 @@ */ package org.openhab.binding.sleepiq.internal.config; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Configuration class for a SleepIQ bed. * * @author Gregory Moyer - Initial contribution */ +@NonNullByDefault public class SleepIQBedConfiguration { public static final String BED_ID = "bedId"; - public String bedId; + public @Nullable String bedId; } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/discovery/SleepIQBedDiscoveryService.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/discovery/SleepIQBedDiscoveryService.java index b9b14a7abe0c5..e60525e220bc2 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/discovery/SleepIQBedDiscoveryService.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/discovery/SleepIQBedDiscoveryService.java @@ -13,10 +13,12 @@ package org.openhab.binding.sleepiq.internal.discovery; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.openhab.binding.sleepiq.api.model.Bed; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.sleepiq.internal.SleepIQBindingConstants; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; import org.openhab.binding.sleepiq.internal.config.SleepIQBedConfiguration; import org.openhab.binding.sleepiq.internal.handler.SleepIQCloudHandler; import org.openhab.binding.sleepiq.internal.handler.SleepIQDualBedHandler; @@ -33,6 +35,7 @@ * * @author Gregory Moyer - Initial contribution */ +@NonNullByDefault public class SleepIQBedDiscoveryService extends AbstractDiscoveryService { private static final int TIMEOUT = 60; @@ -59,34 +62,34 @@ protected void startBackgroundDiscovery() { @Override protected void startScan() { logger.debug("Starting scan for new beds"); + List beds = cloudHandler.getBeds(); + if (beds != null) { + for (Bed bed : beds) { + // only dual chamber beds are supported currently + if (!bed.isDualSleep()) { + logger.info("Found a bed that is not dual chamber - currently unsupported"); + continue; + } - for (Bed bed : cloudHandler.getBeds()) { - // only dual chamber beds are supported currently - if (!bed.isDualSleep()) { - logger.info("Found a bed that is not dual chamber - currently unsupported"); - continue; - } - - ThingUID bridgeUID = cloudHandler.getThing().getUID(); - ThingUID thingUID = new ThingUID(SleepIQBindingConstants.THING_TYPE_DUAL_BED, bridgeUID, - bed.getMacAddress()); - - // thing already exists - if (cloudHandler.getThing().getThing(thingUID) != null) { - continue; - } + ThingUID bridgeUID = cloudHandler.getThing().getUID(); + ThingUID thingUID = new ThingUID(SleepIQBindingConstants.THING_TYPE_DUAL_BED, bridgeUID, + bed.getMacAddress()); - logger.debug("New bed found with MAC address {}", bed.getMacAddress()); + // thing already exists + if (cloudHandler.getThing().getThing(thingUID) != null) { + continue; + } - @SuppressWarnings({ "unchecked", "rawtypes" }) - Map properties = (Map) cloudHandler.updateProperties(bed, new HashMap<>()); - properties.put(SleepIQBedConfiguration.BED_ID, bed.getBedId()); + logger.debug("New bed found with MAC address {}", bed.getMacAddress()); + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map properties = (Map) cloudHandler.updateProperties(bed, new HashMap<>()); + properties.put(SleepIQBedConfiguration.BED_ID, bed.getBedId()); - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) - .withBridge(bridgeUID).withLabel(bed.getName() + " - " + bed.getModel()).build(); - thingDiscovered(discoveryResult); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) + .withBridge(bridgeUID).withLabel(bed.getName() + " - " + bed.getModel()).build(); + thingDiscovered(discoveryResult); + } } - stopScan(); } } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/BedStatusListener.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/BedStatusListener.java index 7ecc336550356..aa9c3b0734179 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/BedStatusListener.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/BedStatusListener.java @@ -12,14 +12,17 @@ */ package org.openhab.binding.sleepiq.internal.handler; -import org.openhab.binding.sleepiq.api.SleepIQ; -import org.openhab.binding.sleepiq.api.model.BedStatus; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.sleepiq.internal.api.dto.BedStatus; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; /** * The {@link BedStatusListener} is notified when a chamber is updated. * * @author Gregory Moyer - Initial contribution */ +@NonNullByDefault public interface BedStatusListener { /** * This method will be called whenever a new bed status is received by the cloud handler. @@ -27,5 +30,7 @@ public interface BedStatusListener { * @param cloud the cloud service that can be used to gather additional information * @param status the status returned from the cloud service */ - public void onBedStateChanged(SleepIQ cloud, BedStatus status); + public void onBedStateChanged(BedStatus status); + + public void onSleeperChanged(@Nullable Sleeper sleeper); } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQCloudHandler.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQCloudHandler.java index 052553692f9be..30f316084476a 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQCloudHandler.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQCloudHandler.java @@ -25,19 +25,24 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.client.ClientBuilder; - -import org.openhab.binding.sleepiq.api.Configuration; -import org.openhab.binding.sleepiq.api.LoginException; -import org.openhab.binding.sleepiq.api.SleepIQ; -import org.openhab.binding.sleepiq.api.UnauthorizedException; -import org.openhab.binding.sleepiq.api.model.Bed; -import org.openhab.binding.sleepiq.api.model.BedStatus; -import org.openhab.binding.sleepiq.api.model.FamilyStatus; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.sleepiq.internal.SleepIQBindingConstants; import org.openhab.binding.sleepiq.internal.SleepIQConfigStatusMessage; +import org.openhab.binding.sleepiq.internal.api.Configuration; +import org.openhab.binding.sleepiq.internal.api.LoginException; +import org.openhab.binding.sleepiq.internal.api.SleepIQ; +import org.openhab.binding.sleepiq.internal.api.SleepIQException; +import org.openhab.binding.sleepiq.internal.api.UnauthorizedException; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; +import org.openhab.binding.sleepiq.internal.api.dto.BedStatus; +import org.openhab.binding.sleepiq.internal.api.dto.FamilyStatusResponse; +import org.openhab.binding.sleepiq.internal.api.dto.SleepDataResponse; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; +import org.openhab.binding.sleepiq.internal.api.enums.Side; +import org.openhab.binding.sleepiq.internal.api.enums.SleepDataInterval; import org.openhab.binding.sleepiq.internal.config.SleepIQCloudConfiguration; -import org.openhab.core.cache.ExpiringCache; import org.openhab.core.config.core.status.ConfigStatusMessage; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -56,124 +61,82 @@ * * @author Gregory Moyer - Initial contribution */ +@NonNullByDefault public class SleepIQCloudHandler extends ConfigStatusBridgeHandler { public static final Set SUPPORTED_THING_TYPE_UIDS = Collections.singleton(THING_TYPE_CLOUD); + private static final int SLEEPER_POLLING_INTERVAL_HOURS = 12; + private final Logger logger = LoggerFactory.getLogger(SleepIQCloudHandler.class); - private final List bedStatusListeners = new CopyOnWriteArrayList<>(); + private final HttpClient httpClient; - private ExpiringCache statusCache; + private final List bedStatusListeners = new CopyOnWriteArrayList<>(); - private ScheduledFuture pollingJob; + private @Nullable ScheduledFuture statusPollingJob; + private @Nullable ScheduledFuture sleeperPollingJob; - private SleepIQ cloud; + private @Nullable SleepIQ cloud; - private ClientBuilder clientBuilder; + private @Nullable List sleepers; - public SleepIQCloudHandler(final Bridge bridge, ClientBuilder clientBuilder) { + public SleepIQCloudHandler(final Bridge bridge, HttpClient httpClient) { super(bridge); - this.clientBuilder = clientBuilder; + this.httpClient = httpClient; } @Override public void initialize() { - try { - logger.debug("Configuring bed status cache"); - statusCache = new ExpiringCache<>(TimeUnit.SECONDS.toMillis(getPollingInterval() / 2), - () -> cloud.getFamilyStatus()); - - createCloudConnection(); - - logger.debug("Setting SleepIQ cloud online"); - updateListenerManagement(); - updateStatus(ThingStatus.ONLINE); - } catch (UnauthorizedException e) { - logger.debug("SleepIQ cloud authentication failed", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid SleepIQ credentials"); - } catch (LoginException e) { - logger.debug("SleepIQ cloud login failed", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "SleepIQ cloud login failed: " + e.getMessage()); - } catch (Exception e) { - logger.debug("Unexpected error while communicating with SleepIQ cloud", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Unable to connect to SleepIQ cloud: " + e.getMessage()); - } - } - - /** - * Create a new SleepIQ cloud service connection. If a connection already exists, it will be lost. - * - * @param clientBuilder2 - * - * @throws LoginException if there is an error while authenticating to the service - */ - private void createCloudConnection() throws LoginException { - logger.debug("Reading SleepIQ cloud binding configuration"); - SleepIQCloudConfiguration bindingConfig = getConfigAs(SleepIQCloudConfiguration.class); - - logger.debug("Creating SleepIQ client"); - Configuration cloudConfig = new Configuration().withUsername(bindingConfig.username) - .withPassword(bindingConfig.password).withLogging(logger.isDebugEnabled()); - cloud = SleepIQ.create(cloudConfig, clientBuilder); - - logger.debug("Authenticating at the SleepIQ cloud service"); - cloud.login(); - - logger.info("Successfully authenticated at the SleepIQ cloud service"); + scheduler.execute(() -> { + try { + createCloudConnection(); + updateListenerManagement(); + updateStatus(ThingStatus.ONLINE); + } catch (UnauthorizedException e) { + logger.debug("CloudHandler: SleepIQ cloud authentication failed", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid SleepIQ credentials"); + } catch (LoginException e) { + logger.debug("CloudHandler: SleepIQ cloud login failed", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "SleepIQ cloud login failed: " + e.getMessage()); + } + }); } @Override public synchronized void dispose() { - logger.debug("Disposing SleepIQ cloud handler"); - - if (pollingJob != null && !pollingJob.isCancelled()) { - pollingJob.cancel(true); - pollingJob = null; + stopSleeperPollingJob(); + stopStatusPollingJob(); + if (cloud != null) { + cloud.shutdown(); } } - /** - * Start or stop a background polling job to look for bed status updates based on whether or not there are any - * listeners to notify. - */ - private synchronized void updateListenerManagement() { - if (!bedStatusListeners.isEmpty() && (pollingJob == null || pollingJob.isCancelled())) { - int pollingInterval = getPollingInterval(); - pollingJob = scheduler.scheduleWithFixedDelay(this::refreshBedStatus, pollingInterval, pollingInterval, - TimeUnit.SECONDS); - } else if (bedStatusListeners.isEmpty() && pollingJob != null && !pollingJob.isCancelled()) { - pollingJob.cancel(true); - pollingJob = null; - } + @Override + public void handleCommand(final ChannelUID channelUID, final Command command) { + // cloud handler has no channels } /** - * Retrieve the polling interval for updating bed status. + * Validate the config from openHAB * - * @return the polling interval in seconds + * @return validity status of config parameters */ - private int getPollingInterval() { - return getConfigAs(SleepIQCloudConfiguration.class).pollingInterval; - } - - /** - * Retrieve the latest status on all beds and update all registered listeners. - */ - public void refreshBedStatus() { - try { - FamilyStatus status = statusCache.getValue(); - updateStatus(ThingStatus.ONLINE); - - for (BedStatus bedStatus : status.getBeds()) { - bedStatusListeners.stream().forEach(l -> l.onBedStateChanged(cloud, bedStatus)); - } - } catch (Exception e) { - logger.debug("Unexpected error while communicating with SleepIQ cloud", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Unable to connect to SleepIQ cloud: " + e.getMessage()); + @Override + public Collection getConfigStatus() { + Collection configStatusMessages = new ArrayList<>(); + SleepIQCloudConfiguration config = getConfigAs(SleepIQCloudConfiguration.class); + String username = config.username; + String password = config.password; + if (username.isBlank()) { + configStatusMessages.add(ConfigStatusMessage.Builder.error(USERNAME) + .withMessageKeySuffix(SleepIQConfigStatusMessage.USERNAME_MISSING).withArguments(USERNAME).build()); } + if (password.isBlank()) { + configStatusMessages.add(ConfigStatusMessage.Builder.error(PASSWORD) + .withMessageKeySuffix(SleepIQConfigStatusMessage.PASSWORD_MISSING).withArguments(PASSWORD).build()); + } + return configStatusMessages; } /** @@ -182,13 +145,12 @@ public void refreshBedStatus() { * @param listener the listener to register */ public void registerBedStatusListener(final BedStatusListener listener) { - if (listener == null) { - return; - } - bedStatusListeners.add(listener); - refreshBedStatus(); - updateListenerManagement(); + scheduler.execute(() -> { + refreshSleepers(); + refreshBedStatus(); + updateListenerManagement(); + }); } /** @@ -203,59 +165,101 @@ public boolean unregisterBedStatusListener(final BedStatusListener listener) { if (result) { updateListenerManagement(); } - return result; } - @Override - public void handleCommand(final ChannelUID channelUID, final Command command) { - // cloud handler has no channels + /** + * Get a list of all beds registered to the cloud service account. + * + * @return the list of beds or null if unable to get list + */ + public @Nullable List getBeds() { + try { + return cloud.getBeds(); + } catch (SleepIQException e) { + logger.debug("CloudHandler: Exception getting list of beds", e); + return null; + } } - @Override - public Collection getConfigStatus() { - Collection configStatusMessages = new ArrayList<>(); - - SleepIQCloudConfiguration config = getConfigAs(SleepIQCloudConfiguration.class); - String username = config.username; - String password = config.password; - - if (username.isEmpty()) { - configStatusMessages.add(ConfigStatusMessage.Builder.error(USERNAME) - .withMessageKeySuffix(SleepIQConfigStatusMessage.USERNAME_MISSING).withArguments(USERNAME).build()); + /** + * Get the bed corresponding to the given bed id + * + * @param bedId the bed identifier + * @return the identified {@link Bed} or null if no such bed exists + */ + public @Nullable Bed getBed(final @Nullable String bedId) { + logger.debug("CloudHandler: Get bed object for bedId={}", bedId); + if (bedId == null) { + return null; } - - if (password.isEmpty()) { - configStatusMessages.add(ConfigStatusMessage.Builder.error(PASSWORD) - .withMessageKeySuffix(SleepIQConfigStatusMessage.PASSWORD_MISSING).withArguments(PASSWORD).build()); + List beds = getBeds(); + if (beds != null) { + for (Bed bed : beds) { + if (bedId.equals(bed.getBedId())) { + return bed; + } + } } - - return configStatusMessages; + return null; } /** - * Get a list of all beds registered to the cloud service account. + * Get the sleeper associated with the bedId and side * - * @return the list of beds (never null) + * @param bedId the bed identifier + * @param side the side of the bed + * @return the sleeper or null if sleeper not found */ - public List getBeds() { - return cloud.getBeds(); + public @Nullable Sleeper getSleeper(@Nullable String bedId, Side side) { + logger.debug("CloudHandler: Get sleeper object for bedId={}, side={}", bedId, side); + if (bedId == null) { + return null; + } + List localSleepers = sleepers; + if (localSleepers != null) { + for (Sleeper sleeper : localSleepers) { + if (bedId.equals(sleeper.getBedId()) && side.equals(sleeper.getSide())) { + return sleeper; + } + } + } + return null; } /** - * Get the {@link Bed} corresponding to the given identifier. + * Set the sleep number of the specified chamber * * @param bedId the bed identifier - * @return the identified {@link Bed} or null if no such bed exists + * @param sleepNumber the sleep number multiple of 5 between 5 and 100 + * @param side the chamber to set */ - public Bed getBed(final String bedId) { - for (Bed bed : getBeds()) { - if (bedId.equals(bed.getBedId())) { - return bed; - } + public void setSleepNumber(@Nullable String bedId, Side side, int sleepNumber) { + if (bedId == null) { + return; } + try { + cloud.setSleepNumber(bedId, side, sleepNumber); + } catch (SleepIQException e) { + logger.debug("CloudHandler: Exception setting sleep number of bed={}", bedId, e); + } + } - return null; + /** + * Set the pause mode of the specified bed + * + * @param bedId the bed identifier + * @param mode turn pause mode on or off + */ + public void setPauseMode(@Nullable String bedId, boolean mode) { + if (bedId == null) { + return; + } + try { + cloud.setPauseMode(bedId, mode); + } catch (SleepIQException e) { + logger.debug("CloudHandler: Exception setting pause mode of bed={}", bedId, e); + } } /** @@ -266,8 +270,9 @@ public Bed getBed(final String bedId) { * @param properties the properties to update (this may be null) * @return the given map (or a new map if no map was given) with updated/set properties from the supplied bed */ - public Map updateProperties(final Bed bed, Map properties) { + public Map updateProperties(final @Nullable Bed bed, Map properties) { if (bed != null) { + logger.debug("CloudHandler: Updating bed properties for bed={}", bed.getBedId()); properties.put(Thing.PROPERTY_MODEL_ID, bed.getModel()); properties.put(SleepIQBindingConstants.PROPERTY_BASE, bed.getBase()); if (bed.isKidsBed() != null) { @@ -281,7 +286,146 @@ public Map updateProperties(final Bed bed, Map p properties.put(SleepIQBindingConstants.PROPERTY_SIZE, bed.getSize()); properties.put(SleepIQBindingConstants.PROPERTY_SKU, bed.getSku()); } - return properties; } + + /** + * Retrieve the latest status on all beds and update all registered listeners + * with bed status, sleepers and sleep data. + */ + private void refreshBedStatus() { + logger.debug("CloudHandler: Refreshing BED STATUS, updating chanels with status, sleepers, and sleep data"); + try { + FamilyStatusResponse familyStatus = cloud.getFamilyStatus(); + if (familyStatus != null && familyStatus.getBeds() != null) { + updateStatus(ThingStatus.ONLINE); + for (BedStatus bedStatus : familyStatus.getBeds()) { + logger.debug("CloudHandler: Informing listeners with bed status for bedId={}", + bedStatus.getBedId()); + bedStatusListeners.stream().forEach(l -> l.onBedStateChanged(bedStatus)); + } + + List localSleepers = sleepers; + if (localSleepers != null) { + for (Sleeper sleeper : localSleepers) { + logger.debug("CloudHandler: Informing listeners with sleepers for sleeperId={}", + sleeper.getSleeperId()); + bedStatusListeners.stream().forEach(l -> l.onSleeperChanged(sleeper)); + } + } + return; + } + } catch (SleepIQException e) { + logger.debug("CloudHandler: Exception refreshing bed status", e); + } + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Unable to connect to SleepIQ cloud"); + } + + /** + * Refresh the list of sleepers + */ + private void refreshSleepers() { + logger.debug("CloudHandler: Refreshing SLEEPERS"); + try { + sleepers = cloud.getSleepers(); + } catch (SleepIQException e) { + logger.debug("CloudHandler: Exception refreshing list of sleepers", e); + } + } + + public @Nullable SleepDataResponse getDailySleepData(String sleeperId) { + return getSleepData(sleeperId, SleepDataInterval.DAY); + } + + public @Nullable SleepDataResponse getMonthlySleepData(String sleeperId) { + return getSleepData(sleeperId, SleepDataInterval.MONTH); + } + + private @Nullable SleepDataResponse getSleepData(String sleeperId, SleepDataInterval interval) { + try { + return cloud.getSleepData(sleeperId, interval); + } catch (SleepIQException e) { + logger.debug("CloudHandler: Exception getting sleep data for sleeperId={}", sleeperId, e); + } + return null; + } + + /** + * Create a new SleepIQ cloud service connection. If a connection already exists, it will be lost. + * + * @throws LoginException if there is an error while authenticating to the service + */ + private void createCloudConnection() throws LoginException { + SleepIQCloudConfiguration bindingConfig = getConfigAs(SleepIQCloudConfiguration.class); + Configuration cloudConfig = new Configuration().withUsername(bindingConfig.username) + .withPassword(bindingConfig.password).withLogging(logger.isTraceEnabled()); + logger.debug("CloudHandler: Authenticating at the SleepIQ cloud service"); + cloud = SleepIQ.create(cloudConfig, httpClient); + cloud.login(); + } + + /** + * Start or stop the background polling jobs + */ + private synchronized void updateListenerManagement() { + startSleeperPollingJob(); + startStatusPollingJob(); + } + + /** + * Start or stop the bed status polling job + */ + private void startStatusPollingJob() { + ScheduledFuture localPollingJob = statusPollingJob; + if (!bedStatusListeners.isEmpty() && (localPollingJob == null || localPollingJob.isCancelled())) { + int pollingInterval = getStatusPollingIntervalSeconds(); + logger.debug("CloudHandler: Scheduling bed status polling job every {} seconds", pollingInterval); + statusPollingJob = scheduler.scheduleWithFixedDelay(this::refreshBedStatus, pollingInterval, + pollingInterval, TimeUnit.SECONDS); + } else if (bedStatusListeners.isEmpty()) { + stopStatusPollingJob(); + } + } + + /** + * Stop the bed status polling job + */ + private void stopStatusPollingJob() { + ScheduledFuture localPollingJob = statusPollingJob; + if (localPollingJob != null) { + logger.debug("CloudHandler: Canceling bed status polling job"); + localPollingJob.cancel(true); + statusPollingJob = null; + } + } + + private int getStatusPollingIntervalSeconds() { + return getConfigAs(SleepIQCloudConfiguration.class).pollingInterval; + } + + /** + * Start or stop the sleeper polling job + */ + private void startSleeperPollingJob() { + ScheduledFuture localJob = sleeperPollingJob; + if (!bedStatusListeners.isEmpty() && (localJob == null || localJob.isCancelled())) { + logger.debug("CloudHandler: Scheduling sleeper polling job every {} hours", SLEEPER_POLLING_INTERVAL_HOURS); + sleeperPollingJob = scheduler.scheduleWithFixedDelay(this::refreshSleepers, SLEEPER_POLLING_INTERVAL_HOURS, + SLEEPER_POLLING_INTERVAL_HOURS, TimeUnit.HOURS); + } else if (bedStatusListeners.isEmpty()) { + stopSleeperPollingJob(); + } + } + + /** + * Stop the sleeper polling job + */ + private void stopSleeperPollingJob() { + ScheduledFuture localJob = sleeperPollingJob; + if (localJob != null && !localJob.isCancelled()) { + logger.debug("CloudHandler: Canceling sleeper polling job"); + localJob.cancel(true); + sleeperPollingJob = null; + } + } } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQDualBedHandler.java b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQDualBedHandler.java index 00a3c0046be2c..e353aa58896c1 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQDualBedHandler.java +++ b/bundles/org.openhab.binding.sleepiq/src/main/java/org/openhab/binding/sleepiq/internal/handler/SleepIQDualBedHandler.java @@ -16,15 +16,22 @@ import java.util.Collections; import java.util.Set; +import java.util.concurrent.TimeUnit; -import org.openhab.binding.sleepiq.api.SleepIQ; -import org.openhab.binding.sleepiq.api.model.Bed; -import org.openhab.binding.sleepiq.api.model.BedSideStatus; -import org.openhab.binding.sleepiq.api.model.BedStatus; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; +import org.openhab.binding.sleepiq.internal.api.dto.BedSideStatus; +import org.openhab.binding.sleepiq.internal.api.dto.BedStatus; +import org.openhab.binding.sleepiq.internal.api.dto.SleepDataResponse; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; +import org.openhab.binding.sleepiq.internal.api.enums.Side; import org.openhab.binding.sleepiq.internal.config.SleepIQBedConfiguration; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -33,10 +40,10 @@ import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandler; -import org.openhab.core.thing.binding.BridgeHandler; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,12 +52,20 @@ * * @author Gregory Moyer - Initial contribution */ +@NonNullByDefault public class SleepIQDualBedHandler extends BaseThingHandler implements BedStatusListener { public static final Set SUPPORTED_THING_TYPE_UIDS = Collections.singleton(THING_TYPE_DUAL_BED); + private static final long GET_SLEEP_DATA_DELAY_MINUTES = 5; + private final Logger logger = LoggerFactory.getLogger(SleepIQDualBedHandler.class); - private volatile String bedId; + private volatile @Nullable String bedId; + + private @Nullable Sleeper sleeperLeft; + private @Nullable Sleeper sleeperRight; + + private @Nullable BedStatus previousStatus; public SleepIQDualBedHandler(final Thing thing) { super(thing); @@ -58,80 +73,113 @@ public SleepIQDualBedHandler(final Thing thing) { @Override public void initialize() { - logger.debug("Verifying SleepIQ cloud/bridge configuration"); - Bridge bridge = getBridge(); if (bridge == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No cloud service bridge has been configured"); return; } - ThingHandler handler = bridge.getHandler(); if (!(handler instanceof SleepIQCloudHandler)) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Incorrect bridge thing found"); return; } - - logger.debug("Reading SleepIQ bed binding configuration"); bedId = getConfigAs(SleepIQBedConfiguration.class).bedId; + if (bedId == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Bed id not found in configuration"); + return; + } - logger.debug("Registering SleepIQ bed status listener"); + logger.debug("BedHandler: Registering SleepIQ bed status listener for bedId={}", bedId); SleepIQCloudHandler cloudHandler = (SleepIQCloudHandler) handler; cloudHandler.registerBedStatusListener(this); if (ThingStatus.ONLINE != bridge.getStatus()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); } else { - updateProperties(); updateStatus(ThingStatus.ONLINE); + scheduler.execute(() -> { + updateProperties(); + }); + } + } + + @Override + public void dispose() { + SleepIQCloudHandler cloudHandler = getCloudHandler(); + if (cloudHandler != null) { + cloudHandler.unregisterBedStatusListener(this); } + bedId = null; } @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { super.bridgeStatusChanged(bridgeStatusInfo); - if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) { updateProperties(); } } - private void updateProperties() { - logger.debug("Updating SleepIQ bed properties for bed {}", bedId); - - SleepIQCloudHandler cloudHandler = (SleepIQCloudHandler) getBridge().getHandler(); - Bed bed = cloudHandler.getBed(bedId); - if (bed == null) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bed found with ID " + bedId); + @Override + public void handleCommand(final ChannelUID channelUID, final Command command) { + if (command == RefreshType.REFRESH) { + // Channels will be refreshed automatically by cloud handler return; } - updateProperties(cloudHandler.updateProperties(bed, editProperties())); + switch (channelUID.getId()) { + case CHANNEL_LEFT_SLEEP_NUMBER: + case CHANNEL_RIGHT_SLEEP_NUMBER: + if (command instanceof DecimalType) { + Side side = Side.convertFromGroup(channelUID.getGroupId()); + logger.debug("BedHandler: Set sleepnumber to {} for bedId={}, side={}", command, bedId, side); + SleepIQCloudHandler cloudHandler = getCloudHandler(); + if (cloudHandler != null) { + cloudHandler.setSleepNumber(bedId, side, ((DecimalType) command).intValue()); + } + } + break; + case CHANNEL_LEFT_PRIVACY_MODE: + case CHANNEL_RIGHT_PRIVACY_MODE: + if (command instanceof OnOffType) { + Side side = Side.convertFromGroup(channelUID.getGroupId()); + logger.debug("BedHandler: Set sleepnumber to {} for bedId={}, side={}", command, bedId, side); + SleepIQCloudHandler cloudHandler = getCloudHandler(); + if (cloudHandler != null) { + cloudHandler.setPauseMode(bedId, command == OnOffType.ON ? true : false); + } + } + break; + } } @Override - public void dispose() { - logger.debug("Disposing bed handler for bed {}", bedId); - - Bridge bridge = getBridge(); - if (bridge != null) { - BridgeHandler bridgeHandler = bridge.getHandler(); - if (bridgeHandler instanceof SleepIQCloudHandler) { - ((SleepIQCloudHandler) bridgeHandler).unregisterBedStatusListener(this); - } + public void onSleeperChanged(final @Nullable Sleeper sleeper) { + if (sleeper == null || !sleeper.getBedId().equals(bedId)) { + return; } + logger.debug("BedHandler: Updating sleeper information channels for bed={}, side={}", bedId, sleeper.getSide()); - bedId = null; + if (sleeper.getSide().equals(Side.LEFT)) { + sleeperLeft = sleeper; + updateState(CHANNEL_LEFT_FIRST_NAME, new StringType(sleeper.getFirstName())); + updateState(CHANNEL_LEFT_SLEEP_GOAL_MINUTES, new QuantityType<>(sleeper.getSleepGoal(), Units.MINUTE)); + } else { + sleeperRight = sleeper; + updateState(CHANNEL_RIGHT_FIRST_NAME, new StringType(sleeper.getFirstName())); + updateState(CHANNEL_RIGHT_SLEEP_GOAL_MINUTES, new QuantityType<>(sleeper.getSleepGoal(), Units.MINUTE)); + } } @Override - public void onBedStateChanged(final SleepIQ cloud, final BedStatus status) { - if (!status.getBedId().equals(bedId)) { + public void onBedStateChanged(final @Nullable BedStatus status) { + if (status == null || !status.getBedId().equals(bedId)) { return; } + logger.debug("BedHandler: Updating bed status channels for bed {}", bedId); - logger.debug("Updating left side status for bed {}", bedId); BedSideStatus left = status.getLeftSide(); updateState(CHANNEL_LEFT_IN_BED, left.isInBed() ? OnOffType.ON : OnOffType.OFF); updateState(CHANNEL_LEFT_SLEEP_NUMBER, new DecimalType(left.getSleepNumber())); @@ -139,8 +187,10 @@ public void onBedStateChanged(final SleepIQ cloud, final BedStatus status) { updateState(CHANNEL_LEFT_LAST_LINK, new StringType(left.getLastLink().toString())); updateState(CHANNEL_LEFT_ALERT_ID, new DecimalType(left.getAlertId())); updateState(CHANNEL_LEFT_ALERT_DETAILED_MESSAGE, new StringType(left.getAlertDetailedMessage())); + if (previousStatus != null) { + updateSleepDataChannels(previousStatus.getLeftSide(), left, sleeperLeft); + } - logger.debug("Updating right side status for bed {}", bedId); BedSideStatus right = status.getRightSide(); updateState(CHANNEL_RIGHT_IN_BED, right.isInBed() ? OnOffType.ON : OnOffType.OFF); updateState(CHANNEL_RIGHT_SLEEP_NUMBER, new DecimalType(right.getSleepNumber())); @@ -148,15 +198,147 @@ public void onBedStateChanged(final SleepIQ cloud, final BedStatus status) { updateState(CHANNEL_RIGHT_LAST_LINK, new StringType(right.getLastLink().toString())); updateState(CHANNEL_RIGHT_ALERT_ID, new DecimalType(right.getAlertId())); updateState(CHANNEL_RIGHT_ALERT_DETAILED_MESSAGE, new StringType(right.getAlertDetailedMessage())); + if (previousStatus != null) { + updateSleepDataChannels(previousStatus.getRightSide(), right, sleeperRight); + } + + previousStatus = status; } - @Override - public void handleCommand(final ChannelUID channelUID, final Command command) { - // all channels are read-only + private void updateSleepDataChannels(BedSideStatus previousSideStatus, BedSideStatus currentSideStatus, + @Nullable Sleeper sleeper) { + if (sleeper == null) { + logger.debug("BedHandler: Can't update sleep data channels because sleeper is null"); + return; + } + if (previousSideStatus.isInBed() && !currentSideStatus.isInBed()) { + logger.debug("BedHandler: Bed status changed from IN BED to OUT OF BED for {}, side {}", bedId, + sleeper.getSide()); + scheduler.schedule(() -> { + updateDailySleepDataChannels(sleeper); + updateMonthlySleepDataChannels(sleeper); + }, GET_SLEEP_DATA_DELAY_MINUTES, TimeUnit.MINUTES); + } + } - if (command == RefreshType.REFRESH) { - SleepIQCloudHandler cloudHandler = (SleepIQCloudHandler) getBridge().getHandler(); - cloudHandler.refreshBedStatus(); + public void updateDailySleepDataChannels(final @Nullable Sleeper sleeper) { + SleepIQCloudHandler cloudHandler = getCloudHandler(); + if (cloudHandler == null || sleeper == null) { + return; + } + SleepDataResponse sleepData = cloudHandler.getDailySleepData(sleeper.getSleeperId()); + if (sleepData == null) { + logger.debug("BedHandler: Received no daily sleep data for bedId={}, sleeperId={}", sleeper.getBedId(), + sleeper.getSleeperId()); + return; + } + + logger.debug("BedHandler: UPDATING DAILY SLEEP DATA CHANNELS for bedId={}, sleeperId={}", sleeper.getBedId(), + sleeper.getSleeperId()); + if (sleepData.getSleepDataDays() == null || sleepData.getSleepDataDays().size() != 1) { + if (sleeper.getSide().equals(Side.LEFT)) { + updateState(CHANNEL_LEFT_TODAY_SLEEP_IQ, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_AVG_HEART_RATE, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_AVG_RESPIRATION_RATE, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_MESSAGE, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_SLEEP_DURATION_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_SLEEP_IN_BED_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_SLEEP_OUT_OF_BED_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_SLEEP_RESTFUL_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_LEFT_TODAY_SLEEP_RESTLESS_SECONDS, UnDefType.UNDEF); + } else { + updateState(CHANNEL_RIGHT_TODAY_SLEEP_IQ, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_AVG_HEART_RATE, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_AVG_RESPIRATION_RATE, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_MESSAGE, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_DURATION_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_IN_BED_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_OUT_OF_BED_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_RESTFUL_SECONDS, UnDefType.UNDEF); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_RESTLESS_SECONDS, UnDefType.UNDEF); + } + return; + } else if (sleeper.getSide().equals(Side.LEFT)) { + updateState(CHANNEL_LEFT_TODAY_SLEEP_IQ, new DecimalType(sleepData.getAverageSleepIQ())); + updateState(CHANNEL_LEFT_TODAY_AVG_HEART_RATE, new DecimalType(sleepData.getAverageHeartRate())); + updateState(CHANNEL_LEFT_TODAY_AVG_RESPIRATION_RATE, + new DecimalType(sleepData.getAverageRespirationRate())); + updateState(CHANNEL_LEFT_TODAY_MESSAGE, new StringType(sleepData.getSleepDataDays().get(0).getMessage())); + updateState(CHANNEL_LEFT_TODAY_SLEEP_DURATION_SECONDS, + new QuantityType<>(sleepData.getTotalSleepSessionTime(), Units.SECOND)); + updateState(CHANNEL_LEFT_TODAY_SLEEP_IN_BED_SECONDS, + new QuantityType<>(sleepData.getTotalInBedSeconds(), Units.SECOND)); + updateState(CHANNEL_LEFT_TODAY_SLEEP_OUT_OF_BED_SECONDS, + new QuantityType<>(sleepData.getTotalOutOfBedSeconds(), Units.SECOND)); + updateState(CHANNEL_LEFT_TODAY_SLEEP_RESTFUL_SECONDS, + new QuantityType<>(sleepData.getTotalRestfulSeconds(), Units.SECOND)); + updateState(CHANNEL_LEFT_TODAY_SLEEP_RESTLESS_SECONDS, + new QuantityType<>(sleepData.getTotalRestlessSeconds(), Units.SECOND)); + } else if (sleeper.getSide().equals(Side.RIGHT)) { + updateState(CHANNEL_RIGHT_TODAY_SLEEP_IQ, new DecimalType(sleepData.getAverageSleepIQ())); + updateState(CHANNEL_RIGHT_TODAY_AVG_HEART_RATE, new DecimalType(sleepData.getAverageHeartRate())); + updateState(CHANNEL_RIGHT_TODAY_AVG_RESPIRATION_RATE, + new DecimalType(sleepData.getAverageRespirationRate())); + updateState(CHANNEL_RIGHT_TODAY_MESSAGE, new StringType(sleepData.getSleepDataDays().get(0).getMessage())); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_DURATION_SECONDS, + new QuantityType<>(sleepData.getTotalSleepSessionTime(), Units.SECOND)); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_IN_BED_SECONDS, + new QuantityType<>(sleepData.getTotalInBedSeconds(), Units.SECOND)); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_OUT_OF_BED_SECONDS, + new QuantityType<>(sleepData.getTotalOutOfBedSeconds(), Units.SECOND)); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_RESTFUL_SECONDS, + new QuantityType<>(sleepData.getTotalRestfulSeconds(), Units.SECOND)); + updateState(CHANNEL_RIGHT_TODAY_SLEEP_RESTLESS_SECONDS, + new QuantityType<>(sleepData.getTotalRestlessSeconds(), Units.SECOND)); + } + } + + public void updateMonthlySleepDataChannels(final @Nullable Sleeper sleeper) { + SleepIQCloudHandler cloudHandler = getCloudHandler(); + if (cloudHandler == null || sleeper == null) { + return; + } + SleepDataResponse sleepData = cloudHandler.getMonthlySleepData(sleeper.getSleeperId()); + if (sleepData == null) { + logger.debug("BedHandler: Received no monthly sleep data for bedId={}, sleeperId={}", sleeper.getBedId(), + sleeper.getSleeperId()); + return; + } + + logger.debug("BedHandler: UPDATING MONTHLY SLEEP DATA CHANNELS for bedId={}, sleeperId={}", sleeper.getBedId(), + sleeper.getSleeperId()); + if (sleeper.getSide().equals(Side.LEFT)) { + updateState(CHANNEL_LEFT_MONTHLY_SLEEP_IQ, new DecimalType(sleepData.getAverageSleepIQ())); + updateState(CHANNEL_LEFT_MONTHLY_AVG_HEART_RATE, new DecimalType(sleepData.getAverageHeartRate())); + updateState(CHANNEL_LEFT_MONTHLY_AVG_RESPIRATION_RATE, + new DecimalType(sleepData.getAverageRespirationRate())); + } else { + updateState(CHANNEL_RIGHT_MONTHLY_SLEEP_IQ, new DecimalType(sleepData.getAverageSleepIQ())); + updateState(CHANNEL_RIGHT_MONTHLY_AVG_HEART_RATE, new DecimalType(sleepData.getAverageHeartRate())); + updateState(CHANNEL_RIGHT_MONTHLY_AVG_RESPIRATION_RATE, + new DecimalType(sleepData.getAverageRespirationRate())); + } + } + + private void updateProperties() { + logger.debug("BedHandler: Updating bed properties for bedId={}", bedId); + SleepIQCloudHandler cloudHandler = getCloudHandler(); + if (cloudHandler != null) { + Bed bed = cloudHandler.getBed(bedId); + if (bed == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "No bed found with ID " + bedId); + return; + } + updateProperties(cloudHandler.updateProperties(bed, editProperties())); + } + } + + private @Nullable SleepIQCloudHandler getCloudHandler() { + Bridge bridge = getBridge(); + if (bridge != null) { + return (SleepIQCloudHandler) bridge.getHandler(); } + return null; } } diff --git a/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/i18n/sleepiq.properties b/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/i18n/sleepiq.properties index b845d01c8987e..0e1bff9e3bc67 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/i18n/sleepiq.properties +++ b/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/i18n/sleepiq.properties @@ -1,3 +1,76 @@ +# add-on + +addon.sleepiq.name = SleepIQ Binding +addon.sleepiq.description = This is the binding for the Sleep Number SleepIQ system. + +# thing types + +thing-type.sleepiq.cloud.label = SleepIQ Cloud +thing-type.sleepiq.cloud.description = The SleepIQ cloud service provides connections to all beds in an account. +thing-type.sleepiq.dualBed.label = Dual Chamber Bed +thing-type.sleepiq.dualBed.description = A Sleep Number bed with SleepIQ and two individual air chambers + +# thing types config + +thing-type.config.sleepiq.cloud.password.label = Password +thing-type.config.sleepiq.cloud.password.description = Password of a registered SleepIQ account owner +thing-type.config.sleepiq.cloud.pollingInterval.label = Polling Interval +thing-type.config.sleepiq.cloud.pollingInterval.description = Seconds between fetching values from the cloud service +thing-type.config.sleepiq.cloud.username.label = Username +thing-type.config.sleepiq.cloud.username.description = Username of a registered SleepIQ account owner +thing-type.config.sleepiq.dualBed.bedId.label = Bed ID +thing-type.config.sleepiq.dualBed.bedId.description = The bed identifier identifies a specific bed + +# channel group types + +channel-group-type.sleepiq.chamberGroupType.label = Chamber + +# channel types + +channel-type.sleepiq.alertDetailedMessageType.label = Alert Detailed Message +channel-type.sleepiq.alertDetailedMessageType.description = A detailed message describing an alert condition with the chamber +channel-type.sleepiq.alertIdType.label = Alert ID +channel-type.sleepiq.alertIdType.description = Identifier for an alert condition with the chamber +channel-type.sleepiq.firstNameType.label = First Name +channel-type.sleepiq.firstNameType.description = The first name of the sleeper +channel-type.sleepiq.inBedType.label = In Bed +channel-type.sleepiq.inBedType.description = The presence of a person or object on the chamber +channel-type.sleepiq.lastLinkType.label = Last Link +channel-type.sleepiq.lastLinkType.description = The amount of time that has passed since a connection was made from the chamber to the cloud service (D d HH:MM:SS) +channel-type.sleepiq.monthlyAverageHeartRateType.label = Monthly Avg Heart Rate +channel-type.sleepiq.monthlyAverageHeartRateType.description = The average heart rate for the past month +channel-type.sleepiq.monthlyAverageRespirationRateType.label = Monthly Avg Respiration Rate +channel-type.sleepiq.monthlyAverageRespirationRateType.description = The average respiration rate for the past month +channel-type.sleepiq.monthlySleepIQType.label = Monthly Sleep IQ +channel-type.sleepiq.monthlySleepIQType.description = The overall Sleep Quotient for the past month +channel-type.sleepiq.pressureType.label = Pressure +channel-type.sleepiq.pressureType.description = The current pressure inside the chamber +channel-type.sleepiq.privacyMode.label = Privacy Mode +channel-type.sleepiq.privacyMode.description = Enable/disable privacy mode +channel-type.sleepiq.sleepGoalMinutesType.label = Sleep Goal +channel-type.sleepiq.sleepGoalMinutesType.description = The goal for the amount of sleep per night (in minutes) +channel-type.sleepiq.sleepNumberType.label = Sleep Number +channel-type.sleepiq.sleepNumberType.description = The Sleep Number setting of the chamber +channel-type.sleepiq.todayAverageHeartRateType.label = Today's Avg Heart Rate +channel-type.sleepiq.todayAverageHeartRateType.description = The average heart rate for today +channel-type.sleepiq.todayAverageRespirationRateType.label = Today's Avg Respiration Rate +channel-type.sleepiq.todayAverageRespirationRateType.description = The average respiration rate for today +channel-type.sleepiq.todayMessageType.label = Today's Message +channel-type.sleepiq.todayMessageType.description = The description of today's sleep quality +channel-type.sleepiq.todaySleepDurationSecondsType.label = Today's Sleep Duration +channel-type.sleepiq.todaySleepDurationSecondsType.description = The total duration of sleep for today in seconds +channel-type.sleepiq.todaySleepIQType.label = Today's Sleep IQ +channel-type.sleepiq.todaySleepIQType.description = The Sleep Quotient for today +channel-type.sleepiq.todaySleepInBedSecondsType.label = Today's Sleep In Bed Duration +channel-type.sleepiq.todaySleepInBedSecondsType.description = The total duration of time in bed in seconds +channel-type.sleepiq.todaySleepOutOfBedSecondsType.label = Today's Sleep Out Of Bed Duration +channel-type.sleepiq.todaySleepOutOfBedSecondsType.description = The total duration of time out of bed in seconds +channel-type.sleepiq.todaySleepRestfulSecondsType.label = Today's Sleep Restful Duration +channel-type.sleepiq.todaySleepRestfulSecondsType.description = The total duration of restful sleep for today in seconds +channel-type.sleepiq.todaySleepRestlessSecondsType.label = Today's Sleep Restless Duration +channel-type.sleepiq.todaySleepRestlessSecondsType.description = The total duration of restless sleep for today in seconds + # configuration messages -config-status.error.missing-username-configuration=No username for the SleepIQ cloud has been provided. -config-status.error.missing-password-configuration=No password for the SleepIQ cloud has been provided. + +config-status.error.missing-username-configuration = No username for the SleepIQ cloud has been provided. +config-status.error.missing-password-configuration = No password for the SleepIQ cloud has been provided. diff --git a/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/thing/thing-types.xml index ebef3f07c6f14..0c8c60a388b63 100644 --- a/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.sleepiq/src/main/resources/OH-INF/thing/thing-types.xml @@ -18,15 +18,15 @@ Username of a registered SleepIQ account owner - - password + Password of a registered SleepIQ account owner + password - + Seconds between fetching values from the cloud service - 60 + 120 @@ -86,6 +86,21 @@ + + + + + + + + + + + + + + + @@ -99,7 +114,7 @@ Number The Sleep Number setting of the chamber - + Number @@ -126,5 +141,95 @@ A detailed message describing an alert condition with the chamber + + String + + The first name of the sleeper + + + + Number:Time + + The goal for the amount of sleep per night (in minutes) + + + + Switch + + Enable/disable privacy mode + + + + Number + + The Sleep Quotient for today + + + + Number + + The average heart rate for today + + + + Number + + The average respiration rate for today + + + + String + + The description of today's sleep quality + + + + Number:Time + + The total duration of sleep for today in seconds + + + + Number:Time + + The total duration of time in bed in seconds + + + + Number:Time + + The total duration of time out of bed in seconds + + + + Number:Time + + The total duration of restful sleep for today in seconds + + + + Number:Time + + The total duration of restless sleep for today in seconds + + + + Number + + The overall Sleep Quotient for the past month + + + + Number + + The average heart rate for the past month + + + + Number + + The average respiration rate for the past month + + diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedSideStatusTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedSideStatusTest.java similarity index 63% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedSideStatusTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedSideStatusTest.java index 796bd5aeb30ef..8c510540d1a68 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedSideStatusTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedSideStatusTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -19,20 +16,23 @@ import java.io.FileReader; -import org.junit.jupiter.api.BeforeAll; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.BedSideStatus; +import org.openhab.binding.sleepiq.internal.api.dto.TimeSince; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; import com.google.gson.Gson; +/** + * The {@link BedSideStatusTest} tests deserialization of a bed side status object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class BedSideStatusTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } + private static Gson gson = GsonGenerator.create(true); @Test public void testSerializeAllFields() throws Exception { diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedStatusTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedStatusTest.java similarity index 62% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedStatusTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedStatusTest.java index 331675ba80fcd..83bbf813280a0 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedStatusTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedStatusTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -19,20 +16,23 @@ import java.io.FileReader; -import org.junit.jupiter.api.BeforeAll; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.BedSideStatus; +import org.openhab.binding.sleepiq.internal.api.dto.BedStatus; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; import com.google.gson.Gson; +/** + * The {@link BedStatusText} tests deserialization of a bed status object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class BedStatusTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } + private static Gson gson = GsonGenerator.create(true); @Test public void testSerializeAllFields() throws Exception { diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedTest.java similarity index 77% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedTest.java index 3873351011fd2..5bf26e0b76bab 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -21,20 +18,22 @@ import java.time.ZoneId; import java.time.ZonedDateTime; -import org.junit.jupiter.api.BeforeAll; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; import com.google.gson.Gson; +/** + * The {@link BedTest} tests deserialization of a bed object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class BedTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } + private static Gson gson = GsonGenerator.create(true); @Test public void testSerializeAllFields() throws Exception { diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedsResponseTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedsResponseTest.java similarity index 58% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedsResponseTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedsResponseTest.java index 5a1f6c4074f7d..147613a59a6f0 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/BedsResponseTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/BedsResponseTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -21,20 +18,23 @@ import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.BeforeAll; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.Bed; +import org.openhab.binding.sleepiq.internal.api.dto.BedsResponse; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; import com.google.gson.Gson; +/** + * The {@link BedsResponseTest} tests deserialization of a beds response object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class BedsResponseTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } + private static Gson gson = GsonGenerator.create(true); @Test public void testSerializeAllFields() throws Exception { diff --git a/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/FamilyStatusTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/FamilyStatusTest.java new file mode 100644 index 0000000000000..6dd01daeb0de8 --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/FamilyStatusTest.java @@ -0,0 +1,57 @@ +/** + * 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.sleepiq.api.model; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.FileReader; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.BedStatus; +import org.openhab.binding.sleepiq.internal.api.dto.FamilyStatusResponse; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; + +import com.google.gson.Gson; + +/** + * The {@link FamilyStatusText} tests deserialization of a family status object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault +public class FamilyStatusTest extends AbstractTest { + private static Gson gson = GsonGenerator.create(true); + + @Test + public void testSerializeAllFields() throws Exception { + FamilyStatusResponse familyStatus = new FamilyStatusResponse() + .withBeds(Arrays.asList(new BedStatus().withStatus(1L))); + assertEquals(readJson("family-status.json"), gson.toJson(familyStatus)); + } + + @Test + public void testDeserializeAllFields() throws Exception { + try (FileReader reader = new FileReader(getTestDataFile("family-status.json"))) { + FamilyStatusResponse familyStatus = gson.fromJson(reader, FamilyStatusResponse.class); + + List beds = familyStatus.getBeds(); + assertNotNull(beds); + assertEquals(1, beds.size()); + assertEquals(Long.valueOf(1L), beds.get(0).getStatus()); + } + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/PauseModeTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/PauseModeTest.java new file mode 100644 index 0000000000000..438c05e01fd7b --- /dev/null +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/PauseModeTest.java @@ -0,0 +1,52 @@ +/** + * 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.sleepiq.api.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.FileReader; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.PauseModeResponse; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; + +import com.google.gson.Gson; + +/** + * The {@link PauseModeTest} tests deserialization of a pause mode object. + * + * @author Mark Hilbush - Initial contribution + */ +@NonNullByDefault +public class PauseModeTest extends AbstractTest { + private static Gson gson = GsonGenerator.create(true); + + @Test + public void testSerializeAllFields() throws Exception { + PauseModeResponse pauseMode = new PauseModeResponse().withAccountId("-8888888888888888888") + .withBedId("-9999999999999999999").withPauseMode("off"); + assertEquals(readJson("pause-mode.json"), gson.toJson(pauseMode)); + } + + @Test + public void testDeserializeAllFields() throws Exception { + try (FileReader reader = new FileReader(getTestDataFile("pause-mode.json"))) { + PauseModeResponse pauseMode = gson.fromJson(reader, PauseModeResponse.class); + assertEquals("-8888888888888888888", pauseMode.getAccountId()); + assertEquals("-9999999999999999999", pauseMode.getBedId()); + assertEquals("off", pauseMode.getPauseMode()); + } + } +} diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/SleeperTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/SleeperTest.java similarity index 76% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/SleeperTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/SleeperTest.java index 8b184b6c39b56..28cbabfbf5b58 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/SleeperTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/SleeperTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -19,20 +16,23 @@ import java.io.FileReader; -import org.junit.jupiter.api.BeforeAll; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; +import org.openhab.binding.sleepiq.internal.api.enums.Side; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; import com.google.gson.Gson; +/** + * The {@link SleeperTest} tests deserialization of a sleeper object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class SleeperTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } + private static Gson gson = GsonGenerator.create(true); @Test public void testSerializeAllFields() throws Exception { @@ -40,7 +40,7 @@ public void testSerializeAllFields() throws Exception { .withAvatar("").withBedId("-9999999999999999999").withBirthMonth(6).withBirthYear("1970") .withChild(false).withDuration("").withEmail("alice@domain.com").withEmailValidated(true) .withFirstName("Alice").withHeight(64).withLastLogin("2017-02-17 20:19:36 CST").withLicenseVersion(6L) - .withMale(false).withSide(1).withSleeperId("-1111111111111111111").withSleepGoal(450) + .withMale(false).withSide(Side.RIGHT).withSleeperId("-1111111111111111111").withSleepGoal(450) .withTimezone("US/Pacific").withUsername("alice@domain.com").withWeight(110).withZipCode("90210"); assertEquals(readJson("sleeper.json"), gson.toJson(sleeper)); } @@ -71,7 +71,7 @@ public void testDeserializeAllFields() throws Exception { assertEquals("2017-02-17 20:19:36 CST", sleeper.getLastLogin()); assertEquals(Long.valueOf(6L), sleeper.getLicenseVersion()); assertEquals(false, sleeper.isMale()); - assertEquals(Integer.valueOf(1), sleeper.getSide()); + assertEquals(Side.RIGHT, sleeper.getSide()); assertEquals("-1111111111111111111", sleeper.getSleeperId()); assertEquals(Integer.valueOf(450), sleeper.getSleepGoal()); assertEquals("US/Pacific", sleeper.getTimezone()); diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/SleepersResponseTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/SleepersResponseTest.java similarity index 60% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/SleepersResponseTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/SleepersResponseTest.java index 118a24a4ccb43..6c866222aacd5 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/SleepersResponseTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/SleepersResponseTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -21,20 +18,23 @@ import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.BeforeAll; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.sleepiq.api.impl.GsonGenerator; import org.openhab.binding.sleepiq.api.test.AbstractTest; +import org.openhab.binding.sleepiq.internal.api.dto.Sleeper; +import org.openhab.binding.sleepiq.internal.api.dto.SleepersResponse; +import org.openhab.binding.sleepiq.internal.api.impl.GsonGenerator; import com.google.gson.Gson; +/** + * The {@link SleepersResponseTest} tests deserialization of a sleepers response object. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class SleepersResponseTest extends AbstractTest { - private static Gson gson; - - @BeforeAll - public static void setUpBeforeClass() { - gson = GsonGenerator.create(true); - } + private static Gson gson = GsonGenerator.create(true); @Test public void testSerializeAllFields() throws Exception { diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/TimeSinceTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/TimeSinceTest.java similarity index 78% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/TimeSinceTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/TimeSinceTest.java index 3e5d3d14be031..50f15d3eef684 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/model/TimeSinceTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/model/TimeSinceTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.model; @@ -19,8 +16,16 @@ import java.time.Duration; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; +import org.openhab.binding.sleepiq.internal.api.dto.TimeSince; +/** + * The {@link TimeSinceText} tests TimeSince. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public class TimeSinceTest { @Test public void testWithDuration() { diff --git a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/test/AbstractTest.java b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/test/AbstractTest.java similarity index 62% rename from bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/test/AbstractTest.java rename to bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/test/AbstractTest.java index 955f7e2ef86b4..d3b541a9e500b 100644 --- a/bundles/org.openhab.binding.sleepiq/src/3rdparty/test/org/openhab/binding/sleepiq/api/test/AbstractTest.java +++ b/bundles/org.openhab.binding.sleepiq/src/test/java/org/openhab/binding/sleepiq/api/test/AbstractTest.java @@ -1,17 +1,14 @@ -/* - * Copyright 2017 Gregory Moyer +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * See the NOTICE file(s) distributed with this work for additional + * information. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.binding.sleepiq.api.test; @@ -24,6 +21,14 @@ import java.util.Arrays; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link AbstractTest} tests deserialization of a sleepiq API objects. + * + * @author Gregory Moyer - Initial contribution + */ +@NonNullByDefault public abstract class AbstractTest { private static final String RESOURCES_PATH = "src/test/resources/"; @@ -31,6 +36,7 @@ protected File getTestDataFile(String name) { return getTestDataPath(name).toFile(); } + @SuppressWarnings("null") protected Path getTestDataPath(String name) { String packageName = this.getClass().getPackage().getName(); diff --git a/bundles/org.openhab.binding.smartmeter/pom.xml b/bundles/org.openhab.binding.smartmeter/pom.xml index b2e6537a91e65..54131d553654d 100644 --- a/bundles/org.openhab.binding.smartmeter/pom.xml +++ b/bundles/org.openhab.binding.smartmeter/pom.xml @@ -18,13 +18,13 @@ io.reactivex.rxjava2 rxjava - 2.2.3 + 2.2.21 compile org.reactivestreams reactive-streams - 1.0.2 + 1.0.4 compile @@ -36,8 +36,14 @@ org.openmuc j62056 - 2.1.0 + 2.2.0 compile + + + org.openmuc + jrxtx + + @@ -54,7 +60,7 @@ generate-sources - src/3rdparty + src/3rdparty/java diff --git a/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java index 1134310e850a6..2f9a5dec1190a 100644 --- a/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java +++ b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java @@ -25,7 +25,6 @@ import javax.measure.Quantity; import javax.measure.Unit; -import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.DefaultLocation; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -101,7 +100,7 @@ public void initialize() { "Parameter 'port' is mandatory and must be configured"); } else { byte[] pullSequence = config.initMessage == null ? null - : HexUtils.hexToBytes(StringUtils.deleteWhitespace(config.initMessage)); + : HexUtils.hexToBytes(config.initMessage.replaceAll("\\s+", "")); int baudrate = config.baudrate == null ? Baudrate.AUTO.getBaudrate() : Baudrate.fromString(config.baudrate).getBaudrate(); this.conformity = config.conformity == null ? Conformity.NONE : Conformity.valueOf(config.conformity); diff --git a/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java index bc51e0fed945d..23cbc214d563e 100644 --- a/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java +++ b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.smartmeter.internal.helper; -import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.io.transport.serial.SerialPort; @@ -66,7 +65,7 @@ public String toString() { */ public static SerialParameter fromString(String params) { try { - return valueOf("_" + StringUtils.upperCase(params)); + return valueOf("_" + params.toUpperCase()); } catch (IllegalArgumentException e) { return SerialParameter._8N1; } diff --git a/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java b/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java index 32ea25ca4bb3b..c80acdc8f8c4c 100644 --- a/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java +++ b/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java @@ -54,10 +54,10 @@ public void testContinousReading() throws Exception { MeterDevice meter = getMeterDevice(connector); MeterValueListener changeListener = Mockito.mock(MeterValueListener.class); meter.addValueChangeListener(changeListener); - Disposable disposable = meter.readValues(5000, Executors.newScheduledThreadPool(1), period); + long executionTime = period.toMillis() * executionCount; + Disposable disposable = meter.readValues(executionTime, Executors.newScheduledThreadPool(1), period); try { - verify(changeListener, after(executionCount * period.toMillis() + period.toMillis() / 2).never()) - .errorOccurred(any()); + verify(changeListener, after(executionTime + period.toMillis() / 2 + 50).never()).errorOccurred(any()); verify(changeListener, times(executionCount)).valueChanged(any()); } finally { disposable.dispose(); @@ -90,7 +90,7 @@ public void testTimeoutHandling() { final int timeout = 5000; MockMeterReaderConnector connector = spy(getMockedConnector(true, () -> { try { - Thread.sleep(timeout + 2000); + Thread.sleep(timeout); } catch (InterruptedException e) { } return new Object(); @@ -98,9 +98,9 @@ public void testTimeoutHandling() { MeterDevice meter = getMeterDevice(connector); MeterValueListener changeListener = Mockito.mock(MeterValueListener.class); meter.addValueChangeListener(changeListener); - Disposable disposable = meter.readValues(5000, Executors.newScheduledThreadPool(2), period); + Disposable disposable = meter.readValues(timeout / 2, Executors.newScheduledThreadPool(2), period); try { - verify(changeListener, after(timeout + 3000).times(1)).errorOccurred(any(TimeoutException.class)); + verify(changeListener, timeout(timeout)).errorOccurred(any(TimeoutException.class)); } finally { disposable.dispose(); } @@ -112,7 +112,7 @@ public void shouldNotReportToFallbackException() { final int timeout = 5000; MockMeterReaderConnector connector = spy(getMockedConnector(true, () -> { try { - Thread.sleep(timeout + 2000); + Thread.sleep(timeout); } catch (InterruptedException e) { } throw new RuntimeException(new IOException("fucked up")); @@ -122,9 +122,9 @@ public void shouldNotReportToFallbackException() { RxJavaPlugins.setErrorHandler(errorHandler); MeterValueListener changeListener = Mockito.mock(MeterValueListener.class); meter.addValueChangeListener(changeListener); - Disposable disposable = meter.readValues(5000, Executors.newScheduledThreadPool(2), period); + Disposable disposable = meter.readValues(timeout / 2, Executors.newScheduledThreadPool(2), period); try { - verify(changeListener, after(timeout + 3000).times(1)).errorOccurred(any(TimeoutException.class)); + verify(changeListener, timeout(timeout)).errorOccurred(any(TimeoutException.class)); verifyNoMoreInteractions(errorHandler); } finally { disposable.dispose(); diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java index cd95c41ea9aa4..638b8383d081b 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java @@ -73,6 +73,7 @@ import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -184,10 +185,11 @@ public void initialize() { private void createHttpClient() { // let's create the right http client + String clientName = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); if (thingConfig.isDevMode()) { - this.httpClient = new HttpClient(new SslContextFactory.Client(true)); + this.httpClient = httpClientFactory.createHttpClient(clientName, new SslContextFactory.Client(true)); } else { - this.httpClient = httpClientFactory.createHttpClient("somfy_" + thing.getUID().getId()); + this.httpClient = httpClientFactory.createHttpClient(clientName); } try { diff --git a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoBindingConstants.java b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoBindingConstants.java index 7f7e66a2a1484..4317f5301610f 100644 --- a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoBindingConstants.java +++ b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoBindingConstants.java @@ -24,7 +24,7 @@ @NonNullByDefault public class SomneoBindingConstants { - private static final String BINDING_ID = "somneo"; + public static final String BINDING_ID = "somneo"; // List of all Thing properties public static final String PROPERTY_VENDOR_NAME = "Philips"; diff --git a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoHandlerFactory.java b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoHandlerFactory.java index d7bbdd3fd95dd..17f8eda306ff0 100644 --- a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoHandlerFactory.java +++ b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoHandlerFactory.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.somneo.internal; -import static org.openhab.binding.somneo.internal.SomneoBindingConstants.THING_TYPE_HF367X; +import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*; import java.util.Set; @@ -20,6 +20,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; @@ -50,11 +51,14 @@ public class SomneoHandlerFactory extends BaseThingHandlerFactory implements Htt private final SomneoPresetStateDescriptionProvider provider; @Activate - public SomneoHandlerFactory(@Reference SomneoPresetStateDescriptionProvider provider) { + public SomneoHandlerFactory(final @Reference HttpClientFactory httpClientFactory, + @Reference SomneoPresetStateDescriptionProvider provider) { this.provider = provider; - this.secureClient = new HttpClient(new SslContextFactory.Client(false)); - this.insecureClient = new HttpClient(new SslContextFactory.Client(true)); + this.secureClient = httpClientFactory.createHttpClient(BINDING_ID + "-secure", + new SslContextFactory.Client(false)); + this.insecureClient = httpClientFactory.createHttpClient(BINDING_ID + "-insecure", + new SslContextFactory.Client(true)); try { this.secureClient.start(); diff --git a/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosEntry.java b/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosEntry.java index e950b90f3591d..6acea10587e79 100644 --- a/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosEntry.java +++ b/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosEntry.java @@ -14,9 +14,9 @@ import java.io.Serializable; -import org.apache.commons.lang3.StringEscapeUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.sonos.internal.util.StringUtils; /** * The {@link SonosEntry} is a datastructure to describe @@ -120,7 +120,7 @@ public String getAlbum() { * @return the URI for the album art. */ public String getAlbumArtUri() { - return StringEscapeUtils.unescapeXml(albumArtUri); + return StringUtils.unEscapeXml(albumArtUri); } /** diff --git a/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosXMLParser.java b/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosXMLParser.java index 6587b3ea1d831..a1716a8d848cf 100644 --- a/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosXMLParser.java +++ b/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/SonosXMLParser.java @@ -25,9 +25,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.StringEscapeUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.sonos.internal.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.Attributes; @@ -1084,7 +1084,7 @@ public static String compileMetadataString(SonosEntry entry) { upnpClass = resourceMetaData.getUpnpClass(); } - title = StringEscapeUtils.escapeXml(title); + title = StringUtils.escapeXml(title); String metadata = METADATA_FORMAT.format(new Object[] { id, parentId, title, upnpClass, desc }); diff --git a/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/util/StringUtils.java b/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/util/StringUtils.java new file mode 100644 index 0000000000000..4600b1824fff5 --- /dev/null +++ b/bundles/org.openhab.binding.sonos/src/main/java/org/openhab/binding/sonos/internal/util/StringUtils.java @@ -0,0 +1,60 @@ +/** + * 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.sonos.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link StringUtils} class defines some static string utility methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class StringUtils { + + /** + * Simple method to escape XML special characters in String. + * There are five XML Special characters which needs to be escaped : + * & - & + * < - < + * > - > + * " - " + * ' - ' + */ + public static String escapeXml(String xml) { + xml = xml.replaceAll("&", "&"); + xml = xml.replaceAll("<", "<"); + xml = xml.replaceAll(">", ">"); + xml = xml.replaceAll("\"", """); + xml = xml.replaceAll("'", "'"); + return xml; + } + + /** + * Simple method to un escape XML special characters in String. + * There are five XML Special characters which needs to be escaped : + * & - & + * < - < + * > - > + * " - " + * ' - ' + */ + public static String unEscapeXml(String xml) { + xml = xml.replaceAll("&", "&"); + xml = xml.replaceAll("<", "<"); + xml = xml.replaceAll(">", ">"); + xml = xml.replaceAll(""", "\""); + xml = xml.replaceAll("'", "'"); + return xml; + } +} diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/TapoControlHandlerFactory.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/TapoControlHandlerFactory.java index 6eda41b101843..5d0b2086e4222 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/TapoControlHandlerFactory.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/TapoControlHandlerFactory.java @@ -27,6 +27,7 @@ import org.openhab.binding.tapocontrol.internal.device.TapoSmartBulb; import org.openhab.binding.tapocontrol.internal.device.TapoSmartPlug; import org.openhab.binding.tapocontrol.internal.device.TapoUniversalDevice; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -37,6 +38,7 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,9 +56,9 @@ public class TapoControlHandlerFactory extends BaseThingHandlerFactory { private final HttpClient httpClient; @Activate - public TapoControlHandlerFactory() { + public TapoControlHandlerFactory(final @Reference HttpClientFactory httpClientFactory) { // create new httpClient - httpClient = new HttpClient(new SslContextFactory.Client()); + httpClient = httpClientFactory.createHttpClient(BINDING_ID, new SslContextFactory.Client()); httpClient.setFollowRedirects(false); httpClient.setMaxConnectionsPerDestination(HTTP_MAX_CONNECTIONS); httpClient.setMaxRequestsQueuedPerDestination(HTTP_MAX_QUEUED_REQUESTS); diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java index 826f710081032..beec69d7f9fd3 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java @@ -22,7 +22,6 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.websocket.api.Session; @@ -32,6 +31,8 @@ import org.eclipse.jetty.websocket.client.WebSocketClient; import org.openhab.binding.tesla.internal.protocol.Event; import org.openhab.core.io.net.http.WebSocketFactory; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,7 +55,6 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL private String endpointId; protected WebSocketFactory webSocketFactory; - private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(); private WebSocketClient client; private ConnectionState connectionState = ConnectionState.CLOSED; @@ -62,11 +62,12 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL private EventHandler eventHandler; private final Gson gson = new Gson(); - public TeslaEventEndpoint(WebSocketFactory webSocketFactory) { + public TeslaEventEndpoint(ThingUID uid, WebSocketFactory webSocketFactory) { try { - this.endpointId = "TeslaEventEndpoint-" + INSTANCE_COUNTER.incrementAndGet(); + this.endpointId = "TeslaEventEndpoint-" + uid.getAsString(); - client = webSocketFactory.createWebSocketClient(endpointId); + String name = ThingWebClientUtil.buildWebClientConsumerName(uid, null); + client = webSocketFactory.createWebSocketClient(name); this.client.setConnectTimeout(TIMEOUT_MILLISECONDS); this.client.setMaxIdleTimeout(IDLE_TIMEOUT_MILLISECONDS); } catch (Exception e) { diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java index 6a2b51c4a6468..8903a96102049 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java @@ -193,7 +193,7 @@ public void initialize() { if (enableEvents) { if (eventThread == null) { - eventThread = new Thread(eventRunnable, "openHAB-Tesla-Events-" + getThing().getUID()); + eventThread = new Thread(eventRunnable, "OH-binding-" + getThing().getUID() + "-events"); eventThread.start(); } } @@ -1130,7 +1130,7 @@ protected BigDecimal roundBigDecimal(BigDecimal value) { @Override public void run() { - eventEndpoint = new TeslaEventEndpoint(webSocketFactory); + eventEndpoint = new TeslaEventEndpoint(getThing().getUID(), webSocketFactory); eventEndpoint.addEventHandler(new TeslaEventEndpoint.EventHandler() { @Override public void handleEvent(Event event) { diff --git a/bundles/org.openhab.binding.tr064/README.md b/bundles/org.openhab.binding.tr064/README.md index d154350b9a4fb..8f777e4275e8e 100644 --- a/bundles/org.openhab.binding.tr064/README.md +++ b/bundles/org.openhab.binding.tr064/README.md @@ -38,6 +38,10 @@ If you only configured password authentication for your device, the `user` param The second credential parameter is `password`, which is mandatory. For security reasons it is highly recommended to set both, username and password. +Another optional and advanced configuration parameter is `timeout`. +This parameter applies to all requests to the device (SOAP requests, phonebook retrieval, call lists, ...). +It only needs to be changed from the default value of `5` seconds when the remote device is unexpectedly slow and does not respond within that time. + ### `fritzbox` The `fritzbox` devices can give additional informations in dedicated channels, controlled @@ -70,12 +74,19 @@ If the `PHONEBOOK` profile shall be used, it is necessary to retrieve the phoneb The `phonebookInterval` is used to set the refresh cycle for phonebooks. It defaults to 600 seconds, and it can be set to 0 if phonebooks are not used. -Some parameters (e.g. `macOnline`, `wanBlockIPs`) accept lists. +Some parameters (e.g. `macOnline`, `wanBlockIPs`) accept lists. List items are configured one per line in the UI, or are comma separated values when using textual config. These parameters that accept list can also contain comments. Comments are separated from the value with a '#' (e.g. `192.168.0.77 # Daughter's iPhone`). The full string is used for the channel label. +Two more advanced parameters are used for the backup thing action. +The `backupDirectory` is the directory where the backup files are stored. +The default value is the userdata directory. +The `backupPassword` is used to encrypt the backup file. +This is equivalent to setting a password in the UI. +If no password is given, the user password (parameter `password`) is used. + ### `subdevice`, `subdeviceLan` Additional informations (i.e. channels) are available in subdevices of the bridge. @@ -117,18 +128,17 @@ The call-types are the same as provided by the FritzBox, i.e. `1` (inbound), `2` ### LAN `subdeviceLan` channels -| channel | item-type | advanced | description | -|----------------------------|---------------------------|:--------:|----------------------------------------------------------------| -| `wifi24GHzEnable` | `Switch` | | Enable/Disable the 2.4 GHz WiFi device. | -| `wifi5GHzEnable` | `Switch` | | Enable/Disable the 5.0 GHz WiFi device. | -| `wifiGuestEnable` | `Switch` | | Enable/Disable the guest WiFi. | -| `macOnline` | `Switch` | x | Online status of the device with the given MAC | -| `macIP` | `String` | x | IP of the device with the given MAC | -| `macSignalStrength1` | `Number` | x | Wifi Signal Strength of the device with the given MAC. This is set in case the Device is connected to 2.4Ghz | -| `macSpeed1` | `Number:DataTransferRate` | x | Wifi Speed of the device with the given MAC. This is set in case the Device is connected to 2.4Ghz | -| `macSignalStrength2` | `Number` | x | Wifi Signal Strength of the device with the given MAC. This is set in case the Device is connected to 5Ghz | -| `macSpeed2` | `Number:DataTransferRate` | x | Wifi Speed of the device with the given MAC. This is set in case the Device is connected to 5Ghz | - +| channel | item-type | advanced | description | +|----------------------|---------------------------|:--------:|--------------------------------------------------------------------------------------------------------------| +| `wifi24GHzEnable` | `Switch` | | Enable/Disable the 2.4 GHz WiFi device. | +| `wifi5GHzEnable` | `Switch` | | Enable/Disable the 5.0 GHz WiFi device. | +| `wifiGuestEnable` | `Switch` | | Enable/Disable the guest WiFi. | +| `macOnline` | `Switch` | x | Online status of the device with the given MAC | +| `macOnlineIpAddress` | `String` | x | IP of the MAC (uses same parameter as `macOnline`) | +| `macSignalStrength1` | `Number` | x | Wifi Signal Strength of the device with the given MAC. This is set in case the Device is connected to 2.4Ghz | +| `macSpeed1` | `Number:DataTransferRate` | x | Wifi Speed of the device with the given MAC. This is set in case the Device is connected to 2.4Ghz | +| `macSignalStrength2` | `Number` | x | Wifi Signal Strength of the device with the given MAC. This is set in case the Device is connected to 5Ghz | +| `macSpeed2` | `Number:DataTransferRate` | x | Wifi Speed of the device with the given MAC. This is set in case the Device is connected to 5Ghz | Older FritzBox devices may not support 5 GHz WiFi. In this case you have to use the `wifi5GHzEnable` channel for switching the guest WiFi. @@ -140,34 +150,36 @@ In this case you have to use the `wifi5GHzEnable` channel for switching the gues | `pppUptime` | `Number:Time` | | Uptime (if using PPP) | | `wanConnectionStatus` | `String` | | Connection Status | | `wanPppConnectionStatus` | `String` | | Connection Status (if using PPP) | -| `wanIpAddress` | `String` | x | WAN IP Address | -| `wanPppIpAddress` | `String` | x | WAN IP Address (if using PPP) | +| `wanIpAddress` | `String` | x | WAN IP Address | +| `wanPppIpAddress` | `String` | x | WAN IP Address (if using PPP) | ### WAN `subdevice` channels | channel | item-type | advanced | description | |----------------------------|---------------------------|:--------:|----------------------------------------------------------------| -| `dslCRCErrors` | `Number:Dimensionless` | x | DSL CRC Errors | -| `dslDownstreamMaxRate` | `Number:DataTransferRate` | x | DSL Max Downstream Rate | -| `dslDownstreamCurrRate` | `Number:DataTransferRate` | x | DSL Curr. Downstream Rate | -| `dslDownstreamNoiseMargin` | `Number:Dimensionless` | x | DSL Downstream Noise Margin | -| `dslDownstreamAttenuation` | `Number:Dimensionless` | x | DSL Downstream Attenuation | +| `dslCRCErrors` | `Number:Dimensionless` | x | DSL CRC Errors | +| `dslDownstreamMaxRate` | `Number:DataTransferRate` | x | DSL Max Downstream Rate | +| `dslDownstreamCurrRate` | `Number:DataTransferRate` | x | DSL Curr. Downstream Rate | +| `dslDownstreamNoiseMargin` | `Number:Dimensionless` | x | DSL Downstream Noise Margin | +| `dslDownstreamAttenuation` | `Number:Dimensionless` | x | DSL Downstream Attenuation | | `dslEnable` | `Switch` | | DSL Enable | -| `dslFECErrors` | `Number:Dimensionless` | x | DSL FEC Errors | -| `dslHECErrors` | `Number:Dimensionless` | x | DSL HEC Errors | -| `dslStatus` | `Switch` | | DSL Status | -| `dslUpstreamMaxRate` | `Number:DataTransferRate` | x | DSL Max Upstream Rate | -| `dslUpstreamCurrRate` | `Number:DataTransferRate` | x | DSL Curr. Upstream Rate | -| `dslUpstreamNoiseMargin` | `Number:Dimensionless` | x | DSL Upstream Noise Margin | -| `dslUpstreamAttenuation` | `Number:Dimensionless` | x | DSL Upstream Attenuation | -| `wanAccessType` | `String` | x | Access Type | -| `wanMaxDownstreamRate` | `Number:DataTransferRate` | x | Max. Downstream Rate | -| `wanMaxUpstreamRate` | `Number:DataTransferRate` | x | Max. Upstream Rate | -| `wanPhysicalLinkStatus` | `String` | x | Link Status | -| `wanTotalBytesReceived` | `Number:DataAmount` | x | Total Bytes Received | -| `wanTotalBytesSent` | `Number:DataAmount` | x | Total Bytes Sent | +| `dslFECErrors` | `Number:Dimensionless` | x | DSL FEC Errors | +| `dslHECErrors` | `Number:Dimensionless` | x | DSL HEC Errors | +| `dslStatus` | `String` | | DSL Status | +| `dslUpstreamMaxRate` | `Number:DataTransferRate` | x | DSL Max Upstream Rate | +| `dslUpstreamCurrRate` | `Number:DataTransferRate` | x | DSL Curr. Upstream Rate | +| `dslUpstreamNoiseMargin` | `Number:Dimensionless` | x | DSL Upstream Noise Margin | +| `dslUpstreamAttenuation` | `Number:Dimensionless` | x | DSL Upstream Attenuation | +| `wanAccessType` | `String` | x | Access Type | +| `wanMaxDownstreamRate` | `Number:DataTransferRate` | x | Max. Downstream Rate | +| `wanMaxUpstreamRate` | `Number:DataTransferRate` | x | Max. Upstream Rate | +| `wanCurrentDownstreamRate` | `Number:DataTransferRate` | x | Current Downstream Rate (average last 15 seconds) | +| `wanCurrentUpstreamRate` | `Number:DataTransferRate` | x | Current Upstream Rate (average last 15 seconds) | +| `wanPhysicalLinkStatus` | `String` | x | Link Status | +| `wanTotalBytesReceived` | `Number:DataAmount` | x | Total Bytes Received | +| `wanTotalBytesSent` | `Number:DataAmount` | x | Total Bytes Sent | -**Note:** AVM Fritzbox devices use 4-byte-unsigned-integers for `wanTotalBytesReceived` and `wanTotalBytesSent`, because of that the counters are reset after around 4GB data. +**Note:** AVM FritzBox devices use 4-byte-unsigned-integers for `wanTotalBytesReceived` and `wanTotalBytesSent`, because of that the counters are reset after around 4GB data. ## `PHONEBOOK` Profile @@ -179,12 +191,15 @@ If only a specific phonebook from the device should be used, this can be specifi The default is to use all available phonebooks from the specified thing. In case the format of the number in the phonebook and the format of the number from the channel are different (e.g. regarding country prefixes), the `matchCount` parameter can be used. The configured `matchCount` is counted from the right end and denotes the number of matching characters needed to consider this number as matching. +Negative `matchCount` values skip digits from the left (e.g. if the input number is `033998005671` a `matchCount` of `-1` would remove the leading `0` ). A `matchCount` of `0` is considered as "match everything". Matching is done on normalized versions of the numbers that have all characters except digits, '+' and '*' removed. There is an optional configuration parameter called `phoneNumberIndex` that should be used when linking to a channel with item type `StringListType` (like `Call` in the example below), which determines which number to be picked, i.e. to or from. ## Rule Action +### Phonebook lookup + The phonebooks of a `fritzbox` thing can be used to lookup a number from rules via a thing action: `String name = phonebookLookup(String number, String phonebook, int matchCount)` @@ -192,17 +207,36 @@ The phonebooks of a `fritzbox` thing can be used to lookup a number from rules v `phonebook` and `matchCount` are optional parameters. You can omit one or both of these parameters. The configured `matchCount` is counted from the right end and denotes the number of matching characters needed to consider this number as matching. -A `matchCount` of `0` is considered as "match everything" and is used as default if no other value is given. +Negative `matchCount` values skip digits from the left (e.g. if the input number is `033998005671` a `matchCount` of `-1` would remove the leading `0` ). +A `matchCount` of `0` is considered as "match everything" and is used as default if no other value is given. As in the phonebook profile, matching is done on normalized versions of the numbers that have all characters except digits, '+' and '*' removed. The return value is either the phonebook entry (if found) or the input number. Example (use all phonebooks, match 5 digits from right): -``` +```java val tr064Actions = getActions("tr064","tr064:fritzbox:2a28aee1ee") val result = tr064Actions.phonebookLookup("49157712341234", 5) ``` +### Fritz!Box Backup + +The `fritzbox` things can create configuration backups of the Fritz!Box. + +The default configuration of the Fritz!Boxes requires 2-factor-authentication for creating backups. +If you see a `Failed to get configuration backup URL: HTTP-Response-Code 500 (Internal Server Error), SOAP-Fault: 866 (second factor authentication required)` warning, you need to disable 2-actor authentication. +But beware: depending on your configuration this might be a security issue. +The setting can be found under "System -> FRITZ!Box Users -> Login to the Home Network -> Confirm". + +When executed, the action requests a backup file with the given password in the configured path. +The backup file is names as `ThingFriendlyName dd.mm.yyyy HHMM.export` (e.g. `My FritzBox 18.06.2021 1720.export`). +Files with the same name will be overwritten, so make sure that you trigger the rules at different times if your devices have the same friendly name. + +```java +val tr064Actions = getActions("tr064","tr064:fritzbox:2a28aee1ee") +tr064Actions.createConfigurationBackup() +``` + ## A note on textual configuration Textual configuration through a `.things` file is possible but, at present, strongly discouraged because it is significantly more error-prone @@ -230,7 +264,6 @@ The channel are automatically generated and it is simpler to use the Main User I ``` Switch PresXX "[%s]" {channel="tr064:subdeviceLan:rootuid:LAN:macOnline_XX_3AXX_3AXX_3AXX_3AXX_3AXX"} Switch PresYY "[%s]" {channel="tr064:subdeviceLan:rootuid:LAN:macOnline_YY_3AYY_3AYY_3AYY_3AYY_3AYY"} - ``` Example `*.items` file using the `PHONEBOOK` profile for storing the name of a caller in an item. it matches 8 digits from the right of the "from" number (note the escaping of `:` to `_3A`): diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookActions.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java similarity index 58% rename from bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookActions.java rename to bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java index 7a907277ffdc6..c905ae6ae1a44 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookActions.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java @@ -10,14 +10,31 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tr064.internal.phonebook; +package org.openhab.binding.tr064.internal; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Collection; +import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.xml.soap.SOAPMessage; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.tr064.internal.Tr064RootHandler; +import org.eclipse.jetty.client.api.ContentResponse; +import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDServiceType; +import org.openhab.binding.tr064.internal.phonebook.Phonebook; +import org.openhab.binding.tr064.internal.soap.SOAPRequest; +import org.openhab.binding.tr064.internal.util.SCPDUtil; +import org.openhab.binding.tr064.internal.util.Util; import org.openhab.core.automation.annotation.ActionInput; import org.openhab.core.automation.annotation.ActionOutput; import org.openhab.core.automation.annotation.RuleAction; @@ -28,14 +45,16 @@ import org.slf4j.LoggerFactory; /** - * The {@link PhonebookActions} is responsible for handling phonebook actions + * The {@link FritzboxActions} is responsible for handling phone book actions * * @author Jan N. Klug - Initial contribution */ @ThingActionsScope(name = "tr064") @NonNullByDefault -public class PhonebookActions implements ThingActions { - private final Logger logger = LoggerFactory.getLogger(PhonebookActions.class); +public class FritzboxActions implements ThingActions { + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy_HHmm"); + + private final Logger logger = LoggerFactory.getLogger(FritzboxActions.class); private @Nullable Tr064RootHandler handler; @@ -76,16 +95,67 @@ public class PhonebookActions implements ThingActions { } else { int matchCountInt = matchCount == null ? 0 : matchCount; if (phonebook != null && !phonebook.isEmpty()) { - return handler.getPhonebookByName(phonebook).flatMap(p -> p.lookupNumber(phonenumber, matchCountInt)) - .orElse(phonenumber); + return Objects.requireNonNull(handler.getPhonebookByName(phonebook) + .flatMap(p -> p.lookupNumber(phonenumber, matchCountInt)).orElse(phonenumber)); } else { Collection phonebooks = handler.getPhonebooks(); - return phonebooks.stream().map(p -> p.lookupNumber(phonenumber, matchCountInt)) - .filter(Optional::isPresent).map(Optional::get).findAny().orElse(phonenumber); + return Objects.requireNonNull(phonebooks.stream().map(p -> p.lookupNumber(phonenumber, matchCountInt)) + .filter(Optional::isPresent).map(Optional::get).findAny().orElse(phonenumber)); } } } + @RuleAction(label = "create configuration backup", description = "Creates a configuration backup") + public void createConfigurationBackup() { + Tr064RootHandler handler = this.handler; + + if (handler == null) { + logger.warn("TR064 action service ThingHandler is null!"); + return; + } + + SCPDUtil scpdUtil = handler.getSCPDUtil(); + if (scpdUtil == null) { + logger.warn("Could not get SCPDUtil, handler seems to be uninitialized."); + return; + } + + Optional scpdService = scpdUtil.getDevice("") + .flatMap(deviceType -> deviceType.getServiceList().stream().filter( + service -> service.getServiceId().equals("urn:DeviceConfig-com:serviceId:DeviceConfig1")) + .findFirst()); + if (scpdService.isEmpty()) { + logger.warn("Could not get service."); + return; + } + + BackupConfiguration configuration = handler.getBackupConfiguration(); + try { + SOAPRequest soapRequest = new SOAPRequest(scpdService.get(), "X_AVM-DE_GetConfigFile", + Map.of("NewX_AVM-DE_Password", configuration.password)); + SOAPMessage soapMessage = handler.getSOAPConnector().doSOAPRequestUncached(soapRequest); + String configBackupURL = Util.getSOAPElement(soapMessage, "NewX_AVM-DE_ConfigFileUrl") + .orElseThrow(() -> new Tr064CommunicationException("Empty URL")); + + ContentResponse content = handler.getUrl(configBackupURL); + + String fileName = String.format("%s %s.export", handler.getFriendlyName(), + DATE_TIME_FORMATTER.format(LocalDateTime.now())); + Path filePath = FileSystems.getDefault().getPath(configuration.directory, fileName); + Path folder = filePath.getParent(); + if (folder != null) { + Files.createDirectories(folder); + } + Files.write(filePath, content.getContent()); + } catch (Tr064CommunicationException e) { + logger.warn("Failed to get configuration backup URL: {}", e.getMessage()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + logger.warn("Failed to get remote backup file: {}", e.getMessage()); + } catch (IOException e) { + logger.warn("Failed to create backup file: {}", e.getMessage()); + } + } + public static String phonebookLookup(ThingActions actions, @Nullable String phonenumber, @Nullable Integer matchCount) { return phonebookLookup(actions, phonenumber, null, matchCount); @@ -102,18 +172,23 @@ public static String phonebookLookup(ThingActions actions, @Nullable String phon public static String phonebookLookup(ThingActions actions, @Nullable String phonenumber, @Nullable String phonebook, @Nullable Integer matchCount) { - return ((PhonebookActions) actions).phonebookLookup(phonenumber, phonebook, matchCount); + return ((FritzboxActions) actions).phonebookLookup(phonenumber, phonebook, matchCount); + } + + public static void createConfigurationBackup(ThingActions actions) { + ((FritzboxActions) actions).createConfigurationBackup(); } @Override public void setThingHandler(@Nullable ThingHandler handler) { - if (handler instanceof Tr064RootHandler) { - this.handler = (Tr064RootHandler) handler; - } + this.handler = (Tr064RootHandler) handler; } @Override public @Nullable ThingHandler getThingHandler() { return handler; } + + public record BackupConfiguration(String directory, String password) { + } } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064CommunicationException.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064CommunicationException.java index 089c65f50534a..c144aa855ad69 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064CommunicationException.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064CommunicationException.java @@ -37,7 +37,7 @@ public Tr064CommunicationException(String s, Integer httpError, String soapError super(s); this.httpError = httpError; this.soapError = soapError; - }; + } public String getSoapError() { return soapError; diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DiscoveryService.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DiscoveryService.java index 7084f1602829d..f209416d04481 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DiscoveryService.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DiscoveryService.java @@ -14,9 +14,7 @@ import static org.openhab.binding.tr064.internal.Tr064BindingConstants.*; -import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -44,7 +42,7 @@ @NonNullByDefault public class Tr064DiscoveryService extends AbstractDiscoveryService implements ThingHandlerService { private static final int SEARCH_TIME = 5; - public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_SUBDEVICE); + public static final Set SUPPORTED_THING_TYPES = Set.of(THING_TYPE_SUBDEVICE); private final Logger logger = LoggerFactory.getLogger(Tr064DiscoveryService.class); private @Nullable Tr064RootHandler bridgeHandler; @@ -101,13 +99,12 @@ public void startScan() { } ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, UIDUtils.encode(udn)); - Map properties = new HashMap<>(2); - properties.put("uuid", udn); - properties.put("deviceType", device.getDeviceType()); - - DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel(device.getFriendlyName()) - .withBridge(bridgeHandler.getThing().getUID()).withProperties(properties) - .withRepresentationProperty("uuid").build(); + DiscoveryResult result = DiscoveryResultBuilder.create(thingUID) // + .withLabel(device.getFriendlyName()) // + .withBridge(bridgeUID) // + .withProperties(Map.of("uuid", udn, "deviceType", device.getDeviceType())) // + .withRepresentationProperty("uuid") // + .build(); thingDiscovered(result); } }); diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DynamicStateDescriptionProvider.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DynamicStateDescriptionProvider.java deleted file mode 100644 index 2f900ce714011..0000000000000 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064DynamicStateDescriptionProvider.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 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.tr064.internal; - -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.thing.Channel; -import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.ThingUID; -import org.openhab.core.thing.type.DynamicStateDescriptionProvider; -import org.openhab.core.types.StateDescription; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Dynamic channel state description provider. - * Overrides the state description for the controls, which receive its configuration in the runtime. - * - * @author Jan N. Klug - Initial contribution - */ -@NonNullByDefault -@Component(service = { DynamicStateDescriptionProvider.class, Tr064DynamicStateDescriptionProvider.class }) -public class Tr064DynamicStateDescriptionProvider implements DynamicStateDescriptionProvider { - private final Logger logger = LoggerFactory.getLogger(Tr064DynamicStateDescriptionProvider.class); - private final Map descriptions = new ConcurrentHashMap<>(); - - /** - * Set a state description for a channel. This description will be used when preparing the channel state by - * the framework for presentation. A previous description, if existed, will be replaced. - * - * @param channelUID channel UID - * @param description state description for the channel - */ - public void setDescription(ChannelUID channelUID, StateDescription description) { - logger.trace("adding state description for channel {}", channelUID); - descriptions.put(channelUID, description); - } - - /** - * remove all descriptions for a given thing - * - * @param thingUID the thing's UID - */ - public void removeDescriptionsForThing(ThingUID thingUID) { - logger.trace("removing state description for thing {}", thingUID); - descriptions.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID)); - } - - @Override - public @Nullable StateDescription getStateDescription(Channel channel, - @Nullable StateDescription originalStateDescription, @Nullable Locale locale) { - if (descriptions.containsKey(channel.getUID())) { - return descriptions.get(channel.getUID()); - } else { - return null; - } - } -} diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064HandlerFactory.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064HandlerFactory.java index 2d23cde6890c0..a23ae326485e1 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064HandlerFactory.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064HandlerFactory.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.tr064.internal; -import static org.openhab.binding.tr064.internal.Tr064BindingConstants.THING_TYPE_FRITZBOX; +import static org.openhab.binding.tr064.internal.Tr064BindingConstants.*; import java.util.Set; import java.util.stream.Collectors; @@ -23,6 +23,7 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.tr064.internal.phonebook.PhonebookProfileFactory; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -61,11 +62,12 @@ public class Tr064HandlerFactory extends BaseThingHandlerFactory { @Activate public Tr064HandlerFactory(@Reference Tr064ChannelTypeProvider channelTypeProvider, - @Reference PhonebookProfileFactory phonebookProfileFactory) { + @Reference PhonebookProfileFactory phonebookProfileFactory, + final @Reference HttpClientFactory httpClientFactory) { this.channelTypeProvider = channelTypeProvider; this.phonebookProfileFactory = phonebookProfileFactory; // use an insecure client (i.e. without verifying the certificate) - this.httpClient = new HttpClient(new SslContextFactory.Client(true)); + this.httpClient = httpClientFactory.createHttpClient(BINDING_ID, new SslContextFactory.Client(true)); try { this.httpClient.start(); } catch (Exception e) { @@ -96,7 +98,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (Tr064RootHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { Tr064RootHandler handler = new Tr064RootHandler((Bridge) thing, httpClient); - if (thingTypeUID.equals(THING_TYPE_FRITZBOX)) { + if (THING_TYPE_FRITZBOX.equals(thingTypeUID)) { phonebookProfileFactory.registerPhonebookProvider(handler); } return handler; diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064RootHandler.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064RootHandler.java index 0023b87da1830..a80dd8e6ec768 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064RootHandler.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064RootHandler.java @@ -23,10 +23,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -38,6 +41,7 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; +import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.util.DigestAuthentication; import org.openhab.binding.tr064.internal.config.Tr064ChannelConfig; import org.openhab.binding.tr064.internal.config.Tr064RootConfiguration; @@ -45,7 +49,6 @@ import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDServiceType; import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDActionType; import org.openhab.binding.tr064.internal.phonebook.Phonebook; -import org.openhab.binding.tr064.internal.phonebook.PhonebookActions; import org.openhab.binding.tr064.internal.phonebook.PhonebookProvider; import org.openhab.binding.tr064.internal.phonebook.Tr064PhonebookImpl; import org.openhab.binding.tr064.internal.soap.SOAPConnector; @@ -61,6 +64,7 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.types.Command; @@ -85,12 +89,15 @@ public class Tr064RootHandler extends BaseBridgeHandler implements PhonebookProv private final Logger logger = LoggerFactory.getLogger(Tr064RootHandler.class); private final HttpClient httpClient; - private Tr064RootConfiguration config = new Tr064RootConfiguration(); - private String deviceType = ""; - private @Nullable SCPDUtil scpdUtil; private SOAPConnector soapConnector; + + // these are set when the config is available + private Tr064RootConfiguration config = new Tr064RootConfiguration(); private String endpointBaseURL = ""; + private int timeout = Tr064RootConfiguration.DEFAULT_HTTP_TIMEOUT; + + private String deviceType = ""; private final Map channels = new HashMap<>(); // caching is used to prevent excessive calls to the same action @@ -106,7 +113,7 @@ public class Tr064RootHandler extends BaseBridgeHandler implements PhonebookProv Tr064RootHandler(Bridge bridge, HttpClient httpClient) { super(bridge); this.httpClient = httpClient; - this.soapConnector = new SOAPConnector(httpClient, endpointBaseURL); + this.soapConnector = new SOAPConnector(httpClient, endpointBaseURL, timeout); } @Override @@ -147,7 +154,8 @@ public void initialize() { } endpointBaseURL = "http://" + config.host + ":49000"; - soapConnector = new SOAPConnector(httpClient, endpointBaseURL); + soapConnector = new SOAPConnector(httpClient, endpointBaseURL, timeout); + timeout = config.timeout; updateStatus(ThingStatus.UNKNOWN); connectFuture = scheduler.scheduleWithFixedDelay(this::internalInitialize, 0, RETRY_INTERVAL, TimeUnit.SECONDS); @@ -158,7 +166,7 @@ public void initialize() { */ private void internalInitialize() { try { - scpdUtil = new SCPDUtil(httpClient, endpointBaseURL); + scpdUtil = new SCPDUtil(httpClient, endpointBaseURL, timeout); } catch (SCPDException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "could not get device definitions from " + config.host); @@ -172,8 +180,9 @@ private void internalInitialize() { ThingBuilder thingBuilder = editThing(); thingBuilder.withoutChannels(thing.getChannels()); final SCPDUtil scpdUtil = this.scpdUtil; - if (scpdUtil != null) { - Util.checkAvailableChannels(thing, thingBuilder, scpdUtil, "", deviceType, channels); + final ThingHandlerCallback callback = getCallback(); + if (scpdUtil != null && callback != null) { + Util.checkAvailableChannels(thing, callback, thingBuilder, scpdUtil, "", deviceType, channels); updateThing(thingBuilder.build()); } @@ -197,6 +206,7 @@ public void dispose() { removeConnectScheduler(); uninstallPolling(); stateCache.clear(); + scpdUtil = null; super.dispose(); } @@ -205,19 +215,25 @@ public void dispose() { * poll remote device for channel values */ private void poll() { - channels.forEach((channelUID, channelConfig) -> { - if (isLinked(channelUID)) { - State state = stateCache.putIfAbsentAndGet(channelUID, - () -> soapConnector.getChannelStateFromDevice(channelConfig, channels, stateCache)); - if (state != null) { - updateState(channelUID, state); + try { + channels.forEach((channelUID, channelConfig) -> { + if (isLinked(channelUID)) { + State state = stateCache.putIfAbsentAndGet(channelUID, + () -> soapConnector.getChannelStateFromDevice(channelConfig, channels, stateCache)); + if (state != null) { + updateState(channelUID, state); + } } - } - }); + }); + } catch (RuntimeException e) { + logger.warn("Exception while refreshing remote data for thing '{}':", thing.getUID(), e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Refresh exception: " + e.getMessage()); + } } /** - * establish the connection - get secure port (if avallable), install authentication, get device properties + * establish the connection - get secure port (if available), install authentication, get device properties * * @return true if successful */ @@ -238,11 +254,11 @@ private boolean establishSecureConnectionAndUpdateProperties() { SOAPMessage soapResponse = soapConnector .doSOAPRequest(new SOAPRequest(deviceService, "GetSecurityPort")); if (!soapResponse.getSOAPBody().hasFault()) { - SOAPValueConverter soapValueConverter = new SOAPValueConverter(httpClient); + SOAPValueConverter soapValueConverter = new SOAPValueConverter(httpClient, timeout); soapValueConverter.getStateFromSOAPValue(soapResponse, "NewSecurityPort", null) .ifPresentOrElse(port -> { - endpointBaseURL = "https://" + config.host + ":" + port.toString(); - soapConnector = new SOAPConnector(httpClient, endpointBaseURL); + endpointBaseURL = "https://" + config.host + ":" + port; + soapConnector = new SOAPConnector(httpClient, endpointBaseURL, timeout); logger.debug("endpointBaseURL is now '{}'", endpointBaseURL); }, () -> logger.warn("Could not determine secure port, disabling https")); } else { @@ -250,9 +266,10 @@ private boolean establishSecureConnectionAndUpdateProperties() { } // clear auth cache and force re-auth - httpClient.getAuthenticationStore().clearAuthenticationResults(); - AuthenticationStore auth = httpClient.getAuthenticationStore(); - auth.addAuthentication(new DigestAuthentication(new URI(endpointBaseURL), Authentication.ANY_REALM, + AuthenticationStore authStore = httpClient.getAuthenticationStore(); + authStore.clearAuthentications(); + authStore.clearAuthenticationResults(); + authStore.addAuthentication(new DigestAuthentication(new URI(endpointBaseURL), Authentication.ANY_REALM, config.user, config.password)); // check & update properties @@ -263,7 +280,7 @@ private boolean establishSecureConnectionAndUpdateProperties() { .orElseThrow(() -> new SCPDException("Action 'GetInfo' not found")); SOAPMessage soapResponse1 = soapConnector .doSOAPRequest(new SOAPRequest(deviceService, getInfoAction.getName())); - SOAPValueConverter soapValueConverter = new SOAPValueConverter(httpClient); + SOAPValueConverter soapValueConverter = new SOAPValueConverter(httpClient, timeout); Map properties = editProperties(); PROPERTY_ARGUMENTS.forEach(argumentName -> getInfoAction.getArgumentList().stream() .filter(argument -> argument.getName().equals(argumentName)).findFirst() @@ -301,6 +318,22 @@ public SOAPConnector getSOAPConnector() { return soapConnector; } + /** + * return the result of an (authenticated) GET request + * + * @param url the requested URL + * + * @return a {@link ContentResponse} with the result of the request + * @throws ExecutionException + * @throws InterruptedException + * @throws TimeoutException + */ + public ContentResponse getUrl(String url) throws ExecutionException, InterruptedException, TimeoutException { + httpClient.getAuthenticationStore().addAuthentication( + new DigestAuthentication(URI.create(url), Authentication.ANY_REALM, config.user, config.password)); + return httpClient.GET(URI.create(url)); + } + /** * get the SCPD processing utility * @@ -341,21 +374,21 @@ private void installPolling() { @SuppressWarnings("unchecked") private Collection processPhonebookList(SOAPMessage soapMessagePhonebookList, SCPDServiceType scpdService) { - SOAPValueConverter soapValueConverter = new SOAPValueConverter(httpClient); - return (Collection) soapValueConverter + SOAPValueConverter soapValueConverter = new SOAPValueConverter(httpClient, timeout); + Optional> phonebookStream = soapValueConverter .getStateFromSOAPValue(soapMessagePhonebookList, "NewPhonebookList", null) - .map(phonebookList -> Arrays.stream(phonebookList.toString().split(","))).orElse(Stream.empty()) - .map(index -> { - try { - SOAPMessage soapMessageURL = soapConnector.doSOAPRequest( - new SOAPRequest(scpdService, "GetPhonebook", Map.of("NewPhonebookID", index))); - return soapValueConverter.getStateFromSOAPValue(soapMessageURL, "NewPhonebookURL", null) - .map(url -> (Phonebook) new Tr064PhonebookImpl(httpClient, url.toString())); - } catch (Tr064CommunicationException e) { - logger.warn("Failed to get phonebook with index {}:", index, e); - } - return Optional.empty(); - }).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); + .map(phonebookList -> Arrays.stream(phonebookList.toString().split(","))); + return phonebookStream.map(stringStream -> (Collection) stringStream.map(index -> { + try { + SOAPMessage soapMessageURL = soapConnector + .doSOAPRequest(new SOAPRequest(scpdService, "GetPhonebook", Map.of("NewPhonebookID", index))); + return soapValueConverter.getStateFromSOAPValue(soapMessageURL, "NewPhonebookURL", null) + .map(url -> (Phonebook) new Tr064PhonebookImpl(httpClient, url.toString(), timeout)); + } catch (Tr064CommunicationException e) { + logger.warn("Failed to get phonebook with index {}:", index, e); + } + return Optional.empty(); + }).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())).orElseGet(Set::of); } private void retrievePhonebooks() { @@ -368,14 +401,14 @@ private void retrievePhonebooks() { Optional scpdService = scpdUtil.getDevice("").flatMap(deviceType -> deviceType.getServiceList() .stream().filter(service -> service.getServiceId().equals(serviceId)).findFirst()); - phonebooks = scpdService.map(service -> { + phonebooks = Objects.requireNonNull(scpdService.map(service -> { try { return processPhonebookList(soapConnector.doSOAPRequest(new SOAPRequest(service, "GetPhonebookList")), service); } catch (Tr064CommunicationException e) { return Collections. emptyList(); } - }).orElse(List.of()); + }).orElse(List.of())); if (phonebooks.isEmpty()) { logger.warn("Could not get phonebooks for thing {}", thing.getUID()); @@ -405,6 +438,20 @@ public String getFriendlyName() { @Override public Collection> getServices() { - return Set.of(Tr064DiscoveryService.class, PhonebookActions.class); + if (THING_TYPE_FRITZBOX.equals(thing.getThingTypeUID())) { + return Set.of(Tr064DiscoveryService.class, FritzboxActions.class); + } else { + return Set.of(Tr064DiscoveryService.class); + } + } + + /** + * get the backup configuration for this thing (only applies to FritzBox devices + * + * @return the configuration + */ + public FritzboxActions.BackupConfiguration getBackupConfiguration() { + return new FritzboxActions.BackupConfiguration(config.backupDirectory, + Objects.requireNonNullElse(config.backupPassword, config.password)); } } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064SubHandler.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064SubHandler.java index 4823a60b92d55..75040d959b771 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064SubHandler.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/Tr064SubHandler.java @@ -37,6 +37,7 @@ import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; @@ -77,7 +78,6 @@ public class Tr064SubHandler extends BaseThingHandler { } @Override - @SuppressWarnings("null") public void handleCommand(ChannelUID channelUID, Command command) { Tr064ChannelConfig channelConfig = channels.get(channelUID); if (channelConfig == null) { @@ -86,6 +86,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (command instanceof RefreshType) { + final SOAPConnector soapConnector = this.soapConnector; State state = stateCache.putIfAbsentAndGet(channelUID, () -> soapConnector == null ? UnDefType.UNDEF : soapConnector.getChannelStateFromDevice(channelConfig, channels, stateCache)); if (state != null) { @@ -99,6 +100,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } scheduler.execute(() -> { + final SOAPConnector soapConnector = this.soapConnector; if (soapConnector == null) { logger.warn("Could not send command because connector not available"); } else { @@ -141,12 +143,16 @@ private void internalInitialize() { "Could not get device definitions"); return; } - + final ThingHandlerCallback callback = getCallback(); + if (callback == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Could not get callback"); + return; + } if (checkProperties(scpdUtil)) { // properties set, check channels ThingBuilder thingBuilder = editThing(); thingBuilder.withoutChannels(thing.getChannels()); - Util.checkAvailableChannels(thing, thingBuilder, scpdUtil, config.uuid, deviceType, channels); + Util.checkAvailableChannels(thing, callback, thingBuilder, scpdUtil, config.uuid, deviceType, channels); updateThing(thingBuilder.build()); // remove connect scheduler diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064ChannelConfig.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064ChannelConfig.java index 6b1239bae8285..2e67ccbe3367d 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064ChannelConfig.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064ChannelConfig.java @@ -25,8 +25,8 @@ */ @NonNullByDefault public class Tr064ChannelConfig { - private ChannelTypeDescription channelTypeDescription; - private SCPDServiceType service; + private final ChannelTypeDescription channelTypeDescription; + private final SCPDServiceType service; private @Nullable SCPDActionType getAction; private String dataType = ""; private @Nullable String parameter; diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064RootConfiguration.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064RootConfiguration.java index 5e24dde3543de..2b619af26f14c 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064RootConfiguration.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/config/Tr064RootConfiguration.java @@ -15,6 +15,8 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.OpenHAB; /** * The {@link Tr064RootConfiguration} class contains fields mapping thing configuration parameters. @@ -23,9 +25,12 @@ */ @NonNullByDefault public class Tr064RootConfiguration extends Tr064BaseThingConfiguration { + public static final int DEFAULT_HTTP_TIMEOUT = 5; // in s + public String host = ""; public String user = "dslf-config"; public String password = ""; + public int timeout = DEFAULT_HTTP_TIMEOUT; /* following parameters only available in fritzbox thing */ public List tamIndices = List.of(); @@ -38,6 +43,10 @@ public class Tr064RootConfiguration extends Tr064BaseThingConfiguration { public List wanBlockIPs = List.of(); public int phonebookInterval = 600; + // Backup data + public String backupDirectory = OpenHAB.getUserDataFolder(); + public @Nullable String backupPassword; + public boolean isValid() { return !host.isEmpty() && !user.isEmpty() && !password.isEmpty(); } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfile.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfile.java index 783cce5e33c7c..d0292a2a9e5a5 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfile.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfile.java @@ -14,6 +14,7 @@ import java.math.BigDecimal; import java.util.Map; +import java.util.Objects; import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -139,15 +140,13 @@ public void onStateUpdateFromHandler(State state) { } if (state instanceof StringType) { Optional match = resolveNumber(state.toString()); - State newState = match.map(name -> (State) new StringType(name)).orElse(state); - // Compare by reference to check if the name is mapped to the same state - if (newState == state) { + State newState = Objects.requireNonNull(match.map(name -> (State) new StringType(name)).orElse(state)); + if (newState.equals(state)) { logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", state, phonebookName, thingUID); } callback.sendUpdate(newState); - } else if (state instanceof StringListType) { - StringListType stringList = (StringListType) state; + } else if (state instanceof StringListType stringList) { try { String phoneNumber = stringList.getValue(phoneNumberIndex); Optional match = resolveNumber(phoneNumber); diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImpl.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImpl.java index f25510819496d..11ffc3ed74d93 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImpl.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImpl.java @@ -33,31 +33,39 @@ public class Tr064PhonebookImpl implements Phonebook { private final Logger logger = LoggerFactory.getLogger(Tr064PhonebookImpl.class); - private Map phonebook = new HashMap<>(); + protected Map phonebook = new HashMap<>(); private final HttpClient httpClient; private final String phonebookUrl; + private final int httpTimeout; private String phonebookName = ""; - public Tr064PhonebookImpl(HttpClient httpClient, String phonebookUrl) { + public Tr064PhonebookImpl(HttpClient httpClient, String phonebookUrl, int httpTimeout) { this.httpClient = httpClient; this.phonebookUrl = phonebookUrl; + this.httpTimeout = httpTimeout; getPhonebook(); } private void getPhonebook() { - PhonebooksType phonebooksType = Util.getAndUnmarshalXML(httpClient, phonebookUrl, PhonebooksType.class); - if (phonebooksType != null) { - phonebookName = phonebooksType.getPhonebook().getName(); - phonebook = phonebooksType.getPhonebook().getContact().stream().map(contact -> { - String contactName = contact.getPerson().getRealName(); - return contact.getTelephony().getNumber().stream() - .collect(Collectors.toMap(number -> normalizeNumber(number.getValue()), number -> contactName, - this::mergeSameContactNames)); - }).collect(HashMap::new, HashMap::putAll, HashMap::putAll); - logger.debug("Downloaded phonebook {}: {}", phonebookName, phonebook); + PhonebooksType phonebooksType = Util.getAndUnmarshalXML(httpClient, phonebookUrl, PhonebooksType.class, + httpTimeout); + if (phonebooksType == null) { + logger.warn("Failed to get phonebook with URL '{}'", phonebookUrl); + return; } + phonebookName = phonebooksType.getPhonebook().getName(); + + phonebook = phonebooksType.getPhonebook().getContact().stream().map(contact -> { + String contactName = contact.getPerson().getRealName(); + if (contactName == null || contactName.isBlank()) { + return new HashMap(); + } + return contact.getTelephony().getNumber().stream().collect(Collectors.toMap( + number -> normalizeNumber(number.getValue()), number -> contactName, this::mergeSameContactNames)); + }).collect(HashMap::new, HashMap::putAll, HashMap::putAll); + logger.debug("Downloaded phonebook {}: {}", phonebookName, phonebook); } // in case there are multiple phone entries with same number -> name mapping, i.e. in phonebooks exported from @@ -78,9 +86,14 @@ public String getName() { @Override public Optional lookupNumber(String number, int matchCount) { String normalized = normalizeNumber(number); - String matchString = matchCount > 0 && matchCount < normalized.length() - ? normalized.substring(normalized.length() - matchCount) - : normalized; + String matchString; + if (matchCount > 0 && matchCount < normalized.length()) { + matchString = normalized.substring(normalized.length() - matchCount); + } else if (matchCount < 0 && (-matchCount) < normalized.length()) { + matchString = normalized.substring(-matchCount); + } else { + matchString = normalized; + } logger.trace("Normalized '{}' to '{}', matchString is '{}'", number, normalized, matchString); return matchString.isBlank() ? Optional.empty() : phonebook.keySet().stream().filter(n -> n.endsWith(matchString)).findFirst().map(phonebook::get); @@ -91,8 +104,13 @@ public String toString() { return "Phonebook{" + "phonebookName='" + phonebookName + "', phonebook=" + phonebook + '}'; } - private String normalizeNumber(String number) { - // Naive normalization: remove all non-digit characters + /** + * normalize a phone number (remove everything except digits and *) for comparison + * + * @param number the input phone number string + * @return normalized phone number string + */ + public final String normalizeNumber(String number) { return number.replaceAll("[^0-9\\*\\+]", ""); } } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListEntry.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListEntry.java index 445a6f3c415b3..24e0cc978a5a3 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListEntry.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListEntry.java @@ -12,10 +12,8 @@ */ package org.openhab.binding.tr064.internal.soap; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -23,14 +21,14 @@ import org.openhab.binding.tr064.internal.dto.additions.Call; /** - * The {@link CallListEntry} is used for post processing the retrieved call + * The {@link CallListEntry} is used for post-processing the retrieved call * lists * * @author Jan N. Klug - Initial contribution */ @NonNullByDefault public class CallListEntry { - private static final DateTimeFormatter DATE_FORMAT_PARSER = DateTimeFormatter.ofPattern("dd.MM.yy HH:mm"); + private static final SimpleDateFormat DATE_FORMAT_PARSER = new SimpleDateFormat("dd.MM.yy HH:mm"); public @Nullable String localNumber; public @Nullable String remoteNumber; public @Nullable Date date; @@ -39,9 +37,10 @@ public class CallListEntry { public CallListEntry(Call call) { try { - date = Date.from( - LocalDateTime.parse(call.getDate(), DATE_FORMAT_PARSER).atZone(ZoneId.systemDefault()).toInstant()); - } catch (DateTimeParseException e) { + synchronized (DATE_FORMAT_PARSER) { + date = DATE_FORMAT_PARSER.parse(call.getDate()); + } + } catch (ParseException e) { // ignore parsing error date = null; } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListType.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListType.java index ce172e315c410..7026ab60860cb 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListType.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/CallListType.java @@ -15,11 +15,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * The {@link CallListType} is used for post processing the retrieved call list + * The {@link CallListType} is used for post-processing the retrieved call list * * @author Jan N. Klug - Initial contribution */ - @NonNullByDefault public enum CallListType { MISSED_COUNT("2"), diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/PostProcessingException.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/PostProcessingException.java index ef41671cf86f9..d97acbb895a36 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/PostProcessingException.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/PostProcessingException.java @@ -16,8 +16,8 @@ /** * - * The{@link PostProcessingException} is a catched Exception that is thrown in case of conversion errors during post - * processing + * The {@link PostProcessingException} is an Exception that is thrown in case of conversion errors during + * post-processing * * @author Jan N. Klug - Initial contribution */ diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPConnector.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPConnector.java index 515f33e46aca2..3aa372d58f28a 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPConnector.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPConnector.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.time.Duration; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.concurrent.ExecutionException; @@ -28,7 +27,6 @@ import java.util.stream.Collectors; import javax.xml.soap.MessageFactory; -import javax.xml.soap.MimeHeader; import javax.xml.soap.MimeHeaders; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPElement; @@ -67,19 +65,20 @@ */ @NonNullByDefault public class SOAPConnector { - private static final int SOAP_TIMEOUT = 5; // in private final Logger logger = LoggerFactory.getLogger(SOAPConnector.class); private final HttpClient httpClient; private final String endpointBaseURL; private final SOAPValueConverter soapValueConverter; + private final int timeout; private final ExpiringCacheMap soapMessageCache = new ExpiringCacheMap<>( Duration.ofMillis(2000)); - public SOAPConnector(HttpClient httpClient, String endpointBaseURL) { + public SOAPConnector(HttpClient httpClient, String endpointBaseURL, int timeout) { this.httpClient = httpClient; this.endpointBaseURL = endpointBaseURL; - this.soapValueConverter = new SOAPValueConverter(httpClient); + this.timeout = timeout; + this.soapValueConverter = new SOAPValueConverter(httpClient, timeout); } /** @@ -118,7 +117,7 @@ private Request prepareSOAPRequest(SOAPRequest soapRequest) throws IOException, // create Request and add headers and content Request request = httpClient.newRequest(endpointBaseURL + soapRequest.service.getControlURL()) .method(HttpMethod.POST); - ((Iterator) soapMessage.getMimeHeaders().getAllHeaders()) + soapMessage.getMimeHeaders().getAllHeaders() .forEachRemaining(header -> request.header(header.getName(), header.getValue())); try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) { soapMessage.writeTo(os); @@ -169,7 +168,7 @@ public SOAPMessage doSOAPRequest(SOAPRequest soapRequest) throws Tr064Communicat */ public synchronized SOAPMessage doSOAPRequestUncached(SOAPRequest soapRequest) throws Tr064CommunicationException { try { - Request request = prepareSOAPRequest(soapRequest).timeout(SOAP_TIMEOUT, TimeUnit.SECONDS); + Request request = prepareSOAPRequest(soapRequest).timeout(timeout, TimeUnit.SECONDS); if (logger.isTraceEnabled()) { request.getContent().forEach(buffer -> logger.trace("Request: {}", new String(buffer.array()))); } @@ -179,7 +178,7 @@ public synchronized SOAPMessage doSOAPRequestUncached(SOAPRequest soapRequest) t // retry once if authentication expired logger.trace("Re-Auth needed."); httpClient.getAuthenticationStore().clearAuthenticationResults(); - request = prepareSOAPRequest(soapRequest).timeout(SOAP_TIMEOUT, TimeUnit.SECONDS); + request = prepareSOAPRequest(soapRequest).timeout(timeout, TimeUnit.SECONDS); response = request.send(); } try (final ByteArrayInputStream is = new ByteArrayInputStream(response.getContent())) { @@ -247,14 +246,11 @@ public State getChannelStateFromDevice(final Tr064ChannelConfig channelConfig, final SCPDActionType getAction = channelConfig.getGetAction(); if (getAction == null) { // channel has no get action, return a default - switch (channelConfig.getDataType()) { - case "boolean": - return OnOffType.OFF; - case "string": - return StringType.EMPTY; - default: - return UnDefType.UNDEF; - } + return switch (channelConfig.getDataType()) { + case "boolean" -> OnOffType.OFF; + case "string" -> StringType.EMPTY; + default -> UnDefType.UNDEF; + }; } // get value(s) from remote device @@ -290,11 +286,13 @@ public State getChannelStateFromDevice(final Tr064ChannelConfig channelConfig, } catch (Tr064CommunicationException e) { if (e.getHttpError() == 500) { switch (e.getSoapError()) { - case "714": + case "714" -> { // NoSuchEntryInArray usually is an unknown entry in the MAC list logger.debug("Failed to get {}: {}", channelConfig, e.getMessage()); return UnDefType.UNDEF; - default: + } + default -> { + } } } // all other cases are an error diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPRequest.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPRequest.java index 2b50ef644766f..5e4944234f4df 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPRequest.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPRequest.java @@ -48,7 +48,6 @@ public boolean equals(@Nullable Object o) { if (o == null || getClass() != o.getClass()) { return false; } - SOAPRequest that = (SOAPRequest) o; if (!service.equals(that.service)) { @@ -57,6 +56,7 @@ public boolean equals(@Nullable Object o) { if (!soapAction.equals(that.soapAction)) { return false; } + return arguments.equals(that.arguments); } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPValueConverter.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPValueConverter.java index 1822bb7ba8b23..7324ccf838ce7 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPValueConverter.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/soap/SOAPValueConverter.java @@ -17,6 +17,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; @@ -57,9 +58,11 @@ public class SOAPValueConverter { private final Logger logger = LoggerFactory.getLogger(SOAPValueConverter.class); private final HttpClient httpClient; + private final int timeout; - public SOAPValueConverter(HttpClient httpClient) { + public SOAPValueConverter(HttpClient httpClient, int timeout) { this.httpClient = httpClient; + this.timeout = timeout; } /** @@ -83,24 +86,26 @@ public Optional getSOAPValueFromCommand(Command command, String dataType return Optional.empty(); } switch (dataType) { - case "ui1": - case "ui2": + case "ui1", "ui2" -> { return Optional.of(String.valueOf(value.shortValue())); - case "i4": - case "ui4": + } + case "i4", "ui4" -> { return Optional.of(String.valueOf(value.intValue())); - default: + } + default -> { + } } } else if (command instanceof DecimalType) { BigDecimal value = ((DecimalType) command).toBigDecimal(); switch (dataType) { - case "ui1": - case "ui2": + case "ui1", "ui2" -> { return Optional.of(String.valueOf(value.shortValue())); - case "i4": - case "ui4": + } + case "i4", "ui4" -> { return Optional.of(String.valueOf(value.intValue())); - default: + } + default -> { + } } } else if (command instanceof StringType) { if ("string".equals(dataType)) { @@ -127,28 +132,35 @@ public Optional getStateFromSOAPValue(SOAPMessage soapMessage, String ele @Nullable Tr064ChannelConfig channelConfig) { String dataType = channelConfig != null ? channelConfig.getDataType() : "string"; String unit = channelConfig != null ? channelConfig.getChannelTypeDescription().getItem().getUnit() : ""; + BigDecimal factor = channelConfig != null ? channelConfig.getChannelTypeDescription().getItem().getFactor() + : null; return getSOAPElement(soapMessage, element).map(rawValue -> { // map rawValue to State switch (dataType) { - case "boolean": + case "boolean" -> { return rawValue.equals("0") ? OnOffType.OFF : OnOffType.ON; - case "string": + } + case "string" -> { return new StringType(rawValue); - case "ui1": - case "ui2": - case "i4": - case "ui4": + } + case "ui1", "ui2", "i4", "ui4" -> { + BigDecimal decimalValue = new BigDecimal(rawValue); + if (factor != null) { + decimalValue = decimalValue.multiply(factor); + } if (!unit.isEmpty()) { - return new QuantityType<>(rawValue + " " + unit); + return new QuantityType<>(decimalValue + " " + unit); } else { - return new DecimalType(rawValue); + return new DecimalType(decimalValue); } - default: + } + default -> { return null; + } } }).map(state -> { - // check if we need post processing + // check if we need post-processing if (channelConfig == null || channelConfig.getChannelTypeDescription().getGetAction().getPostProcessor() == null) { return state; @@ -172,6 +184,26 @@ public Optional getStateFromSOAPValue(SOAPMessage soapMessage, String ele }).or(Optional::empty); } + /** + * post processor for current bitrate + */ + @SuppressWarnings("unused") + private State processCurrentBitrate(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException { + Double bps = Arrays.stream(state.toString().split(",")).mapToDouble(s -> { + try { + return Double.parseDouble(s); + } catch (NumberFormatException e) { + return 0.0; + } + }).limit(3).average().orElse(Double.NaN); + + if (bps.equals(Double.NaN)) { + return UnDefType.UNDEF; + } else { + return new QuantityType<>(bps * 8.0 / 1024.0, Units.KILOBIT_PER_SECOND); + } + } + /** * post processor to map mac device signal strength to system.signal-strength 0-4 * @@ -201,20 +233,6 @@ private State processMacSignalStrength(State state, Tr064ChannelConfig channelCo return mappedSignalStrength; } - /** - * post processor for decibel values (which are served as deca decibel) - * - * @param state the channel value in deca decibel - * @param channelConfig channel config of the channel - * @return the state converted to decibel - */ - @SuppressWarnings("unused") - private State processDecaDecibel(State state, Tr064ChannelConfig channelConfig) { - Float value = state.as(DecimalType.class).floatValue() / 10; - - return new QuantityType<>(value, Units.DECIBEL); - } - /** * post processor for answering machine new messages channel * @@ -226,16 +244,14 @@ private State processDecaDecibel(State state, Tr064ChannelConfig channelConfig) @SuppressWarnings("unused") private State processTamListURL(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException { try { - ContentResponse response = httpClient.newRequest(state.toString()).timeout(1500, TimeUnit.MILLISECONDS) + ContentResponse response = httpClient.newRequest(state.toString()).timeout(timeout, TimeUnit.MILLISECONDS) .send(); String responseContent = response.getContentAsString(); int messageCount = responseContent.split("1").length - 1; return new DecimalType(messageCount); - } catch (TimeoutException e) { - throw new PostProcessingException("Failed to get TAM list due to time out from URL " + state.toString(), e); - } catch (InterruptedException | ExecutionException e) { - throw new PostProcessingException("Failed to get TAM list from URL " + state.toString(), e); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + throw new PostProcessingException("Failed to get TAM list from URL " + state, e); } } @@ -315,23 +331,22 @@ private State processCallListJSON(State state, Tr064ChannelConfig channelConfig) */ private State processCallList(State state, @Nullable String days, CallListType type) throws PostProcessingException { - Root callListRoot = Util.getAndUnmarshalXML(httpClient, state.toString() + "&days=" + days, Root.class); + Root callListRoot = Util.getAndUnmarshalXML(httpClient, state + "&days=" + days, Root.class, timeout); if (callListRoot == null) { - throw new PostProcessingException("Failed to get call list from URL " + state.toString()); + throw new PostProcessingException("Failed to get call list from URL " + state); } List calls = callListRoot.getCall(); switch (type) { - case INBOUND_COUNT: - case MISSED_COUNT: - case OUTBOUND_COUNT: - case REJECTED_COUNT: + case INBOUND_COUNT, MISSED_COUNT, OUTBOUND_COUNT, REJECTED_COUNT -> { long callCount = calls.stream().filter(call -> type.typeString().equals(call.getType())).count(); return new DecimalType(callCount); - case JSON_LIST: + } + case JSON_LIST -> { Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssX").serializeNulls().create(); List callListEntries = calls.stream().map(CallListEntry::new) .collect(Collectors.toList()); return new StringType(gson.toJson(callListEntries)); + } } return UnDefType.UNDEF; } diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/SCPDUtil.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/SCPDUtil.java index 34b6f49e1316e..f5540299daa00 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/SCPDUtil.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/SCPDUtil.java @@ -36,22 +36,23 @@ */ @NonNullByDefault public class SCPDUtil { - private SCPDRootType scpdRoot; + private final SCPDRootType scpdRoot; private final List scpdDevicesList = new ArrayList<>(); private final Map serviceMap = new HashMap<>(); - public SCPDUtil(HttpClient httpClient, String endpoint) throws SCPDException { - SCPDRootType scpdRoot = Util.getAndUnmarshalXML(httpClient, endpoint + "/tr64desc.xml", SCPDRootType.class); + public SCPDUtil(HttpClient httpClient, String endpoint, int timeout) throws SCPDException { + SCPDRootType scpdRoot = Util.getAndUnmarshalXML(httpClient, endpoint + "/tr64desc.xml", SCPDRootType.class, + timeout); if (scpdRoot == null) { throw new SCPDException("could not get SCPD root"); } this.scpdRoot = scpdRoot; - scpdDevicesList.addAll(flatDeviceList(scpdRoot.getDevice()).collect(Collectors.toList())); + scpdDevicesList.addAll(flatDeviceList(scpdRoot.getDevice()).toList()); for (SCPDDeviceType device : scpdDevicesList) { for (SCPDServiceType service : device.getServiceList()) { SCPDScpdType scpd = serviceMap.computeIfAbsent(service.getServiceId(), serviceId -> Util - .getAndUnmarshalXML(httpClient, endpoint + service.getSCPDURL(), SCPDScpdType.class)); + .getAndUnmarshalXML(httpClient, endpoint + service.getSCPDURL(), SCPDScpdType.class, timeout)); if (scpd == null) { throw new SCPDException("could not get SCPD service"); } @@ -80,7 +81,7 @@ public List getAllSubDevices() { } /** - * get a single device by it's UDN + * get a single device by its UDN * * @param udn the device UDN * @return the device @@ -94,7 +95,7 @@ public Optional getDevice(String udn) { } /** - * get a single service by it's serviceId + * get a single service by its serviceId * * @param serviceId the service id * @return the service diff --git a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/Util.java b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/Util.java index ea4129399f4ce..dc4c7c37b33d8 100644 --- a/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/Util.java +++ b/bundles/org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/util/Util.java @@ -18,11 +18,10 @@ import java.io.InputStream; import java.lang.reflect.Field; import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -64,9 +63,10 @@ import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDScpdType; import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDStateVariableType; import org.openhab.core.cache.ExpiringCacheMap; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; -import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.util.UIDUtils; @@ -82,7 +82,6 @@ @NonNullByDefault public class Util { private static final Logger LOGGER = LoggerFactory.getLogger(Util.class); - private static final int HTTP_REQUEST_TIMEOUT = 5; // in s // cache XML content for 5s private static final ExpiringCacheMap XML_OBJECT_CACHE = new ExpiringCacheMap<>( Duration.ofMillis(3000)); @@ -169,8 +168,8 @@ private static SCPDActionType getAction(SCPDScpdType serviceRoot, String actionN * @param deviceType the (SCPD) device-type for this thing * @param channels a (mutable) channel list for storing all channels */ - public static void checkAvailableChannels(Thing thing, ThingBuilder thingBuilder, SCPDUtil scpdUtil, - String deviceId, String deviceType, Map channels) { + public static void checkAvailableChannels(Thing thing, ThingHandlerCallback callback, ThingBuilder thingBuilder, + SCPDUtil scpdUtil, String deviceId, String deviceType, Map channels) { Tr064BaseThingConfiguration thingConfig = Tr064RootHandler.SUPPORTED_THING_TYPES .contains(thing.getThingTypeUID()) ? thing.getConfiguration().as(Tr064RootConfiguration.class) : thing.getConfiguration().as(Tr064SubConfiguration.class); @@ -179,13 +178,6 @@ public static void checkAvailableChannels(Thing thing, ThingBuilder thingBuilder .forEach(channelTypeDescription -> { String channelId = channelTypeDescription.getName(); String serviceId = channelTypeDescription.getService().getServiceId(); - String typeId = channelTypeDescription.getTypeId(); - Map channelProperties = new HashMap(); - - if (typeId != null) { - channelProperties.put("typeId", typeId); - } - Set parameters = new HashSet<>(); try { SCPDServiceType deviceService = scpdUtil.getDevice(deviceId) @@ -199,6 +191,7 @@ public static void checkAvailableChannels(Thing thing, ThingBuilder thingBuilder deviceService); // get + boolean fixedValue = false; ActionType getAction = channelTypeDescription.getGetAction(); if (getAction != null) { String actionName = getAction.getName(); @@ -208,7 +201,9 @@ public static void checkAvailableChannels(Thing thing, ThingBuilder thingBuilder SCPDStateVariableType relatedStateVariable = getStateVariable(serviceRoot, scpdArgument); parameters.addAll( getAndCheckParameters(channelId, getAction, scpdAction, serviceRoot, thingConfig)); - + if (getAction.getParameter() != null && getAction.getParameter().getFixedValue() != null) { + fixedValue = true; + } channelConfig.setGetAction(scpdAction); channelConfig.setDataType(relatedStateVariable.getDataType()); } @@ -233,16 +228,20 @@ public static void checkAvailableChannels(Thing thing, ThingBuilder thingBuilder } // everything is available, create the channel - ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, - channelTypeDescription.getName()); - if (parameters.isEmpty()) { + String channelType = Objects.requireNonNullElse(channelTypeDescription.getTypeId(), ""); + ChannelTypeUID channelTypeUID = channelType.isBlank() + ? new ChannelTypeUID(BINDING_ID, channelTypeDescription.getName()) + : new ChannelTypeUID(channelType); + if (parameters.isEmpty() || fixedValue) { // we have no parameters, so create a single channel ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId); - ChannelBuilder channelBuilder = ChannelBuilder - .create(channelUID, channelTypeDescription.getItem().getType()) - .withType(channelTypeUID).withProperties(channelProperties); - thingBuilder.withChannel(channelBuilder.build()); - channels.put(channelUID, channelConfig); + Channel channel = callback.createChannelBuilder(channelUID, channelTypeUID).build(); + thingBuilder.withChannel(channel); + Tr064ChannelConfig channelConfig1 = new Tr064ChannelConfig(channelConfig); + if (fixedValue) { + channelConfig1.setParameter(parameters.iterator().next()); + } + channels.put(channelUID, channelConfig1); } else { // create a channel for each parameter parameters.forEach(parameter -> { @@ -252,11 +251,9 @@ public static void checkAvailableChannels(Thing thing, ThingBuilder thingBuilder String normalizedParameter = UIDUtils.encode(rawParameter); ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId + "_" + normalizedParameter); - ChannelBuilder channelBuilder = ChannelBuilder - .create(channelUID, channelTypeDescription.getItem().getType()) - .withType(channelTypeUID).withProperties(channelProperties) - .withLabel(channelTypeDescription.getLabel() + " " + parameter); - thingBuilder.withChannel(channelBuilder.build()); + Channel channel = callback.createChannelBuilder(channelUID, channelTypeUID) + .withLabel(channelTypeDescription.getLabel() + " " + parameter).build(); + thingBuilder.withChannel(channel); Tr064ChannelConfig channelConfig1 = new Tr064ChannelConfig(channelConfig); channelConfig1.setParameter(rawParameter); channels.put(channelUID, channelConfig1); @@ -272,8 +269,12 @@ private static Set getAndCheckParameters(String channelId, ActionType ac SCPDScpdType serviceRoot, Tr064BaseThingConfiguration thingConfig) throws ChannelConfigException { ParameterType parameter = action.getParameter(); if (parameter == null) { - return Collections.emptySet(); + return Set.of(); } + if (parameter.getFixedValue() != null) { + return Set.of(parameter.getFixedValue()); + } + // process list of thing parameters try { Set parameters = new HashSet<>(); @@ -292,9 +293,12 @@ private static Set getAndCheckParameters(String channelId, ActionType ac String parameterPattern = parameter.getPattern(); if (parameterPattern != null) { parameters.removeIf(param -> { - if (!param.matches(parameterPattern)) { - LOGGER.warn("Removing {} while processing {}, does not match pattern {}, check config.", param, - channelId, parameterPattern); + if (param.isBlank()) { + LOGGER.debug("Removing empty parameter while processing '{}'.", channelId); + return true; + } else if (!param.matches(parameterPattern)) { + LOGGER.warn("Removing '{}' while processing '{}', does not match pattern '{}', check config.", + param, channelId, parameterPattern); return true; } else { return false; @@ -344,16 +348,17 @@ public static Optional getSOAPElement(SOAPMessage soapMessage, String el * * @param uri the uri of the XML file * @param clazz the class describing the XML file + * @param timeout timeout in s * @return unmarshalling result */ @SuppressWarnings("unchecked") - public static @Nullable T getAndUnmarshalXML(HttpClient httpClient, String uri, Class clazz) { + public static @Nullable T getAndUnmarshalXML(HttpClient httpClient, String uri, Class clazz, int timeout) { try { T returnValue = (T) XML_OBJECT_CACHE.putIfAbsentAndGet(uri, () -> { try { LOGGER.trace("Refreshing cache for '{}'", uri); - ContentResponse contentResponse = httpClient.newRequest(uri) - .timeout(HTTP_REQUEST_TIMEOUT, TimeUnit.SECONDS).method(HttpMethod.GET).send(); + ContentResponse contentResponse = httpClient.newRequest(uri).timeout(timeout, TimeUnit.SECONDS) + .method(HttpMethod.GET).send(); byte[] response = contentResponse.getContent(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("XML = {}", new String(response)); diff --git a/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/config/phonebookProfile.xml b/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/config/phonebookProfile.xml index 32de7b2667ecd..c0e8804aea0e3 100644 --- a/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/config/phonebookProfile.xml +++ b/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/config/phonebookProfile.xml @@ -9,9 +9,10 @@ The name of the the phone book. - + - The number of digits matching the incoming value, counted from far right (default is 0 = all matching) + The number of digits matching the incoming value, counted from far right (default is 0 = all matching). + Negative numbers skip digits from the left. 0 diff --git a/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/i18n/tr064.properties b/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/i18n/tr064.properties index de1394b2b0b50..80141aeb90756 100644 --- a/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/i18n/tr064.properties +++ b/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/i18n/tr064.properties @@ -15,6 +15,10 @@ thing-type.tr064.subdeviceLan.description = A virtual Sub-Device (LAN). # thing types config +thing-type.config.tr064.fritzbox.backupDirectory.label = Backup Directory +thing-type.config.tr064.fritzbox.backupDirectory.description = The directory where configuration backups are stored (default to userdata directory). +thing-type.config.tr064.fritzbox.backupPassword.label = Backup Password +thing-type.config.tr064.fritzbox.backupPassword.description = The password used to encrypt the backup data. thing-type.config.tr064.fritzbox.callDeflectionIndices.label = Call Deflection thing-type.config.tr064.fritzbox.callDeflectionIndices.description = List of call deflection IDs (starting with 0). thing-type.config.tr064.fritzbox.callListDays.label = Call List Days @@ -35,6 +39,8 @@ thing-type.config.tr064.fritzbox.rejectedCallDays.label = Rejected Call Days thing-type.config.tr064.fritzbox.rejectedCallDays.description = List of days for which rejected calls should be calculated. thing-type.config.tr064.fritzbox.tamIndices.label = TAM thing-type.config.tr064.fritzbox.tamIndices.description = List of answering machines (starting with 0). +thing-type.config.tr064.fritzbox.timeout.label = Timeout +thing-type.config.tr064.fritzbox.timeout.description = Timeout for all requests (SOAP requests, phone book retrieval, call lists, ...). thing-type.config.tr064.fritzbox.user.label = Username thing-type.config.tr064.fritzbox.wanBlockIPs.label = WAN Block IPs thing-type.config.tr064.fritzbox.wanBlockIPs.description = List of IPs that can be blocked for WAN access. @@ -42,6 +48,8 @@ thing-type.config.tr064.generic.host.label = Host thing-type.config.tr064.generic.host.description = Host name or IP address. thing-type.config.tr064.generic.password.label = Password thing-type.config.tr064.generic.refresh.label = Refresh Interval +thing-type.config.tr064.generic.timeout.label = Timeout +thing-type.config.tr064.generic.timeout.description = Timeout for all requests (SOAP requests, phone book retrieval, call lists, ...). thing-type.config.tr064.generic.user.label = Username thing-type.config.tr064.subdevice.refresh.label = Refresh Interval thing-type.config.tr064.subdevice.uuid.label = UUID diff --git a/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/thing/thing-types.xml index 50cad826894cb..72a762b984f0a 100644 --- a/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.tr064/src/main/resources/OH-INF/thing/thing-types.xml @@ -27,6 +27,12 @@ 60 + + + Timeout for all requests (SOAP requests, phone book retrieval, call lists, ...). + 5 + true + @@ -54,6 +60,12 @@ 60 + + + Timeout for all requests (SOAP requests, phone book retrieval, call lists, ...). + 5 + true + List of answering machines (starting with 0). @@ -100,6 +112,17 @@ 600 true + + + The directory where configuration backups are stored (default to userdata directory). + true + + + + The password used to encrypt the backup data. + password + true + diff --git a/bundles/org.openhab.binding.tr064/src/main/resources/channels.xml b/bundles/org.openhab.binding.tr064/src/main/resources/channels.xml index 80e310c4096cf..7db46f24e9f21 100644 --- a/bundles/org.openhab.binding.tr064/src/main/resources/channels.xml +++ b/bundles/org.openhab.binding.tr064/src/main/resources/channels.xml @@ -65,7 +65,7 @@ serviceId="urn:X_AVM-DE_HostFilter-com:serviceId:X_AVM-DE_HostFilter1"/> + pattern="((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!([\s#]|$))|([\s#]|$))){4}(\s*#.*)*"/> @@ -138,7 +138,8 @@ - + @@ -146,7 +147,8 @@ pattern="([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}(\s*#.*)*"/> - + @@ -154,12 +156,11 @@ pattern="([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}(\s*#.*)*"/> - + typeId="system:signal-strength"> @@ -171,7 +172,7 @@ + the given MAC (see macOnline). This is set in case the Device is connected to 2.4Ghz"> @@ -184,8 +185,8 @@ + the given MAC (see macOnline). This is set in case the Device is connected to 5Ghz" + typeId="system:signal-strength"> @@ -197,7 +198,7 @@ + the given MAC (see macOnline). This is set in case the Device is connected to 5Ghz"> @@ -244,6 +245,24 @@ serviceId="urn:WANCIfConfig-com:serviceId:WANCommonInterfaceConfig1"/> + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + diff --git a/bundles/org.openhab.binding.tr064/src/main/resources/xsd/channeltypes.xsd b/bundles/org.openhab.binding.tr064/src/main/resources/xsd/channeltypes.xsd index cbc7c26c0b24e..85e1381cd4c5a 100644 --- a/bundles/org.openhab.binding.tr064/src/main/resources/xsd/channeltypes.xsd +++ b/bundles/org.openhab.binding.tr064/src/main/resources/xsd/channeltypes.xsd @@ -6,6 +6,7 @@ + @@ -22,7 +23,8 @@ - + + diff --git a/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfileTest.java b/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfileTest.java index e6827db2b569d..ed802b347fe13 100644 --- a/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfileTest.java +++ b/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/PhonebookProfileTest.java @@ -53,7 +53,6 @@ @MockitoSettings(strictness = Strictness.LENIENT) @NonNullByDefault class PhonebookProfileTest { - private static final String INTERNAL_PHONE_NUMBER = "999"; private static final String OTHER_PHONE_NUMBER = "555-456"; private static final String JOHN_DOES_PHONE_NUMBER = "12345"; @@ -61,6 +60,7 @@ class PhonebookProfileTest { private static final ThingUID THING_UID = new ThingUID(BINDING_ID, THING_TYPE_FRITZBOX.getId(), "test"); private static final String MY_PHONEBOOK = UIDUtils.encode(THING_UID.getAsString()) + ":MyPhonebook"; + @NonNullByDefault public static class ParameterSet { public final State state; public final State resultingState; @@ -108,12 +108,10 @@ public static Collection parameters() { private final Phonebook phonebook = new Phonebook() { @Override public Optional lookupNumber(String number, int matchCount) { - switch (number) { - case JOHN_DOES_PHONE_NUMBER: - return Optional.of(JOHN_DOES_NAME); - default: - return Optional.empty(); - } + return switch (number) { + case JOHN_DOES_PHONE_NUMBER -> Optional.of(JOHN_DOES_NAME); + default -> Optional.empty(); + }; } @Override diff --git a/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImplTest.java b/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImplTest.java new file mode 100644 index 0000000000000..2c31a9b968c8b --- /dev/null +++ b/bundles/org.openhab.binding.tr064/src/test/java/org/openhab/binding/tr064/internal/phonebook/Tr064PhonebookImplTest.java @@ -0,0 +1,90 @@ +/** + * 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.tr064.internal.phonebook; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +/** + * The {@link Tr064PhonebookImplTest} class implements test cases for the {@link Tr064PhonebookImpl} class + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +@MockitoSettings(strictness = Strictness.WARN) +@ExtendWith(MockitoExtension.class) +public class Tr064PhonebookImplTest { + @Mock + private @NonNullByDefault({}) HttpClient httpClient; + + // key -> input, value -> output + public static Collection> phoneNumbers() { + return List.of( // + Map.entry("**820", "**820"), // + Map.entry("49200123456", "49200123456"), // + Map.entry("+49-200-123456", "+49200123456"), // + Map.entry("49 (200) 123456", "49200123456"), // + Map.entry("+49 200/123456", "+49200123456")); + } + + @ParameterizedTest + @MethodSource("phoneNumbers") + public void testNormalization(Map.Entry input) { + when(httpClient.newRequest((String) any())).thenThrow(new IllegalArgumentException("testing")); + Tr064PhonebookImpl testPhonebook = new Tr064PhonebookImpl(httpClient, "", 0); + assertEquals(input.getValue(), testPhonebook.normalizeNumber(input.getKey())); + } + + @Test + public void testLookup() { + when(httpClient.newRequest((String) any())).thenThrow(new IllegalArgumentException("testing")); + TestPhonebook testPhonebook = new TestPhonebook(httpClient, "", 0); + testPhonebook.setPhonebook(Map.of("+491238007001", "foo", "+4933998005671", "bar")); + + Optional result = testPhonebook.lookupNumber("01238007001", 0); + assertEquals(Optional.empty(), result); + + result = testPhonebook.lookupNumber("01238007001", 10); + assertEquals("foo", result.get()); + + result = testPhonebook.lookupNumber("033998005671", -1); + assertEquals("bar", result.get()); + } + + private static class TestPhonebook extends Tr064PhonebookImpl { + public TestPhonebook(HttpClient httpClient, String phonebookUrl, int httpTimeout) { + super(httpClient, phonebookUrl, httpTimeout); + } + + public void setPhonebook(Map phonebook) { + this.phonebook = phonebook; + } + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java index 032189c58877d..e07b8a151a33a 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java @@ -50,9 +50,7 @@ public class UniFiThingHandlerFactory extends BaseThingHandlerFactory { @Activate public UniFiThingHandlerFactory(@Reference final HttpClientFactory httpClientFactory) { - // [wip] mgb: disabled due to missing common name attributes with certs - // this.httpClient = httpClientFactory.getCommonHttpClient(); - httpClient = new HttpClient(new SslContextFactory.Client(true)); + httpClient = httpClientFactory.createHttpClient(BINDING_ID, new SslContextFactory.Client(true)); try { httpClient.start(); } catch (final Exception e) { diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/audiosink/UpnpAudioSinkReg.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/audiosink/UpnpAudioSinkReg.java index 965a92ca313db..734551a307e9d 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/audiosink/UpnpAudioSinkReg.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/audiosink/UpnpAudioSinkReg.java @@ -13,13 +13,12 @@ package org.openhab.binding.upnpcontrol.internal.audiosink; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.upnpcontrol.internal.UpnpControlHandlerFactory; import org.openhab.binding.upnpcontrol.internal.handler.UpnpRendererHandler; /** - * Interface class to be implemented in {@link UpnpControlHandlerFactory}, allows a {UpnpRendererHandler} to register - * itself as an audio sink when it supports audio. If it supports audio is only known after the communication with the - * renderer is established. + * Interface class to be implemented in {@link org.openhab.binding.upnpcontrol.internal.UpnpControlHandlerFactory + * UpnpControlHandlerFactory}, allows a {@link UpnpRendererHandler} to register itself as an audio sink when it supports + * audio. If it supports audio is only known after the communication with the renderer is established. * * @author Mark Herwege - Initial contribution */ diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/discovery/UpnpControlDiscoveryParticipant.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/discovery/UpnpControlDiscoveryParticipant.java index e92111c5a2d2b..7deb5e8d796af 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/discovery/UpnpControlDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/discovery/UpnpControlDiscoveryParticipant.java @@ -84,11 +84,11 @@ public Set getSupportedThingTypeUIDs() { logger.debug("Device type {}, manufacturer {}, model {}, SN# {}, UDN {}", deviceType, manufacturer, model, serialNumber, udn); - if (deviceType.equalsIgnoreCase("MediaRenderer")) { + if ("MediaRenderer".equalsIgnoreCase(deviceType)) { this.logger.debug("Media renderer found: {}, {}", manufacturer, model); ThingTypeUID thingTypeUID = THING_TYPE_RENDERER; result = new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString()); - } else if (deviceType.equalsIgnoreCase("MediaServer")) { + } else if ("MediaServer".equalsIgnoreCase(deviceType)) { this.logger.debug("Media server found: {}, {}", manufacturer, model); ThingTypeUID thingTypeUID = THING_TYPE_SERVER; result = new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString()); diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpHandler.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpHandler.java index 7d230ab306df4..b22baa73d6617 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpHandler.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpHandler.java @@ -29,7 +29,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.jupnp.model.meta.RemoteDevice; -import org.jupnp.registry.RegistryListener; import org.openhab.binding.upnpcontrol.internal.UpnpChannelName; import org.openhab.binding.upnpcontrol.internal.UpnpDynamicCommandDescriptionProvider; import org.openhab.binding.upnpcontrol.internal.UpnpDynamicStateDescriptionProvider; @@ -229,7 +228,8 @@ protected void updateStatus(ThingStatus status) { /** * Method called when a the remote device represented by the thing for this handler is added to the jupnp - * {@link RegistryListener} or is updated. Configuration info can be retrieved from the {@link RemoteDevice}. + * {@link org.jupnp.registry.RegistryListener RegistryListener} or is updated. Configuration info can be retrieved + * from the {@link RemoteDevice}. * * @param device */ @@ -293,7 +293,7 @@ protected void updateChannels() { /** * Invoke PrepareForConnection on the UPnP Connection Manager. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. * * @param remoteProtocolInfo * @param peerConnectionManager @@ -340,7 +340,7 @@ protected void connectionComplete() { /** * Invoke GetCurrentConnectionIDs on the UPnP Connection Manager. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getCurrentConnectionIDs() { Map inputs = Collections.emptyMap(); @@ -350,7 +350,7 @@ protected void getCurrentConnectionIDs() { /** * Invoke GetCurrentConnectionInfo on the UPnP Connection Manager. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getCurrentConnectionInfo() { CompletableFuture settingAVTransport = isAvTransportIdSet; @@ -374,7 +374,7 @@ protected void getCurrentConnectionInfo() { /** * Invoke GetFeatureList on the UPnP Connection Manager. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getFeatureList() { Map inputs = Collections.emptyMap(); @@ -384,7 +384,7 @@ protected void getFeatureList() { /** * Invoke GetProtocolInfo on UPnP Connection Manager. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getProtocolInfo() { Map inputs = Collections.emptyMap(); @@ -416,10 +416,10 @@ public void onStatusChanged(boolean status) { } /** - * This method wraps {@link org.openhab.core.io.transport.upnp.UpnpIOService.invokeAction}. It schedules and - * submits the call and calls {@link onValueReceived} upon completion. All state updates or other actions depending - * on the results should be triggered from {@link onValueReceived} because the class fields with results will be - * filled asynchronously. + * This method wraps {@link org.openhab.core.io.transport.upnp.UpnpIOService#invokeAction invokeAction}. It + * schedules and submits the call and calls {@link #onValueReceived} upon completion. All state updates or other + * actions depending on the results should be triggered from {@link #onValueReceived} because the class fields with + * results will be filled asynchronously. * * @param serviceId * @param actionId diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpRendererHandler.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpRendererHandler.java index fe2cdaa78e575..2ece784b1fe6b 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpRendererHandler.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpRendererHandler.java @@ -41,7 +41,6 @@ import org.openhab.binding.upnpcontrol.internal.UpnpChannelName; import org.openhab.binding.upnpcontrol.internal.UpnpDynamicCommandDescriptionProvider; import org.openhab.binding.upnpcontrol.internal.UpnpDynamicStateDescriptionProvider; -import org.openhab.binding.upnpcontrol.internal.audiosink.UpnpAudioSink; import org.openhab.binding.upnpcontrol.internal.audiosink.UpnpAudioSinkReg; import org.openhab.binding.upnpcontrol.internal.config.UpnpControlBindingConfiguration; import org.openhab.binding.upnpcontrol.internal.config.UpnpControlRendererConfiguration; @@ -454,7 +453,7 @@ protected void setNextURI(String nextURI, String nextURIMetaData) { /** * Invoke GetTransportState on UPnP AV Transport. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getTransportState() { Map inputs = Collections.singletonMap(INSTANCE_ID, Integer.toString(avTransportId)); @@ -464,7 +463,7 @@ protected void getTransportState() { /** * Invoke getPositionInfo on UPnP AV Transport. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getPositionInfo() { Map inputs = Collections.singletonMap(INSTANCE_ID, Integer.toString(avTransportId)); @@ -474,7 +473,7 @@ protected void getPositionInfo() { /** * Invoke GetMediaInfo on UPnP AV Transport. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. */ protected void getMediaInfo() { Map inputs = Collections.singletonMap(INSTANCE_ID, Integer.toString(avTransportId)); @@ -484,7 +483,9 @@ protected void getMediaInfo() { /** * Retrieves the current volume known to the control point, gets updated by GENA events or after UPnP Rendering - * Control GetVolume call. This method is used to retrieve volume by {@link UpnpAudioSink.getVolume}. + * Control GetVolume call. This method is used to retrieve volume with the + * {@link org.openhab.binding.upnpcontrol.internal.audiosink.UpnpAudioSink#getVolume UpnpAudioSink.getVolume} + * method. * * @return current volume */ @@ -494,7 +495,7 @@ public PercentType getCurrentVolume() { /** * Invoke GetVolume on UPnP Rendering Control. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. * * @param channel */ @@ -535,7 +536,7 @@ public void setVolume(PercentType volume) { /** * Invoke getMute on UPnP Rendering Control. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. * * @param channel */ @@ -564,7 +565,7 @@ protected void setMute(String channel, OnOffType mute) { /** * Invoke getMute on UPnP Rendering Control. - * Result is received in {@link onValueReceived}. + * Result is received in {@link #onValueReceived}. * * @param channel */ @@ -661,6 +662,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case SHUFFLE: handleCommandShuffle(channelUID, command); + break; case ONLY_PLAY_ONE: handleCommandOnlyPlayOne(channelUID, command); break; diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpServerHandler.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpServerHandler.java index d8d84f381da79..8355367f0eb26 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpServerHandler.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/handler/UpnpServerHandler.java @@ -32,7 +32,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.upnpcontrol.internal.UpnpControlHandlerFactory; import org.openhab.binding.upnpcontrol.internal.UpnpDynamicCommandDescriptionProvider; import org.openhab.binding.upnpcontrol.internal.UpnpDynamicStateDescriptionProvider; import org.openhab.binding.upnpcontrol.internal.config.UpnpControlBindingConfiguration; @@ -192,7 +191,7 @@ protected void initJob() { } /** - * Method that does a UPnP browse on a content directory. Results will be retrieved in the {@link onValueReceived} + * Method that does a UPnP browse on a content directory. Results will be retrieved in the {@link #onValueReceived} * method. * * @param objectID content directory object @@ -234,7 +233,7 @@ protected void browse(String objectID, String browseFlag, String filter, String } /** - * Method that does a UPnP search on a content directory. Results will be retrieved in the {@link onValueReceived} + * Method that does a UPnP search on a content directory. Results will be retrieved in the {@link #onValueReceived} * method. * * @param containerID content directory container @@ -544,7 +543,8 @@ private void handleCommandInRenderer(ChannelUID channelUID, Command command) { /** * Add a renderer to the renderer channel state option list. - * This method is called from the {@link UpnpControlHandlerFactory} class when creating a renderer handler. + * This method is called from the {@link org.openhab.binding.upnpcontrol.internal.UpnpControlHandlerFactory + * UpnpControlHandlerFactory} class when creating a renderer handler. * * @param key */ @@ -561,7 +561,8 @@ public void addRendererOption(String key) { /** * Remove a renderer from the renderer channel state option list. - * This method is called from the {@link UpnpControlHandlerFactory} class when removing a renderer handler. + * This method is called from the {@link org.openhab.binding.upnpcontrol.internal.UpnpControlHandlerFactory + * UpnpControlHandlerFactory} class when removing a renderer handler. * * @param key */ diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntry.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntry.java index e3ff96c273862..2c25cd8a63be3 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntry.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntry.java @@ -18,9 +18,9 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.apache.commons.lang3.StringEscapeUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.upnpcontrol.internal.util.StringUtils; /** * @@ -178,7 +178,7 @@ public String getAlbum() { * @return the URI for the album art. */ public String getAlbumArtUri() { - return StringEscapeUtils.unescapeXml(albumArtUri); + return StringUtils.unEscapeXml(albumArtUri); } /** diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntryQueue.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntryQueue.java index 1e89ff795aea7..2efbed0c0c0a1 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntryQueue.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/queue/UpnpEntryQueue.java @@ -40,7 +40,7 @@ * of a current index in the queue. It has convenience methods to play previous/next entries, whereby the queue can be * organized to play from first to last (with no repetition), to restart at the start when the end is reached (in a * continuous loop), or to random shuffle the entries. Repeat and shuffle are off by default, but can be set using the - * {@link setRepeat} and {@link setShuffle} methods. + * {@link #setRepeat} and {@link #setShuffle} methods. * * @author Mark Herwege - Initial contribution * @@ -186,7 +186,7 @@ public int index() { } /** - * @return the index of the next element in the queue that will be served if {@link next} is called, or -1 if + * @return the index of the next element in the queue that will be served if {@link #next} is called, or -1 if * nothing to serve for next. */ public synchronized int nextIndex() { @@ -198,7 +198,7 @@ public synchronized int nextIndex() { } /** - * @return the index of the previous element in the queue that will be served if {@link previous} is called, or -1 + * @return the index of the previous element in the queue that will be served if {@link #previous} is called, or -1 * if nothing to serve for next. */ public synchronized int previousIndex() { @@ -210,7 +210,7 @@ public synchronized int previousIndex() { } /** - * @return true if there is an element to server when calling {@link next}. + * @return true if there is an element to server when calling {@link #next}. */ public synchronized boolean hasNext() { int size = currentQueue.size(); @@ -221,7 +221,7 @@ public synchronized boolean hasNext() { } /** - * @return true if there is an element to server when calling {@link previous}. + * @return true if there is an element to server when calling {@link #previous}. */ public synchronized boolean hasPrevious() { int size = currentQueue.size(); diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/StringUtils.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/StringUtils.java new file mode 100644 index 0000000000000..080d4b66239af --- /dev/null +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/StringUtils.java @@ -0,0 +1,60 @@ +/** + * 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.upnpcontrol.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link StringUtils} class defines some static string utility methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class StringUtils { + + /** + * Simple method to escape XML special characters in String. + * There are five XML Special characters which needs to be escaped : + * & - & + * < - < + * > - > + * " - " + * ' - ' + */ + public static String escapeXml(String xml) { + xml = xml.replaceAll("&", "&"); + xml = xml.replaceAll("<", "<"); + xml = xml.replaceAll(">", ">"); + xml = xml.replaceAll("\"", """); + xml = xml.replaceAll("'", "'"); + return xml; + } + + /** + * Simple method to un escape XML special characters in String. + * There are five XML Special characters which needs to be escaped : + * & - & + * < - < + * > - > + * " - " + * ' - ' + */ + public static String unEscapeXml(String xml) { + xml = xml.replaceAll("&", "&"); + xml = xml.replaceAll("<", "<"); + xml = xml.replaceAll(">", ">"); + xml = xml.replaceAll(""", "\""); + xml = xml.replaceAll("'", "'"); + return xml; + } +} diff --git a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/UpnpXMLParser.java b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/UpnpXMLParser.java index 27b86add791cc..89983602f107e 100644 --- a/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/UpnpXMLParser.java +++ b/bundles/org.openhab.binding.upnpcontrol/src/main/java/org/openhab/binding/upnpcontrol/internal/util/UpnpXMLParser.java @@ -25,7 +25,6 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.apache.commons.lang3.StringEscapeUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.upnpcontrol.internal.queue.UpnpEntry; @@ -406,14 +405,14 @@ public List getEntries() { public static String compileMetadataString(UpnpEntry entry) { String id = entry.getId(); String parentId = entry.getParentId(); - String title = StringEscapeUtils.escapeXml(entry.getTitle()); + String title = StringUtils.escapeXml(entry.getTitle()); String upnpClass = entry.getUpnpClass(); - String album = StringEscapeUtils.escapeXml(entry.getAlbum()); + String album = StringUtils.escapeXml(entry.getAlbum()); String albumArtUri = entry.getAlbumArtUri(); - String creator = StringEscapeUtils.escapeXml(entry.getCreator()); - String artist = StringEscapeUtils.escapeXml(entry.getArtist()); - String publisher = StringEscapeUtils.escapeXml(entry.getPublisher()); - String genre = StringEscapeUtils.escapeXml(entry.getGenre()); + String creator = StringUtils.escapeXml(entry.getCreator()); + String artist = StringUtils.escapeXml(entry.getArtist()); + String publisher = StringUtils.escapeXml(entry.getPublisher()); + String genre = StringUtils.escapeXml(entry.getGenre()); Integer trackNumber = entry.getOriginalTrackNumber(); final MessageFormat messageFormat = new MessageFormat(METADATA_PATTERN); diff --git a/bundles/org.openhab.binding.vizio/README.md b/bundles/org.openhab.binding.vizio/README.md index 7c72f7b0ffa74..008072c066395 100644 --- a/bundles/org.openhab.binding.vizio/README.md +++ b/bundles/org.openhab.binding.vizio/README.md @@ -134,7 +134,7 @@ If an app that is in the JSON database fails to start when selected, try adjusti A current list of `APP_ID`'s can be found at http://hometest.buddytv.netdna-cdn.com/appservice/vizio_apps_prod.json and `NAME_SPACE` & `MESSAGE` values needed can be found at http://hometest.buddytv.netdna-cdn.com/appservice/app_availability_prod.json -If there is an error in the user supplied `appListJson`, the thing will fail to start and display a CONFIGURATION_PENDING message. +If there is an error in the user supplied `appListJson`, the thing will fail to start and display a CONFIGURATION_ERROR message. If all text in `appListJson` is removed (set to null) and the thing configuration saved, the binding will restore `appListJson` from the binding's JSON db. ## Full Example diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java index 1967fb4f94794..2246218deea24 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java @@ -16,7 +16,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.vizio.internal.appdb.VizioAppDbService; import org.openhab.binding.vizio.internal.handler.VizioHandler; import org.openhab.core.io.net.http.HttpClientFactory; @@ -39,7 +38,7 @@ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.vizio") public class VizioHandlerFactory extends BaseThingHandlerFactory { - private final HttpClient httpClient; + private final HttpClientFactory httpClientFactory; private final VizioStateDescriptionOptionProvider stateDescriptionProvider; private final String vizioAppsJson; @@ -47,7 +46,7 @@ public class VizioHandlerFactory extends BaseThingHandlerFactory { public VizioHandlerFactory(final @Reference HttpClientFactory httpClientFactory, final @Reference VizioStateDescriptionOptionProvider provider, final @Reference VizioAppDbService vizioAppDbService) { - this.httpClient = httpClientFactory.getCommonHttpClient(); + this.httpClientFactory = httpClientFactory; this.stateDescriptionProvider = provider; this.vizioAppsJson = vizioAppDbService.getVizioAppsJson(); } @@ -62,7 +61,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { - VizioHandler handler = new VizioHandler(thing, httpClient, stateDescriptionProvider, vizioAppsJson); + VizioHandler handler = new VizioHandler(thing, httpClientFactory, stateDescriptionProvider, vizioAppsJson); return handler; } diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java index 34ff83de38f5a..632aa573ed9b4 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java @@ -196,7 +196,7 @@ public PutResponse sendKeyPress(String commandJSON) throws VizioException { * @throws VizioException * */ - public PairingStart starPairing(String deviceName, int deviceId) throws VizioException { + public PairingStart startPairing(String deviceName, int deviceId) throws VizioException { return fromJson( putCommand(urlStartPairing, String.format("{ \"DEVICE_NAME\": \"%s\", \"DEVICE_ID\": \"%d\" }", deviceName, deviceId)), diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java deleted file mode 100644 index e825c190e376c..0000000000000 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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.vizio.internal.communication; - -import java.net.MalformedURLException; -import java.security.cert.CertificateException; - -import javax.net.ssl.X509ExtendedTrustManager; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.io.net.http.PEMTrustManager; -import org.openhab.core.io.net.http.TlsTrustManagerProvider; -import org.openhab.core.io.net.http.TrustAllTrustManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Provides a {@link PEMTrustManager} to allow secure connections to a Vizio TV that uses self signed - * certificates. - * - * @author Christoph Weitkamp - Initial Contribution - * @author Michael Lobstein - Adapted for Vizio binding - */ -@NonNullByDefault -public class VizioTlsTrustManagerProvider implements TlsTrustManagerProvider { - private final String hostname; - - private final Logger logger = LoggerFactory.getLogger(VizioTlsTrustManagerProvider.class); - - public VizioTlsTrustManagerProvider(String hostname) { - this.hostname = hostname; - } - - @Override - public String getHostName() { - return hostname; - } - - @Override - public X509ExtendedTrustManager getTrustManager() { - try { - logger.trace("Use self-signed certificate downloaded from Vizio TV."); - return PEMTrustManager.getInstanceFromServer("https://" + getHostName()); - } catch (CertificateException | MalformedURLException e) { - logger.debug("An unexpected exception occurred - returning a TrustAllTrustManager: {}", e.getMessage(), e); - } - return TrustAllTrustManager.getInstance(); - } -} diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java index 69cfb41bd3538..5a55582b3ad6b 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java @@ -107,7 +107,7 @@ public void execute(String[] args, Console console) { Random rng = new Random(); int pairingDeviceId = rng.nextInt(100000); - int pairingToken = communicator.starPairing(args[2], pairingDeviceId).getItem() + int pairingToken = communicator.startPairing(args[2], pairingDeviceId).getItem() .getPairingReqToken(); if (pairingToken != -1) { handler.setPairingDeviceId(pairingDeviceId); diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java index 1bb3d7ae1db2d..1bb0975b41c08 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java @@ -25,11 +25,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.vizio.internal.VizioConfiguration; import org.openhab.binding.vizio.internal.VizioException; import org.openhab.binding.vizio.internal.VizioStateDescriptionOptionProvider; import org.openhab.binding.vizio.internal.communication.VizioCommunicator; -import org.openhab.binding.vizio.internal.communication.VizioTlsTrustManagerProvider; import org.openhab.binding.vizio.internal.dto.app.CurrentApp; import org.openhab.binding.vizio.internal.dto.applist.VizioApp; import org.openhab.binding.vizio.internal.dto.applist.VizioApps; @@ -40,7 +40,7 @@ import org.openhab.binding.vizio.internal.dto.power.PowerMode; import org.openhab.binding.vizio.internal.enums.KeyCommand; import org.openhab.core.config.core.Configuration; -import org.openhab.core.io.net.http.TlsTrustManagerProvider; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.library.types.NextPreviousType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; @@ -53,12 +53,11 @@ import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.StateOption; import org.openhab.core.types.UnDefType; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,11 +73,11 @@ @NonNullByDefault public class VizioHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(VizioHandler.class); - private final HttpClient httpClient; + private final HttpClientFactory httpClientFactory; + private @Nullable HttpClient httpClient; private final VizioStateDescriptionOptionProvider stateDescriptionProvider; private final String dbAppsJson; - private @Nullable ServiceRegistration serviceRegistration; private @Nullable ScheduledFuture refreshJob; private @Nullable ScheduledFuture metadataRefreshJob; @@ -97,13 +96,13 @@ public class VizioHandler extends BaseThingHandler { private boolean powerOn = false; private boolean debounce = true; - public VizioHandler(Thing thing, HttpClient httpClient, + public VizioHandler(Thing thing, HttpClientFactory httpClientFactory, VizioStateDescriptionOptionProvider stateDescriptionProvider, String vizioAppsJson) { super(thing); - this.httpClient = httpClient; + this.httpClientFactory = httpClientFactory; this.stateDescriptionProvider = stateDescriptionProvider; this.dbAppsJson = vizioAppsJson; - this.communicator = new VizioCommunicator(httpClient, EMPTY, -1, EMPTY); + this.communicator = new VizioCommunicator(httpClientFactory.getCommonHttpClient(), EMPTY, -1, EMPTY); } @Override @@ -127,13 +126,22 @@ public void initialize() { host = "[" + host + "]"; } - this.communicator = new VizioCommunicator(httpClient, host, config.port, authToken != null ? authToken : EMPTY); - - // register trustmanager service to allow httpClient to accept self signed cert from the Vizio TV - VizioTlsTrustManagerProvider tlsTrustManagerProvider = new VizioTlsTrustManagerProvider( - host + ":" + config.port); - serviceRegistration = FrameworkUtil.getBundle(getClass()).getBundleContext() - .registerService(TlsTrustManagerProvider.class.getName(), tlsTrustManagerProvider, null); + final String httpClientName = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); + try { + httpClient = httpClientFactory.createHttpClient(httpClientName, new SslContextFactory.Client(true)); + final HttpClient localHttpClient = this.httpClient; + if (localHttpClient != null) { + localHttpClient.start(); + this.communicator = new VizioCommunicator(localHttpClient, host, config.port, + authToken != null ? authToken : EMPTY); + } + } catch (Exception e) { + logger.error( + "Long running HttpClient for Vizio handler {} cannot be started. Creating Handler failed. Exception: {}", + httpClientName, e.getMessage(), e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } if (authToken == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, @@ -363,11 +371,14 @@ public void dispose() { this.metadataRefreshJob = null; } - ServiceRegistration localServiceRegistration = serviceRegistration; - if (localServiceRegistration != null) { - // remove trustmanager service - localServiceRegistration.unregister(); - serviceRegistration = null; + try { + HttpClient localHttpClient = this.httpClient; + if (localHttpClient != null) { + localHttpClient.stop(); + } + this.httpClient = null; + } catch (Exception e) { + logger.debug("Unable to stop Vizio httpClient. Exception: {}", e.getMessage(), e); } } diff --git a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/VolvoOnCallHandlerFactory.java b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/VolvoOnCallHandlerFactory.java index 4d79c6661cffe..a7cf6af52a6c4 100644 --- a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/VolvoOnCallHandlerFactory.java +++ b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/VolvoOnCallHandlerFactory.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.volvooncall.internal.handler.VehicleHandler; import org.openhab.binding.volvooncall.internal.handler.VehicleStateDescriptionProvider; import org.openhab.binding.volvooncall.internal.handler.VolvoOnCallBridgeHandler; @@ -55,13 +54,13 @@ public class VolvoOnCallHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(VolvoOnCallHandlerFactory.class); private final VehicleStateDescriptionProvider stateDescriptionProvider; private final Gson gson; - private final HttpClient httpClient; + private final HttpClientFactory httpClientFactory; @Activate public VolvoOnCallHandlerFactory(@Reference VehicleStateDescriptionProvider provider, @Reference HttpClientFactory httpClientFactory) { this.stateDescriptionProvider = provider; - this.httpClient = httpClientFactory.createHttpClient(BINDING_ID); + this.httpClientFactory = httpClientFactory; this.gson = new GsonBuilder() .registerTypeAdapter(ZonedDateTime.class, (JsonDeserializer) (json, type, jsonDeserializationContext) -> ZonedDateTime @@ -87,7 +86,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (APIBRIDGE_THING_TYPE.equals(thingTypeUID)) { - return new VolvoOnCallBridgeHandler((Bridge) thing, gson, httpClient); + return new VolvoOnCallBridgeHandler((Bridge) thing, gson, httpClientFactory); } else if (VEHICLE_THING_TYPE.equals(thingTypeUID)) { return new VehicleHandler(thing, stateDescriptionProvider); } diff --git a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/api/VocHttpApi.java b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/api/VocHttpApi.java index e960bfc58d688..f0f9d99b87c76 100644 --- a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/api/VocHttpApi.java +++ b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/api/VocHttpApi.java @@ -36,6 +36,7 @@ import org.openhab.binding.volvooncall.internal.dto.VocAnswer; import org.openhab.core.cache.ExpiringCacheMap; import org.openhab.core.id.InstanceUUID; +import org.openhab.core.io.net.http.HttpClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,12 +62,12 @@ public class VocHttpApi { private final HttpClient httpClient; private final ApiBridgeConfiguration configuration; - public VocHttpApi(ApiBridgeConfiguration configuration, Gson gson, HttpClient httpClient) - throws VolvoOnCallException { + public VocHttpApi(String clientName, ApiBridgeConfiguration configuration, Gson gson, + HttpClientFactory httpClientFactory) throws VolvoOnCallException { this.gson = gson; this.cache = new ExpiringCacheMap<>(120 * 1000); this.configuration = configuration; - this.httpClient = httpClient; + this.httpClient = httpClientFactory.createHttpClient(clientName); httpClient.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, "openhab/voc_binding/" + InstanceUUID.get())); try { diff --git a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/handler/VolvoOnCallBridgeHandler.java b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/handler/VolvoOnCallBridgeHandler.java index 713fbbe6f67c5..a255302b53e69 100644 --- a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/handler/VolvoOnCallBridgeHandler.java +++ b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/handler/VolvoOnCallBridgeHandler.java @@ -17,18 +17,19 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.volvooncall.internal.VolvoOnCallException; import org.openhab.binding.volvooncall.internal.api.VocHttpApi; import org.openhab.binding.volvooncall.internal.config.ApiBridgeConfiguration; import org.openhab.binding.volvooncall.internal.discovery.VolvoVehicleDiscoveryService; import org.openhab.binding.volvooncall.internal.dto.CustomerAccounts; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,14 +47,14 @@ public class VolvoOnCallBridgeHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(VolvoOnCallBridgeHandler.class); private final Gson gson; - private final HttpClient httpClient; + private final HttpClientFactory httpClientFactory; private @Nullable VocHttpApi api; - public VolvoOnCallBridgeHandler(Bridge bridge, Gson gson, HttpClient httpClient) { + public VolvoOnCallBridgeHandler(Bridge bridge, Gson gson, HttpClientFactory httpClientFactory) { super(bridge); this.gson = gson; - this.httpClient = httpClient; + this.httpClientFactory = httpClientFactory; } @Override @@ -62,7 +63,8 @@ public void initialize() { ApiBridgeConfiguration configuration = getConfigAs(ApiBridgeConfiguration.class); try { - VocHttpApi vocApi = new VocHttpApi(configuration, gson, httpClient); + String clientName = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); + VocHttpApi vocApi = new VocHttpApi(clientName, configuration, gson, httpClientFactory); CustomerAccounts account = vocApi.getURL("customeraccounts/", CustomerAccounts.class); if (account.username != null) { updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, account.username); diff --git a/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/handler/WeatherCompanyBridgeHandler.java b/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/handler/WeatherCompanyBridgeHandler.java index f030b3f9f170e..d4d316b958bd5 100644 --- a/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/handler/WeatherCompanyBridgeHandler.java +++ b/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/handler/WeatherCompanyBridgeHandler.java @@ -16,11 +16,11 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpResponseException; import org.openhab.binding.weathercompany.internal.config.WeatherCompanyBridgeConfig; +import org.openhab.binding.weathercompany.internal.util.ExceptionUtils; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -58,7 +58,7 @@ public void run() { updateStatus(ThingStatus.ONLINE); cancelValidateApiKeyJob(); } catch (IOException e) { - Throwable rootcause = ExceptionUtils.getRootCause(e); + Throwable rootcause = ExceptionUtils.getRootThrowable(e); if (rootcause instanceof HttpResponseException && rootcause.getMessage().contains("Authentication challenge without")) { logger.debug("Bridge: HttpResponseException: API key is not valid"); diff --git a/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/util/ExceptionUtils.java b/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/util/ExceptionUtils.java new file mode 100644 index 0000000000000..481d90e522659 --- /dev/null +++ b/bundles/org.openhab.binding.weathercompany/src/main/java/org/openhab/binding/weathercompany/internal/util/ExceptionUtils.java @@ -0,0 +1,39 @@ +/** + * 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.weathercompany.internal.util; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ExceptionUtils} class defines some static utility methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class ExceptionUtils { + + public static Throwable getRootThrowable(Throwable throwable) { + List list = new ArrayList<>(); + while (!list.contains(throwable)) { + list.add(throwable); + Throwable throwableLocal = throwable.getCause(); + if (throwableLocal != null) { + throwable = throwableLocal; + } + } + return throwable; + } +} diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryService.java b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryService.java index e8561aecfd7d9..72236b298ba3a 100644 --- a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryService.java +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryService.java @@ -104,7 +104,7 @@ protected void startScan() { setScanning(true); if (servletControls != null && !servletControls.isActive()) { servletWasInactive = true; - servletControls.activate(); + servletControls.enable(); } thinglessStationIds.keySet().forEach(this::createDiscoveryResult); } @@ -114,7 +114,7 @@ protected synchronized void stopScan() { super.stopScan(); thinglessStationIds.keySet().forEach(this::createDiscoveryResult); if (!isBackgroundDiscoveryEnabled() && servletControls != null && servletWasInactive) { - servletControls.deactivate(); + servletControls.disable(); } setScanning(false); } diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverHandlerFactory.java b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverHandlerFactory.java index 72ce61e642226..7d1a3f42bb64a 100644 --- a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverHandlerFactory.java +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverHandlerFactory.java @@ -42,24 +42,21 @@ public class WundergroundUpdateReceiverHandlerFactory extends BaseThingHandlerFa private final WundergroundUpdateReceiverDiscoveryService discoveryService; private final ChannelTypeRegistry channelTypeRegistry; private final WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider; - private final WundergroundUpdateReceiverServlet wunderGroundUpdateReceiverServlet; private final ManagedThingProvider managedThingProvider; + private final WundergroundUpdateReceiverServlet wunderGroundUpdateReceiverServlet; @Activate public WundergroundUpdateReceiverHandlerFactory(@Reference HttpService httpService, @Reference WundergroundUpdateReceiverDiscoveryService discoveryService, @Reference WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider, - @Reference ChannelTypeRegistry channelTypeRegistry, @Reference ManagedThingProvider managedThingProvider) { + @Reference ChannelTypeRegistry channelTypeRegistry, @Reference ManagedThingProvider managedThingProvider, + @Reference WundergroundUpdateReceiverServlet wunderGroundUpdateReceiverServlet) { this.discoveryService = discoveryService; this.channelTypeRegistry = channelTypeRegistry; this.channelTypeProvider = channelTypeProvider; this.managedThingProvider = managedThingProvider; - this.wunderGroundUpdateReceiverServlet = new WundergroundUpdateReceiverServlet(httpService, - this.discoveryService); - this.discoveryService.servletControls = this.wunderGroundUpdateReceiverServlet; - if (this.discoveryService.isBackgroundDiscoveryEnabled()) { - this.wunderGroundUpdateReceiverServlet.activate(); - } + this.wunderGroundUpdateReceiverServlet = wunderGroundUpdateReceiverServlet; + this.discoveryService.servletControls = wunderGroundUpdateReceiverServlet; } @Override @@ -82,8 +79,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected void deactivate(ComponentContext componentContext) { - this.wunderGroundUpdateReceiverServlet.deactivate(); - this.wunderGroundUpdateReceiverServlet.dispose(); + this.wunderGroundUpdateReceiverServlet.disable(); super.deactivate(componentContext); } } diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServlet.java b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServlet.java index 729bda89eda6a..5d16632c53040 100644 --- a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServlet.java +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServlet.java @@ -20,25 +20,26 @@ import java.io.PrintWriter; import java.time.Instant; import java.util.Collections; -import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; -import java.util.Hashtable; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; -import javax.servlet.ServletException; +import javax.servlet.Servlet; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.io.http.servlet.BaseOpenHABServlet; -import org.osgi.service.http.HttpContext; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletName; +import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletPattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +50,10 @@ * @author Daniel Demus - Initial contribution */ @NonNullByDefault -public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet +@HttpWhiteboardServletName(WundergroundUpdateReceiverServlet.SERVLET_URL) +@HttpWhiteboardServletPattern(WundergroundUpdateReceiverServlet.SERVLET_URL) +@Component(immediate = true, service = { Servlet.class, WundergroundUpdateReceiverServlet.class }) +public class WundergroundUpdateReceiverServlet extends HttpServlet implements WundergroundUpdateReceiverServletControls { public static final String SERVLET_URL = "/weatherstation/updateweatherstation.php"; @@ -65,10 +69,12 @@ public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet private boolean active = false; private String errorDetail = ""; - public WundergroundUpdateReceiverServlet(HttpService httpService, - WundergroundUpdateReceiverDiscoveryService discoveryService) { - super(httpService); + @Activate + public WundergroundUpdateReceiverServlet( + final @Reference WundergroundUpdateReceiverDiscoveryService discoveryService) { this.discoveryService = discoveryService; + errorDetail = ""; + active = discoveryService.isBackgroundDiscoveryEnabled(); } public boolean isActive() { @@ -87,10 +93,6 @@ public Set getStationIds() { return this.handlers.keySet(); } - public void activate() { - activate(SERVLET_URL, httpService.createDefaultHttpContext()); - } - public void addHandler(WundergroundUpdateReceiverHandler handler) { synchronized (this.handlers) { if (this.handlers.containsKey(handler.getStationId())) { @@ -102,7 +104,7 @@ public void addHandler(WundergroundUpdateReceiverHandler handler) { this.handlers.put(handler.getStationId(), handler); errorDetail = ""; if (!isActive()) { - activate(); + enable(); } } } @@ -114,22 +116,20 @@ public void removeHandler(String stationId) { this.handlers.remove(stationId); } if (this.handlers.isEmpty() && !this.discoveryService.isBackgroundDiscoveryEnabled()) { - deactivate(); + disable(); } } } - public void deactivate() { - synchronized (LOCK) { - logger.debug("Stopping servlet {} at {}", getClass().getSimpleName(), SERVLET_URL); - try { - super.deactivate(SERVLET_URL); - } catch (IllegalArgumentException ignored) { - // SERVLET_URL is already unregistered - } - errorDetail = ""; - active = false; - } + @Override + public void enable() { + active = true; + } + + @Deactivate + public void disable() { + errorDetail = ""; + active = false; } public void handlerConfigUpdated(WundergroundUpdateReceiverHandler handler) { @@ -150,28 +150,7 @@ public void dispose() { synchronized (this.handlers) { Set stationIds = new HashSet<>(getStationIds()); stationIds.forEach(this::removeHandler); - deactivate(); - } - } - - @Override - protected void activate(String alias, HttpContext httpContext) { - synchronized (LOCK) { - try { - logger.debug("Starting servlet {} at {}", getClass().getSimpleName(), alias); - Dictionary props = new Hashtable<>(1, 10); - httpService.registerServlet(alias, this, props, httpContext); - errorDetail = ""; - active = true; - } catch (NamespaceException e) { - active = false; - errorDetail = "Servlet couldn't be registered - alias " + alias + " already in use"; - logger.warn("Error during servlet registration - alias {} already in use", alias, e); - } catch (ServletException e) { - active = false; - errorDetail = "Servlet couldn't be registered - " + e.getMessage(); - logger.warn("Error during servlet registration", e); - } + disable(); } } diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletControls.java b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletControls.java index d8237b31f0f16..0b97716adadbe 100644 --- a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletControls.java +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletControls.java @@ -20,9 +20,9 @@ @NonNullByDefault public interface WundergroundUpdateReceiverServletControls { - void activate(); + void enable(); - void deactivate(); + void disable(); boolean isActive(); } diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryServiceTest.java b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryServiceTest.java index be675d020abcf..50dec648cf4c1 100644 --- a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryServiceTest.java +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryServiceTest.java @@ -89,7 +89,7 @@ void programmaticChannelsAreAddedCorrectlyOnce() { WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService( true); HttpService httpService = mock(HttpService.class); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req.getParameterMap())); Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID) .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID))) @@ -138,15 +138,13 @@ void aRequestWithAnUnregisteredStationidIsAddedToTheQueueOnce() + "action=updateraw&" + "realtime=1&" + "rtfreq=5"; WundergroundUpdateReceiverDiscoveryService discoveryService = mock( WundergroundUpdateReceiverDiscoveryService.class); - HttpService httpService = mock(HttpService.class); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class); when(handler.getStationId()).thenReturn(STATION_ID_1); sut.addHandler(handler); when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(false); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); HttpChannel httpChannel = mock(HttpChannel.class); @@ -182,7 +180,7 @@ void multipleIndexedParametersOfTheSameChanneltypeAreCorrectlyDiscovered() throw WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService( false); HttpService httpService = mock(HttpService.class); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req.getParameterMap())); Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID) .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID))) @@ -196,7 +194,7 @@ void multipleIndexedParametersOfTheSameChanneltypeAreCorrectlyDiscovered() throw sut.addHandler(handler); // When - sut.activate(); + sut.enable(); // Then assertThat(sut.isActive(), is(true)); @@ -228,7 +226,7 @@ void unregisteredChannelsAreAddedOnTheFlyWhenDiscovered() throws IOException { WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService( true); HttpService httpService = mock(HttpService.class); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap())); Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID) .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID))) @@ -261,7 +259,7 @@ void unregisteredChannelsAreAddedOnTheFlyWhenDiscovered() throws IOException { HttpVersion.HTTP_1_1, new HttpFields()); Request req2 = new Request(httpChannel, null); req2.setMetaData(request); - sut.activate(); + sut.enable(); // Then assertThat(sut.isActive(), is(true)); @@ -295,7 +293,7 @@ void unregisteredChannelsAreNotAddedOnUnmanagedThings() throws IOException { WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService( true); HttpService httpService = mock(HttpService.class); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap())); Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID) .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID))) @@ -328,7 +326,7 @@ void unregisteredChannelsAreNotAddedOnUnmanagedThings() throws IOException { HttpVersion.HTTP_1_1, new HttpFields()); Request req2 = new Request(httpChannel, null); req2.setMetaData(request); - sut.activate(); + sut.enable(); // Then assertThat(sut.isActive(), is(true)); @@ -359,7 +357,7 @@ void lastQueryTriggerIsMigratedSuccessfully() throws IOException { WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService( true); HttpService httpService = mock(HttpService.class); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap())); Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID) .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID))) @@ -403,7 +401,7 @@ void lastQueryTriggerIsMigratedSuccessfully() throws IOException { HttpVersion.HTTP_1_1, new HttpFields()); Request req2 = new Request(httpChannel, null); req2.setMetaData(request); - sut.activate(); + sut.enable(); // Then assertThat(sut.isActive(), is(true)); diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletTest.java b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletTest.java index 45c1ad4c45bd3..2b6d95e1453de 100644 --- a/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletTest.java +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletTest.java @@ -19,7 +19,6 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -75,7 +74,6 @@ import org.openhab.core.thing.type.ChannelKind; import org.openhab.core.thing.type.ChannelTypeBuilder; import org.openhab.core.thing.type.ChannelTypeRegistry; -import org.osgi.service.http.HttpService; import org.osgi.service.http.NamespaceException; /** @@ -90,7 +88,6 @@ class WundergroundUpdateReceiverServletTest { private static final ThingUID TEST_THING_UID = new ThingUID( WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, "test-receiver"); - private @Mock HttpService httpService; private @Mock ChannelTypeRegistry channelTypeRegistry; private @Mock WundergroundUpdateReceiverDiscoveryService discoveryService; private @Mock ManagedThingProvider managedThingProvider; @@ -103,7 +100,7 @@ public void setUp() { @Test void theServletIsActiveAfterTheFirstHandlerIsAdded() throws ServletException, NamespaceException { // Given - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class); when(handler.getStationId()).thenReturn(STATION_ID_1); @@ -111,7 +108,6 @@ void theServletIsActiveAfterTheFirstHandlerIsAdded() throws ServletException, Na sut.addHandler(handler); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); } @@ -119,7 +115,7 @@ void theServletIsActiveAfterTheFirstHandlerIsAdded() throws ServletException, Na void theServletIsInactiveAfterTheLastHandlerIsRemovedAndBackgroundDiscoveryIsDisabled() throws ServletException, NamespaceException { // Given - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class); when(handler.getStationId()).thenReturn(STATION_ID_1); when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(false); @@ -128,14 +124,12 @@ void theServletIsInactiveAfterTheLastHandlerIsRemovedAndBackgroundDiscoveryIsDis sut.addHandler(handler); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); // When sut.removeHandler(handler.getStationId()); // Then - verify(httpService).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL); assertThat(sut.isActive(), is(false)); } @@ -143,7 +137,7 @@ void theServletIsInactiveAfterTheLastHandlerIsRemovedAndBackgroundDiscoveryIsDis void theServletIsActiveAfterTheLastHandlerIsRemovedButBackgroundDiscoveryIsEnabled() throws ServletException, NamespaceException { // Given - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class); when(handler.getStationId()).thenReturn(STATION_ID_1); when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(true); @@ -152,21 +146,19 @@ void theServletIsActiveAfterTheLastHandlerIsRemovedButBackgroundDiscoveryIsEnabl sut.addHandler(handler); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); // When sut.removeHandler(handler.getStationId()); // Then - verify(httpService, never()).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL); assertThat(sut.isActive(), is(true)); } @Test void onDisposeAllHandlersAreRemovedAndServletIsInactive() throws ServletException, NamespaceException { // Given - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler1 = mock(WundergroundUpdateReceiverHandler.class); when(handler1.getStationId()).thenReturn(STATION_ID_1); WundergroundUpdateReceiverHandler handler2 = mock(WundergroundUpdateReceiverHandler.class); @@ -177,14 +169,12 @@ void onDisposeAllHandlersAreRemovedAndServletIsInactive() throws ServletExceptio sut.addHandler(handler2); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); // When sut.dispose(); // Then - verify(httpService, times(2)).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL); assertThat(sut.isActive(), is(false)); } @@ -192,7 +182,7 @@ void onDisposeAllHandlersAreRemovedAndServletIsInactive() throws ServletExceptio void OnDisposeAllHandlersAreRemovedAndServletIsInactiveEvenThoughBackgroundDiscoveryIsEnabled() throws ServletException, NamespaceException { // Given - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler1 = mock(WundergroundUpdateReceiverHandler.class); when(handler1.getStationId()).thenReturn(STATION_ID_1); WundergroundUpdateReceiverHandler handler2 = mock(WundergroundUpdateReceiverHandler.class); @@ -204,14 +194,12 @@ void OnDisposeAllHandlersAreRemovedAndServletIsInactiveEvenThoughBackgroundDisco sut.addHandler(handler2); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); // When sut.dispose(); // Then - verify(httpService).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL); assertThat(sut.isActive(), is(false)); } @@ -231,7 +219,7 @@ void changedStationIdPropagatesToHandlerKey() throws ServletException, Namespace .thenReturn(ChannelTypeBuilder.state(LAST_QUERY_STATE_CHANNELTYPEUID, "Label", "String").build()); when(this.channelTypeRegistry.getChannelType(LAST_QUERY_TRIGGER_CHANNELTYPEUID)) .thenReturn(ChannelTypeBuilder.trigger(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "Label").build()); - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService, new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider); ThingHandlerCallback mockCallback = mock(ThingHandlerCallback.class); @@ -241,7 +229,6 @@ void changedStationIdPropagatesToHandlerKey() throws ServletException, Namespace handler.initialize(); // Then - verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any()); assertThat(sut.isActive(), is(true)); assertThat(sut.getStationIds(), hasItems(STATION_ID_1)); @@ -263,7 +250,7 @@ void aGetRequestIsCorrectlyParsed() throws IOException { ThingUID testThingUID = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, "test-receiver"); final String queryString = "ID=dfggger&PASSWORD=XXXXXX&tempf=26.1&humidity=74&dewptf=18.9&windchillf=26.1&winddir=14&windspeedmph=1.34&windgustmph=2.46&rainin=0.00&dailyrainin=0.00&weeklyrainin=0.00&monthlyrainin=0.08&yearlyrainin=3.06&solarradiation=42.24&UV=1&indoortempf=69.3&indoorhumidity=32&baromin=30.39&AqNOX=21&lowbatt=1&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5"; - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); List channels = List.of( ChannelBuilder .create(new ChannelUID(testThingUID, METADATA_GROUP, @@ -407,7 +394,7 @@ void aGetRequestWithIndexedParametresAreCorrectlyParsed() throws IOException { ThingUID testThingUID = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, "test-receiver"); final String queryString = "ID=dfggger&PASSWORD=XXXXXX&temp1f=26.1&humidity=74&temp2f=25.1&lowbatt=1&soilmoisture1=78&soilmoisture2=73&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5"; - WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService); + WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(discoveryService); List channels = List.of( ChannelBuilder .create(new ChannelUID(testThingUID, METADATA_GROUP, diff --git a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java index 404f7e4640abd..c45051b0d5214 100644 --- a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java +++ b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java @@ -68,6 +68,7 @@ public class YIOremoteDockHandler extends BaseThingHandler { private ClientUpgradeRequest yioremoteDockwebSocketClientrequest = new ClientUpgradeRequest(); private @Nullable URI websocketAddress; private YioRemoteDockHandleStatus yioRemoteDockActualStatus = YioRemoteDockHandleStatus.UNINITIALIZED_STATE; + private @Nullable Future initJob; private @Nullable Future webSocketPollingJob; private @Nullable Future webSocketReconnectionPollingJob; public String receivedMessage = ""; @@ -90,7 +91,7 @@ public YIOremoteDockHandler(Thing thing) { @Override public void initialize() { updateStatus(ThingStatus.UNKNOWN); - scheduler.execute(() -> { + initJob = scheduler.submit(() -> { try { websocketAddress = new URI("ws://" + localConfig.host + ":946"); yioRemoteDockActualStatus = YioRemoteDockHandleStatus.AUTHENTICATION_PROCESS; @@ -257,8 +258,18 @@ public Collection> getServices() { @Override public void dispose() { + Future job = initJob; + if (job != null) { + job.cancel(true); + initJob = null; + } disposeWebsocketPollingJob(); disposeWebSocketReconnectionPollingJob(); + try { + webSocketClient.stop(); + } catch (Exception e) { + logger.debug("Could not stop webSocketClient, message {}", e.getMessage()); + } } @Override @@ -342,21 +353,19 @@ private void authenticateWebsocket() { } private void disposeWebsocketPollingJob() { - if (webSocketPollingJob != null) { - if (!webSocketPollingJob.isCancelled() && webSocketPollingJob != null) { - webSocketPollingJob.cancel(true); - } + Future job = webSocketPollingJob; + if (job != null) { + job.cancel(true); webSocketPollingJob = null; } } private void disposeWebSocketReconnectionPollingJob() { - if (webSocketReconnectionPollingJob != null) { - if (!webSocketReconnectionPollingJob.isCancelled() && webSocketReconnectionPollingJob != null) { - webSocketReconnectionPollingJob.cancel(true); - } + Future job = webSocketReconnectionPollingJob; + if (job != null) { + job.cancel(true); + webSocketReconnectionPollingJob = null; } - webSocketReconnectionPollingJob = null; logger.debug("disposereconnection"); reconnectionCounter = 0; } diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java index f504fa65d422f..a973b76b848a8 100644 --- a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java @@ -194,7 +194,7 @@ protected void activate() { try { httpService.unregister(DISCOVERY_FILE); - } catch (IllegalArgumentException ignore) { + } catch (RuntimeException ignore) { } try { diff --git a/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java b/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java index ebdd4ad2f745a..0b554a846249d 100644 --- a/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java +++ b/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java @@ -15,13 +15,13 @@ import java.math.BigDecimal; import java.math.RoundingMode; -import org.apache.commons.lang3.StringUtils; import org.openhab.core.items.Item; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.io.imperihome.internal.model.param.DeviceParam; import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.util.StringUtils; /** * RGB light device. @@ -80,7 +80,7 @@ public void stateUpdated(Item item, State newState) { private String toHex(int value) { String hex = Integer.toHexString(value); - return StringUtils.leftPad(hex, 2, '0'); + return StringUtils.padLeft(hex, 2, "0"); } private int convertPercentToByte(PercentType percent) { diff --git a/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java b/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java index c69b6bd2de860..00a69f92a6062 100644 --- a/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java +++ b/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang3.BooleanUtils; import org.openhab.core.items.Item; import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.ItemRegistryChangeListener; @@ -58,6 +57,7 @@ import org.openhab.io.imperihome.internal.model.param.DeviceParam; import org.openhab.io.imperihome.internal.model.param.ParamType; import org.openhab.io.imperihome.internal.util.DigestUtil; +import org.openhab.io.imperihome.internal.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -227,7 +227,7 @@ private String getLabel(Item item, Map> issTags) { } private boolean isInverted(Map> issTags) { - return issTags.containsKey(TagType.INVERT) && BooleanUtils.toBoolean(issTags.get(TagType.INVERT).get(0)); + return issTags.containsKey(TagType.INVERT) && StringUtils.toBoolean(issTags.get(TagType.INVERT).get(0)); } private void setDeviceRoom(AbstractDevice device, Map> issTags) { diff --git a/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/util/StringUtils.java b/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/util/StringUtils.java new file mode 100644 index 0000000000000..7e2f219408b48 --- /dev/null +++ b/bundles/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/util/StringUtils.java @@ -0,0 +1,44 @@ +/** + * 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.io.imperihome.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link StringUtils} class defines some static string utility methods + * + * @author Leo Siepel - Initial contribution + */ +@NonNullByDefault +public class StringUtils { + + /** + * Simple method to create boolean from string. + * 'true', 'on', 'y', 't' or 'yes' (case insensitive) will return true. Otherwise, false is returned. + */ + public static boolean toBoolean(@Nullable String input) { + if (input != null) { + input = input.toLowerCase(); + } + return "true".equals(input) || "on".equals(input) || "y".equals(input) || "t".equals(input) + || "yes".equals(input); + } + + public static String padLeft(@Nullable String input, int minSize, String padString) { + if (input == null) { + input = ""; + } + return String.format("%" + minSize + "s", input).replace(" ", padString); + } +} diff --git a/bundles/org.openhab.persistence.jpa/pom.xml b/bundles/org.openhab.persistence.jpa/pom.xml index 4d6d32df6e1ba..821653d1f030e 100644 --- a/bundles/org.openhab.persistence.jpa/pom.xml +++ b/bundles/org.openhab.persistence.jpa/pom.xml @@ -32,6 +32,11 @@ derby 10.14.2.0 + + javax.el + el-api + 2.2 + diff --git a/bundles/org.openhab.transform.rollershutterposition/NOTICE b/bundles/org.openhab.transform.rollershutterposition/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.transform.rollershutterposition/README.md b/bundles/org.openhab.transform.rollershutterposition/README.md new file mode 100644 index 0000000000000..dd1a0bf22c09e --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/README.md @@ -0,0 +1,19 @@ +# Rollershutter Position Emulation Profile Service + +The Rollershutter Position emulates absolute position setting for Rollershutter devices which only support basic UP/DOWN/STOP commands. +This allows a Rollershutter to be set to an absolution position from 0..100 even if the controller does not support this feature (i.e. Somfy controllers). + +The logic code used for this profile service was adapted from Tarag Gautier's JavaScript implementation VASRollershutter.js. +By implementing as a profile, it eliminates the need for setting up a jsr233 js environment and simplifies the configuration. + +## Configuration + +To use this profile, simply include the profile on the Rollershutter item which is assigned to the Rollershutter channel. +The parameters and are the time it takes for the Rollershutter to fully extend or close in seconds. +The precision parameter can be used to specify the minimum movement that can be made. +This is useful when latencies in the system limit prevent very small movements and will reduce the accuracy of the position estimation. + +```java +Rollershutter { channel=""[profile="rollershutter:position", uptime=, downtime=, precision=]]} +``` + diff --git a/bundles/org.openhab.transform.rollershutterposition/pom.xml b/bundles/org.openhab.transform.rollershutterposition/pom.xml new file mode 100644 index 0000000000000..aed1db8b5684b --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.0.0-SNAPSHOT + + + org.openhab.transform.rollershutterposition + + openHAB Add-ons :: Bundles :: Transformation Service :: Roller Shutter Position + + diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/feature/feature.xml b/bundles/org.openhab.transform.rollershutterposition/src/main/feature/feature.xml new file mode 100644 index 0000000000000..f442520f8de9f --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.transform.rollershutterposition/${project.version} + + diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionConstants.java b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionConstants.java new file mode 100644 index 0000000000000..b0cc6ec647a74 --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionConstants.java @@ -0,0 +1,39 @@ +/** + * 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.transform.rollershutterposition.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.profiles.ProfileTypeUID; +import org.openhab.core.transform.TransformationService; + +/** + * The {@link RollerShutterPositionConstants} class to define transform constants + * used across the whole binding. + * + * @author Jeff James - Initial contribution + */ +@NonNullByDefault +public class RollerShutterPositionConstants { + + // Profile Type UID + public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID( + TransformationService.TRANSFORM_PROFILE_SCOPE, "ROLLERSHUTTERPOSITION"); + + // Parameters + public static final String UPTIME_PARAM = "uptime"; + public static final String DOWNTIME_PARAM = "downtime"; + public static final String PRECISION_PARAM = "precision"; + + public static final int POSITION_UPDATE_PERIOD_MILLISECONDS = 800; + public static final int DEFAULT_PRECISION = 5; +} diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionProfile.java b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionProfile.java new file mode 100644 index 0000000000000..192a544b34509 --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionProfile.java @@ -0,0 +1,282 @@ +/** + * 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.transform.rollershutterposition.internal; + +import static org.openhab.transform.rollershutterposition.internal.RollerShutterPositionConstants.*; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.ThreadPoolManager; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.profiles.ProfileCallback; +import org.openhab.core.thing.profiles.ProfileContext; +import org.openhab.core.thing.profiles.ProfileTypeUID; +import org.openhab.core.thing.profiles.StateProfile; +import org.openhab.core.types.Command; +import org.openhab.core.types.State; +import org.openhab.transform.rollershutterposition.internal.config.RollerShutterPositionConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Profile to implement the RollerShutterPosition ItemChannelLink + * + * @author Jeff James - Initial contribution + * + * Core logic in this module has been heavily adapted from Tarag Gautier js script implementation + * VASRollershutter.js + */ +@NonNullByDefault +public class RollerShutterPositionProfile implements StateProfile { + private static final String PROFILE_THREADPOOL_NAME = "profile-rollershutterposition"; + private final Logger logger = LoggerFactory.getLogger(RollerShutterPositionProfile.class); + + private final ProfileCallback callback; + RollerShutterPositionConfig configuration; + + private int position = 0; // current position of the roller shutter (assumes 0 when system starts) + private int targetPosition; + private boolean isValidConfiguration = false; + private Instant movingSince = Instant.MIN; + private UpDownType direction = UpDownType.DOWN; + + private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(PROFILE_THREADPOOL_NAME); + protected @Nullable ScheduledFuture stopTimer = null; + protected @Nullable ScheduledFuture updateTimer = null; + + public RollerShutterPositionProfile(final ProfileCallback callback, final ProfileContext context) { + this.callback = callback; + this.configuration = context.getConfiguration().as(RollerShutterPositionConfig.class); + + if (configuration.uptime == 0) { + logger.info("Profile paramater {} must not be 0", UPTIME_PARAM); + return; + } + + if (configuration.downtime == 0) { + configuration.downtime = configuration.uptime; + } + + if (configuration.precision == 0) { + configuration.precision = DEFAULT_PRECISION; + } + + this.isValidConfiguration = true; + + logger.debug("Profile configured with '{}'='{}' ms, '{}'={} ms, '{}'={}", UPTIME_PARAM, configuration.uptime, + DOWNTIME_PARAM, configuration.downtime, PRECISION_PARAM, configuration.precision); + } + + @Override + public ProfileTypeUID getProfileTypeUID() { + return PROFILE_TYPE_UID; + } + + @Override + public void onCommandFromItem(Command command) { + logger.debug("onCommandFromItem: {}", command); + + // pass through command if profile has not been configured properly + if (!isValidConfiguration) { + callback.handleCommand(command); + return; + } + + if (command instanceof UpDownType) { + if (command == UpDownType.UP) { + moveTo(0); + } else if (command == UpDownType.DOWN) { + moveTo(100); + } + } else if (command instanceof StopMoveType) { + stop(); + } else { + moveTo(((PercentType) command).intValue()); + } + } + + private boolean isMoving() { + return (!movingSince.equals(Instant.MIN)); + } + + private void moveTo(int targetPos) { + boolean alreadyMoving = false; + + if (targetPos < 0 || targetPos > 100) { + logger.debug("moveTo() position is invalid: {}", targetPos); + return; + } + + int curPos = currentPosition(); + int posOffset = targetPos - curPos; + + UpDownType newCmd; + + if (targetPos == position && !isMoving()) { + logger.debug("moveTo() position already current: {}", targetPos); + if (targetPos == 0) { // always send command if either 0 or 100 in case it is not already in that position + callback.handleCommand(UpDownType.UP); + } else if (targetPos == 100) { + callback.handleCommand(UpDownType.DOWN); + } + return; + } else if (targetPos == 0 || targetPos == 100) { + logger.debug("moveTo() bounding position"); + newCmd = targetPos == 0 ? UpDownType.UP : UpDownType.DOWN; + } else if (Math.abs(posOffset) < configuration.precision) { + callback.sendUpdate(new PercentType(position)); // update position because autoupdate will assume the + // movement happened + logger.info("moveTo() is less than the precision setting of {}", configuration.precision); + return; + } else { + newCmd = posOffset > 0 ? UpDownType.DOWN : UpDownType.UP; + } + + logger.debug("moveTo() targetPosition: {} from currentPosition: {}", targetPos, curPos); + + long time = (long) ((Math.abs(posOffset) / 100d) + * (posOffset > 0 ? (double) configuration.downtime * 1000 : (double) configuration.uptime * 1000)); + logger.debug("moveTo() computed movement offset: {} / {} / {} ms", posOffset, newCmd, time); + + if (isMoving()) { + position = curPos; // Update "starting" position if already in motion since the last move did not finish + + if (direction == newCmd) { + alreadyMoving = true; + } + } + + this.targetPosition = targetPos; + this.direction = newCmd; + this.movingSince = Instant.now(); + + if (stopTimer != null) { + Objects.requireNonNull(stopTimer).cancel(true); + } + this.stopTimer = scheduler.schedule(stopTimeoutTask, time, TimeUnit.MILLISECONDS); + + if (updateTimer != null) { + Objects.requireNonNull(updateTimer).cancel(true); + } + this.updateTimer = scheduler.scheduleWithFixedDelay(updateTimeoutTask, 0, POSITION_UPDATE_PERIOD_MILLISECONDS, + TimeUnit.MILLISECONDS); + + if (!alreadyMoving) { + logger.debug("moveTo() sending command for movement: {}, timer set in {} ms", direction, time); + callback.handleCommand(direction); + } else { + logger.debug("moveTo() updating timing but already moving in right directio: {}, timer set in {} ms", + direction, time); + } + } + + private void stop() { + callback.handleCommand(StopMoveType.STOP); + + this.position = currentPosition(); + this.movingSince = Instant.MIN; + + if (stopTimer != null) { + Objects.requireNonNull(stopTimer).cancel(true); + this.stopTimer = null; + } + if (updateTimer != null) { + Objects.requireNonNull(updateTimer).cancel(true); + this.updateTimer = null; + } + + callback.sendUpdate(new PercentType(position)); + } + + private int currentPosition() { + if (isMoving()) { + logger.trace("currentPosition() while moving"); + + // movingSince is always set if moving + long millis = movingSince.until(Instant.now(), ChronoUnit.MILLIS); + double delta = 0; + + if (direction == UpDownType.UP) { + delta = -(millis / (configuration.uptime * 1000)) * 100d; + } else { + delta = (millis / (configuration.downtime * 1000)) * 100d; + } + + return (int) Math.max(0, Math.min(100, Math.round(position + delta))); + } else { + return position; + } + } + + // Runnable task to time duration of the move to make + private Runnable stopTimeoutTask = new Runnable() { + @Override + public void run() { + if (targetPosition == 0 || targetPosition == 100) { + // Don't send stop command to re-sync position using the motor end stop + logger.debug("arrived at end position, not stopping for calibration"); + } else { + callback.handleCommand(StopMoveType.STOP); + logger.debug("arrived at position, sending STOP command"); + } + + logger.trace("stopTimeoutTask() position: {}", targetPosition); + + if (updateTimer != null) { + Objects.requireNonNull(updateTimer).cancel(true); + updateTimer = null; + } + + movingSince = Instant.MIN; + position = targetPosition; + targetPosition = -1; + callback.sendUpdate(new PercentType(position)); + } + }; + + // Runnable task to update the item on position while the roller shutter is moving + private Runnable updateTimeoutTask = new Runnable() { + @Override + public void run() { + if (isMoving()) { + int pos = currentPosition(); + if (pos < 0 || pos > 100) { + return; + } + callback.sendUpdate(new PercentType(pos)); + logger.trace("updateTimeoutTask(): {}", pos); + } + } + }; + + @Override + public void onStateUpdateFromItem(State state) { + } + + @Override + public void onCommandFromHandler(Command command) { + } + + @Override + public void onStateUpdateFromHandler(State state) { + } +} diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionProfileFactory.java b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionProfileFactory.java new file mode 100644 index 0000000000000..19faf866c67a4 --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/RollerShutterPositionProfileFactory.java @@ -0,0 +1,58 @@ +/** + * 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.transform.rollershutterposition.internal; + +import static org.openhab.transform.rollershutterposition.internal.RollerShutterPositionConstants.PROFILE_TYPE_UID; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.thing.profiles.Profile; +import org.openhab.core.thing.profiles.ProfileCallback; +import org.openhab.core.thing.profiles.ProfileContext; +import org.openhab.core.thing.profiles.ProfileFactory; +import org.openhab.core.thing.profiles.ProfileType; +import org.openhab.core.thing.profiles.ProfileTypeBuilder; +import org.openhab.core.thing.profiles.ProfileTypeProvider; +import org.openhab.core.thing.profiles.ProfileTypeUID; +import org.osgi.service.component.annotations.Component; + +/** + * {@link RollerShutterPositionProfileFactory} Factory to create the profile + * + * @author Jeff James - Initial contribution + */ +@NonNullByDefault +@Component(service = { ProfileFactory.class, ProfileTypeProvider.class }) +public class RollerShutterPositionProfileFactory implements ProfileFactory, ProfileTypeProvider { + @Override + public Collection getProfileTypes(@Nullable Locale locale) { + return List.of(ProfileTypeBuilder.newState(PROFILE_TYPE_UID, PROFILE_TYPE_UID.getId()) + .withSupportedItemTypes(CoreItemFactory.ROLLERSHUTTER).build()); + } + + @Override + public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback, + ProfileContext profileContext) { + return new RollerShutterPositionProfile(callback, profileContext); + } + + @Override + public Collection getSupportedProfileTypeUIDs() { + return List.of(PROFILE_TYPE_UID); + } +} diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/config/RollerShutterPositionConfig.java b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/config/RollerShutterPositionConfig.java new file mode 100644 index 0000000000000..f9ba83c180e11 --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/java/org/openhab/transform/rollershutterposition/internal/config/RollerShutterPositionConfig.java @@ -0,0 +1,30 @@ +/** + * 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.transform.rollershutterposition.internal.config; + +import static org.openhab.transform.rollershutterposition.internal.RollerShutterPositionConstants.DEFAULT_PRECISION; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link RollerShutterPositionConfig} class contains the parameters for RollerShutterPosition + * + * @author Jeff James - initial contribution + * + */ +@NonNullByDefault +public class RollerShutterPositionConfig { + public float uptime; // uptime in seconds (set by param) + public float downtime; // downtime in seconds (set by param) + public int precision = DEFAULT_PRECISION; // minimum movement +} diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/config/rollerShutterPosition.xml b/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/config/rollerShutterPosition.xml new file mode 100644 index 0000000000000..79fea2939535b --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/config/rollerShutterPosition.xml @@ -0,0 +1,27 @@ + + + + + + + Time it takes for roller shutter to fully open (in seconds). + true + + + + Time it takes for roller shutter to extend the full length (in seconds). Defaults to Up Time if not + specified. + + + + Minimum movement (in percent) that can be requested. If the requested change is less than this amount, + no action will be taken. This may be required for systems where there is a lag in the stop command and + consequently + it is not possible for fine control of movement. (default = 5) + 5 + + + diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/i18n/rollershutter.properties b/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/i18n/rollershutter.properties new file mode 100644 index 0000000000000..8ca40892c1d4c --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/i18n/rollershutter.properties @@ -0,0 +1,6 @@ +profile.config.rollershutter.position.downtime.label = Down Time +profile.config.rollershutter.position.downtime.description = Time it takes for roller shutter to extend the full length (in seconds). Defaults to Up Time if not specified. +profile.config.rollershutter.position.precision.label = Precision +profile.config.rollershutter.position.precision.description = Minimum movement (in percent) that can be requested. If the requested change is less than this amount, no action will be taken. This may be required for systems where there is a lag in the stop command and consequently it is not possible for fine control of movement. (default = 5) +profile.config.rollershutter.position.uptime.label = Up Time +profile.config.rollershutter.position.uptime.description = Time it takes for roller shutter to fully open (in seconds). diff --git a/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/i18n/rollershutter_it.properties b/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/i18n/rollershutter_it.properties new file mode 100644 index 0000000000000..c8a2eba7abb50 --- /dev/null +++ b/bundles/org.openhab.transform.rollershutterposition/src/main/resources/OH-INF/i18n/rollershutter_it.properties @@ -0,0 +1,6 @@ +profile.config.rollershutter.position.downtime.label = Tempo Discesa +profile.config.rollershutter.position.downtime.description = Tempo necessario per la tapparella per estendere l'intera lunghezza (in secondi). Predefinito a Tempo di salita se non specificato. +profile.config.rollershutter.position.precision.label = Precisione +profile.config.rollershutter.position.precision.description = Movimento minimo (in percentuale) che può essere richiesto. Se la modifica richiesta è inferiore a questo importo, non verrà intrapresa alcuna azione. Ciò può essere richiesto per i sistemi in cui vi è un ritardo nel comando di arresto e di conseguenza non è possibile un controllo fine del movimento. (valore predefinito \= 5) +profile.config.rollershutter.position.uptime.label = Tempo Salita +profile.config.rollershutter.position.uptime.description = Tempo necessario affinché la tapparella si apra completamente (in secondi). diff --git a/bundles/org.openhab.voice.marytts/pom.xml b/bundles/org.openhab.voice.marytts/pom.xml index cf48daa2bff0d..541ea45c95577 100644 --- a/bundles/org.openhab.voice.marytts/pom.xml +++ b/bundles/org.openhab.voice.marytts/pom.xml @@ -18,6 +18,30 @@ com.twmacinta.util;resolution:=optional,gnu.trove;resolution:=optional,Jampack;resolution:=optional,net.didion.jwnl*;resolution:=optional,org.apache.http*;resolution:=optional,org.apache.xerces.impl*;resolution:=optional,org.hsqldb;resolution:=optional,org.jdesktop.layout*;resolution:=optional;sun.nio.ch.* + + + + + biz.aQute.bnd + bnd-maven-plugin + + + + + + + + + com.ibm.icu diff --git a/bundles/pom.xml b/bundles/pom.xml index 7ceb398af4889..e22d555988c61 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -39,6 +39,7 @@ org.openhab.transform.jsonpath org.openhab.transform.map org.openhab.transform.regex + org.openhab.transform.rollershutterposition org.openhab.transform.scale org.openhab.transform.xpath org.openhab.transform.xslt @@ -74,6 +75,7 @@ org.openhab.binding.bluetooth.enoceanble org.openhab.binding.bluetooth.generic org.openhab.binding.bluetooth.govee + org.openhab.binding.bluetooth.radoneye org.openhab.binding.bluetooth.roaming org.openhab.binding.bluetooth.ruuvitag org.openhab.binding.bondhome diff --git a/features/openhab-addons/src/main/resources/footer.xml b/features/openhab-addons/src/main/resources/footer.xml index d38fbdf3ff242..61351094f07f1 100644 --- a/features/openhab-addons/src/main/resources/footer.xml +++ b/features/openhab-addons/src/main/resources/footer.xml @@ -13,6 +13,7 @@ mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.enoceanble/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.generic/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.govee/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.radoneye/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.roaming/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.ruuvitag/${project.version} diff --git a/itests/org.openhab.automation.jsscriptingnashorn.tests/itest.bndrun b/itests/org.openhab.automation.jsscriptingnashorn.tests/itest.bndrun index b2a9e884ec5b9..b29335e8f5ba2 100644 --- a/itests/org.openhab.automation.jsscriptingnashorn.tests/itest.bndrun +++ b/itests/org.openhab.automation.jsscriptingnashorn.tests/itest.bndrun @@ -16,7 +16,6 @@ Fragment-Host: org.openhab.automation.jsscriptingnashorn # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -39,17 +38,8 @@ Fragment-Host: org.openhab.automation.jsscriptingnashorn junit-platform-commons;version='[1.8.1,1.8.2)',\ junit-platform-engine;version='[1.8.1,1.8.2)',\ junit-platform-launcher;version='[1.8.1,1.8.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ @@ -69,5 +59,17 @@ Fragment-Host: org.openhab.automation.jsscriptingnashorn org.threeten.extra;version='[1.5.0,1.5.1)',\ com.sun.jna;version='[5.12.1,5.12.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)' + org.osgi.service.cm;version='[1.6.0,1.6.1)',\ + xstream;version='[1.4.19,1.4.20)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)' diff --git a/itests/org.openhab.binding.astro.tests/itest.bndrun b/itests/org.openhab.binding.astro.tests/itest.bndrun index df7005041dc0e..e3beabcfcc4d6 100644 --- a/itests/org.openhab.binding.astro.tests/itest.bndrun +++ b/itests/org.openhab.binding.astro.tests/itest.bndrun @@ -35,13 +35,9 @@ Fragment-Host: org.openhab.binding.astro net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.astro;version='[4.0.0,4.0.1)',\ org.openhab.binding.astro.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ @@ -52,4 +48,10 @@ Fragment-Host: org.openhab.binding.astro org.openhab.core.thing;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + xstream;version='[1.4.19,1.4.20)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)' diff --git a/itests/org.openhab.binding.avmfritz.tests/itest.bndrun b/itests/org.openhab.binding.avmfritz.tests/itest.bndrun index c924f527b3091..fdfce77ef08ee 100644 --- a/itests/org.openhab.binding.avmfritz.tests/itest.bndrun +++ b/itests/org.openhab.binding.avmfritz.tests/itest.bndrun @@ -14,7 +14,6 @@ Fragment-Host: org.openhab.binding.avmfritz # -runbundles: \ org.osgi.service.event;version='[1.4.0,1.4.1)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ @@ -22,8 +21,6 @@ Fragment-Host: org.openhab.binding.avmfritz com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\ org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ - org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\ - org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\ jakarta.annotation-api;version='[2.0.0,2.0.1)',\ jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ javax.measure.unit-api;version='[2.1.2,2.1.3)',\ @@ -42,30 +39,12 @@ Fragment-Host: org.openhab.binding.avmfritz net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.apache.xbean.bundleutils;version='[4.21.0,4.21.1)',\ - org.apache.xbean.finder;version='[4.21.0,4.21.1)',\ - org.eclipse.jetty.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.api;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.common;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ - org.ops4j.pax.web.pax-web-api;version='[7.3.25,7.3.26)',\ org.jupnp;version='[2.6.1,2.6.2)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.avmfritz;version='[4.0.0,4.0.1)',\ org.openhab.binding.avmfritz.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ @@ -77,6 +56,24 @@ Fragment-Host: org.openhab.binding.avmfritz org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ - org.objectweb.asm;version='[9.4.0,9.4.1)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + xstream;version='[1.4.19,1.4.20)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.api;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.common;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.ops4j.pax.web.pax-web-api;version='[8.0.15,8.0.16)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)' diff --git a/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzThingHandlerOSGiTest.java b/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzThingHandlerOSGiTest.java index f9b05783121b0..6c59d5cd183e4 100644 --- a/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzThingHandlerOSGiTest.java +++ b/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzThingHandlerOSGiTest.java @@ -31,6 +31,7 @@ import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ManagedThingProvider; import org.openhab.core.thing.ThingProvider; +import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.builder.BridgeBuilder; @@ -74,7 +75,10 @@ public void setUp() { bridgeHandler.setCallback(callback); - assertNull(bridge.getHandler()); + ThingHandler oldHandler = bridge.getHandler(); + if (oldHandler != null) { + oldHandler.dispose(); + } bridge.setHandler(bridgeHandler); assertNotNull(bridge.getHandler()); diff --git a/itests/org.openhab.binding.feed.tests/itest.bndrun b/itests/org.openhab.binding.feed.tests/itest.bndrun index 37640825c04dd..6562772d9b4b4 100644 --- a/itests/org.openhab.binding.feed.tests/itest.bndrun +++ b/itests/org.openhab.binding.feed.tests/itest.bndrun @@ -5,7 +5,6 @@ Fragment-Host: org.openhab.binding.feed -runrequires: \ bnd.identity;id='org.openhab.binding.feed.tests',\ - bnd.identity;id='org.openhab.core.thing.xml',\ bnd.identity;id='org.apache.felix.configadmin',\ osgi.identity;filter:='(&(osgi.identity=org.ops4j.pax.web.pax-web-runtime)(version>=7.2.3))' @@ -19,15 +18,12 @@ Fragment-Host: org.openhab.binding.feed -runbundles: \ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\ jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\ org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ - org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\ - org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\ jakarta.annotation-api;version='[2.0.0,2.0.1)',\ jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ javax.measure.unit-api;version='[2.1.2,2.1.3)',\ @@ -42,26 +38,9 @@ Fragment-Host: org.openhab.binding.feed junit-platform-commons;version='[1.8.1,1.8.2)',\ junit-platform-engine;version='[1.8.1,1.8.2)',\ junit-platform-launcher;version='[1.8.1,1.8.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.apache.xbean.bundleutils;version='[4.21.0,4.21.1)',\ - org.apache.xbean.finder;version='[4.21.0,4.21.1)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.xml;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ - org.ops4j.pax.web.pax-web-api;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-jetty;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-runtime;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-spi;version='[7.3.25,7.3.26)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ @@ -69,12 +48,28 @@ Fragment-Host: org.openhab.binding.feed org.openhab.binding.feed.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ - org.objectweb.asm;version='[9.4.0,9.4.1)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.jaas;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.xml;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.ops4j.pax.web.pax-web-api;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-jetty;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-runtime;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-spi;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-tomcat-common;version='[8.0.15,8.0.16)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)' diff --git a/itests/org.openhab.binding.hue.tests/itest.bndrun b/itests/org.openhab.binding.hue.tests/itest.bndrun index 4a69e4fe54141..8ff707c73465e 100644 --- a/itests/org.openhab.binding.hue.tests/itest.bndrun +++ b/itests/org.openhab.binding.hue.tests/itest.bndrun @@ -5,7 +5,6 @@ Fragment-Host: org.openhab.binding.hue -runrequires: \ bnd.identity;id='org.openhab.binding.hue.tests',\ - bnd.identity;id='org.openhab.core.thing.xml',\ bnd.identity;id='org.eclipse.jdt.annotation' # We would like to use the "volatile" storage only @@ -16,7 +15,6 @@ Fragment-Host: org.openhab.binding.hue # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -39,23 +37,9 @@ Fragment-Host: org.openhab.binding.hue junit-platform-commons;version='[1.8.1,1.8.2)',\ junit-platform-engine;version='[1.8.1,1.8.2)',\ junit-platform-launcher;version='[1.8.1,1.8.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.api;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.common;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ org.eclipse.jdt.annotation;version='[2.2.100,2.2.101)',\ javax.jmdns;version='[3.5.8,3.5.9)',\ net.bytebuddy.byte-buddy;version='[1.12.1,1.12.2)',\ @@ -65,23 +49,37 @@ Fragment-Host: org.openhab.binding.hue ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.hue;version='[4.0.0,4.0.1)',\ org.openhab.binding.hue.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery.mdns;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.net;version='[4.0.0,4.0.1)',\ org.openhab.core.io.transport.mdns;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ com.sun.jna;version='[5.12.1,5.12.2)',\ - org.eclipse.jetty.alpn.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http2.common;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http2.hpack;version='[9.4.46,9.4.47)' + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.api;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.common;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.alpn.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http2.common;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http2.hpack;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.max.tests/itest.bndrun b/itests/org.openhab.binding.max.tests/itest.bndrun index 50fc6538b01b2..372a4223147c0 100644 --- a/itests/org.openhab.binding.max.tests/itest.bndrun +++ b/itests/org.openhab.binding.max.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.max -runrequires: \ - bnd.identity;id='org.openhab.binding.max.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.max.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -15,7 +14,6 @@ Fragment-Host: org.openhab.binding.max # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -39,33 +37,33 @@ Fragment-Host: org.openhab.binding.max junit-platform-commons;version='[1.8.1,1.8.2)',\ junit-platform-engine;version='[1.8.1,1.8.2)',\ junit-platform-launcher;version='[1.8.1,1.8.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.max;version='[4.0.0,4.0.1)',\ org.openhab.binding.max.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.mielecloud.tests/itest.bndrun b/itests/org.openhab.binding.mielecloud.tests/itest.bndrun index d999752ec4629..f7ee85bd7bcb6 100644 --- a/itests/org.openhab.binding.mielecloud.tests/itest.bndrun +++ b/itests/org.openhab.binding.mielecloud.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.mielecloud -runrequires: \ - bnd.identity;id='org.openhab.binding.mielecloud.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.mielecloud.tests' -runblacklist: \ bnd.identity;id='org.openhab.core.storage.json',\ @@ -18,14 +17,11 @@ Fragment-Host: org.openhab.binding.mielecloud org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\ jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ - org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\ - org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\ jakarta.annotation-api;version='[2.0.0,2.0.1)',\ jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ javax.measure.unit-api;version='[2.1.2,2.1.3)',\ @@ -44,47 +40,46 @@ Fragment-Host: org.openhab.binding.mielecloud net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.apache.xbean.bundleutils;version='[4.21.0,4.21.1)',\ - org.apache.xbean.finder;version='[4.21.0,4.21.1)',\ - org.eclipse.jetty.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.api;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.common;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.xml;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ - org.ops4j.pax.web.pax-web-api;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-jetty;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-runtime;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-spi;version='[7.3.25,7.3.26)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.mielecloud;version='[4.0.0,4.0.1)',\ org.openhab.binding.mielecloud.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.auth.oauth2client;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.net;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ - org.objectweb.asm;version='[9.4.0,9.4.1)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.jaas;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.api;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.common;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.xml;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.ops4j.pax.web.pax-web-api;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-jetty;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-runtime;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-spi;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-tomcat-common;version='[8.0.15,8.0.16)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.modbus.tests/itest.bndrun b/itests/org.openhab.binding.modbus.tests/itest.bndrun index 3944e44a27c27..54fdf53c0cc96 100644 --- a/itests/org.openhab.binding.modbus.tests/itest.bndrun +++ b/itests/org.openhab.binding.modbus.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.modbus -runrequires: \ - bnd.identity;id='org.openhab.binding.modbus.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.modbus.tests', # 1) We would like to use the "volatile" storage only, drop other storage -runblacklist: \ @@ -18,7 +17,6 @@ Fragment-Host: org.openhab.binding.modbus -runbundles: \ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ nrjavaserial;version='[5.2.1,5.2.2)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ @@ -46,35 +44,35 @@ Fragment-Host: org.openhab.binding.modbus org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.modbus;version='[4.0.0,4.0.1)',\ org.openhab.binding.modbus.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.transport.modbus;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.transform;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun b/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun index 3d0ef56a8f637..9855881f62910 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun @@ -12,8 +12,7 @@ Import-Package: \ moquette-broker-[0-9.]*.jar;lib:=true -runrequires: \ - bnd.identity;id='org.openhab.binding.mqtt.homeassistant.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.mqtt.homeassistant.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -27,7 +26,6 @@ Import-Package: \ # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ @@ -63,7 +61,6 @@ Import-Package: \ net.bytebuddy.byte-buddy;version='[1.12.1,1.12.2)',\ net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.apache.aries.javax.jax.rs-api;version='[1.0.1,1.0.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ org.jsr-305;version='[3.0.2,3.0.3)',\ org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ @@ -81,20 +78,9 @@ Import-Package: \ com.zaxxer.HikariCP;version='[2.4.7,2.4.8)',\ io.dropwizard.metrics.core;version='[3.2.2,3.2.3)',\ io.netty.codec-mqtt;version='[4.1.72,4.1.73)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.mqtt;version='[4.0.0,4.0.1)',\ org.openhab.binding.mqtt.generic;version='[4.0.0,4.0.1)',\ org.openhab.binding.mqtt.homeassistant;version='[4.0.0,4.0.1)',\ @@ -102,14 +88,25 @@ Import-Package: \ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.transport.mqtt;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.transform;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ com.sun.jna;version='[5.12.1,5.12.2)',\ - jakarta.ws.rs-api;version='[2.1.6,2.1.7)' + org.apache.commons.commons-codec;version='[1.15.0,1.15.1)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun b/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun index 9ab9537a2cda1..410b6b6236bf9 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun +++ b/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun @@ -12,8 +12,7 @@ Import-Package: \ moquette-broker-[0-9.]*.jar;lib:=true -runrequires: \ - bnd.identity;id='org.openhab.binding.mqtt.homie.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.mqtt.homie.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -27,7 +26,6 @@ Import-Package: \ # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ @@ -63,7 +61,6 @@ Import-Package: \ net.bytebuddy.byte-buddy;version='[1.12.1,1.12.2)',\ net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.apache.aries.javax.jax.rs-api;version='[1.0.1,1.0.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ org.jsr-305;version='[3.0.2,3.0.3)',\ org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ @@ -81,20 +78,9 @@ Import-Package: \ com.zaxxer.HikariCP;version='[2.4.7,2.4.8)',\ io.dropwizard.metrics.core;version='[3.2.2,3.2.3)',\ io.netty.codec-mqtt;version='[4.1.72,4.1.73)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.mqtt;version='[4.0.0,4.0.1)',\ org.openhab.binding.mqtt.generic;version='[4.0.0,4.0.1)',\ org.openhab.binding.mqtt.homie;version='[4.0.0,4.0.1)',\ @@ -102,15 +88,26 @@ Import-Package: \ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.transport.mqtt;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.transform;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ com.sun.jna;version='[5.12.1,5.12.2)',\ - jakarta.ws.rs-api;version='[2.1.6,2.1.7)' + org.apache.commons.commons-codec;version='[1.15.0,1.15.1)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.nest.tests/itest.bndrun b/itests/org.openhab.binding.nest.tests/itest.bndrun index a27e30fc0902f..f2e466ffb217e 100644 --- a/itests/org.openhab.binding.nest.tests/itest.bndrun +++ b/itests/org.openhab.binding.nest.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.nest -runrequires: \ - bnd.identity;id='org.openhab.binding.nest.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.nest.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -16,7 +15,6 @@ Fragment-Host: org.openhab.binding.nest # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.osgi.service.jaxrs;version='[1.0.0,1.0.1)',\ @@ -27,16 +25,11 @@ Fragment-Host: org.openhab.binding.nest org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ org.apache.aries.javax.jax.rs-api;version='[1.0.1,1.0.2)',\ - org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\ - org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\ jakarta.annotation-api;version='[1.3.5,1.3.6)',\ jakarta.xml.soap-api;version='[1.4.2,1.4.3)',\ jakarta.xml.ws-api;version='[2.3.3,2.3.4)',\ org.apache.aries.component-dsl.component-dsl;version='[1.2.2,1.2.3)',\ - org.apache.aries.spifly.dynamic.bundle;version='[1.3.2,1.3.3)',\ org.apache.ws.xmlschema.core;version='[2.2.5,2.2.6)',\ - org.objectweb.asm.tree.analysis;version='[9.0.0,9.0.1)',\ - org.objectweb.asm.util;version='[9.0.0,9.0.1)',\ stax2-api;version='[4.2.1,4.2.2)',\ jakarta.annotation-api;version='[2.0.0,2.0.1)',\ jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ @@ -63,48 +56,54 @@ Fragment-Host: org.openhab.binding.nest org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ org.apache.aries.jax.rs.whiteboard;version='[2.0.0,2.0.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.apache.xbean.bundleutils;version='[4.21.0,4.21.1)',\ - org.apache.xbean.finder;version='[4.21.0,4.21.1)',\ - org.eclipse.jetty.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.api;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.common;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.xml;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ - org.ops4j.pax.web.pax-web-api;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-jetty;version='[7.3.25,7.3.26)',\ - org.ops4j.pax.web.pax-web-spi;version='[7.3.25,7.3.26)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ org.jsr-305;version='[3.0.2,3.0.3)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.nest;version='[4.0.0,4.0.1)',\ org.openhab.binding.nest.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.auth.oauth2client;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.net;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ org.objectweb.asm;version='[9.4.0,9.4.1)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ com.sun.jna;version='[5.12.1,5.12.2)',\ - com.fasterxml.woodstox.woodstox-core;version='[6.4.0,6.4.1)' + com.fasterxml.woodstox.woodstox-core;version='[6.4.0,6.4.1)',\ + org.apache.aries.spifly.dynamic.bundle;version='[1.3.4,1.3.5)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.jaas;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.api;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.common;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.xml;version='[9.4.50,9.4.51)',\ + org.objectweb.asm.commons;version='[9.2.0,9.2.1)',\ + org.objectweb.asm.tree;version='[9.2.0,9.2.1)',\ + org.objectweb.asm.tree.analysis;version='[9.2.0,9.2.1)',\ + org.objectweb.asm.util;version='[9.2.0,9.2.1)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.ops4j.pax.web.pax-web-api;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-jetty;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-runtime;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-spi;version='[8.0.15,8.0.16)',\ + org.ops4j.pax.web.pax-web-tomcat-common;version='[8.0.15,8.0.16)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.ntp.tests/itest.bndrun b/itests/org.openhab.binding.ntp.tests/itest.bndrun index 6744333b881bc..6da9fffe1656a 100644 --- a/itests/org.openhab.binding.ntp.tests/itest.bndrun +++ b/itests/org.openhab.binding.ntp.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.ntp -runrequires: \ - bnd.identity;id='org.openhab.binding.ntp.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.ntp.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -15,7 +14,6 @@ Fragment-Host: org.openhab.binding.ntp # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -42,34 +40,34 @@ Fragment-Host: org.openhab.binding.ntp net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ org.apache.commons.commons-net;version='[3.9.0,3.9.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ org.openhab.binding.ntp;version='[4.0.0,4.0.1)',\ org.openhab.binding.ntp.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.systeminfo.tests/itest.bndrun b/itests/org.openhab.binding.systeminfo.tests/itest.bndrun index 094c6b2ab9173..5faf438894501 100644 --- a/itests/org.openhab.binding.systeminfo.tests/itest.bndrun +++ b/itests/org.openhab.binding.systeminfo.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.systeminfo -runrequires: \ - bnd.identity;id='org.openhab.binding.systeminfo.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.systeminfo.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -17,7 +16,6 @@ Fragment-Host: org.openhab.binding.systeminfo # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -46,33 +44,33 @@ Fragment-Host: org.openhab.binding.systeminfo net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.systeminfo;version='[4.0.0,4.0.1)',\ org.openhab.binding.systeminfo.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ - io.methvin.directory-watcher;version='[0.17.1,0.17.2)' + io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.tradfri.tests/itest.bndrun b/itests/org.openhab.binding.tradfri.tests/itest.bndrun index 10dd93ad5a15e..e8bc5d09e2d59 100644 --- a/itests/org.openhab.binding.tradfri.tests/itest.bndrun +++ b/itests/org.openhab.binding.tradfri.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.tradfri -runrequires: \ - bnd.identity;id='org.openhab.binding.tradfri.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.tradfri.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -18,7 +17,6 @@ Fragment-Host: org.openhab.binding.tradfri org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ javax.jmdns;version='[3.5.8,3.5.9)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\ @@ -44,23 +42,12 @@ Fragment-Host: org.openhab.binding.tradfri org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ net.i2p.crypto.eddsa;version='[0.3.0,0.3.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.eclipse.californium.core;version='[2.7.4,2.7.5)',\ org.eclipse.californium.element-connector;version='[2.7.4,2.7.5)',\ org.eclipse.californium.scandium;version='[2.7.4,2.7.5)',\ @@ -71,12 +58,23 @@ Fragment-Host: org.openhab.binding.tradfri org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery.mdns;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.transport.mdns;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.binding.wemo.tests/itest.bndrun b/itests/org.openhab.binding.wemo.tests/itest.bndrun index 78c81f0c1f03b..b88bae9aaeb94 100644 --- a/itests/org.openhab.binding.wemo.tests/itest.bndrun +++ b/itests/org.openhab.binding.wemo.tests/itest.bndrun @@ -4,8 +4,7 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.wemo -runrequires: \ - bnd.identity;id='org.openhab.binding.wemo.tests',\ - bnd.identity;id='org.openhab.core.thing.xml' + bnd.identity;id='org.openhab.binding.wemo.tests' # We would like to use the "volatile" storage only -runblacklist: \ @@ -15,7 +14,6 @@ Fragment-Host: org.openhab.binding.wemo # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -24,8 +22,6 @@ Fragment-Host: org.openhab.binding.wemo jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\ org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ - org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\ - org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\ jakarta.annotation-api;version='[2.0.0,2.0.1)',\ jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ javax.measure.unit-api;version='[2.1.2,2.1.3)',\ @@ -44,45 +40,42 @@ Fragment-Host: org.openhab.binding.wemo net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ org.objenesis;version='[3.2.0,3.2.1)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ xstream;version='[1.4.19,1.4.20)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.apache.xbean.bundleutils;version='[4.21.0,4.21.1)',\ - org.apache.xbean.finder;version='[4.21.0,4.21.1)',\ - org.eclipse.jetty.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.api;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.client;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.websocket.common;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ - org.ops4j.pax.web.pax-web-api;version='[7.3.25,7.3.26)',\ org.jupnp;version='[2.6.1,2.6.2)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.openhab.binding.wemo;version='[4.0.0,4.0.1)',\ org.openhab.binding.wemo.tests;version='[4.0.0,4.0.1)',\ org.openhab.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.core;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery;version='[4.0.0,4.0.1)',\ org.openhab.core.config.discovery.upnp;version='[4.0.0,4.0.1)',\ - org.openhab.core.config.xml;version='[4.0.0,4.0.1)',\ org.openhab.core.io.console;version='[4.0.0,4.0.1)',\ org.openhab.core.io.net;version='[4.0.0,4.0.1)',\ org.openhab.core.io.transport.upnp;version='[4.0.0,4.0.1)',\ org.openhab.core.test;version='[4.0.0,4.0.1)',\ org.openhab.core.thing;version='[4.0.0,4.0.1)',\ - org.openhab.core.thing.xml;version='[4.0.0,4.0.1)',\ com.google.gson;version='[2.9.1,2.9.2)',\ - org.objectweb.asm;version='[9.4.0,9.4.1)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ - com.sun.jna;version='[5.12.1,5.12.2)' + com.sun.jna;version='[5.12.1,5.12.2)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.api;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.client;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.websocket.common;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.ops4j.pax.web.pax-web-api;version='[8.0.15,8.0.16)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)' diff --git a/itests/org.openhab.persistence.mapdb.tests/itest.bndrun b/itests/org.openhab.persistence.mapdb.tests/itest.bndrun index 84cd06581d4c1..be29ccce7710d 100644 --- a/itests/org.openhab.persistence.mapdb.tests/itest.bndrun +++ b/itests/org.openhab.persistence.mapdb.tests/itest.bndrun @@ -16,7 +16,6 @@ Fragment-Host: org.openhab.persistence.mapdb # done # -runbundles: \ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ @@ -39,17 +38,8 @@ Fragment-Host: org.openhab.persistence.mapdb junit-platform-commons;version='[1.8.1,1.8.2)',\ junit-platform-engine;version='[1.8.1,1.8.2)',\ junit-platform-launcher;version='[1.8.1,1.8.2)',\ - org.apache.felix.scr;version='[2.1.30,2.1.31)',\ org.osgi.util.function;version='[1.2.0,1.2.1)',\ org.osgi.util.promise;version='[1.2.0,1.2.1)',\ - org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.security;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.server;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.servlet;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util;version='[9.4.46,9.4.47)',\ - org.eclipse.jetty.util.ajax;version='[9.4.46,9.4.47)',\ - org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\ ch.qos.logback.classic;version='[1.2.11,1.2.12)',\ ch.qos.logback.core;version='[1.2.11,1.2.12)',\ biz.aQute.tester.junit-platform;version='[6.4.0,6.4.1)',\ @@ -62,5 +52,17 @@ Fragment-Host: org.openhab.persistence.mapdb com.google.gson;version='[2.9.1,2.9.2)',\ io.methvin.directory-watcher;version='[0.17.1,0.17.2)',\ com.sun.jna;version='[5.12.1,5.12.2)',\ - org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\ - org.osgi.service.cm;version='[1.6.0,1.6.1)' + org.osgi.service.cm;version='[1.6.0,1.6.1)',\ + xstream;version='[1.4.19,1.4.20)',\ + org.apache.felix.configadmin;version='[1.9.26,1.9.27)',\ + org.apache.felix.http.servlet-api;version='[1.2.0,1.2.1)',\ + org.apache.felix.scr;version='[2.2.4,2.2.5)',\ + org.eclipse.jetty.http;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.io;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.security;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.server;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.servlet;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util;version='[9.4.50,9.4.51)',\ + org.eclipse.jetty.util.ajax;version='[9.4.50,9.4.51)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.2.0,2.2.1)',\ + org.osgi.service.component;version='[1.5.0,1.5.1)' diff --git a/pom.xml b/pom.xml index 5bf839cfadf07..f8c92ff4a4845 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ 3.9.0 2.2.1 2.14.1 - 4.3.7 + 4.4.3 4.1.72.Final 3.14.9 0.13.0 @@ -86,6 +86,32 @@ -${.}/NOTICE, -${.}/*.xsd src/main/feature/feature.xml + + + @@ -140,30 +166,7 @@ bnd-maven-plugin ${bnd.version} - + true