From 7d9b75b9b3b3c00f087210ca3df4869dc14ee882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Grze=C5=9Blowski?= Date: Fri, 15 Dec 2023 17:11:16 +0100 Subject: [PATCH] format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin GrzeĊ›lowski --- .../salus/internal/SalusBindingConstants.java | 61 ++++------ .../salus/internal/SalusHandlerFactory.java | 11 +- .../internal/discovery/CloudDiscovery.java | 26 ++--- .../salus/internal/handler/CloudApi.java | 9 +- .../internal/handler/CloudBridgeHandler.java | 97 ++++++++-------- .../salus/internal/handler/DeviceHandler.java | 84 ++++++-------- .../salus/internal/handler/It600Handler.java | 102 +++++++---------- .../salus/internal/rest/AuthToken.java | 18 +-- .../binding/salus/internal/rest/Device.java | 5 +- .../salus/internal/rest/DeviceProperty.java | 24 ++-- .../salus/internal/rest/GsonMapper.java | 96 +++++++--------- .../internal/rest/HttpClientException.java | 3 +- .../salus/internal/rest/HttpException.java | 3 +- .../internal/rest/HttpForbiddenException.java | 2 +- .../internal/rest/HttpServerException.java | 1 + .../rest/HttpUnauthorizedException.java | 2 +- .../internal/rest/HttpUnknownException.java | 2 +- .../salus/internal/rest/JettyHttpClient.java | 8 +- .../salus/internal/rest/RestClient.java | 3 +- .../binding/salus/internal/rest/SalusApi.java | 44 +++----- .../src/main/resources/OH-INF/thing/it600.xml | 106 +++++++++--------- .../resources/OH-INF/thing/salus-bridge.xml | 78 ++++++------- .../discovery/CloudDiscoveryTest.java | 32 ++---- .../salus/internal/rest/DeviceTest.java | 8 +- .../salus/internal/rest/GsonMapperTest.java | 13 +-- .../salus/internal/rest/SalusApiTest.java | 92 +++++++-------- 26 files changed, 410 insertions(+), 520 deletions(-) diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusBindingConstants.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusBindingConstants.java index 4847e8f6770a7..b6a3fc130a783 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusBindingConstants.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusBindingConstants.java @@ -12,11 +12,11 @@ */ package org.openhab.binding.salus.internal; +import java.util.Set; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; -import java.util.Set; - /** * The {@link SalusBindingConstants} class defines common constants, which are * used across the whole binding. @@ -34,9 +34,8 @@ public class SalusBindingConstants { public static final ThingTypeUID SALUS_IT600_DEVICE_TYPE = new ThingTypeUID(BINDING_ID, "salus-it600-device"); public static final ThingTypeUID SALUS_SERVER_TYPE = new ThingTypeUID(BINDING_ID, "salus-cloud-bridge"); - - public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(SALUS_DEVICE_TYPE, SALUS_IT600_DEVICE_TYPE, SALUS_SERVER_TYPE); - + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(SALUS_DEVICE_TYPE, + SALUS_IT600_DEVICE_TYPE, SALUS_SERVER_TYPE); public static class SalusCloud { } @@ -50,9 +49,10 @@ public static class HoldType { public static final int AUTO = 0; public static final int MANUAL = 2; public static final int TEMPORARY_MANUAL = 1; - public static final int OFF= 7; + public static final int OFF = 7; } } + public static class Channels { public static class It600 { public static final String TEMPERATURE = "temperature"; @@ -68,38 +68,21 @@ public static class It600 { public static final String GENERIC_INPUT_NUMBER_CHANNEL = "generic-input-number-channel"; public static final String TEMPERATURE_OUTPUT_NUMBER_CHANNEL = "temperature-output-channel"; public static final String TEMPERATURE_INPUT_NUMBER_CHANNEL = "temperature-input-channel"; - public static final Set TEMPERATURE_CHANNELS = Set.of( - "ep_9:sIT600TH:AutoCoolingSetpoint_x100", - "ep_9:sIT600TH:AutoCoolingSetpoint_x100_a", - "ep_9:sIT600TH:AutoHeatingSetpoint_x100", - "ep_9:sIT600TH:AutoHeatingSetpoint_x100_a", - "ep_9:sIT600TH:CoolingSetpoint_x100", - "ep_9:sIT600TH:CoolingSetpoint_x100_a", - "ep_9:sIT600TH:FloorCoolingMax_x100", - "ep_9:sIT600TH:FloorCoolingMin_x100", - "ep_9:sIT600TH:FloorHeatingMax_x100", - "ep_9:sIT600TH:FloorHeatingMin_x100", - "ep_9:sIT600TH:FrostSetpoint_x100", - "ep_9:sIT600TH:HeatingSetpoint_x100", - "ep_9:sIT600TH:HeatingSetpoint_x100_a", - "ep_9:sIT600TH:LocalTemperature_x100", - "ep_9:sIT600TH:MaxCoolSetpoint_x100", - "ep_9:sIT600TH:MaxHeatSetpoint_x100", - "ep_9:sIT600TH:MaxHeatSetpoint_x100_a", - "ep_9:sIT600TH:MinCoolSetpoint_x100", - "ep_9:sIT600TH:MinCoolSetpoint_x100_a", - "ep_9:sIT600TH:MinHeatSetpoint_x100", - "ep_9:sIT600TH:PipeTemperature_x100", - "ep_9:sIT600TH:SetAutoCoolingSetpoint_x100", - "ep_9:sIT600TH:SetAutoHeatingSetpoint_x100", - "ep_9:sIT600TH:SetCoolingSetpoint_x100", - "ep_9:sIT600TH:SetFloorCoolingMin_x100", - "ep_9:sIT600TH:SetFloorHeatingMax_x100", - "ep_9:sIT600TH:SetFloorHeatingMin_x100", - "ep_9:sIT600TH:SetFrostSetpoint_x100", - "ep_9:sIT600TH:SetHeatingSetpoint_x100", - "ep_9:sIT600TH:SetMaxHeatSetpoint_x100", - "ep_9:sIT600TH:SetMinCoolSetpoint_x100" - ); + public static final Set TEMPERATURE_CHANNELS = Set.of("ep_9:sIT600TH:AutoCoolingSetpoint_x100", + "ep_9:sIT600TH:AutoCoolingSetpoint_x100_a", "ep_9:sIT600TH:AutoHeatingSetpoint_x100", + "ep_9:sIT600TH:AutoHeatingSetpoint_x100_a", "ep_9:sIT600TH:CoolingSetpoint_x100", + "ep_9:sIT600TH:CoolingSetpoint_x100_a", "ep_9:sIT600TH:FloorCoolingMax_x100", + "ep_9:sIT600TH:FloorCoolingMin_x100", "ep_9:sIT600TH:FloorHeatingMax_x100", + "ep_9:sIT600TH:FloorHeatingMin_x100", "ep_9:sIT600TH:FrostSetpoint_x100", + "ep_9:sIT600TH:HeatingSetpoint_x100", "ep_9:sIT600TH:HeatingSetpoint_x100_a", + "ep_9:sIT600TH:LocalTemperature_x100", "ep_9:sIT600TH:MaxCoolSetpoint_x100", + "ep_9:sIT600TH:MaxHeatSetpoint_x100", "ep_9:sIT600TH:MaxHeatSetpoint_x100_a", + "ep_9:sIT600TH:MinCoolSetpoint_x100", "ep_9:sIT600TH:MinCoolSetpoint_x100_a", + "ep_9:sIT600TH:MinHeatSetpoint_x100", "ep_9:sIT600TH:PipeTemperature_x100", + "ep_9:sIT600TH:SetAutoCoolingSetpoint_x100", "ep_9:sIT600TH:SetAutoHeatingSetpoint_x100", + "ep_9:sIT600TH:SetCoolingSetpoint_x100", "ep_9:sIT600TH:SetFloorCoolingMin_x100", + "ep_9:sIT600TH:SetFloorHeatingMax_x100", "ep_9:sIT600TH:SetFloorHeatingMin_x100", + "ep_9:sIT600TH:SetFrostSetpoint_x100", "ep_9:sIT600TH:SetHeatingSetpoint_x100", + "ep_9:sIT600TH:SetMaxHeatSetpoint_x100", "ep_9:sIT600TH:SetMinCoolSetpoint_x100"); } } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusHandlerFactory.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusHandlerFactory.java index ddc94571d7119..ee84b927c524b 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusHandlerFactory.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/SalusHandlerFactory.java @@ -12,10 +12,14 @@ */ package org.openhab.binding.salus.internal; +import static org.openhab.binding.salus.internal.SalusBindingConstants.*; + +import java.util.Hashtable; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.salus.internal.handler.CloudBridgeHandler; import org.openhab.binding.salus.internal.discovery.CloudDiscovery; +import org.openhab.binding.salus.internal.handler.CloudBridgeHandler; import org.openhab.binding.salus.internal.handler.DeviceHandler; import org.openhab.binding.salus.internal.handler.It600Handler; import org.openhab.core.config.discovery.DiscoveryService; @@ -32,10 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Hashtable; - -import static org.openhab.binding.salus.internal.SalusBindingConstants.*; - /** * The {@link SalusHandlerFactory} is responsible for creating things and thing * handlers. @@ -85,6 +85,7 @@ private ThingHandler newIt600(Thing thing) { logger.debug("Registering IT600"); return new It600Handler(thing); } + private ThingHandler newSalusCloudBridge(Thing thing) { logger.debug("Registering CloudBridgeHandler"); var handler = new CloudBridgeHandler((Bridge) thing, httpClientFactory); diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/discovery/CloudDiscovery.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/discovery/CloudDiscovery.java index daa3dcd290690..120573e66f0a4 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/discovery/CloudDiscovery.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/discovery/CloudDiscovery.java @@ -1,5 +1,12 @@ package org.openhab.binding.salus.internal.discovery; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.openhab.binding.salus.internal.SalusBindingConstants.*; +import static org.openhab.binding.salus.internal.SalusBindingConstants.SalusDevice.DSN; + +import java.util.Locale; +import java.util.Map; + import org.openhab.binding.salus.internal.handler.CloudApi; import org.openhab.binding.salus.internal.handler.CloudBridgeHandler; import org.openhab.binding.salus.internal.rest.Device; @@ -11,19 +18,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Locale; -import java.util.Map; - -import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.openhab.binding.salus.internal.SalusBindingConstants.*; -import static org.openhab.binding.salus.internal.SalusBindingConstants.SalusDevice.DSN; - public class CloudDiscovery extends AbstractDiscoveryService { private final Logger logger = LoggerFactory.getLogger(CloudDiscovery.class); private final CloudApi cloudApi; private final ThingUID bridgeUid; - public CloudDiscovery(CloudBridgeHandler bridgeHandler, CloudApi cloudApi, ThingUID bridgeUid) throws IllegalArgumentException { + public CloudDiscovery(CloudBridgeHandler bridgeHandler, CloudApi cloudApi, ThingUID bridgeUid) + throws IllegalArgumentException { super(SUPPORTED_THING_TYPES_UIDS, 10, true); this.cloudApi = cloudApi; this.bridgeUid = bridgeUid; @@ -35,9 +36,7 @@ protected void startScan() { try { var devices = cloudApi.findDevices(); logger.debug("Found {} devices while scanning", devices.size()); - devices.stream() - .filter(Device::isConnected) - .forEach(this::addThing); + devices.stream().filter(Device::isConnected).forEach(this::addThing); } catch (Exception e) { logger.error("Error while scanning", e); } @@ -65,10 +64,7 @@ private static ThingTypeUID findDeviceType(Device device) { } private DiscoveryResult createDiscoveryResult(ThingUID thingUID, String label, Map properties) { - return DiscoveryResultBuilder.create(thingUID) - .withBridge(bridgeUid) - .withProperties(properties) - .withLabel(label) + return DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid).withProperties(properties).withLabel(label) .build(); } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudApi.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudApi.java index 271d4027a0715..33cde010966d8 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudApi.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudApi.java @@ -1,16 +1,17 @@ package org.openhab.binding.salus.internal.handler; -import org.openhab.binding.salus.internal.rest.Device; -import org.openhab.binding.salus.internal.rest.DeviceProperty; - import java.util.Optional; import java.util.SortedSet; +import org.openhab.binding.salus.internal.rest.Device; +import org.openhab.binding.salus.internal.rest.DeviceProperty; + public interface CloudApi { SortedSet findDevices(); + Optional findDevice(String dsn); - void setValueForProperty(String dsn, String propertyName, Object value) ; + void setValueForProperty(String dsn, String propertyName, Object value); SortedSet> findPropertiesForDevice(String dsn); } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudBridgeHandler.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudBridgeHandler.java index 51b252da5491b..e9dfeb5031dc9 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudBridgeHandler.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/CloudBridgeHandler.java @@ -1,8 +1,20 @@ package org.openhab.binding.salus.internal.handler; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import com.google.gson.Gson; +import static java.util.Collections.emptySortedSet; +import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElse; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.openhab.core.thing.ThingStatus.OFFLINE; +import static org.openhab.core.thing.ThingStatus.ONLINE; +import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR; +import static org.openhab.core.types.RefreshType.REFRESH; + +import java.math.BigDecimal; +import java.time.Duration; +import java.util.Optional; +import java.util.SortedSet; +import java.util.concurrent.ScheduledFuture; + import org.apache.commons.lang3.StringUtils; import org.openhab.binding.salus.internal.rest.*; import org.openhab.core.common.ThreadPoolManager; @@ -14,20 +26,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigDecimal; -import java.time.Duration; -import java.util.Optional; -import java.util.SortedSet; -import java.util.concurrent.ScheduledFuture; - -import static java.util.Collections.emptySortedSet; -import static java.util.Objects.requireNonNull; -import static java.util.Objects.requireNonNullElse; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.openhab.core.thing.ThingStatus.OFFLINE; -import static org.openhab.core.thing.ThingStatus.ONLINE; -import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR; -import static org.openhab.core.types.RefreshType.REFRESH; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; public final class CloudBridgeHandler extends BaseBridgeHandler implements CloudApi { private Logger logger = LoggerFactory.getLogger(CloudBridgeHandler.class.getName()); @@ -78,7 +78,8 @@ private void internalInitialize() { logger.warn("At this point SalusApi should be null!"); } salusApi = new SalusApi(username, password, url, httpClient, GsonMapper.INSTANCE); - logger = LoggerFactory.getLogger(CloudBridgeHandler.class.getName() + "[" + username.replaceAll("\\.", "_") + "]"); + logger = LoggerFactory + .getLogger(CloudBridgeHandler.class.getName() + "[" + username.replaceAll("\\.", "_") + "]"); try { var devices = salusApi.findDevices(); } catch (Exception ex) { @@ -87,17 +88,12 @@ private void internalInitialize() { updateStatus(OFFLINE, CONFIGURATION_ERROR, msg + " " + ex.getMessage()); return; } - this.devicePropertiesCache = Caffeine.newBuilder() - .maximumSize(10_000) + this.devicePropertiesCache = Caffeine.newBuilder().maximumSize(10_000) .expireAfterWrite(Duration.ofSeconds(propertiesRefreshInterval)) - .refreshAfterWrite(Duration.ofSeconds(propertiesRefreshInterval)) - .build(this::loadPropertiesForDevice); + .refreshAfterWrite(Duration.ofSeconds(propertiesRefreshInterval)).build(this::loadPropertiesForDevice); var scheduledPool = ThreadPoolManager.getScheduledPool("Salus"); - this.scheduledFuture = scheduledPool.scheduleWithFixedDelay( - this::refreshCloudDevices, - refreshInterval * 2, - refreshInterval, - SECONDS); + this.scheduledFuture = scheduledPool.scheduleWithFixedDelay(this::refreshCloudDevices, refreshInterval * 2, + refreshInterval, SECONDS); // done updateStatus(ONLINE); @@ -149,7 +145,8 @@ private void refreshCloudDevices() { @Override public void handleCommand(ChannelUID channelUID, Command command) { // no commands in this bridge - logger.debug("Bridge does not support any commands to any channels. channelUID={}, command={}", channelUID, command); + logger.debug("Bridge does not support any commands to any channels. channelUID={}, command={}", channelUID, + command); } @Override @@ -180,33 +177,34 @@ private SortedSet> loadPropertiesForDevice(String dsn) { return response.body(); } - @Override public void setValueForProperty(String dsn, String propertyName, Object value) { if (salusApi == null) { - logger.error("Cannot set value for property {} on device {} because salusClient is null", propertyName, dsn); - return ; + logger.error("Cannot set value for property {} on device {} because salusClient is null", propertyName, + dsn); + return; } logger.debug("Setting property {} on device {} to value {} using salusClient", propertyName, dsn, value); var response = salusApi.setValueForProperty(dsn, propertyName, value); if (response.failed()) { - logger.error("Cannot set property {} on device {} to value {} using salusClient\n{}", - propertyName, dsn, value, response.error()); - return ; + logger.error("Cannot set property {} on device {} to value {} using salusClient\n{}", propertyName, dsn, + value, response.error()); + return; } var setValue = response.body(); - if (setValue instanceof Boolean || setValue instanceof String || setValue instanceof Long || setValue instanceof Integer) { - var property = devicePropertiesCache.get(dsn) - .stream() - .filter(prop -> prop.getName().equals(propertyName)) + if (setValue instanceof Boolean || setValue instanceof String || setValue instanceof Long + || setValue instanceof Integer) { + var property = devicePropertiesCache.get(dsn).stream().filter(prop -> prop.getName().equals(propertyName)) .findFirst(); - if(property.isPresent()) { + if (property.isPresent()) { var prop = property.get(); if (setValue instanceof Boolean b && prop instanceof DeviceProperty.BooleanDeviceProperty boolProp) { boolProp.setValue(b); - } else if (setValue instanceof String s && prop instanceof DeviceProperty.StringDeviceProperty stringProp) { + } else if (setValue instanceof String s + && prop instanceof DeviceProperty.StringDeviceProperty stringProp) { stringProp.setValue(s); - } else if ((setValue instanceof Long || setValue instanceof Integer) && prop instanceof DeviceProperty.LongDeviceProperty longProp) { + } else if ((setValue instanceof Long || setValue instanceof Integer) + && prop instanceof DeviceProperty.LongDeviceProperty longProp) { long v; if (setValue instanceof Integer i) { v = i.longValue(); @@ -215,16 +213,20 @@ public void setValueForProperty(String dsn, String propertyName, Object value) { } longProp.setValue(v); } else { - logger.warn("Cannot set value {} ({}) for property {} ({}) on device {} because value class does not match property class", - setValue, setValue.getClass().getSimpleName(), propertyName, prop.getClass().getSimpleName(), dsn); + logger.warn( + "Cannot set value {} ({}) for property {} ({}) on device {} because value class does not match property class", + setValue, setValue.getClass().getSimpleName(), propertyName, + prop.getClass().getSimpleName(), dsn); } } else { - logger.warn("Cannot set value {} ({}) for property {} on device {} because it is not found in the cache. Invalidating cache", + logger.warn( + "Cannot set value {} ({}) for property {} on device {} because it is not found in the cache. Invalidating cache", setValue, setValue.getClass().getSimpleName(), propertyName, dsn); devicePropertiesCache.invalidate(dsn); } } else { - logger.warn("Cannot set value {} ({}) for property {} on device {} because it is not a Boolean, String, Long or Integer", + logger.warn( + "Cannot set value {} ({}) for property {} on device {} because it is not a Boolean, String, Long or Integer", setValue, setValue.getClass().getSimpleName(), propertyName, dsn); } } @@ -247,9 +249,6 @@ public SortedSet findDevices() { @Override public Optional findDevice(String dsn) { - return findDevices() - .stream() - .filter(device -> device.dsn().equals(dsn)) - .findFirst(); + return findDevices().stream().filter(device -> device.dsn().equals(dsn)).findFirst(); } } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/DeviceHandler.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/DeviceHandler.java index d2e9d0cc22544..4d05f0b9dec45 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/DeviceHandler.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/DeviceHandler.java @@ -1,5 +1,21 @@ package org.openhab.binding.salus.internal.handler; +import static java.math.RoundingMode.HALF_EVEN; +import static org.openhab.binding.salus.internal.SalusBindingConstants.BINDING_ID; +import static org.openhab.binding.salus.internal.SalusBindingConstants.Channels.*; +import static org.openhab.binding.salus.internal.SalusBindingConstants.SalusDevice.DSN; +import static org.openhab.core.thing.ThingStatus.OFFLINE; +import static org.openhab.core.thing.ThingStatus.ONLINE; +import static org.openhab.core.thing.ThingStatusDetail.*; +import static org.openhab.core.types.RefreshType.REFRESH; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; + import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.salus.internal.rest.DeviceProperty; @@ -16,22 +32,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedSet; - -import static java.math.RoundingMode.HALF_EVEN; -import static org.openhab.binding.salus.internal.SalusBindingConstants.BINDING_ID; -import static org.openhab.binding.salus.internal.SalusBindingConstants.Channels.*; -import static org.openhab.binding.salus.internal.SalusBindingConstants.SalusDevice.DSN; -import static org.openhab.core.thing.ThingStatus.OFFLINE; -import static org.openhab.core.thing.ThingStatus.ONLINE; -import static org.openhab.core.thing.ThingStatusDetail.*; -import static org.openhab.core.types.RefreshType.REFRESH; - public class DeviceHandler extends BaseThingHandler { private static final BigDecimal ONE_HUNDRED = new BigDecimal(100); private final Logger logger; @@ -61,9 +61,7 @@ private void internalInitialize() { var bridge = getBridge(); if (bridge == null) { logger.debug("No bridge for thing with UID {}", thing.getUID()); - updateStatus( - OFFLINE, - BRIDGE_UNINITIALIZED, + updateStatus(OFFLINE, BRIDGE_UNINITIALIZED, "There is no bridge for this thing. Remove it and add it again."); return; } @@ -80,9 +78,7 @@ private void internalInitialize() { if (StringUtils.isEmpty(dsn)) { logger.debug("No {} for thing with UID {}", DSN, thing.getUID()); - updateStatus( - OFFLINE, - CONFIGURATION_ERROR, + updateStatus(OFFLINE, CONFIGURATION_ERROR, "There is no " + DSN + " for this thing. Remove it and add it again."); return; } @@ -101,15 +97,9 @@ private void internalInitialize() { updateStatus(OFFLINE, COMMUNICATION_ERROR, msg); return; } - var channels = findDeviceProperties() - .stream() - .map(this::buildChannel) - .toList(); + var channels = findDeviceProperties().stream().map(this::buildChannel).toList(); if (channels.isEmpty()) { - updateStatus( - OFFLINE, - CONFIGURATION_ERROR, - "There are no channels for " + dsn + "."); + updateStatus(OFFLINE, CONFIGURATION_ERROR, "There are no channels for " + dsn + "."); return; } updateChannels(channels); @@ -133,25 +123,25 @@ private Channel buildChannel(DeviceProperty property) { } else if (property instanceof DeviceProperty.LongDeviceProperty longDeviceProperty) { if (TEMPERATURE_CHANNELS.contains(longDeviceProperty.getName())) { // a temp channel - channelId = inOrOut(property.getDirection(), TEMPERATURE_INPUT_NUMBER_CHANNEL, TEMPERATURE_OUTPUT_NUMBER_CHANNEL); + channelId = inOrOut(property.getDirection(), TEMPERATURE_INPUT_NUMBER_CHANNEL, + TEMPERATURE_OUTPUT_NUMBER_CHANNEL); } else { - channelId = inOrOut(property.getDirection(), GENERIC_INPUT_NUMBER_CHANNEL, GENERIC_OUTPUT_NUMBER_CHANNEL); + channelId = inOrOut(property.getDirection(), GENERIC_INPUT_NUMBER_CHANNEL, + GENERIC_OUTPUT_NUMBER_CHANNEL); } acceptedItemType = "Number"; } else if (property instanceof DeviceProperty.StringDeviceProperty stringDeviceProperty) { channelId = inOrOut(property.getDirection(), GENERIC_INPUT_CHANNEL, GENERIC_OUTPUT_CHANNEL); acceptedItemType = "String"; } else { - throw new UnsupportedOperationException("Property class " + property.getClass().getSimpleName() + " is not supported!"); + throw new UnsupportedOperationException( + "Property class " + property.getClass().getSimpleName() + " is not supported!"); } var channelUid = new ChannelUID(thing.getUID(), buildChannelUid(property.getName())); var channelTypeUID = new ChannelTypeUID(BINDING_ID, channelId); - return ChannelBuilder - .create(channelUid, acceptedItemType) - .withType(channelTypeUID) - .withLabel(buildChannelDisplayName(property.getDisplayName())) - .build(); + return ChannelBuilder.create(channelUid, acceptedItemType).withType(channelTypeUID) + .withLabel(buildChannelDisplayName(property.getDisplayName())).build(); } private String buildChannelUid(final String name) { @@ -186,7 +176,6 @@ private static String removeX100(String name) { return withoutSuffix; } - private String inOrOut(String direction, String in, String out) { if ("output".equalsIgnoreCase(direction)) { return out; @@ -224,12 +213,12 @@ public void handleCommand(@NonNullByDefault ChannelUID channelUID, @NonNullByDef } else if (command instanceof StringType typedCommand) { handleStringCommand(channelUID, typedCommand); } else { - logger.warn("Does not know how to handle command `{}` ({}) on channel `{}`!", - command, command.getClass().getSimpleName(), channelUID); + logger.warn("Does not know how to handle command `{}` ({}) on channel `{}`!", command, + command.getClass().getSimpleName(), channelUID); } } catch (Exception ex) { - logger.error("Error occurred while handling command `{}` ({}) on channel `{}`!", - command, command.getClass().getSimpleName(), channelUID.getId(), ex); + logger.error("Error occurred while handling command `{}` ({}) on channel `{}`!", command, + command.getClass().getSimpleName(), channelUID.getId(), ex); } } @@ -248,9 +237,7 @@ private void handleRefreshCommand(ChannelUID channelUID) { return; } - var propertyOptional = findDeviceProperties() - .stream() - .filter(property -> property.getName().equals(salusId)) + var propertyOptional = findDeviceProperties().stream().filter(property -> property.getName().equals(salusId)) .findFirst(); if (propertyOptional.isEmpty()) { logger.warn("Property {} not found in response!", salusId); @@ -262,7 +249,8 @@ private void handleRefreshCommand(ChannelUID channelUID) { state = booleanProperty.getValue() ? OnOffType.ON : OnOffType.OFF; } else if (property instanceof DeviceProperty.LongDeviceProperty longDeviceProperty) { if (isX100) { - state = new DecimalType(new BigDecimal(longDeviceProperty.getValue()).divide(ONE_HUNDRED, new MathContext(5, HALF_EVEN))); + state = new DecimalType(new BigDecimal(longDeviceProperty.getValue()).divide(ONE_HUNDRED, + new MathContext(5, HALF_EVEN))); } else { state = new DecimalType(longDeviceProperty.getValue()); } @@ -293,7 +281,6 @@ private void handleBoolCommand(ChannelUID channelUID, boolean command) { handleCommand(channelUID, REFRESH); } - private void handleDecimalCommand(ChannelUID channelUID, DecimalType command) { var id = channelUID.getId(); String salusId; @@ -325,5 +312,4 @@ private void handleStringCommand(ChannelUID channelUID, StringType command) { cloudApi.setValueForProperty(dsn, salusId, value); handleCommand(channelUID, REFRESH); } - } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/It600Handler.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/It600Handler.java index b8f867801b2b5..5dfef6c14e616 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/It600Handler.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/handler/It600Handler.java @@ -1,5 +1,21 @@ package org.openhab.binding.salus.internal.handler; +import static java.math.RoundingMode.HALF_EVEN; +import static org.openhab.binding.salus.internal.SalusBindingConstants.Channels.It600.*; +import static org.openhab.binding.salus.internal.SalusBindingConstants.It600Device.HoldType.*; +import static org.openhab.binding.salus.internal.SalusBindingConstants.SalusDevice.DSN; +import static org.openhab.core.thing.ThingStatus.OFFLINE; +import static org.openhab.core.thing.ThingStatus.ONLINE; +import static org.openhab.core.thing.ThingStatusDetail.*; +import static org.openhab.core.types.RefreshType.REFRESH; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.Optional; +import java.util.Set; +import java.util.SortedSet; + import org.apache.commons.lang3.StringUtils; import org.openhab.binding.salus.internal.rest.DeviceProperty; import org.openhab.core.library.types.DecimalType; @@ -14,36 +30,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.ArrayList; -import java.util.Optional; -import java.util.Set; -import java.util.SortedSet; - -import static java.math.RoundingMode.HALF_EVEN; -import static org.openhab.binding.salus.internal.SalusBindingConstants.Channels.It600.*; -import static org.openhab.binding.salus.internal.SalusBindingConstants.It600Device.HoldType.*; -import static org.openhab.binding.salus.internal.SalusBindingConstants.SalusDevice.DSN; -import static org.openhab.core.thing.ThingStatus.OFFLINE; -import static org.openhab.core.thing.ThingStatus.ONLINE; -import static org.openhab.core.thing.ThingStatusDetail.*; -import static org.openhab.core.types.RefreshType.REFRESH; - public class It600Handler extends BaseThingHandler { private static final BigDecimal ONE_HUNDRED = new BigDecimal(100); - private static final Set REQUIRED_CHANNELS = Set.of( - "ep_9:sIT600TH:LocalTemperature_x100", - "ep_9:sIT600TH:HeatingSetpoint_x100", - "ep_9:sIT600TH:SetHeatingSetpoint_x100", - "ep_9:sIT600TH:HoldType", - "ep_9:sIT600TH:SetHoldType" - ); + private static final Set REQUIRED_CHANNELS = Set.of("ep_9:sIT600TH:LocalTemperature_x100", + "ep_9:sIT600TH:HeatingSetpoint_x100", "ep_9:sIT600TH:SetHeatingSetpoint_x100", "ep_9:sIT600TH:HoldType", + "ep_9:sIT600TH:SetHoldType"); private final Logger logger; private String dsn; private CloudApi cloudApi; - public It600Handler(Thing thing) { super(thing); logger = LoggerFactory.getLogger(It600Handler.class.getName() + "[" + thing.getUID().getId() + "]"); @@ -61,24 +56,19 @@ public void initialize() { } } - private void internalInitialize() { { var bridge = getBridge(); if (bridge == null) { logger.debug("No bridge for thing with UID {}", thing.getUID()); - updateStatus( - OFFLINE, - BRIDGE_UNINITIALIZED, + updateStatus(OFFLINE, BRIDGE_UNINITIALIZED, "There is no bridge for this thing. Remove it and add it again."); return; } var bridgeHandler = bridge.getHandler(); if (!(bridgeHandler instanceof CloudBridgeHandler cloudHandler)) { - var bridgeHandlerClassName = Optional.ofNullable(bridgeHandler) - .map(BridgeHandler::getClass) - .map(Class::getSimpleName) - .orElse("null"); + var bridgeHandlerClassName = Optional.ofNullable(bridgeHandler).map(BridgeHandler::getClass) + .map(Class::getSimpleName).orElse("null"); logger.debug("Bridge is not instance of {}! Current bridge class {}, Thing UID {}", CloudBridgeHandler.class.getSimpleName(), bridgeHandlerClassName, thing.getUID()); updateStatus(OFFLINE, BRIDGE_UNINITIALIZED, "There is wrong type of bridge for cloud device!"); @@ -91,9 +81,7 @@ private void internalInitialize() { if (StringUtils.isEmpty(dsn)) { logger.debug("No {} for thing with UID {}", DSN, thing.getUID()); - updateStatus( - OFFLINE, - CONFIGURATION_ERROR, + updateStatus(OFFLINE, CONFIGURATION_ERROR, "There is no " + DSN + " for this thing. Remove it and add it again."); return; } @@ -115,10 +103,7 @@ private void internalInitialize() { return; } // device is missing properties - var deviceProperties = findDeviceProperties() - .stream() - .map(DeviceProperty::getName) - .toList(); + var deviceProperties = findDeviceProperties().stream().map(DeviceProperty::getName).toList(); var result = new ArrayList<>(REQUIRED_CHANNELS); result.removeAll(deviceProperties); if (result.size() > 0) { @@ -164,20 +149,16 @@ private void handleCommandForTemperature(ChannelUID channelUID, Command command) } findLongProperty("ep_9:sIT600TH:LocalTemperature_x100", "LocalTemperature_x100") - .map(DeviceProperty.LongDeviceProperty::getValue) - .map(BigDecimal::new) - .map(value -> value.divide(ONE_HUNDRED, new MathContext(5, HALF_EVEN))) - .map(DecimalType::new) + .map(DeviceProperty.LongDeviceProperty::getValue).map(BigDecimal::new) + .map(value -> value.divide(ONE_HUNDRED, new MathContext(5, HALF_EVEN))).map(DecimalType::new) .ifPresent(state -> updateState(channelUID, state)); } private void handleCommandForExpectedTemperature(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { findLongProperty("ep_9:sIT600TH:HeatingSetpoint_x100", "HeatingSetpoint_x100") - .map(DeviceProperty.LongDeviceProperty::getValue) - .map(BigDecimal::new) - .map(value -> value.divide(ONE_HUNDRED, new MathContext(5, HALF_EVEN))) - .map(DecimalType::new) + .map(DeviceProperty.LongDeviceProperty::getValue).map(BigDecimal::new) + .map(value -> value.divide(ONE_HUNDRED, new MathContext(5, HALF_EVEN))).map(DecimalType::new) .ifPresent(state -> updateState(channelUID, state)); return; } @@ -201,14 +182,13 @@ private void handleCommandForExpectedTemperature(ChannelUID channelUID, Command return; } - logger.debug("Does not know how to handle command `{}` ({}) on channel `{}`!", - command, command.getClass().getSimpleName(), channelUID); + logger.debug("Does not know how to handle command `{}` ({}) on channel `{}`!", command, + command.getClass().getSimpleName(), channelUID); } private void handleCommandForWorkType(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { - findLongProperty("ep_9:sIT600TH:HoldType", "HoldType") - .map(DeviceProperty.LongDeviceProperty::getValue) + findLongProperty("ep_9:sIT600TH:HoldType", "HoldType").map(DeviceProperty.LongDeviceProperty::getValue) .map(value -> switch (value.intValue()) { case AUTO -> "AUTO"; case MANUAL -> "MANUAL"; @@ -218,9 +198,7 @@ private void handleCommandForWorkType(ChannelUID channelUID, Command command) { logger.warn("Unknown value {} for property HoldType!", value); yield "AUTO"; } - }) - .map(StringType::new) - .ifPresent(state -> updateState(channelUID, state)); + }).map(StringType::new).ifPresent(state -> updateState(channelUID, state)); return; } @@ -247,23 +225,19 @@ private void handleCommandForWorkType(ChannelUID channelUID, Command command) { return; } - logger.debug("Does not know how to handle command `{}` ({}) on channel `{}`!", - command, command.getClass().getSimpleName(), channelUID); + logger.debug("Does not know how to handle command `{}` ({}) on channel `{}`!", command, + command.getClass().getSimpleName(), channelUID); } private Optional findLongProperty(String name, String shortName) { var deviceProperties = findDeviceProperties(); - var property = deviceProperties.stream() - .filter(p -> p.getName().equals(name)) + var property = deviceProperties.stream().filter(p -> p.getName().equals(name)) .filter(DeviceProperty.LongDeviceProperty.class::isInstance) - .map(DeviceProperty.LongDeviceProperty.class::cast) - .findAny(); + .map(DeviceProperty.LongDeviceProperty.class::cast).findAny(); if (property.isEmpty()) { - property = deviceProperties.stream() - .filter(p -> p.getName().contains(shortName)) + property = deviceProperties.stream().filter(p -> p.getName().contains(shortName)) .filter(DeviceProperty.LongDeviceProperty.class::isInstance) - .map(DeviceProperty.LongDeviceProperty.class::cast) - .findAny(); + .map(DeviceProperty.LongDeviceProperty.class::cast).findAny(); } if (property.isEmpty()) { logger.warn("{} property not found!", shortName); diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/AuthToken.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/AuthToken.java index 7fe440bb75ced..2da16b73ce7a3 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/AuthToken.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/AuthToken.java @@ -1,13 +1,11 @@ package org.openhab.binding.salus.internal.rest; -import com.google.gson.annotations.SerializedName; - import java.util.Objects; -public record AuthToken( - @SerializedName("access_token") String accessToken, - @SerializedName("refresh_token") String refreshToken, - @SerializedName("expires_in") Long expiresIn, +import com.google.gson.annotations.SerializedName; + +public record AuthToken(@SerializedName("access_token") String accessToken, + @SerializedName("refresh_token") String refreshToken, @SerializedName("expires_in") Long expiresIn, @SerializedName("role") String role) { public AuthToken { Objects.requireNonNull(accessToken, "accessToken cannot be null!"); @@ -16,11 +14,7 @@ public record AuthToken( @Override public String toString() { - return "AuthToken{" + - "accessToken=''" + - ", refreshToken=''" + - ", expiresIn=" + expiresIn + - ", role='" + role + '\'' + - '}'; + return "AuthToken{" + "accessToken=''" + ", refreshToken=''" + ", expiresIn=" + expiresIn + + ", role='" + role + '\'' + '}'; } } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/Device.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/Device.java index 24c9f4d4ba15b..a7834cb20cacc 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/Device.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/Device.java @@ -1,9 +1,10 @@ package org.openhab.binding.salus.internal.rest; -import javax.validation.constraints.NotNull; +import static java.util.Objects.requireNonNull; + import java.util.Map; -import static java.util.Objects.requireNonNull; +import javax.validation.constraints.NotNull; public record Device(@NotNull String dsn, @NotNull String name, @NotNull Map properties) implements Comparable { diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/DeviceProperty.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/DeviceProperty.java index 6deb13c5811ac..46ebc1cad775d 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/DeviceProperty.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/DeviceProperty.java @@ -1,9 +1,10 @@ package org.openhab.binding.salus.internal.rest; -import javax.validation.constraints.NotNull; import java.util.Map; import java.util.Objects; +import javax.validation.constraints.NotNull; + public abstract sealed class DeviceProperty implements Comparable { private final @NotNull String name; @@ -15,7 +16,8 @@ public abstract sealed class DeviceProperty implements Comparable properties; - protected DeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, String productName, String displayName, T value, Map properties) { + protected DeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, String productName, + String displayName, T value, Map properties) { this.name = Objects.requireNonNull(name, "name cannot be null!"); this.readOnly = readOnly != null ? readOnly : true; this.direction = direction; @@ -63,7 +65,6 @@ public Map getProperties() { return properties; } - @Override public boolean equals(Object o) { if (this == o) @@ -88,17 +89,14 @@ public int compareTo(DeviceProperty o) { @Override public String toString() { - return "DeviceProperty{" + - "name='" + name + '\'' + - ", readOnly=" + readOnly + - ", direction='" + direction + '\'' + - ", value=" + value + - '}'; + return "DeviceProperty{" + "name='" + name + '\'' + ", readOnly=" + readOnly + ", direction='" + direction + + '\'' + ", value=" + value + '}'; } public static final class BooleanDeviceProperty extends DeviceProperty { - protected BooleanDeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, String productName, String displayName, Boolean value, Map properties) { + protected BooleanDeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, + String productName, String displayName, Boolean value, Map properties) { super(name, readOnly, direction, dataUpdatedAt, productName, displayName, findValue(value), properties); } @@ -109,7 +107,8 @@ private static Boolean findValue(Boolean value) { public static final class LongDeviceProperty extends DeviceProperty { - protected LongDeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, String productName, String displayName, Long value, Map properties) { + protected LongDeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, + String productName, String displayName, Long value, Map properties) { super(name, readOnly, direction, dataUpdatedAt, productName, displayName, findValue(value), properties); } @@ -120,7 +119,8 @@ private static Long findValue(Long value) { public static final class StringDeviceProperty extends DeviceProperty { - protected StringDeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, String productName, String displayName, String value, Map properties) { + protected StringDeviceProperty(String name, Boolean readOnly, String direction, String dataUpdatedAt, + String productName, String displayName, String value, Map properties) { super(name, readOnly, direction, dataUpdatedAt, productName, displayName, findValue(value), properties); } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/GsonMapper.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/GsonMapper.java index e82dc87f15008..49e5155d229c4 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/GsonMapper.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/GsonMapper.java @@ -1,15 +1,5 @@ package org.openhab.binding.salus.internal.rest; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.reflect.TypeToken; -import org.apache.commons.lang3.tuple.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.stream.Collectors; - import static java.lang.Boolean.parseBoolean; import static java.lang.Long.parseLong; import static java.lang.String.format; @@ -17,8 +7,21 @@ import static java.util.Collections.unmodifiableSortedMap; import static java.util.Optional.empty; +import java.util.*; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; + /** - * The GsonMapper class is responsible for mapping JSON data to Java objects using the Gson library. It provides methods for converting JSON strings to various types of objects, such as authentication tokens, devices, device properties, and error messages. + * The GsonMapper class is responsible for mapping JSON data to Java objects using the Gson library. It provides methods + * for converting JSON strings to various types of objects, such as authentication tokens, devices, device properties, + * and error messages. */ public class GsonMapper { public static final GsonMapper INSTANCE = new GsonMapper(); @@ -30,9 +33,7 @@ public class GsonMapper { private final Gson gson = new Gson(); public String loginParam(String username, char[] password) { - return gson.toJson( - Map.of("user", - Map.of("email", username, "password", new String(password)))); + return gson.toJson(Map.of("user", Map.of("email", username, "password", new String(password)))); } public AuthToken authToken(String json) { @@ -40,12 +41,8 @@ public AuthToken authToken(String json) { } public List parseDevices(String json) { - return tryParseBody(json, LIST_TYPE_REFERENCE, List.of()) - .stream() - .map(this::parseDevice) - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); + return tryParseBody(json, LIST_TYPE_REFERENCE, List.of()).stream().map(this::parseDevice) + .filter(Optional::isPresent).map(Optional::get).toList(); } private Optional parseDevice(Object obj) { @@ -56,8 +53,7 @@ private Optional parseDevice(Object obj) { if (!firstLevelMap.containsKey("device")) { if (logger.isWarnEnabled()) { - var str = firstLevelMap.entrySet() - .stream() + var str = firstLevelMap.entrySet().stream() .map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) .collect(Collectors.joining("\n")); logger.warn("Cannot parse device, because firstLevelMap does not have [device] key!\n{}", str); @@ -66,7 +62,6 @@ private Optional parseDevice(Object obj) { } var objLevel2 = firstLevelMap.get("device"); - if (!(objLevel2 instanceof Map map)) { logger.warn("Cannot parse device, because object is not type of map!\n", obj); return empty(); @@ -75,9 +70,7 @@ private Optional parseDevice(Object obj) { // parse `dns` if (!map.containsKey("dsn")) { if (logger.isWarnEnabled()) { - var str = map.entrySet() - .stream() - .map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) + var str = map.entrySet().stream().map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) .collect(Collectors.joining("\n")); logger.warn("Cannot parse device, because map does not have [dsn] key!\n{}", str); } @@ -88,9 +81,7 @@ private Optional parseDevice(Object obj) { // parse `name` if (!map.containsKey("product_name")) { if (logger.isWarnEnabled()) { - var str = map.entrySet() - .stream() - .map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) + var str = map.entrySet().stream().map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) .collect(Collectors.joining("\n")); logger.warn("Cannot parse device, because map does not have [product_name] key!\n{}", str); } @@ -99,19 +90,15 @@ private Optional parseDevice(Object obj) { var name = (String) map.get("product_name"); // parse `properties` - var list = map.entrySet() - .stream() - .filter(entry -> entry.getKey() != null) - .filter(entry -> !entry.getKey().equals("name")) - .filter(entry -> !entry.getKey().equals("base_type")) + var list = map.entrySet().stream().filter(entry -> entry.getKey() != null) + .filter(entry -> !entry.getKey().equals("name")).filter(entry -> !entry.getKey().equals("base_type")) .filter(entry -> !entry.getKey().equals("read_only")) .filter(entry -> !entry.getKey().equals("direction")) .filter(entry -> !entry.getKey().equals("data_updated_at")) .filter(entry -> !entry.getKey().equals("product_name")) .filter(entry -> !entry.getKey().equals("display_name")) .filter(entry -> !entry.getKey().equals("value")) - .map(entry -> Pair.of(entry.getKey().toString(), (Object) entry.getValue())) - .toList(); + .map(entry -> Pair.of(entry.getKey().toString(), (Object) entry.getValue())).toList(); Map properties = new LinkedHashMap<>(); for (var entry : list) { properties.put(entry.getKey(), entry.getValue()); @@ -162,11 +149,11 @@ private Optional> parseDeviceProperty(Object obj) { if (!firstLevelMap.containsKey("property")) { if (logger.isWarnEnabled()) { - var str = firstLevelMap.entrySet() - .stream() + var str = firstLevelMap.entrySet().stream() .map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) .collect(Collectors.joining("\n")); - logger.warn("Cannot parse device property, because firstLevelMap does not have [property] key!\n{}", str); + logger.warn("Cannot parse device property, because firstLevelMap does not have [property] key!\n{}", + str); } return empty(); } @@ -180,9 +167,7 @@ private Optional> parseDeviceProperty(Object obj) { // name if (!map.containsKey("name")) { if (logger.isWarnEnabled()) { - var str = map.entrySet() - .stream() - .map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) + var str = map.entrySet().stream().map(entry -> format("%s=%s", entry.getKey(), entry.getValue())) .collect(Collectors.joining("\n")); logger.warn("Cannot parse device property, because map does not have [name] key!\n{}", str); } @@ -200,19 +185,15 @@ private Optional> parseDeviceProperty(Object obj) { var value = findObjectOrNull(map, "value"); // parse `properties` - var list = map.entrySet() - .stream() - .filter(entry -> entry.getKey() != null) - .filter(entry -> !entry.getKey().equals("name")) - .filter(entry -> !entry.getKey().equals("base_type")) + var list = map.entrySet().stream().filter(entry -> entry.getKey() != null) + .filter(entry -> !entry.getKey().equals("name")).filter(entry -> !entry.getKey().equals("base_type")) .filter(entry -> !entry.getKey().equals("read_only")) .filter(entry -> !entry.getKey().equals("direction")) .filter(entry -> !entry.getKey().equals("data_updated_at")) .filter(entry -> !entry.getKey().equals("product_name")) .filter(entry -> !entry.getKey().equals("display_name")) .filter(entry -> !entry.getKey().equals("value")) - .map(entry -> Pair.of(entry.getKey().toString(), (Object) entry.getValue())) - .toList(); + .map(entry -> Pair.of(entry.getKey().toString(), (Object) entry.getValue())).toList(); // this weird thing need to be done, // because `Collectors.toMap` does not support value=null // and in our case, sometimes the values are null @@ -222,10 +203,13 @@ private Optional> parseDeviceProperty(Object obj) { } properties = unmodifiableSortedMap(properties); - return Optional.of(buildDeviceProperty(name, baseType, value, readOnly, direction, dataUpdatedAt, productName, displayName, properties)); + return Optional.of(buildDeviceProperty(name, baseType, value, readOnly, direction, dataUpdatedAt, productName, + displayName, properties)); } - private DeviceProperty buildDeviceProperty(String name, String baseType, Object value, Boolean readOnly, String direction, String dataUpdatedAt, String productName, String displayName, SortedMap properties) { + private DeviceProperty buildDeviceProperty(String name, String baseType, Object value, Boolean readOnly, + String direction, String dataUpdatedAt, String productName, String displayName, + SortedMap properties) { if ("boolean".equalsIgnoreCase(baseType)) { Boolean bool; if (value == null) { @@ -240,7 +224,8 @@ private DeviceProperty buildDeviceProperty(String name, String baseType, Obje logger.warn("Cannot parse boolean from [" + value + "]"); bool = null; } - return new DeviceProperty.BooleanDeviceProperty(name, readOnly, direction, dataUpdatedAt, productName, displayName, bool, properties); + return new DeviceProperty.BooleanDeviceProperty(name, readOnly, direction, dataUpdatedAt, productName, + displayName, bool, properties); } if ("integer".equalsIgnoreCase(baseType)) { Long longValue; @@ -261,10 +246,12 @@ private DeviceProperty buildDeviceProperty(String name, String baseType, Obje logger.warn("Cannot parse long from [" + value + "]"); longValue = null; } - return new DeviceProperty.LongDeviceProperty(name, readOnly, direction, dataUpdatedAt, productName, displayName, longValue, properties); + return new DeviceProperty.LongDeviceProperty(name, readOnly, direction, dataUpdatedAt, productName, + displayName, longValue, properties); } var string = value != null ? value.toString() : null; - return new DeviceProperty.StringDeviceProperty(name, readOnly, direction, dataUpdatedAt, productName, displayName, string, properties); + return new DeviceProperty.StringDeviceProperty(name, readOnly, direction, dataUpdatedAt, productName, + displayName, string, properties); } private String findOrNull(Map map, String name) { @@ -274,7 +261,6 @@ private String findOrNull(Map map, String name) { return (String) map.get(name); } - @SuppressWarnings("SameParameterValue") private Boolean findBoolOrNull(Map map, String name) { if (!map.containsKey(name)) { diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpClientException.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpClientException.java index ab5d37860ed60..f86c2d832b76d 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpClientException.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpClientException.java @@ -3,9 +3,10 @@ import java.io.Serial; @SuppressWarnings("SerializableHasSerializationMethods") -public class HttpClientException extends HttpException{ +public class HttpClientException extends HttpException { @Serial private static final long serialVersionUID = 1L; + public HttpClientException(int code, String method, String url) { super(code, "Client Error", method, url); } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpException.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpException.java index 9755281e08d70..d66ef311c0810 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpException.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpException.java @@ -2,13 +2,14 @@ import java.io.Serial; -public class HttpException extends RuntimeException{ +public class HttpException extends RuntimeException { @Serial private static final long serialVersionUID = 1453496993827105778L; public HttpException(int code, String message, String method, String url) { super(message); } + public HttpException(String method, String url, Exception ex) { super(String.format("Error occurred when executing %s %s", method, url), ex); } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpForbiddenException.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpForbiddenException.java index bcd0bb7a41a82..1fb5023f202e8 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpForbiddenException.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpForbiddenException.java @@ -2,7 +2,7 @@ import java.io.Serial; -public class HttpForbiddenException extends HttpException{ +public class HttpForbiddenException extends HttpException { @Serial private static final long serialVersionUID = 1L; diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpServerException.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpServerException.java index 0116213228672..d5d692d4d9f53 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpServerException.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpServerException.java @@ -6,6 +6,7 @@ public class HttpServerException extends HttpException { @Serial private static final long serialVersionUID = 1L; + public HttpServerException(int code, String method, String url) { super(code, "Server Error", method, url); } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnauthorizedException.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnauthorizedException.java index 8bc5370eb996a..7cc6207d5a674 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnauthorizedException.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnauthorizedException.java @@ -3,7 +3,7 @@ import java.io.Serial; @SuppressWarnings("SerializableHasSerializationMethods") -public class HttpUnauthorizedException extends HttpException{ +public class HttpUnauthorizedException extends HttpException { @Serial private static final long serialVersionUID = 1L; diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnknownException.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnknownException.java index 05be685623db0..13696bc1d4daa 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnknownException.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/HttpUnknownException.java @@ -3,7 +3,7 @@ import java.io.Serial; @SuppressWarnings("SerializableHasSerializationMethods") -public class HttpUnknownException extends HttpException{ +public class HttpUnknownException extends HttpException { @Serial private static final long serialVersionUID = 1L; diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/JettyHttpClient.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/JettyHttpClient.java index 2272a55fd1b5d..8c5d0bed87060 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/JettyHttpClient.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/JettyHttpClient.java @@ -1,5 +1,9 @@ package org.openhab.binding.salus.internal.rest; +import static java.util.Objects.requireNonNull; + +import javax.validation.constraints.NotNull; + import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpResponseException; import org.eclipse.jetty.client.api.Request; @@ -7,10 +11,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.validation.constraints.NotNull; - -import static java.util.Objects.requireNonNull; - public class JettyHttpClient implements RestClient { private final Logger logger = LoggerFactory.getLogger(JettyHttpClient.class); private final HttpClient client; diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/RestClient.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/RestClient.java index c7e597588c8ca..452a5659e7e1f 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/RestClient.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/RestClient.java @@ -20,10 +20,9 @@ public Header(String name, String value) { } } - record Response(int statusCode, T body) { + record Response (int statusCode, T body) { public Response map(Function mapper) { return new Response<>(statusCode, mapper.apply(body)); } } - } diff --git a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/SalusApi.java b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/SalusApi.java index 148070774cf93..3bca2266204eb 100644 --- a/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/SalusApi.java +++ b/bundles/org.openhab.binding.salus/src/main/java/org/openhab/binding/salus/internal/rest/SalusApi.java @@ -1,19 +1,21 @@ package org.openhab.binding.salus.internal.rest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static java.util.Objects.requireNonNull; +import static org.openhab.binding.salus.internal.rest.ApiResponse.error; import java.time.Clock; import java.time.LocalDateTime; import java.util.SortedSet; import java.util.TreeSet; -import static java.util.Objects.requireNonNull; -import static org.openhab.binding.salus.internal.rest.ApiResponse.error; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * The SalusApi class is responsible for interacting with a REST API to perform various operations related to the Salus system. It handles authentication, token management, and provides methods to retrieve and manipulate device information and properties. + * The SalusApi class is responsible for interacting with a REST API to perform various operations related to the Salus + * system. It handles authentication, token management, and provides methods to retrieve and manipulate device + * information and properties. */ public class SalusApi { private static final int MAX_TIMES = 3; @@ -27,12 +29,8 @@ public class SalusApi { private LocalDateTime authTokenExpireTime; private final Clock clock; - public SalusApi(String username, - char[] password, - String baseUrl, - RestClient restClient, - GsonMapper mapper, - Clock clock) { + public SalusApi(String username, char[] password, String baseUrl, RestClient restClient, GsonMapper mapper, + Clock clock) { this.username = requireNonNull(username, "username"); this.password = requireNonNull(password, "password"); this.baseUrl = removeTrailingSlash(requireNonNull(baseUrl, "baseUrl")); @@ -44,11 +42,7 @@ public SalusApi(String username, logger = LoggerFactory.getLogger(SalusApi.class.getName() + "[" + username.replaceAll("\\.", "_") + "]"); } - public SalusApi(String username, - char[] password, - String baseUrl, - RestClient restClient, - GsonMapper mapper) { + public SalusApi(String username, char[] password, String baseUrl, RestClient restClient, GsonMapper mapper) { this(username, password, baseUrl, restClient, mapper, Clock.systemDefaultZone()); } @@ -67,9 +61,10 @@ private RestClient.Response get(String url, RestClient.Header header, in return response; } - private RestClient.Response post(String url, RestClient.Content content, RestClient.Header header, int times) { + private RestClient.Response post(String url, RestClient.Content content, RestClient.Header header, + int times) { refreshAccessToken(); - var response = restClient.post(url,content, header); + var response = restClient.post(url, content, header); if (response.statusCode() == 401) { logger.info("Refreshing access token"); login(username, password); @@ -77,7 +72,7 @@ private RestClient.Response post(String url, RestClient.Content content, logger.warn("Could not refresh access token after {} times", MAX_TIMES); return response; } - return post(url,content, header, times + 1); + return post(url, content, header, times + 1); } return response; } @@ -100,9 +95,7 @@ private void login(String username, char[] password, int times) { var finalUrl = url("/users/sign_in.json"); var method = "POST"; var inputBody = mapper.loginParam(username, password); - var response = restClient.post( - finalUrl, - new RestClient.Content(inputBody, "application/json"), + var response = restClient.post(finalUrl, new RestClient.Content(inputBody, "application/json"), new RestClient.Header("Accept", "application/json")); if (response.statusCode() == 401) { if (times < MAX_TIMES) { @@ -129,8 +122,7 @@ private void login(String username, char[] password, int times) { } authToken = mapper.authToken(response.body()); authTokenExpireTime = LocalDateTime.now(clock).plusSeconds(authToken.expiresIn()); - logger.info("Correctly logged in for user {}, role={}, expires at {} ({} secs)", - username, authToken.role(), + logger.info("Correctly logged in for user {}, role={}, expires at {} ({} secs)", username, authToken.role(), authTokenExpireTime, authToken.expiresIn()); } @@ -209,8 +201,6 @@ public ApiResponse setValueForProperty(String dsn, String propertyName, return error(mapper.parseError(response)); } var datapointValue = response.map(mapper::datapointValue).body(); - return datapointValue - .map(ApiResponse::ok) - .orElseGet(() -> error(new Error(404, "No datapoint in return"))); + return datapointValue.map(ApiResponse::ok).orElseGet(() -> error(new Error(404, "No datapoint in return"))); } } diff --git a/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/it600.xml b/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/it600.xml index 0322de359bdb5..1e02a39dab29f 100644 --- a/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/it600.xml +++ b/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/it600.xml @@ -1,58 +1,60 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - - - + + + + - - - - - - - - dsn - - - - ID in Salus cloud system - - - + + + + + + + + dsn + + + + ID in Salus cloud system + + + - - Number:Temperature - - Current temperature in room - - - - Number:Temperature - - Sets the desired temperature in room - - - - String - - Sets the work type for the device. - OFF - device is turned off - MANUAL - schedules are turned off, following a manual temperature set, - AUTOMATIC - schedules are turned on, following schedule, - TEMPORARY_MANUAL - schedules are turned on, following manual temperature until next schedule. - - - - - - - - - - + + Number:Temperature + + Current temperature in room + + + + Number:Temperature + + Sets the desired temperature in room + + + + String + + Sets the work type for the device. + OFF - device is turned off + MANUAL - schedules are turned off, following + a manual temperature set, + AUTOMATIC - schedules are turned on, following schedule, + TEMPORARY_MANUAL - schedules are + turned on, following manual temperature until next schedule. + + + + + + + + + + diff --git a/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/salus-bridge.xml b/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/salus-bridge.xml index b6330a0612b13..2766cf5df79f1 100644 --- a/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/salus-bridge.xml +++ b/bundles/org.openhab.binding.salus/src/main/resources/OH-INF/thing/salus-bridge.xml @@ -1,47 +1,47 @@ - - - + + + - + - username - - - - - - - - - password - - - - - https://eu.salusconnect.io - true - url - - - - Refresh time in seconds. - true - 30 - - - - How long device properties should be cached - true - 5 - - + username + + + + + + + + + password + + + + + https://eu.salusconnect.io + true + url + + + + Refresh time in seconds. + true + 30 + + + + How long device properties should be cached + true + 5 + + - + diff --git a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/discovery/CloudDiscoveryTest.java b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/discovery/CloudDiscoveryTest.java index 5582601668bf2..38c48fe406f5c 100644 --- a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/discovery/CloudDiscoveryTest.java +++ b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/discovery/CloudDiscoveryTest.java @@ -1,23 +1,19 @@ package org.openhab.binding.salus.internal.discovery; -import org.apache.commons.collections4.list.TreeList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; + +import java.util.*; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatcher; import org.openhab.binding.salus.internal.handler.CloudApi; import org.openhab.binding.salus.internal.handler.CloudBridgeHandler; import org.openhab.binding.salus.internal.rest.Device; import org.openhab.core.config.discovery.DiscoveryListener; -import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.thing.ThingUID; -import java.util.*; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; - - public class CloudDiscoveryTest { @Test @@ -26,7 +22,7 @@ void test_filters_out_disconnected_devices_and_adds_connected_devices_as_things( // Given var cloudApi = mock(CloudApi.class); var bridgeHandler = mock(CloudBridgeHandler.class); - var bridgeUid = new ThingUID("salus","salus-device","boo"); + var bridgeUid = new ThingUID("salus", "salus-device", "boo"); var discoveryService = new CloudDiscovery(bridgeHandler, cloudApi, bridgeUid); var discoveryListener = mock(DiscoveryListener.class); discoveryService.addDiscoveryListener(discoveryListener); @@ -43,17 +39,13 @@ void test_filters_out_disconnected_devices_and_adds_connected_devices_as_things( // Then verify(cloudApi).findDevices(); - verify(discoveryListener).thingDiscovered( - eq(discoveryService), + verify(discoveryListener).thingDiscovered(eq(discoveryService), argThat(discoveryResult -> discoveryResult.getLabel().equals(device1.name()))); - verify(discoveryListener).thingDiscovered( - eq(discoveryService), + verify(discoveryListener).thingDiscovered(eq(discoveryService), argThat(discoveryResult -> discoveryResult.getLabel().equals(device2.name()))); - verify(discoveryListener, never()).thingDiscovered( - eq(discoveryService), + verify(discoveryListener, never()).thingDiscovered(eq(discoveryService), argThat(discoveryResult -> discoveryResult.getLabel().equals(device3.name()))); - verify(discoveryListener, never()).thingDiscovered( - eq(discoveryService), + verify(discoveryListener, never()).thingDiscovered(eq(discoveryService), argThat(discoveryResult -> discoveryResult.getLabel().equals(device4.name()))); } @@ -78,7 +70,7 @@ void test_logs_error_when_cloud_api_throws_exception() { private Device randomDevice(boolean connected) { var random = new Random(); var map = new HashMap(); - if(connected) { + if (connected) { map.put("connection_status", "online"); } return new Device("dsn-" + random.nextInt(), "name-" + random.nextInt(), map); diff --git a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/DeviceTest.java b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/DeviceTest.java index 7b9c5e2ab95bc..b059c75a4b322 100644 --- a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/DeviceTest.java +++ b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/DeviceTest.java @@ -1,17 +1,16 @@ package org.openhab.binding.salus.internal.rest; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.Map; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; @SuppressWarnings("EqualsWithItself") class DeviceTest { - // Returns true if 'connection_status' property exists and is set to 'online' @Test @DisplayName("Returns true if 'connection_status' property exists and is set to 'online'") @@ -208,5 +207,4 @@ public void test_to_string_method_should_return_string_representation_with_dsn_a // Then assertThat(result).isEqualTo("Device{dsn='123456', name='Device'}"); } - } diff --git a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/GsonMapperTest.java b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/GsonMapperTest.java index a5179fc29b577..acc2210ad1246 100644 --- a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/GsonMapperTest.java +++ b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/GsonMapperTest.java @@ -1,17 +1,15 @@ package org.openhab.binding.salus.internal.rest; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Collections; import java.util.List; import java.util.Optional; -import static org.assertj.core.api.Assertions.assertThat; - +import org.junit.jupiter.api.Test; public class GsonMapperTest { - // Can serialize login parameters to JSON @Test public void test_serialize_login_parameters_to_json() { @@ -50,10 +48,8 @@ public void test_parse_list_of_devices_from_json() { // Given GsonMapper gsonMapper = GsonMapper.INSTANCE; String json = "[{\"device\":{\"dsn\":\"123\",\"product_name\":\"Product 1\"}},{\"device\":{\"dsn\":\"456\",\"product_name\":\"Product 2\"}}]"; - List expectedDevices = List.of( - new Device("123", "Product 1", Collections.emptyMap()), - new Device("456", "Product 2", Collections.emptyMap()) - ); + List expectedDevices = List.of(new Device("123", "Product 1", Collections.emptyMap()), + new Device("456", "Product 2", Collections.emptyMap())); // When List devices = gsonMapper.parseDevices(json); @@ -103,5 +99,4 @@ public void test_returns_empty_optional_when_parsing_invalid_json_for_datapoint_ // Then assertThat(datapointValue).isEmpty(); } - } diff --git a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/SalusApiTest.java b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/SalusApiTest.java index 64767361aff7d..02cac0ee20f00 100644 --- a/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/SalusApiTest.java +++ b/bundles/org.openhab.binding.salus/src/test/java/org/openhab/binding/salus/internal/rest/SalusApiTest.java @@ -1,15 +1,5 @@ package org.openhab.binding.salus.internal.rest; -import org.assertj.core.api.ThrowableAssert; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.time.Clock; -import java.util.ArrayList; -import java.util.Optional; -import java.util.SortedSet; -import java.util.TreeSet; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; @@ -17,6 +7,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.time.Clock; +import java.util.ArrayList; +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; public class SalusApiTest { @@ -40,7 +36,7 @@ public void test_find_devices_returns_sorted_set_of_devices() { when(mapper.parseDevices(anyString())).thenReturn(devices); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var result = salusApi.findDevices(); @@ -70,7 +66,7 @@ public void test_find_device_properties_returns_sorted_set_of_device_properties( when(mapper.parseDeviceProperties(anyString())).thenReturn(deviceProperties); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var result = salusApi.findDeviceProperties("dsn"); @@ -100,7 +96,7 @@ public void test_set_value_for_property_returns_ok_response_with_datapoint_value when(mapper.datapointValue(anyString())).thenReturn(Optional.of(datapointValue)); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var result = salusApi.setValueForProperty("dsn", "property_name", "value"); @@ -128,8 +124,7 @@ public void test_login_with_incorrect_credentials_throws_http_unauthorized_excep var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); // When - assertThatThrownBy(salusApi::findDevices) - .isInstanceOf(HttpUnauthorizedException.class); + assertThatThrownBy(salusApi::findDevices).isInstanceOf(HttpUnauthorizedException.class); } // Find devices with invalid auth token throws HttpUnauthorizedException @@ -150,7 +145,7 @@ public void test_find_devices_with_invalid_auth_token_throws_http_unauthorized_e when(restClient.get(anyString(), any())).thenReturn(response); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var objectApiResponse = salusApi.findDevices(); @@ -179,7 +174,7 @@ public void test_find_device_properties_with_invalid_auth_token_throws_http_unau when(restClient.get(anyString(), any())).thenReturn(unauthResponse); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var response = salusApi.findDeviceProperties("dsn"); @@ -205,7 +200,7 @@ public void test_set_value_for_property_with_invalid_auth_token_throws_http_unau when(restClient.post(anyString(), any(), any())).thenReturn(response); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var objectApiResponse = salusApi.setValueForProperty("dsn", "property_name", "value"); @@ -236,7 +231,7 @@ public void test_find_device_properties_with_invalid_dsn_returns_apiresponse_wit when(mapper.parseError(any())).thenReturn(error); var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); - setAuthToken(salusApi,restClient, mapper, authToken); + setAuthToken(salusApi, restClient, mapper, authToken); // When var result = salusApi.findDeviceProperties("invalid_dsn"); @@ -265,39 +260,35 @@ public void test_login_with_incorrect_credentials_3_times_throws_http_forbidden_ var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); // When - assertThatThrownBy(salusApi::findDevices) - .isInstanceOf(HttpForbiddenException.class); + assertThatThrownBy(salusApi::findDevices).isInstanceOf(HttpForbiddenException.class); } // Login with correct credentials returns auth token -// @Test -// @DisplayName("Login with correct credentials returns auth token") -// public void test_login_with_correct_credentials_returns_auth_token() { -// // Given -// var username = "correct_username"; -// var password = "correct_password".toCharArray(); -// var baseUrl = "https://example.com"; -// var restClient = mock(RestClient.class); -// var mapper = mock(GsonMapper.class); -// var clock = Clock.systemDefaultZone(); -// -// var authToken = new AuthToken("access_token", "refresh_token", 3600L, "role"); -// var response = new RestClient.Response<>(200, mapper.authTokenToJson(authToken)); -// when(restClient.post(anyString(), any(), any())).thenReturn(response); -// -// var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); -// -// // When -// salusApi.login(username, password); -// -// // Then -// assertThat(salusApi.getAuthToken()).isEqualTo(authToken); -// assertThat(salusApi.getAuthTokenExpireTime()).isNotNull(); -// } - private void setAuthToken(SalusApi salusApi, - RestClient restClient, - GsonMapper mapper, - AuthToken authToken) { + // @Test + // @DisplayName("Login with correct credentials returns auth token") + // public void test_login_with_correct_credentials_returns_auth_token() { + // // Given + // var username = "correct_username"; + // var password = "correct_password".toCharArray(); + // var baseUrl = "https://example.com"; + // var restClient = mock(RestClient.class); + // var mapper = mock(GsonMapper.class); + // var clock = Clock.systemDefaultZone(); + // + // var authToken = new AuthToken("access_token", "refresh_token", 3600L, "role"); + // var response = new RestClient.Response<>(200, mapper.authTokenToJson(authToken)); + // when(restClient.post(anyString(), any(), any())).thenReturn(response); + // + // var salusApi = new SalusApi(username, password, baseUrl, restClient, mapper, clock); + // + // // When + // salusApi.login(username, password); + // + // // Then + // assertThat(salusApi.getAuthToken()).isEqualTo(authToken); + // assertThat(salusApi.getAuthTokenExpireTime()).isNotNull(); + // } + private void setAuthToken(SalusApi salusApi, RestClient restClient, GsonMapper mapper, AuthToken authToken) { var username = "correct_username"; var password = "correct_password".toCharArray(); var inputBody = "login_param_json"; @@ -306,8 +297,7 @@ private void setAuthToken(SalusApi salusApi, when(mapper.authToken(authTokenJson)).thenReturn(authToken); var response = new RestClient.Response<>(200, authTokenJson); - when(restClient.post(endsWith("/users/sign_in.json"), - eq(new RestClient.Content(inputBody, "application/json")), + when(restClient.post(endsWith("/users/sign_in.json"), eq(new RestClient.Content(inputBody, "application/json")), any())).thenReturn(response); } }