diff --git a/CODEOWNERS b/CODEOWNERS index a9bb24764bb51..1913885694c0e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -326,6 +326,7 @@ /bundles/org.openhab.binding.windcentrale/ @marcelrv /bundles/org.openhab.binding.wlanthermo/ @CSchlipp /bundles/org.openhab.binding.wled/ @Skinah +/bundles/org.openhab.binding.wolfsmartset/ @BoBiene /bundles/org.openhab.binding.xmltv/ @clinique /bundles/org.openhab.binding.xmppclient/ @pavel-gololobov /bundles/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz diff --git a/bundles/org.openhab.binding.wolfsmartset/NOTICE b/bundles/org.openhab.binding.wolfsmartset/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/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.wolfsmartset/README.md b/bundles/org.openhab.binding.wolfsmartset/README.md new file mode 100644 index 0000000000000..ffd0a43d2645c --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/README.md @@ -0,0 +1,149 @@ +# Wolf Smartset Binding + +This binding communicates with the www.wolf-smartset.de API and provides values readonly. +Wolf systems are connected with official gateways (Wolf Link Home or Wolf Link Pro) https://www.wolf.eu/produkte/smarthome/ + +## Supported Things + +- Account (``thing-type:wolfsmartset:account``) + * holding the credentials to connect to the wolf-smartset online portal. +- System (``thing-type:wolfsmartset:system``) + * represents one wolf system connected to the wolf-smartset online portal. +- Unit (``thing-type:wolfsmartset:unit``) + * unit is a part of the system with values and parameter + +## Discovery + +- System things (bridge) are discovered after Account thing (bridge) is set up +- Unit things are discovered after System things are set up + +## Thing Configuration + +### Account (bridge) + +The account thing holds the credentials to connect to the wolf-smartset online portal. + +| Parameter | Type | Defaut | Description | +|-----------------|---------|----------|---------------------------------------------------------------------| +| username | text | | username to authenticate to www.wolf-smartset.de | +| password | text | | password to authenticate to www.wolf-smartset.de | +| refreshIntervalStructure | integer | 10 | Specifies the refresh interval to refresh the Structure in minutes | +| refreshIntervalValues | integer | 15 | Specifies time in seconds to refresh values | +| discoveryEnabled | boolean | true | disable the Thing discovery | + +### System (bridge) + +The system thing represents one wolf system connected via a WOLF Link home or a WOLF Link pro to the wolf-smartset online portal. +You have access to your own or to shared systems. + +| Parameter | Type | Defaut | Description | +|-----------------|---------|----------|---------------------------------------------------------------------| +| systemId | integer | | System ID assigned to the system by WolfSmartset | + +### Unit + +A system is divided into different units. +In the wolf-smartset portal, the system has an "Expert" section, each submenu item within the "Expert" section has multiple tabs. +Each of these tabs is treated as one unit. + +| Parameter | Type | Defaut | Description | +|-----------------|---------|----------|---------------------------------------------------------------------| +| unitId | integer | | The BundleId assigned to the unit by WolfSmartset | + +## Tested WOLF-Devices + +| WOLF Equipment | openhab Version | Used gateway | +|-------------------|-----------------|---------------| +| CSZ (CGB and SM1) | 3.1 | WOLF Link Pro | +| CGB-2 | 3.1 | WOLF Link home| + + +## Channels + +| channel | type | description | +|----------|--------|------------------------------| +| number | Number | a generic number | +| contact | Contact | a generic contact | +| temperature | Number:Temperature | a generic temperature | +| string | String | a generic String | +| datetime | DateTime | a generic DateTime | + +## Full Example + +### Things + +```` +Bridge wolfsmartset:account:account "Wolf Smartset Account" [ username="User", password="Password" ] { + Bridge system 32122305166 "WolfSmartset System CSZ" [ systemId="32122305166" ] { + Thing unitId uinit0 "CSZ Heizgerät" [ unitId="unit0" ] { + } + } +} +```` +_You need to use the corrosponding systemId and unitId returned by the discovery_ + +### Items + +```` +"Number CSZHeizgerat_Raumtemperatur "Raumtemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900000"} +Number CSZHeizgerat_Flamme "Flamme" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900001"} +Number CSZHeizgerat_AnalogeFernbedienung "Analoge Fernbedienung" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900002"} +Number CSZHeizgerat_Raumsolltemperatur "Raumsolltemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900003"} +Number CSZHeizgerat_AusgangA1 "Ausgang A1" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900004"} +String CSZHeizgerat_ZeitprogrammdirekterHeizkreis "Zeitprogramm direkter Heizkreis" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900005"} +Number CSZHeizgerat_Ventil1 "Ventil 1" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900006"} +Number CSZHeizgerat_Ventil2 "Ventil 2" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900007"} +Number CSZHeizgerat_WiSoUmschaltung "Wi/So Umschaltung" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900008"} +Number CSZHeizgerat_Tagtemperatur "Tagtemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900009"} +Number CSZHeizgerat_PWMPumpe "PWM Pumpe" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000010"} +Number CSZHeizgerat_Speichersolltemperatur "Speichersolltemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000011"} +Number CSZHeizgerat_Heizkurve "Heizkurve" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000012"} +Number CSZHeizgerat_Raumeinfluss "Raumeinfluss" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000013"} +Number CSZHeizgerat_TWVorlauf "TW-Vorlauf" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000014"} +Number CSZHeizgerat_Spartemperatur "Spartemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000015"} +Number CSZHeizgerat_Geblase "Gebläse" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000016"} +Number CSZHeizgerat_Vorlaufsolltemperatur "Vorlaufsolltemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000017"} +Group CSZHeizgerat "CSZ Heizgerät" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000018"} +Number CSZHeizgerat_ECOABS "ECO/ABS" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000019"} +Number CSZHeizgerat_Netzbetriebstunden "Netzbetriebstunden" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000020"} +Number CSZHeizgerat_TWAbgas "TW-Abgas" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000021"} +Number CSZHeizgerat_HGStatus "HG Status" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000022"} +Number CSZHeizgerat_EingangE1 "Eingang E1" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000023"}" +```` + +## Supported Heating-Devices + +All devices able to be connected to www.wolf-smartset.de + +### Related Documentation from WOLF + +https://www.wolf.eu/fileadmin/Wolf_Daten/Dokumente/FAQ/3065655_201711.pdf + +| Heating system | WOLF Link home | WOLF Link pro | +|-------------------------------------------|-----------------------|--------------------| +| Gas condensing boiler CGB-2, CGW-2, CGS-2 | ✅ | ✅ | +| Oil condensing boiler TOB | ✅ | ✅ | +| MGK-2 gas condensing boiler | ✅ | ✅ | +| split air/water heat pump BWL-1S | ✅ | ✅ | +| Oil condensing boiler COB | | ✅ | +| gas condensing boiler MGK | | ✅ | +| Gas condensing boilers CGB, CGW, CGS, FGB | | ✅ | +| Gas condensing boilers CGG-2, CGU-2 | | ✅ | +| Boiler controls R2, R3, R21 | | ✅ | +| Monobloc heat pumps BWW-1, BWL-1, BWS-1 | | ✅ | +| mixer module MM, MM-2 | ⬜ | ✅ | +| cascade module KM, KM-2 | ⬜ | ✅ | +| solar modules SM1, SM1-2, SM-2, SM2-2 | ⬜ | ✅ | +| Comfort apartment ventilation CWL Excellent | ⬜ | ✅ | +| Air handling units KG Top, CKL Pool``*`` | | ✅ | +| Air handling units CKL, CFL, CRL``*`` | | ✅ | +| Combined heat and power units | | ✅ | + + +Note: + +⬜ possible in connection with a WOLF Link home compatible heater, +full functionality only for devices with current software version. + +``*`` Modbus interface required in the device, +Special programming cannot be mapped. diff --git a/bundles/org.openhab.binding.wolfsmartset/pom.xml b/bundles/org.openhab.binding.wolfsmartset/pom.xml new file mode 100644 index 0000000000000..bfaa738634bda --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.2.0-SNAPSHOT + + + org.openhab.binding.wolfsmartset + + openHAB Add-ons :: Bundles :: WolfSmartset Binding + + diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/feature/feature.xml b/bundles/org.openhab.binding.wolfsmartset/src/main/feature/feature.xml new file mode 100644 index 0000000000000..ba34a81e8ab63 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/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.binding.wolfsmartset/${project.version} + + diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/WolfSmartsetBindingConstants.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/WolfSmartsetBindingConstants.java new file mode 100644 index 0000000000000..61765b9c7acc9 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/WolfSmartsetBindingConstants.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO; +import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryWithMenuItemTabView; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link WolfSmartsetBindingConstants} class defines common constants that are + * used across the whole binding. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetBindingConstants { + + public static final String BINDING_ID = "wolfsmartset"; + + // Account bridge + public static final String THING_TYPE_ACCOUNT = "account"; + public static final ThingTypeUID UID_ACCOUNT_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_ACCOUNT); + public static final Set SUPPORTED_ACCOUNT_BRIDGE_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(UID_ACCOUNT_BRIDGE).collect(Collectors.toSet())); + + // System bridge + public static final String THING_TYPE_SYSTEM = "system"; + public static final ThingTypeUID UID_SYSTEM_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_SYSTEM); + public static final Set SUPPORTED_SYSTEM_BRIDGE_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(UID_SYSTEM_BRIDGE).collect(Collectors.toSet())); + + // unit thing + public static final String THING_TYPE_UNIT = "unit"; + public static final ThingTypeUID UID_UNIT_THING = new ThingTypeUID(BINDING_ID, THING_TYPE_UNIT); + public static final Set SUPPORTED_UNIT_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(UID_UNIT_THING).collect(Collectors.toSet())); + + // Collection of system and unit thing types + public static final Set SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS = Stream + .concat(SUPPORTED_SYSTEM_BRIDGE_THING_TYPES_UIDS.stream(), SUPPORTED_UNIT_THING_TYPES_UIDS.stream()) + .collect(Collectors.toSet()); + + // Collection of all supported thing types + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( + Stream.of(UID_ACCOUNT_BRIDGE, UID_SYSTEM_BRIDGE, UID_UNIT_THING).collect(Collectors.toSet())); + + // System Properties + public static final String THING_PROPERTY_GATEWAY_ID = "GatewayId"; + public static final String THING_PROPERTY_GATEWAY_USERNAME = "GatewayUsername"; + public static final String THING_PROPERTY_INSTALLATION_DATE = "InstallationDate"; + public static final String THING_PROPERTY_LOCATION = "Location"; + public static final String THING_PROPERTY_OPERATOR_NAME = "OperatorName"; + public static final String THING_PROPERTY_USERNAME_OWNER = "UserNameOwner"; + public static final String THING_PROPERTY_ACCESSLEVEL = "AccessLevel"; + + public static final String CH_TEMPERATURE = "temperature"; + public static final String CH_PRESSURE = "barometric-pressure"; + public static final String CH_STRING = "string"; + public static final String CH_CONTACT = "contact"; + public static final String CH_NUMBER = "number"; + public static final String CH_DATETIME = "datetime"; + + // Background discovery frequency + public static final int DISCOVERY_INTERVAL_SECONDS = 300; + public static final int DISCOVERY_INITIAL_DELAY_SECONDS = 10; + + // System bridge and remote unit thing config parameters + public static final String CONFIG_SYSTEM_ID = "systemId"; + public static final String CONFIG_UNIT_ID = "unitId"; + + public static final List EMPTY_UNITS = Collections + . emptyList(); + public static final List EMPTY_SYSTEMS = Collections. emptyList(); +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/WolfSmartsetHandlerFactory.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/WolfSmartsetHandlerFactory.java new file mode 100644 index 0000000000000..5d892a9315fdd --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/WolfSmartsetHandlerFactory.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal; + +import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetAccountBridgeHandler; +import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetSystemBridgeHandler; +import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetUnitThingHandler; +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; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link WolfSmartsetHandlerFactory} is responsible for creating thing handlers + * for the account bridge, system bridge, and unit thing. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.wolfsmartset", service = ThingHandlerFactory.class) +public class WolfSmartsetHandlerFactory extends BaseThingHandlerFactory { + private final HttpClient httpClient; + + @Activate + public WolfSmartsetHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (SUPPORTED_ACCOUNT_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) { + return new WolfSmartsetAccountBridgeHandler((Bridge) thing, httpClient); + } + if (SUPPORTED_SYSTEM_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) { + return new WolfSmartsetSystemBridgeHandler((Bridge) thing); + } + if (SUPPORTED_UNIT_THING_TYPES_UIDS.contains(thingTypeUID)) { + return new WolfSmartsetUnitThingHandler(thing); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/api/WolfSmartsetApi.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/api/WolfSmartsetApi.java new file mode 100644 index 0000000000000..6e85130b94df5 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/api/WolfSmartsetApi.java @@ -0,0 +1,610 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.api; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +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.HttpResponseException; +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.wolfsmartset.internal.dto.CreateSession2DTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetParameterValuesDTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemStateListDTO; +import org.openhab.binding.wolfsmartset.internal.dto.LoginResponseDTO; +import org.openhab.binding.wolfsmartset.internal.dto.ReadFaultMessagesDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link WolfSmartsetCloudConnector} class is used for connecting to the Wolf Smartset cloud service + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetApi { + private static final int MAX_QUEUE_SIZE = 1000; // maximum queue size + private static final int REQUEST_TIMEOUT_SECONDS = 10; + + private static final DateTimeFormatter SESSION_TIME_STAMP = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final String WOLF_API_URL = "https://www.wolf-smartset.com/portal/"; + + private Instant blockRequestsUntil = Instant.now(); + private String username; + private String password; + private String serviceToken = ""; + private @Nullable CreateSession2DTO session = null; + private int loginFailedCounter = 0; + private HttpClient httpClient; + private int delay = 500; // in ms + private final ScheduledExecutorService scheduler; + private final Gson gson = new GsonBuilder().serializeNulls().create(); + private final LinkedBlockingQueue requestQueue = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE); + private @Nullable ScheduledFuture processJob; + + private final Logger logger = LoggerFactory.getLogger(WolfSmartsetApi.class); + + public WolfSmartsetApi(String username, String password, HttpClient httpClient, ScheduledExecutorService scheduler) + throws WolfSmartsetCloudException { + this.username = username; + this.password = password; + this.httpClient = httpClient; + this.scheduler = scheduler; + if (!checkCredentials()) { + throw new WolfSmartsetCloudException("username or password can't be empty"); + } + } + + /** + * Validate Login to wolf smartset. Returns true if valid token is available, otherwise tries to authenticate with + * wolf smartset portal + */ + public synchronized boolean login() { + if (!checkCredentials()) { + return false; + } + if (!serviceToken.isEmpty()) { + return true; + } + logger.debug("Wolf Smartset login with username {}", username); + try { + loginRequest(); + loginFailedCounter = 0; + this.session = getCreateSession(); + if (this.session != null) { + logger.debug("login successful, browserSessionId {}", session.getBrowserSessionId()); + return true; + } else { + loginFailedCounter++; + this.session = null; + logger.trace("Login succeeded but failed to create session {}", loginFailedCounter); + return false; + } + + } catch (WolfSmartsetCloudException e) { + logger.debug("Error logging on to Wolf Smartset ({}): {}", loginFailedCounter, e.getMessage()); + loginFailedCounter++; + serviceToken = ""; + loginFailedCounterCheck(); + return false; + } + } + + /** + * Request the systems available for the authenticated account + * + * @return a list of the available systems + */ + public List getSystems() { + final String response = getSystemString(); + List devicesList = new ArrayList<>(); + try { + GetSystemListDTO[] cdl = gson.fromJson(response, GetSystemListDTO[].class); + if (cdl != null) { + for (GetSystemListDTO system : cdl) { + devicesList.add(system); + } + } + } catch (JsonSyntaxException | IllegalStateException | ClassCastException e) { + loginFailedCounter++; + logger.warn("Error while parsing devices: {}", e.getMessage()); + } + return devicesList; + } + + /** + * Request the description of the given system + * + * @param systemId the id of the system + * @param gatewayId the id of the gateway the system relates to + * @return dto describing the requested system + */ + public @Nullable GetGuiDescriptionForGatewayDTO getSystemDescription(Integer systemId, Integer gatewayId) { + final String response = getSystemDescriptionString(systemId, gatewayId); + GetGuiDescriptionForGatewayDTO deviceDescription = null; + try { + deviceDescription = gson.fromJson(response, GetGuiDescriptionForGatewayDTO.class); + } catch (JsonSyntaxException | IllegalStateException | ClassCastException e) { + loginFailedCounter++; + logger.warn("Error while parsing device descriptions: {}", e.getMessage()); + } + return deviceDescription; + } + + /** + * Request the system state of the given systems + * + * @param systems a list of {@link GetSystemListDTO} + * @return the {@link GetSystemStateListDTO} descibing the state of the given {@link GetSystemListDTO} items + */ + public @Nullable GetSystemStateListDTO @Nullable [] getSystemState(Collection<@Nullable GetSystemListDTO> systems) { + final String response = getSystemStateString(systems); + GetSystemStateListDTO[] systemState = null; + try { + systemState = gson.fromJson(response, GetSystemStateListDTO[].class); + } catch (JsonSyntaxException | IllegalStateException | ClassCastException e) { + loginFailedCounter++; + logger.warn("Error while parsing device descriptions: {}", e.getMessage()); + } + if (systemState != null && systemState.length >= 1) { + return systemState; + } else { + return null; + } + } + + /** + * Request the fault messages of the given system + * + * @param systemId the id of the system + * @param gatewayId the id of the gateway the system relates to + * @return {@link ReadFaultMessagesDTO} containing the faultmessages + */ + public @Nullable ReadFaultMessagesDTO getFaultMessages(Integer systemId, Integer gatewayId) { + final String response = getFaultMessagesString(systemId, gatewayId); + ReadFaultMessagesDTO faultMessages = null; + try { + faultMessages = gson.fromJson(response, ReadFaultMessagesDTO.class); + } catch (JsonSyntaxException | IllegalStateException | ClassCastException e) { + loginFailedCounter++; + logger.warn("Error while parsing faultmessages: {}", e.getMessage()); + } + return faultMessages; + } + + /** + * Request the current values for a unit associated with the given system. + * if lastAccess is not null, only value changes newer than the given timestamp are returned + * + * @param systemId the id of the system + * @param gatewayId the id of the gateway the system relates to + * @param bundleId the id of the Unit + * @param valueIdList list of the values to request + * @param lastAccess timestamp of the last valid value request + * @return {@link GetParameterValuesDTO} containing the requested values + */ + public @Nullable GetParameterValuesDTO getGetParameterValues(Integer systemId, Integer gatewayId, Long bundleId, + List valueIdList, @Nullable Instant lastAccess) { + final String response = getGetParameterValuesString(systemId, gatewayId, bundleId, valueIdList, lastAccess); + GetParameterValuesDTO parameterValues = null; + try { + parameterValues = gson.fromJson(response, GetParameterValuesDTO.class); + } catch (JsonSyntaxException | IllegalStateException | ClassCastException e) { + loginFailedCounter++; + logger.warn("Error while parsing device parameter values: {}", e.getMessage()); + } + return parameterValues; + } + + public void stopRequestQueue() { + try { + stopProcessJob(); + requestQueue.forEach(queueEntry -> queueEntry.future.completeExceptionally(new CancellationException())); + } catch (Exception e) { + logger.debug("Error stopping request queue background processing:{}", e.getMessage(), e); + } + } + + /** + * Set a new delay + * + * @param delay in ms between to requests + */ + private void setDelay(int delay) { + if (delay < 0) { + throw new IllegalArgumentException("Delay needs to be larger or equal to zero"); + } + this.delay = delay; + stopProcessJob(); + if (delay != 0) { + processJob = scheduler.scheduleWithFixedDelay(() -> processQueue(), 0, delay, TimeUnit.MILLISECONDS); + } + } + + private boolean checkCredentials() { + if (username.trim().isEmpty() || password.trim().isEmpty()) { + logger.debug("Wolf Smartset: username or password missing."); + return false; + } + return true; + } + + private String getCreateSessionString() { + String resp = ""; + try { + JsonObject json = new JsonObject(); + json.addProperty("Timestamp", SESSION_TIME_STAMP.format(LocalDateTime.now())); + resp = requestPOST("api/portal/CreateSession2", json).get(); + logger.trace("api/portal/CreateSession2 response: {}", resp); + } catch (InterruptedException | ExecutionException e) { + logger.warn("getSystemStateString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } catch (WolfSmartsetCloudException e) { + logger.debug("getSystemStateString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } + + return resp; + } + + private @Nullable CreateSession2DTO getCreateSession() { + final String response = getCreateSessionString(); + CreateSession2DTO session = null; + try { + session = gson.fromJson(response, CreateSession2DTO.class); + } catch (JsonSyntaxException | IllegalStateException | ClassCastException e) { + loginFailedCounter++; + logger.warn("getCreateSession failed with {}: {}", e.getCause(), e.getMessage()); + } + return session; + } + + private String getSystemString() { + String resp = ""; + try { + resp = requestGET("api/portal/GetSystemList").get(); + logger.trace("api/portal/GetSystemList response: {}", resp); + } catch (InterruptedException | ExecutionException | WolfSmartsetCloudException e) { + logger.warn("getSystemString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } + return resp; + } + + private String getSystemDescriptionString(Integer systemId, Integer gatewayId) { + String resp = ""; + try { + Map params = new HashMap(); + params.put("SystemId", systemId.toString()); + params.put("GatewayId", gatewayId.toString()); + resp = requestGET("api/portal/GetGuiDescriptionForGateway", params).get(); + logger.trace("api/portal/GetGuiDescriptionForGateway response: {}", resp); + } catch (InterruptedException | ExecutionException | WolfSmartsetCloudException e) { + logger.warn("getSystemDescriptionString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } + return resp; + } + + private String getSystemStateString(Collection<@Nullable GetSystemListDTO> systems) { + String resp = ""; + try { + JsonArray jsonSystemList = new JsonArray(); + + for (@Nullable + GetSystemListDTO system : systems) { + if (system != null) { + JsonObject jsonSystem = new JsonObject(); + jsonSystem.addProperty("SystemId", system.getId()); + jsonSystem.addProperty("GatewayId", system.getGatewayId()); + + if (system.getSystemShareId() != null) { + jsonSystem.addProperty("SystemShareId", system.getSystemShareId()); + } + jsonSystemList.add(jsonSystem); + } + } + + JsonObject json = new JsonObject(); + + json.add("SystemList", jsonSystemList); + resp = requestPOST("api/portal/GetSystemStateList", json).get(); + logger.trace("api/portal/GetSystemStateList response: {}", resp); + } catch (InterruptedException | ExecutionException | WolfSmartsetCloudException e) { + logger.warn("getSystemStateString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } + return resp; + } + + private String getFaultMessagesString(Integer systemId, Integer gatewayId) { + String resp = ""; + try { + JsonObject json = new JsonObject(); + + json.addProperty("SystemId", systemId); + json.addProperty("GatewayId", gatewayId); + resp = requestPOST("api/portal/ReadFaultMessages", json).get(); + logger.trace("api/portal/ReadFaultMessages response: {}", resp); + } catch (InterruptedException | ExecutionException | WolfSmartsetCloudException e) { + logger.warn("getFaultMessagesString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } + return resp; + } + + private String getGetParameterValuesString(Integer systemId, Integer gatewayId, Long bundleId, + List valueIdList, @Nullable Instant lastAccess) { + String resp = ""; + try { + JsonObject json = new JsonObject(); + json.addProperty("SystemId", systemId); + json.addProperty("GatewayId", gatewayId); + json.addProperty("BundleId", bundleId); + json.addProperty("IsSubBundle", false); + json.add("ValueIdList", gson.toJsonTree(valueIdList)); + if (lastAccess != null) { + json.addProperty("LastAccess", DateTimeFormatter.ISO_INSTANT.format(lastAccess)); + } else { + json.addProperty("LastAccess", (String) null); + } + json.addProperty("GuiIdChanged", false); + if (session != null) { + json.addProperty("SessionId", session.getBrowserSessionId()); + } + resp = requestPOST("api/portal/GetParameterValues", json).get(); + logger.trace("api/portal/GetParameterValues response: {}", resp); + } catch (InterruptedException | ExecutionException | WolfSmartsetCloudException e) { + logger.warn("getGetParameterValuesString failed with {}: {}", e.getCause(), e.getMessage()); + loginFailedCounter++; + } + return resp; + } + + private CompletableFuture requestGET(String url) throws WolfSmartsetCloudException { + return requestGET(url, new HashMap()); + } + + private CompletableFuture requestGET(String url, Map params) + throws WolfSmartsetCloudException { + return rateLimtedRequest(() -> { + if (this.serviceToken.isEmpty()) { + throw new WolfSmartsetCloudException("Cannot execute request. service token missing"); + } + loginFailedCounterCheck(); + + var requestUrl = WOLF_API_URL + url; + Request request = httpClient.newRequest(requestUrl).timeout(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // using HTTP GET with ContentType application/x-www-form-urlencoded like the iOS App does + request.header(HttpHeader.AUTHORIZATION, serviceToken); + request.method(HttpMethod.GET); + request.header(HttpHeader.CONTENT_TYPE, "application/x-www-form-urlencoded"); + + for (Entry entry : params.entrySet()) { + logger.debug("Send request param: {}={} to {}", entry.getKey(), entry.getValue().toString(), url); + request.param(entry.getKey(), entry.getValue()); + } + + return request; + }); + } + + private CompletableFuture requestPOST(String url, JsonElement json) throws WolfSmartsetCloudException { + return rateLimtedRequest(() -> { + if (this.serviceToken.isEmpty()) { + throw new WolfSmartsetCloudException("Cannot execute request. service token missing"); + } + loginFailedCounterCheck(); + + var request = createPOSTRequest(url, json); + request.header(HttpHeader.AUTHORIZATION, serviceToken); + return request; + }); + } + + private Request createPOSTRequest(String url, JsonElement json) { + var requestUrl = WOLF_API_URL + url; + Request request = httpClient.newRequest(requestUrl).timeout(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + request.header(HttpHeader.ACCEPT, "application/json"); + request.header(HttpHeader.CONTENT_TYPE, "application/json"); + request.method(HttpMethod.POST); + + request.content(new StringContentProvider(json.toString()), "application/json"); + return request; + } + + private CompletableFuture rateLimtedRequest(SupplyRequestFunctionalInterface buildRequest) { + // if no delay is set, return a completed CompletableFuture + CompletableFuture future = new CompletableFuture<>(); + RequestQueueEntry queueEntry = new RequestQueueEntry(buildRequest, future); + + if (delay == 0) { + queueEntry.completeFuture((r) -> this.getResponse(r)); + } else { + if (!requestQueue.offer(queueEntry)) { + future.completeExceptionally(new RejectedExecutionException("Maximum queue size exceeded.")); + } + } + return future; + } + + private void stopProcessJob() { + ScheduledFuture processJob = this.processJob; + if (processJob != null) { + processJob.cancel(false); + this.processJob = null; + } + } + + private void processQueue() { + // No new Requests until blockRequestsUntil, is set when recieved HttpStatus.TOO_MANY_REQUESTS_429 + if (blockRequestsUntil.isBefore(Instant.now())) { + RequestQueueEntry queueEntry = requestQueue.poll(); + if (queueEntry != null) { + queueEntry.completeFuture((r) -> this.getResponse(r)); + } + } + } + + @FunctionalInterface + interface SupplyRequestFunctionalInterface { + Request get() throws WolfSmartsetCloudException; + } + + @FunctionalInterface + interface GetResponseFunctionalInterface { + String get(Request request) throws WolfSmartsetCloudException; + } + + private String getResponse(Request request) throws WolfSmartsetCloudException { + try { + logger.debug("execute request {} {}", request.getMethod(), request.getURI()); + final ContentResponse response = request.send(); + if (response.getStatus() == HttpStatus.NOT_FOUND_404) { + throw new WolfSmartsetCloudException("Invalid request, not found " + request.getURI()); + } else if (response.getStatus() == HttpStatus.TOO_MANY_REQUESTS_429) { + blockRequestsUntil = Instant.now().plusSeconds(30); + throw new WolfSmartsetCloudException("Error too many requests: " + response.getContentAsString()); + } else if (response.getStatus() >= HttpStatus.BAD_REQUEST_400 + && response.getStatus() < HttpStatus.INTERNAL_SERVER_ERROR_500) { + this.serviceToken = ""; + logger.debug("Status {} while executing request to {} :{}", response.getStatus(), request.getURI(), + response.getContentAsString()); + } else { + return response.getContentAsString(); + } + } catch (HttpResponseException e) { + serviceToken = ""; + logger.debug("Error while executing request to {} :{}", request.getURI(), e.getMessage()); + loginFailedCounter++; + } catch (InterruptedException | TimeoutException | ExecutionException /* | IOException */ e) { + logger.debug("Error while executing request to {} :{}", request.getURI(), e.getMessage()); + loginFailedCounter++; + } + return ""; + } + + void loginFailedCounterCheck() { + if (loginFailedCounter > 10) { + logger.debug("Repeated errors logging on to Wolf Smartset"); + serviceToken = ""; + loginFailedCounter = 0; + } + } + + protected void loginRequest() throws WolfSmartsetCloudException { + try { + setDelay(delay); + logger.trace("Wolf Smartset Login"); + + String url = WOLF_API_URL + "connect/token"; + Request request = httpClient.POST(url).timeout(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + request.header(HttpHeader.CONTENT_TYPE, "application/x-www-form-urlencoded"); + + // Building Request body exacly the way the iOS App did this + var encodedUser = URLEncoder.encode(username, StandardCharsets.UTF_8); + var encodedPassword = URLEncoder.encode(password, StandardCharsets.UTF_8); + var authRequestBody = "grant_type=password&username=" + encodedUser + "&password=" + encodedPassword; + + request.content(new StringContentProvider("application/x-www-form-urlencoded", authRequestBody, + StandardCharsets.UTF_8)); + + final ContentResponse response; + response = request.send(); + + final String content = response.getContentAsString(); + logger.trace("Wolf smartset Login response= {}", response); + logger.trace("Wolf smartset Login content= {}", content); + + switch (response.getStatus()) { + case HttpStatus.FORBIDDEN_403: + throw new WolfSmartsetCloudException( + "Access denied. Did you set the correct password and/or username?"); + case HttpStatus.OK_200: + LoginResponseDTO jsonResp = gson.fromJson(content, LoginResponseDTO.class); + if (jsonResp == null) { + throw new WolfSmartsetCloudException("Error getting logon details: " + content); + } + + serviceToken = jsonResp.getTokenType() + " " + jsonResp.getAccessToken(); + + logger.trace("Wolf Smartset login scope = {}", jsonResp.getScope()); + logger.trace("Wolf Smartset login expiresIn = {}", jsonResp.getExpiresIn()); + logger.trace("Wolf Smartset login tokenType = {}", jsonResp.getTokenType()); + return; + default: + logger.trace("request returned status '{}', reason: {}, content = {}", response.getStatus(), + response.getReason(), response.getContentAsString()); + throw new WolfSmartsetCloudException(response.getStatus() + response.getReason()); + } + } catch (InterruptedException | TimeoutException | ExecutionException | JsonParseException e) { + throw new WolfSmartsetCloudException("Cannot logon to Wolf Smartset cloud: " + e.getMessage(), e); + } + } + + private static class RequestQueueEntry { + private SupplyRequestFunctionalInterface buildRequest; + private CompletableFuture future; + + public RequestQueueEntry(SupplyRequestFunctionalInterface buildRequest, CompletableFuture future) { + this.buildRequest = buildRequest; + this.future = future; + } + + public void completeFuture(GetResponseFunctionalInterface getResponse) { + try { + String response = getResponse.get(this.buildRequest.get()); + future.complete(response); + } catch (WolfSmartsetCloudException e) { + future.completeExceptionally(e); + } + } + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/api/WolfSmartsetCloudException.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/api/WolfSmartsetCloudException.java new file mode 100644 index 0000000000000..010f48fda2759 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/api/WolfSmartsetCloudException.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Will be thrown for cloud errors + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetCloudException extends Exception { + /** + * required variable to avoid IncorrectMultilineIndexException warning + */ + private static final Long serialVersionUID = -1280858607995252321L; + + public WolfSmartsetCloudException() { + super(); + } + + public WolfSmartsetCloudException(@Nullable String message) { + super(message); + } + + public WolfSmartsetCloudException(@Nullable String message, @Nullable Exception e) { + super(message, e); + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetAccountConfiguration.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetAccountConfiguration.java new file mode 100644 index 0000000000000..1f23a0a6abc31 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetAccountConfiguration.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link WolfSmartsetAccountConfiguration} class contains fields mapping + * to the account thing configuration parameters. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetAccountConfiguration { + + public @Nullable String username = ""; + public @Nullable String password = ""; + /** + * Time in seconds between information refresh + */ + public @Nullable Integer refreshIntervalStructure; + + /** + * Time in seconds to wait after successful update, command or action before refresh + */ + public @Nullable Integer refreshIntervalValues; + + /* + * Enable/disable automatic discovery + */ + public @Nullable Boolean discoveryEnabled; +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetSystemConfiguration.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetSystemConfiguration.java new file mode 100644 index 0000000000000..bd852af3f1ea1 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetSystemConfiguration.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link WolfSmartsetSystemConfiguration} class contains fields mapping + * to the system thing configuration parameters. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetSystemConfiguration { + + /** + * System ID assigned by WolfSmartset + */ + public @Nullable String systemId; +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetUnitConfiguration.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetUnitConfiguration.java new file mode 100644 index 0000000000000..3135a1fa7031c --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/config/WolfSmartsetUnitConfiguration.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link WolfSmartsetUnitConfiguration} class contains fields mapping + * to the unit thing configuration parameters. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetUnitConfiguration { + + /** + * Id of remote unit + */ + public @Nullable String unitId; +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/discovery/WolfSmartsetAccountDiscoveryService.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/discovery/WolfSmartsetAccountDiscoveryService.java new file mode 100644 index 0000000000000..41a5f31c632b4 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/discovery/WolfSmartsetAccountDiscoveryService.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.discovery; + +import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO; +import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetAccountBridgeHandler; +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.ThingStatus; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WolfSmartsetAccountDiscoveryService} is responsible for discovering the WolfSmartset + * systems that are associated with the WolfSmartset Account + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetAccountDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService { + + private final Logger logger = LoggerFactory.getLogger(WolfSmartsetAccountDiscoveryService.class); + + private @NonNullByDefault({}) WolfSmartsetAccountBridgeHandler bridgeHandler; + + private @Nullable Future discoveryJob; + + public WolfSmartsetAccountDiscoveryService() { + super(SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS, 8, true); + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof WolfSmartsetAccountBridgeHandler) { + this.bridgeHandler = (WolfSmartsetAccountBridgeHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } + + @Override + public void activate() { + super.activate(null); + } + + @Override + public void deactivate() { + super.deactivate(); + } + + @Override + public Set getSupportedThingTypes() { + return SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS; + } + + @Override + protected void startBackgroundDiscovery() { + logger.debug("WolfSmartsetDiscovery: Starting background discovery job"); + + Future localDiscoveryJob = discoveryJob; + if (localDiscoveryJob == null || localDiscoveryJob.isCancelled()) { + discoveryJob = scheduler.scheduleWithFixedDelay(this::backgroundDiscover, DISCOVERY_INITIAL_DELAY_SECONDS, + DISCOVERY_INTERVAL_SECONDS, TimeUnit.SECONDS); + } + } + + @Override + protected void stopBackgroundDiscovery() { + logger.debug("WolfSmartsetDiscovery: Stopping background discovery job"); + Future localDiscoveryJob = discoveryJob; + if (localDiscoveryJob != null) { + localDiscoveryJob.cancel(true); + discoveryJob = null; + } + } + + @Override + public void startScan() { + logger.debug("WolfSmartsetDiscovery: Starting discovery scan"); + discover(); + } + + private void backgroundDiscover() { + if (!bridgeHandler.isBackgroundDiscoveryEnabled()) { + return; + } + discover(); + } + + private void discover() { + if (bridgeHandler.getThing().getStatus() != ThingStatus.ONLINE) { + logger.debug("WolfSmartsetDiscovery: Skipping discovery because Account Bridge thing is not ONLINE"); + return; + } + logger.debug("WolfSmartsetDiscovery: Discovering WolfSmartset devices"); + discoverSystems(); + } + + private synchronized void discoverSystems() { + logger.debug("WolfSmartsetDiscovery: Discovering systems"); + var registeredSytems = bridgeHandler.getRegisteredSystems(); + if (registeredSytems != null) { + for (GetSystemListDTO system : registeredSytems) { + String name = system.getName(); + String identifier = null; + if (system.getId() != null) { + identifier = system.getId().toString(); + } + if (identifier != null && name != null) { + ThingUID thingUID = new ThingUID(UID_SYSTEM_BRIDGE, bridgeHandler.getThing().getUID(), identifier); + thingDiscovered(createSystemDiscoveryResult(thingUID, identifier, name)); + logger.debug("WolfSmartsetDiscovery: System '{}' and name '{}' added with UID '{}'", identifier, + name, thingUID); + } + } + } + } + + private DiscoveryResult createSystemDiscoveryResult(ThingUID systemUID, String identifier, String name) { + Map properties = new HashMap<>(); + properties.put(CONFIG_SYSTEM_ID, identifier); + return DiscoveryResultBuilder.create(systemUID).withProperties(properties) + .withRepresentationProperty(CONFIG_SYSTEM_ID).withBridge(bridgeHandler.getThing().getUID()) + .withLabel(String.format("WolfSmartset System %s", name)).build(); + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/discovery/WolfSmartsetSystemDiscoveryService.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/discovery/WolfSmartsetSystemDiscoveryService.java new file mode 100644 index 0000000000000..dda2cb90c0b04 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/discovery/WolfSmartsetSystemDiscoveryService.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.discovery; + +import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO; +import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryWithMenuItemTabView; +import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetSystemBridgeHandler; +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.ThingStatus; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WolfSmartsetAccountDiscoveryService} is responsible for discovering the WolfSmartset Units + * that are associated with the WolfSmartset System + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetSystemDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService { + + private final Logger logger = LoggerFactory.getLogger(WolfSmartsetSystemDiscoveryService.class); + + private @NonNullByDefault({}) WolfSmartsetSystemBridgeHandler bridgeHandler; + + private @Nullable Future discoveryJob; + + public WolfSmartsetSystemDiscoveryService() { + super(SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS, 8, true); + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof WolfSmartsetSystemBridgeHandler) { + this.bridgeHandler = (WolfSmartsetSystemBridgeHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } + + @Override + public void activate() { + super.activate(null); + } + + @Override + public void deactivate() { + super.deactivate(); + } + + @Override + public Set getSupportedThingTypes() { + return SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS; + } + + @Override + protected void startBackgroundDiscovery() { + logger.debug("WolfSmartsetSystemDiscovery: Starting background discovery job"); + Future localDiscoveryJob = discoveryJob; + if (localDiscoveryJob == null || localDiscoveryJob.isCancelled()) { + discoveryJob = scheduler.scheduleWithFixedDelay(() -> this.backgroundDiscover(), + DISCOVERY_INITIAL_DELAY_SECONDS, DISCOVERY_INTERVAL_SECONDS, TimeUnit.SECONDS); + } + } + + @Override + protected void stopBackgroundDiscovery() { + logger.debug("WolfSmartsetSystemDiscovery: Stopping background discovery job"); + Future localDiscoveryJob = discoveryJob; + if (localDiscoveryJob != null) { + localDiscoveryJob.cancel(true); + discoveryJob = null; + } + } + + @Override + public void startScan() { + logger.debug("WolfSmartsetSystemDiscovery: Starting discovery scan"); + discover(); + } + + private void backgroundDiscover() { + var accountBridgeHandler = bridgeHandler.getAccountBridgeHandler(); + if (accountBridgeHandler == null || !accountBridgeHandler.isBackgroundDiscoveryEnabled()) { + return; + } + discover(); + } + + private void discover() { + if (bridgeHandler.getThing().getStatus() != ThingStatus.ONLINE) { + logger.debug("WolfSmartsetSystemDiscovery: Skipping discovery because Account Bridge thing is not ONLINE"); + return; + } + logger.debug("WolfSmartsetSystemDiscovery: Discovering WolfSmartset devices"); + discoverUnits(); + } + + private synchronized void discoverUnits() { + if (this.bridgeHandler != null) { + String systemId = this.bridgeHandler.getSystemId(); + var systemConfig = this.bridgeHandler.getSystemConfig(); + if (systemConfig != null) { + logger.debug("WolfSmartsetSystemDiscovery: Discovering units for system '{}' (id {})", + systemConfig.getName(), systemId); + for (var unit : this.bridgeHandler.getUnits()) { + ThingUID bridgeUID = this.bridgeHandler.getThing().getUID(); + ThingUID unitUID = new ThingUID(UID_UNIT_THING, bridgeUID, + unit.menuItemTabViewDTO.bundleId.toString()); + thingDiscovered(createUnitDiscoveryResult(unitUID, bridgeUID, systemConfig, unit)); + logger.debug( + "WolfSmartsetSystemDiscovery: Unit for '{}' with id '{}' and name '{}' added with UID '{}'", + systemId, unit.menuItemTabViewDTO.bundleId, unit.menuItemTabViewDTO.tabName, unitUID); + } + } + } + } + + private DiscoveryResult createUnitDiscoveryResult(ThingUID unitUID, ThingUID bridgeUID, + GetSystemListDTO systemConfig, SubMenuEntryWithMenuItemTabView unit) { + Map properties = new HashMap<>(); + properties.put(CONFIG_UNIT_ID, unit.menuItemTabViewDTO.bundleId.toString()); + var tabName = unit.menuItemTabViewDTO.tabName; + var menuName = unit.subMenuEntryDTO.getName(); + tabName = tabName.isEmpty() || tabName.equalsIgnoreCase("NULL") || menuName.equalsIgnoreCase(tabName) ? "" + : "-" + tabName; + + return DiscoveryResultBuilder.create(unitUID).withProperties(properties) + .withRepresentationProperty(CONFIG_UNIT_ID).withBridge(bridgeUID) + .withLabel(String.format("%s %s%s", systemConfig.getName(), menuName, tabName)).build(); + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/CreateSession2DTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/CreateSession2DTO.java new file mode 100644 index 0000000000000..0d13b4bc1eb05 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/CreateSession2DTO.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class CreateSession2DTO { + + @SerializedName("CultureInfoCode") + @Expose + private String cultureInfoCode; + @SerializedName("IsPasswordReset") + @Expose + private Boolean isPasswordReset; + @SerializedName("IsProfessional") + @Expose + private Boolean isProfessional; + @SerializedName("IsProfessionalPasswordReset") + @Expose + private Boolean isProfessionalPasswordReset; + @SerializedName("BrowserSessionId") + @Expose + private Integer browserSessionId; + + public String getCultureInfoCode() { + return cultureInfoCode; + } + + public void setCultureInfoCode(String cultureInfoCode) { + this.cultureInfoCode = cultureInfoCode; + } + + public Boolean getIsPasswordReset() { + return isPasswordReset; + } + + public void setIsPasswordReset(Boolean isPasswordReset) { + this.isPasswordReset = isPasswordReset; + } + + public Boolean getIsProfessional() { + return isProfessional; + } + + public void setIsProfessional(Boolean isProfessional) { + this.isProfessional = isProfessional; + } + + public Boolean getIsProfessionalPasswordReset() { + return isProfessionalPasswordReset; + } + + public void setIsProfessionalPasswordReset(Boolean isProfessionalPasswordReset) { + this.isProfessionalPasswordReset = isProfessionalPasswordReset; + } + + public Integer getBrowserSessionId() { + return browserSessionId; + } + + public void setBrowserSessionId(Integer browserSessionId) { + this.browserSessionId = browserSessionId; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/CurrentMessageDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/CurrentMessageDTO.java new file mode 100644 index 0000000000000..9868a3ac54e65 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/CurrentMessageDTO.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class CurrentMessageDTO { + + @SerializedName("Id") + @Expose + private Integer id; + @SerializedName("ErrorCode") + @Expose + private Integer errorCode; + @SerializedName("Description") + @Expose + private String description; + @SerializedName("OccurTimeLocal") + @Expose + private String occurTimeLocal; + @SerializedName("Active") + @Expose + private Boolean active; + @SerializedName("Index") + @Expose + private Integer index; + @SerializedName("Device") + @Expose + private String device; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getErrorCode() { + return errorCode; + } + + public void setErrorCode(Integer errorCode) { + this.errorCode = errorCode; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getOccurTimeLocal() { + return occurTimeLocal; + } + + public void setOccurTimeLocal(String occurTimeLocal) { + this.occurTimeLocal = occurTimeLocal; + } + + public Boolean getActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GatewayStateDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GatewayStateDTO.java new file mode 100644 index 0000000000000..f511644cb5497 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GatewayStateDTO.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class GatewayStateDTO { + + @SerializedName("GatewayId") + @Expose + private Integer gatewayId; + @SerializedName("IsOnline") + @Expose + private Boolean isOnline; + @SerializedName("GatewayOfflineCause") + @Expose + private Integer gatewayOfflineCause; + @SerializedName("IsLocked") + @Expose + private Boolean isLocked; + @SerializedName("IsDeleted") + @Expose + private Boolean isDeleted; + @SerializedName("IsBusy") + @Expose + private Boolean isBusy; + @SerializedName("ImageName") + @Expose + private String imageName; + + public Integer getGatewayId() { + return gatewayId; + } + + public void setGatewayId(Integer gatewayId) { + this.gatewayId = gatewayId; + } + + public Boolean getIsOnline() { + return isOnline; + } + + public void setIsOnline(Boolean isOnline) { + this.isOnline = isOnline; + } + + public Integer getGatewayOfflineCause() { + return gatewayOfflineCause; + } + + public void setGatewayOfflineCause(Integer gatewayOfflineCause) { + this.gatewayOfflineCause = gatewayOfflineCause; + } + + public Boolean getIsLocked() { + return isLocked; + } + + public void setIsLocked(Boolean isLocked) { + this.isLocked = isLocked; + } + + public Boolean getIsDeleted() { + return isDeleted; + } + + public void setIsDeleted(Boolean isDeleted) { + this.isDeleted = isDeleted; + } + + public Boolean getIsBusy() { + return isBusy; + } + + public void setIsBusy(Boolean isBusy) { + this.isBusy = isBusy; + } + + public String getImageName() { + return imageName; + } + + public void setImageName(String imageName) { + this.imageName = imageName; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetGuiDescriptionForGatewayDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetGuiDescriptionForGatewayDTO.java new file mode 100644 index 0000000000000..1c5ce124a625a --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetGuiDescriptionForGatewayDTO.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class GetGuiDescriptionForGatewayDTO { + + @SerializedName("MenuItems") + @Expose + private List menuItems = null; + @SerializedName("DynFaultMessageDevices") + @Expose + private List dynFaultMessageDevices = null; + @SerializedName("SystemHasWRSClassicDevices") + @Expose + private Boolean systemHasWRSClassicDevices; + + public List getMenuItems() { + return menuItems; + } + + public void setMenuItems(List menuItems) { + this.menuItems = menuItems; + } + + public List getDynFaultMessageDevices() { + return dynFaultMessageDevices; + } + + public void setDynFaultMessageDevices(List dynFaultMessageDevices) { + this.dynFaultMessageDevices = dynFaultMessageDevices; + } + + public Boolean getSystemHasWRSClassicDevices() { + return systemHasWRSClassicDevices; + } + + public void setSystemHasWRSClassicDevices(Boolean systemHasWRSClassicDevices) { + this.systemHasWRSClassicDevices = systemHasWRSClassicDevices; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetParameterValuesDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetParameterValuesDTO.java new file mode 100644 index 0000000000000..bab0e0a746efb --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetParameterValuesDTO.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class GetParameterValuesDTO { + + @SerializedName("LastAccess") + @Expose + private String lastAccess; + @SerializedName("Values") + @Expose + private List values = null; + @SerializedName("IsNewJobCreated") + @Expose + private Boolean isNewJobCreated; + + public String getLastAccess() { + return lastAccess; + } + + public void setLastAccess(String lastAccess) { + this.lastAccess = lastAccess; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + public Boolean getIsNewJobCreated() { + return isNewJobCreated; + } + + public void setIsNewJobCreated(Boolean isNewJobCreated) { + this.isNewJobCreated = isNewJobCreated; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetSystemListDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetSystemListDTO.java new file mode 100644 index 0000000000000..356b515d4bb59 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetSystemListDTO.java @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import javax.annotation.Generated; + +import org.eclipse.jdt.annotation.Nullable; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class GetSystemListDTO { + + @SerializedName("Id") + @Expose + private Integer id; + @SerializedName("GatewayId") + @Expose + private Integer gatewayId; + @SerializedName("IsForeignSystem") + @Expose + private Boolean isForeignSystem; + @SerializedName("AccessLevel") + @Expose + private Integer accessLevel; + @SerializedName("GatewayUsername") + @Expose + private String gatewayUsername; + @SerializedName("Name") + @Expose + private String name; + @SerializedName("SystemShares") + @Expose + private List systemShares = null; + @SerializedName("GatewaySoftwareVersion") + @Expose + private String gatewaySoftwareVersion; + @SerializedName("UserNameOwner") + @Expose + private String userNameOwner; + @SerializedName("SystemShareId") + @Expose + private Integer systemShareId; + @SerializedName("OperatorName") + @Expose + private String operatorName; + @SerializedName("Location") + @Expose + private String location; + @SerializedName("InstallationDate") + @Expose + private String installationDate; + @SerializedName("ImageId") + @Expose + private Integer imageId; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getGatewayId() { + return gatewayId; + } + + public void setGatewayId(Integer gatewayId) { + this.gatewayId = gatewayId; + } + + public Boolean getIsForeignSystem() { + return isForeignSystem; + } + + public void setIsForeignSystem(Boolean isForeignSystem) { + this.isForeignSystem = isForeignSystem; + } + + public Integer getAccessLevel() { + return accessLevel; + } + + public void setAccessLevel(Integer accessLevel) { + this.accessLevel = accessLevel; + } + + public String getGatewayUsername() { + return gatewayUsername; + } + + public void setGatewayUsername(String gatewayUsername) { + this.gatewayUsername = gatewayUsername; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getSystemShares() { + return systemShares; + } + + public void setSystemShares(List systemShares) { + this.systemShares = systemShares; + } + + public String getGatewaySoftwareVersion() { + return gatewaySoftwareVersion; + } + + public void setGatewaySoftwareVersion(String gatewaySoftwareVersion) { + this.gatewaySoftwareVersion = gatewaySoftwareVersion; + } + + public String getUserNameOwner() { + return userNameOwner; + } + + public void setUserNameOwner(String userNameOwner) { + this.userNameOwner = userNameOwner; + } + + public @Nullable Integer getSystemShareId() { + return systemShareId; + } + + public void setSystemShareId(Integer systemShareId) { + this.systemShareId = systemShareId; + } + + public String getOperatorName() { + return operatorName; + } + + public void setOperatorName(String operatorName) { + this.operatorName = operatorName; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getInstallationDate() { + return installationDate; + } + + public void setInstallationDate(String installationDate) { + this.installationDate = installationDate; + } + + public Integer getImageId() { + return imageId; + } + + public void setImageId(Integer imageId) { + this.imageId = imageId; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetSystemStateListDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetSystemStateListDTO.java new file mode 100644 index 0000000000000..9dbcb1a50b066 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/GetSystemStateListDTO.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class GetSystemStateListDTO { + + @SerializedName("SystemId") + @Expose + private Integer systemId; + @SerializedName("GatewayState") + @Expose + private GatewayStateDTO gatewayState; + @SerializedName("AccessLevel") + @Expose + private Integer accessLevel; + @SerializedName("IsSystemShareDeleted") + @Expose + private Boolean isSystemShareDeleted; + @SerializedName("IsSystemShareRejected") + @Expose + private Boolean isSystemShareRejected; + @SerializedName("IsSystemDeleted") + @Expose + private Boolean isSystemDeleted; + @SerializedName("FirstActiveAlertCode") + @Expose + private Integer firstActiveAlertCode; + + public Integer getSystemId() { + return systemId; + } + + public void setSystemId(Integer systemId) { + this.systemId = systemId; + } + + public GatewayStateDTO getGatewayState() { + return gatewayState; + } + + public void setGatewayState(GatewayStateDTO gatewayState) { + this.gatewayState = gatewayState; + } + + public Integer getAccessLevel() { + return accessLevel; + } + + public void setAccessLevel(Integer accessLevel) { + this.accessLevel = accessLevel; + } + + public Boolean getIsSystemShareDeleted() { + return isSystemShareDeleted; + } + + public void setIsSystemShareDeleted(Boolean isSystemShareDeleted) { + this.isSystemShareDeleted = isSystemShareDeleted; + } + + public Boolean getIsSystemShareRejected() { + return isSystemShareRejected; + } + + public void setIsSystemShareRejected(Boolean isSystemShareRejected) { + this.isSystemShareRejected = isSystemShareRejected; + } + + public Boolean getIsSystemDeleted() { + return isSystemDeleted; + } + + public void setIsSystemDeleted(Boolean isSystemDeleted) { + this.isSystemDeleted = isSystemDeleted; + } + + public Integer getFirstActiveAlertCode() { + return firstActiveAlertCode; + } + + public void setFirstActiveAlertCode(Integer firstActiveAlertCode) { + this.firstActiveAlertCode = firstActiveAlertCode; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/HistoryMessageDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/HistoryMessageDTO.java new file mode 100644 index 0000000000000..ad292e7e360bf --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/HistoryMessageDTO.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class HistoryMessageDTO { + + @SerializedName("Id") + @Expose + private Integer id; + @SerializedName("ErrorCode") + @Expose + private Integer errorCode; + @SerializedName("Description") + @Expose + private String description; + @SerializedName("OccurTimeLocal") + @Expose + private String occurTimeLocal; + @SerializedName("Active") + @Expose + private Boolean active; + @SerializedName("Index") + @Expose + private Integer index; + @SerializedName("Device") + @Expose + private String device; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getErrorCode() { + return errorCode; + } + + public void setErrorCode(Integer errorCode) { + this.errorCode = errorCode; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getOccurTimeLocal() { + return occurTimeLocal; + } + + public void setOccurTimeLocal(String occurTimeLocal) { + this.occurTimeLocal = occurTimeLocal; + } + + public Boolean getActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/LoginResponseDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/LoginResponseDTO.java new file mode 100644 index 0000000000000..f6f6a233e234a --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/LoginResponseDTO.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class LoginResponseDTO { + + @SerializedName("access_token") + @Expose + private String accessToken; + @SerializedName("expires_in") + @Expose + private Integer expiresIn; + @SerializedName("token_type") + @Expose + private String tokenType; + @SerializedName("refresh_token") + @Expose + private String refreshToken; + @SerializedName("scope") + @Expose + private String scope; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + } + + public String getTokenType() { + return tokenType; + } + + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/MenuItemDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/MenuItemDTO.java new file mode 100644 index 0000000000000..ffe488d6884d7 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/MenuItemDTO.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class MenuItemDTO { + + @SerializedName("Name") + @Expose + private String name; + @SerializedName("SubMenuEntries") + @Expose + private List subMenuEntries = null; + @SerializedName("ParameterNode") + @Expose + private Boolean parameterNode; + @SerializedName("ImageName") + @Expose + private String imageName; + @SerializedName("TabViews") + @Expose + private List tabViews = null; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getSubMenuEntries() { + return subMenuEntries; + } + + public void setSubMenuEntries(List subMenuEntries) { + this.subMenuEntries = subMenuEntries; + } + + public Boolean getParameterNode() { + return parameterNode; + } + + public void setParameterNode(Boolean parameterNode) { + this.parameterNode = parameterNode; + } + + public String getImageName() { + return imageName; + } + + public void setImageName(String imageName) { + this.imageName = imageName; + } + + public List getTabViews() { + return tabViews; + } + + public void setTabViews(List tabViews) { + this.tabViews = tabViews; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/MenuItemTabViewDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/MenuItemTabViewDTO.java new file mode 100644 index 0000000000000..0e9d4b17c9fff --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/MenuItemTabViewDTO.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +public class MenuItemTabViewDTO { + @SerializedName("IsExpertView") + @Expose + public Boolean isExpertView; + + @SerializedName("TabName") + @Expose + public String tabName; + + @SerializedName("GuiId") + @Expose + public Long guiId; + + @SerializedName("BundleId") + @Expose + public Long bundleId; + + @SerializedName("ParameterDescriptors") + @Expose + public List parameterDescriptors; + + @SerializedName("ViewType") + @Expose + public Long viewType; + + @SerializedName("SvgSchemaDeviceId") + @Expose + public Long svgSchemaDeviceId; +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ParameterDescriptorDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ParameterDescriptorDTO.java new file mode 100644 index 0000000000000..1f8aae198688c --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ParameterDescriptorDTO.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +public class ParameterDescriptorDTO { + @SerializedName("ValueId") + @Expose + public Long valueId; + + @SerializedName("SortId") + @Expose + public Long sortId; + + @SerializedName("SubBundleId") + @Expose + public Long subBundleId; + + @SerializedName("ParameterId") + @Expose + public Long parameterId; + + @SerializedName("IsReadOnly") + @Expose + public Boolean isReadOnly; + + @SerializedName("NoDataPoint") + @Expose + public Boolean noDataPoint; + + @SerializedName("IsExpertProtectable") + @Expose + public Boolean isExpertProtectable; + + @SerializedName("Name") + @Expose + public String name; + + @SerializedName("Group") + @Expose + public String group; + + @SerializedName("ControlType") + @Expose + public Integer controlType; + + @SerializedName("Value") + @Expose + public String value; + + @SerializedName("ValueState") + @Expose + public Long valueState; + + @SerializedName("HasDependentParameter") + @Expose + public Boolean hasDependentParameter; + + @SerializedName("ProtGrp") + @Expose + public String protGrp; + + @SerializedName("Unit") + @Expose + public String unit; + + @SerializedName("Decimals") + @Expose + public Long decimals; + + @SerializedName("MinValueCondition") + @Expose + public String minValueCondition; + + @SerializedName("MaxValueCondition") + @Expose + public String maxValueCondition; + + @SerializedName("MinValue") + @Expose + public Long minValue; + + @SerializedName("MaxValue") + @Expose + public Long maxValue; + + @SerializedName("StepWidth") + @Expose + public double stepWidth; + + @SerializedName("NamePrefix") + @Expose + public String namePrefix; + + @SerializedName("TurnOffValue") + @Expose + public Long turnOffValue; +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ReadFaultMessagesDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ReadFaultMessagesDTO.java new file mode 100644 index 0000000000000..6fa2bab2be18a --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ReadFaultMessagesDTO.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class ReadFaultMessagesDTO { + + @SerializedName("MessagesChanged") + @Expose + private Boolean messagesChanged; + @SerializedName("LastRead") + @Expose + private String lastRead; + @SerializedName("CurrentMessages") + @Expose + private List currentMessages = null; + @SerializedName("HistoryMessages") + @Expose + private List historyMessages = null; + @SerializedName("DeviceFaultMessages") + @Expose + private List deviceFaultMessages = null; + + public Boolean getMessagesChanged() { + return messagesChanged; + } + + public void setMessagesChanged(Boolean messagesChanged) { + this.messagesChanged = messagesChanged; + } + + public String getLastRead() { + return lastRead; + } + + public void setLastRead(String lastRead) { + this.lastRead = lastRead; + } + + public List getCurrentMessages() { + return currentMessages; + } + + public void setCurrentMessages(List currentMessages) { + this.currentMessages = currentMessages; + } + + public List getHistoryMessages() { + return historyMessages; + } + + public void setHistoryMessages(List historyMessages) { + this.historyMessages = historyMessages; + } + + public List getDeviceFaultMessages() { + return deviceFaultMessages; + } + + public void setDeviceFaultMessages(List deviceFaultMessages) { + this.deviceFaultMessages = deviceFaultMessages; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/SubMenuEntryDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/SubMenuEntryDTO.java new file mode 100644 index 0000000000000..4ab599f9e7f82 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/SubMenuEntryDTO.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import java.util.List; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class SubMenuEntryDTO { + + @SerializedName("Name") + @Expose + private String name; + @SerializedName("SubMenuEntries") + @Expose + private List subMenuEntries = null; + @SerializedName("ParameterNode") + @Expose + private Boolean parameterNode; + @SerializedName("ImageName") + @Expose + private String imageName; + @SerializedName("TabViews") + @Expose + private List tabViews = null; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getSubMenuEntries() { + return subMenuEntries; + } + + public void setSubMenuEntries(List subMenuEntries) { + this.subMenuEntries = subMenuEntries; + } + + public Boolean getParameterNode() { + return parameterNode; + } + + public void setParameterNode(Boolean parameterNode) { + this.parameterNode = parameterNode; + } + + public String getImageName() { + return imageName; + } + + public void setImageName(String imageName) { + this.imageName = imageName; + } + + public List getTabViews() { + return tabViews; + } + + public void setTabViews(List tabViews) { + this.tabViews = tabViews; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/SubMenuEntryWithMenuItemTabView.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/SubMenuEntryWithMenuItemTabView.java new file mode 100644 index 0000000000000..5c2ec66f69674 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/SubMenuEntryWithMenuItemTabView.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +/** + * Link the SubMenuEntryDTO with the MenuItemTabViewDTO + * + * @author Bo Biene - Initial contribution + */ +public class SubMenuEntryWithMenuItemTabView { + public SubMenuEntryDTO subMenuEntryDTO; + public MenuItemTabViewDTO menuItemTabViewDTO; + + public SubMenuEntryWithMenuItemTabView(SubMenuEntryDTO subMenuEntryDTO, MenuItemTabViewDTO menuItemTabViewDTO) { + this.subMenuEntryDTO = subMenuEntryDTO; + this.menuItemTabViewDTO = menuItemTabViewDTO; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ValueDTO.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ValueDTO.java new file mode 100644 index 0000000000000..f0e021d75b403 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/dto/ValueDTO.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.dto; + +import javax.annotation.Generated; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * generated with https://www.jsonschema2pojo.org/ + * + * @author Bo Biene - Initial contribution + */ +@Generated("jsonschema2pojo") +public class ValueDTO { + + @SerializedName("ValueId") + @Expose + private Long valueId; + @SerializedName("Value") + @Expose + private String value; + @SerializedName("State") + @Expose + private Integer state; + + public Long getValueId() { + return valueId; + } + + public void setValueId(Long valueId) { + this.valueId = valueId; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Integer getState() { + return state; + } + + public void setState(Integer state) { + this.state = state; + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetAccountBridgeHandler.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetAccountBridgeHandler.java new file mode 100644 index 0000000000000..50a64abe95e7f --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetAccountBridgeHandler.java @@ -0,0 +1,276 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.handler; + +import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.CONFIG_SYSTEM_ID; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.wolfsmartset.internal.api.WolfSmartsetApi; +import org.openhab.binding.wolfsmartset.internal.api.WolfSmartsetCloudException; +import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetAccountConfiguration; +import org.openhab.binding.wolfsmartset.internal.discovery.WolfSmartsetAccountDiscoveryService; +import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO; +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; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WolfSmartsetAccountBridgeHandler} is responsible for managing + * communication with the WolfSmartset API. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetAccountBridgeHandler extends BaseBridgeHandler { + private static final int REFRESH_STARTUP_DELAY_SECONDS = 3; + private static final int REFRESH_INTERVAL_SECONDS = 1; + private static final int DEFAULT_REFRESH_INTERVAL_CONFIGURATION_MINUTES = 10; + private static final int DEFAULT_REFRESH_INTERVAL_VALUES_SECONDS = 15; + + private final Logger logger = LoggerFactory.getLogger(WolfSmartsetAccountBridgeHandler.class); + + private final HttpClient httpClient; + + private @NonNullByDefault({}) WolfSmartsetApi api; + private int refreshIntervalStructureMinutes; + private int refreshIntervalValuesSeconds; + private boolean discoveryEnabled; + private @Nullable List cachedSystems = null; + + private final Map systemHandlers = new ConcurrentHashMap<>(); + private final Set systemIds = new CopyOnWriteArraySet<>(); + + private @Nullable Future refreshSystemsJob; + private final AtomicInteger refreshConfigurationCounter = new AtomicInteger(REFRESH_STARTUP_DELAY_SECONDS); + private final AtomicInteger refreshValuesCounter = new AtomicInteger(REFRESH_STARTUP_DELAY_SECONDS); + + public WolfSmartsetAccountBridgeHandler(final Bridge bridge, HttpClient httpClient) { + super(bridge); + this.httpClient = httpClient; + } + + @Override + public void initialize() { + logger.debug("AccountBridge: Initializing"); + + WolfSmartsetAccountConfiguration config = getConfigAs(WolfSmartsetAccountConfiguration.class); + + Integer value; + value = config.refreshIntervalStructure; + refreshIntervalStructureMinutes = value == null ? DEFAULT_REFRESH_INTERVAL_CONFIGURATION_MINUTES : value; + + value = config.refreshIntervalValues; + refreshIntervalValuesSeconds = value == null ? DEFAULT_REFRESH_INTERVAL_VALUES_SECONDS : value; + + String username = config.username; + String password = config.password; + username = username == null ? "" : username; + password = password == null ? "" : password; + + Boolean booleanValue = config.discoveryEnabled; + discoveryEnabled = booleanValue == null ? false : booleanValue.booleanValue(); + logger.debug("AccountBridge: System and unit discovery is {}", discoveryEnabled ? "enabled" : "disabled"); + if (username.trim().isEmpty() || password.trim().isEmpty()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing username or password"); + } else { + try { + api = new WolfSmartsetApi(username, password, httpClient, scheduler); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Checking authorization"); + scheduleRefreshJob(); + } catch (WolfSmartsetCloudException e) { + logger.error("unable to create wolf smartset api", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + } + } + } + + @Override + public void dispose() { + cancelRefreshJob(); + api.stopRequestQueue(); + logger.debug("AccountBridge: Disposing"); + } + + @Override + public Collection> getServices() { + return Collections.singleton(WolfSmartsetAccountDiscoveryService.class); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void childHandlerInitialized(ThingHandler systemHandler, Thing systemThing) { + String systemId = (String) systemThing.getConfiguration().get(CONFIG_SYSTEM_ID); + systemHandlers.put(systemId, (WolfSmartsetSystemBridgeHandler) systemHandler); + systemIds.add(systemId); + scheduleRefreshJob(); + logger.debug("AccountBridge: Adding system handler for {} with id {}", systemThing.getUID(), systemId); + } + + @Override + public void childHandlerDisposed(ThingHandler systemHandler, Thing systemThing) { + String systemId = (String) systemThing.getConfiguration().get(CONFIG_SYSTEM_ID); + systemHandlers.remove(systemId); + systemIds.remove(systemId); + logger.debug("AccountBridge: Removing system handler for {} with id {}", systemThing.getUID(), systemId); + } + + /** + * returns truee if BackgroundDiscoveryEnabled + */ + public boolean isBackgroundDiscoveryEnabled() { + return discoveryEnabled; + } + + /** + * returns the list of the GetSystemListDTO available + */ + public @Nullable List getRegisteredSystems() { + return cachedSystems; + } + + /** + * force a full update of the wolf smartset cloud configuration + */ + public void scheduleRefreshJob() { + logger.debug("AccountBridge: Scheduling system refresh job"); + cancelRefreshJob(); + refreshConfigurationCounter.set(0); + refreshValuesCounter.set(0); + refreshSystemsJob = scheduler.scheduleWithFixedDelay(this::refreshSystems, REFRESH_STARTUP_DELAY_SECONDS, + REFRESH_INTERVAL_SECONDS, TimeUnit.SECONDS); + } + + /** + * The refresh job updates the system channels on the refresh interval set in the system thing config. + * The system update process involves first running a system summary transaction to + * determine if any system data has changed since the last summary. If any change is detected, + * a full query of the systems is performed. + */ + private void refreshSystems() { + if (refreshConfigurationCounter.getAndDecrement() == 0) { + refreshConfigurationCounter.set(refreshIntervalStructureMinutes * 60); + if (api.login()) { + logger.debug("AccountBridge: refreshing configuration"); + updateStatus(ThingStatus.ONLINE); + cachedSystems = api.getSystems(); + if (cachedSystems != null) { + for (GetSystemListDTO system : api.getSystems()) { + WolfSmartsetSystemBridgeHandler handler = systemHandlers.get(system.getId().toString()); + if (handler != null) { + GetGuiDescriptionForGatewayDTO systemDescription = api.getSystemDescription(system.getId(), + system.getGatewayId()); + handler.updateConfiguration(system, systemDescription); + } + } + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Authorization failed"); + } + } + + if (refreshValuesCounter.getAndDecrement() == 0) { + refreshValuesCounter.set(refreshIntervalValuesSeconds); + if (api.login()) { + logger.debug("AccountBridge: refreshing values"); + updateStatus(ThingStatus.ONLINE); + + var systemConfigs = systemHandlers.values().stream().map(s -> s.getSystemConfig()) + .filter(s -> s != null).collect(Collectors.toSet()); + if (systemConfigs != null && systemConfigs.size() > 0) { + var systemStates = api.getSystemState(systemConfigs); + if (systemStates != null) { + for (var systemState : systemStates) { + if (systemState != null) { + var systemHandler = systemHandlers.get(systemState.getSystemId().toString()); + if (systemHandler != null) { + systemHandler.updateSystemState(systemState); + } + } + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Failed to update system states"); + } + + for (var systemHandler : systemHandlers.values()) { + if (systemHandler != null) { + var systemConfig = systemHandler.getSystemConfig(); + if (systemConfig != null) { + var faultMessages = api.getFaultMessages(systemConfig.getId(), + systemConfig.getGatewayId()); + + systemHandler.updateFaultMessages(faultMessages); + + for (var unitHandler : systemHandler.getUnitHandler()) { + if (unitHandler != null) { + var tabmenu = unitHandler.getTabMenu(); + if (tabmenu != null) { + var lastRefreshTime = unitHandler.getLastRefreshTime(); + var valueIds = tabmenu.parameterDescriptors.stream() + .filter(p -> p.valueId > 0).map(p -> p.valueId) + .collect(Collectors.toList()); + var paramValues = api.getGetParameterValues(systemConfig.getId(), + systemConfig.getGatewayId(), tabmenu.bundleId, valueIds, + lastRefreshTime); + + unitHandler.updateValues(paramValues); + } + } + } + } else { + // waiting for config. + systemHandler.updateSystemState(null); + } + } + } + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Authorization failed"); + } + } + } + + private void cancelRefreshJob() { + Future localRefreshSystemsJob = refreshSystemsJob; + if (localRefreshSystemsJob != null) { + localRefreshSystemsJob.cancel(true); + logger.debug("AccountBridge: Canceling system refresh job"); + } + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetSystemBridgeHandler.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetSystemBridgeHandler.java new file mode 100644 index 0000000000000..154c7eef3ffa8 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetSystemBridgeHandler.java @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.handler; + +import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetSystemConfiguration; +import org.openhab.binding.wolfsmartset.internal.discovery.WolfSmartsetSystemDiscoveryService; +import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO; +import org.openhab.binding.wolfsmartset.internal.dto.GetSystemStateListDTO; +import org.openhab.binding.wolfsmartset.internal.dto.ReadFaultMessagesDTO; +import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryWithMenuItemTabView; +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; +import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WolfSmartsetSystemBridgeHandler} is the handler for an WolfSmartset system. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetSystemBridgeHandler extends BaseBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(WolfSmartsetSystemBridgeHandler.class); + + private @NonNullByDefault({}) String systemId; + + private final Map unitHandlers = new ConcurrentHashMap<>(); + + private @Nullable GetSystemListDTO savedSystem; + private @Nullable List savedUnits; + private Map stateCache = new ConcurrentHashMap<>(); + + public WolfSmartsetSystemBridgeHandler(Bridge bridge) { + super(bridge); + } + + @Override + public void initialize() { + systemId = getConfigAs(WolfSmartsetSystemConfiguration.class).systemId; + logger.debug("SystemBridge: Initializing system '{}'", systemId); + clearSavedState(); + updateStatus(WolfSmartsetUtils.isBridgeOnline(getBridge()) ? ThingStatus.ONLINE : ThingStatus.OFFLINE); + } + + @Override + public Collection> getServices() { + return Collections.singleton(WolfSmartsetSystemDiscoveryService.class); + } + + @Override + public void dispose() { + logger.debug("SystemBridge: Disposing system '{}'", systemId); + } + + @Override + public void childHandlerInitialized(ThingHandler unitHandler, Thing unitThing) { + String unitId = (String) unitThing.getConfiguration().get(CONFIG_UNIT_ID); + unitHandlers.put(unitId, (WolfSmartsetUnitThingHandler) unitHandler); + logger.debug("SystemBridge: Saving unit handler for {} with id {}", unitThing.getUID(), unitId); + var accountBridgeHandler = getAccountBridgeHandler(); + if (accountBridgeHandler != null) { + accountBridgeHandler.scheduleRefreshJob(); + } + } + + @Override + public void childHandlerDisposed(ThingHandler unitHandler, Thing unitThing) { + String unitId = (String) unitThing.getConfiguration().get(CONFIG_UNIT_ID); + unitHandlers.remove(unitId); + logger.debug("SystemBridge: Removing unit handler for {} with id {}", unitThing.getUID(), unitId); + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + State state = stateCache.get(channelUID.getId()); + if (state != null) { + updateState(channelUID.getId(), state); + } + } + } + + /** + * Return the associated account bridge handler + * + * @return returns the {@link WolfSmartsetAccountBridgeHandler} linked to this + * {@link WolfSmartsetSystemBridgeHandler} + */ + public @Nullable WolfSmartsetAccountBridgeHandler getAccountBridgeHandler() { + var bridgeHandler = this.getBridge(); + if (bridgeHandler != null) { + return (WolfSmartsetAccountBridgeHandler) bridgeHandler.getHandler(); + } + return null; + } + + /** + * Return the subordinated unit handler + * + * @return a List of {@link WolfSmartsetUnitThingHandler} with the subordinated unit handler + */ + public Collection getUnitHandler() { + return unitHandlers.values(); + } + + /** + * Returns the list configuration of the units available for this system + * + * @return a list of {@link SubMenuEntryWithMenuItemTabView} representing the available units for this system + */ + public List getUnits() { + List localSavedUnits = savedUnits; + return localSavedUnits == null ? EMPTY_UNITS : localSavedUnits; + } + + /** + * Return the configuration of this system + * + * @return {@link GetSystemListDTO} representing the this system + */ + public @Nullable GetSystemListDTO getSystemConfig() { + return savedSystem; + } + + /** + * Return the id of this system + * + * @return the id of this system + */ + public String getSystemId() { + return systemId; + } + + /** + * Update the system state with the dto + * + * @param systemState {@link GetSystemStateListDTO} the dto representing the current state of this system + */ + public void updateSystemState(@Nullable GetSystemStateListDTO systemState) { + if (systemState != null) { + if (systemState.getIsSystemDeleted()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "System has been deleted"); + } else if (systemState.getIsSystemShareDeleted()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "System share has been removed"); + } else if (systemState.getIsSystemShareRejected()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "System share has been rejected"); + } + } + } + + /** + * Process the available fault messages + * + * @param faultMessages {@link ReadFaultMessagesDTO} the dto representing the list of the current faultmessages + */ + public void updateFaultMessages(@Nullable ReadFaultMessagesDTO faultMessages) { + if (faultMessages != null) { + if (faultMessages.getCurrentMessages() != null) { + for (var message : faultMessages.getCurrentMessages()) { + logger.warn("System {} faultmessage: {}, since {}", systemId, message.getDescription(), + message.getOccurTimeLocal()); + } + } + } + } + + /** + * Update the configuration of the system and the subordinated units + * + * @param system {@link GetSystemListDTO} representing this system + * @param systemDescription {@link GetGuiDescriptionForGatewayDTO} repesenting the units of this system + */ + public void updateConfiguration(@Nullable GetSystemListDTO system, + @Nullable GetGuiDescriptionForGatewayDTO systemDescription) { + if (system != null && systemDescription != null) { + logger.debug("SystemBridge: Updating channels for system id {}, name {}", system.getId(), system.getName()); + updateStatus(ThingStatus.ONLINE); + savedSystem = system; + + Map properties = editProperties(); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, system.getGatewaySoftwareVersion()); + properties.put(THING_PROPERTY_GATEWAY_ID, system.getGatewayId().toString()); + properties.put(THING_PROPERTY_GATEWAY_USERNAME, system.getGatewayUsername()); + properties.put(THING_PROPERTY_INSTALLATION_DATE, system.getInstallationDate()); + properties.put(THING_PROPERTY_LOCATION, system.getLocation()); + properties.put(THING_PROPERTY_OPERATOR_NAME, system.getOperatorName()); + properties.put(THING_PROPERTY_USERNAME_OWNER, system.getUserNameOwner()); + properties.put(THING_PROPERTY_ACCESSLEVEL, system.getAccessLevel().toString()); + updateProperties(properties); + + updateUnitsConfiguration(systemDescription); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Unable to retrieve configuration"); + } + } + + private void updateUnitsConfiguration(GetGuiDescriptionForGatewayDTO systemDescription) { + List listUnits = new ArrayList<>(); + var fachmannNode = systemDescription.getMenuItems().stream() + .filter(m -> "Fachmann".equalsIgnoreCase(m.getName())).findFirst(); + + if (fachmannNode.isPresent()) { + for (var submenu : fachmannNode.get().getSubMenuEntries()) { + for (var tabmenu : submenu.getTabViews()) { + listUnits.add(new SubMenuEntryWithMenuItemTabView(submenu, tabmenu)); + + var handler = unitHandlers.get(tabmenu.bundleId.toString()); + if (handler != null) { + handler.updateConfiguration(submenu, tabmenu); + } + } + } + + } + savedUnits = listUnits; + } + + private void clearSavedState() { + savedSystem = null; + savedUnits = null; + stateCache.clear(); + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetUnitThingHandler.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetUnitThingHandler.java new file mode 100644 index 0000000000000..47332440f6a90 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetUnitThingHandler.java @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.handler; + +import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*; + +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetUnitConfiguration; +import org.openhab.binding.wolfsmartset.internal.dto.GetParameterValuesDTO; +import org.openhab.binding.wolfsmartset.internal.dto.MenuItemTabViewDTO; +import org.openhab.binding.wolfsmartset.internal.dto.ParameterDescriptorDTO; +import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryDTO; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.binding.builder.ThingBuilder; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WolfSmartsetUnitThingHandler} is responsible for updating the channels associated + * with an WolfSmartset unit. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public class WolfSmartsetUnitThingHandler extends BaseThingHandler { + + public static final String CAPABILITY_ADC = "adc"; + public static final String CAPABILITY_CO2 = "co2"; + public static final String CAPABILITY_DRY_CONTACT = "dryContact"; + public static final String CAPABILITY_HUMIDITY = "humidity"; + public static final String CAPABILITY_OCCUPANCY = "occupancy"; + public static final String CAPABILITY_TEMPERATURE = "temperature"; + public static final String CAPABILITY_UNKNOWN = "unknown"; + + private final Logger logger = LoggerFactory.getLogger(WolfSmartsetUnitThingHandler.class); + + private @NonNullByDefault({}) String unitId; + private @Nullable Instant lastRefreshTime; + + private Map stateCache = new ConcurrentHashMap<>(); + private Map paramDescriptionMap = new ConcurrentHashMap<>(); + private @Nullable SubMenuEntryDTO submenu; + private @Nullable MenuItemTabViewDTO tabmenu; + + public WolfSmartsetUnitThingHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + unitId = getConfigAs(WolfSmartsetUnitConfiguration.class).unitId; + logger.debug("UnitThing: Initializing unit '{}'", unitId); + clearSavedState(); + var bridgeHandler = getBridge(); + if (bridgeHandler != null) { + bridgeStatusChanged(bridgeHandler.getStatusInfo()); + } + } + + @Override + public void dispose() { + logger.debug("UnitThing: Disposing unit '{}'", unitId); + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) { + if (this.submenu != null && this.tabmenu != null) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING); + } + + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + State state = stateCache.get(channelUID.getId()); + if (state != null) { + updateState(channelUID.getId(), state); + } + } + } + + /** + * Get the {@link SubMenuEntryDTO} for this unit + * + * @return the {@link SubMenuEntryDTO} for this unit + */ + public @Nullable SubMenuEntryDTO getSubMenu() { + return this.submenu; + } + + /** + * Get the {@link MenuItemTabViewDTO} for this unit + * + * @return the {@link MenuItemTabViewDTO} for this unit + */ + public @Nullable MenuItemTabViewDTO getTabMenu() { + return this.tabmenu; + } + + /** + * Get the {@link Instant} of the last valid call of updateValues + * + * @return the getInstallationDate of the last valid call of updateValues + */ + public @Nullable Instant getLastRefreshTime() { + return this.lastRefreshTime; + } + + /** + * Update the configuration of this unit and create / update the related channels + * + * @param submenu the {@link SubMenuEntryDTO} for this unit + * @param tabmenu the {@link MenuItemTabViewDTO} for this unit + */ + public void updateConfiguration(SubMenuEntryDTO submenu, MenuItemTabViewDTO tabmenu) { + this.submenu = submenu; + this.tabmenu = tabmenu; + var bridgeHandler = getBridge(); + if (bridgeHandler != null) { + bridgeStatusChanged(bridgeHandler.getStatusInfo()); + } + lastRefreshTime = null; + + ThingBuilder thingBuilder = editThing(); + var thingId = thing.getUID(); + + paramDescriptionMap.clear(); + for (var param : tabmenu.parameterDescriptors) { + paramDescriptionMap.put(param.valueId, param); + var channelId = new ChannelUID(thingId, param.parameterId.toString()); // "bindingId:type:thingId:1") + if (thing.getChannel(channelId) == null) { + logger.debug("UnitThing: Create channel '{}'", channelId); + Channel channel = ChannelBuilder.create(channelId, getItemType(param.controlType)).withLabel(param.name) + .withType(getChannelType(param)).build(); + thingBuilder.withChannel(channel); + } + } + + updateThing(thingBuilder.build()); + + for (var param : tabmenu.parameterDescriptors) { + var channelId = new ChannelUID(thingId, param.parameterId.toString()); + setState(channelId, WolfSmartsetUtils.undefOrString(param.value)); + } + } + + /** + * Update the values of the channels + * + * @param values {@link GetParameterValuesDTO} representing the new values + */ + public void updateValues(@Nullable GetParameterValuesDTO values) { + var thingId = thing.getUID(); + if (values != null && values.getValues() != null && values.getValues().size() > 0) { + if (!values.getIsNewJobCreated()) { + lastRefreshTime = Instant.now(); + } + + for (var value : values.getValues()) { + var param = paramDescriptionMap.get(value.getValueId()); + if (param != null) { + var channelId = new ChannelUID(thingId, param.parameterId.toString()); + setState(channelId, WolfSmartsetUtils.undefOrString(value.getValue())); + } + } + } + } + + /** + * Stores the state for the channel in stateCache and calls updateState of this Thing + * + * @param channelId {@link ChannelUID} the id of the channel to update + * @param state {@link State} the new state for the channel + */ + private void setState(ChannelUID channelId, State state) { + stateCache.put(channelId.getId(), state); + updateState(channelId, state); + } + + private ChannelTypeUID getChannelType(ParameterDescriptorDTO parmeter) { + if (parmeter.unit == null || parmeter.unit.isBlank()) { + if (parmeter.controlType == null) { + return new ChannelTypeUID(BINDING_ID, CH_STRING); + } else { + switch (parmeter.controlType) { + case 1: + case 3: + case 6: + case 8: + return new ChannelTypeUID(BINDING_ID, CH_NUMBER); + case 5: + return new ChannelTypeUID(BINDING_ID, CH_CONTACT); + case 9: + case 10: + return new ChannelTypeUID(BINDING_ID, CH_DATETIME); + default: + return new ChannelTypeUID(BINDING_ID, CH_STRING); + } + } + } else { + switch (parmeter.unit) { + case "bar": + return new ChannelTypeUID(BINDING_ID, CH_PRESSURE); + case "%": + case "Std": + return new ChannelTypeUID(BINDING_ID, CH_NUMBER); + case "°C": + return new ChannelTypeUID(BINDING_ID, CH_TEMPERATURE); + default: + return new ChannelTypeUID(BINDING_ID, CH_STRING); + } + } + } + + private String getItemType(Integer controlType) { + switch (controlType) { + case 1: + case 3: + case 6: + case 8: + return "Number"; + case 5: + return "Contact"; + case 9: + case 10: + return "DateTime"; + default: + return "String"; + } + } + + private void clearSavedState() { + stateCache.clear(); + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetUtils.java b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetUtils.java new file mode 100644 index 0000000000000..a308069444a09 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/java/org/openhab/binding/wolfsmartset/internal/handler/WolfSmartsetUtils.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2010-2021 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.wolfsmartset.internal.handler; + +import java.time.ZonedDateTime; +import java.util.Date; + +import javax.measure.Unit; +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PointType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The {@link WolfSmartsetUtils} contains utility methods used by the + * thing handler and the bridge handler. + * + * @author Bo Biene - Initial contribution + */ +@NonNullByDefault +public final class WolfSmartsetUtils { + + private static final int UNKNOWN_VALUE = -5002; + + /* + * Checks to see if a bridge is online. + */ + public static boolean isBridgeOnline(@Nullable Bridge bridge) { + boolean bridgeStatus = false; + if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) { + bridgeStatus = true; + } + return bridgeStatus; + } + + /* + * Set the state to the passed value. If value is null, set the state to UNDEF + */ + public static State undefOrOnOff(@Nullable Boolean value) { + return value == null ? UnDefType.UNDEF : (value.booleanValue() ? OnOffType.ON : OnOffType.OFF); + } + + public static State undefOrString(@Nullable String value) { + return value == null ? UnDefType.UNDEF : new StringType(value); + } + + public static State undefOrDecimal(@Nullable Number value) { + return (value == null || isUnknown(value)) ? UnDefType.UNDEF : new DecimalType(value.doubleValue()); + } + + public static State undefOrQuantity(@Nullable Number value, Unit unit) { + return (value == null || isUnknown(value)) ? UnDefType.UNDEF : new QuantityType<>(value, unit); + } + + public static State undefOrTemperature(@Nullable Number value) { + return (value == null || isUnknown(value)) ? UnDefType.UNDEF + : new QuantityType<>(value.doubleValue() / 10.0, ImperialUnits.FAHRENHEIT); + } + + public static State undefOrPoint(@Nullable String value) { + return value == null ? UnDefType.UNDEF : new PointType(value); + } + + public static State undefOrDate(@Nullable Date date, TimeZoneProvider timeZoneProvider) { + return date == null ? UnDefType.UNDEF + : new DateTimeType(ZonedDateTime.ofInstant(date.toInstant(), timeZoneProvider.getTimeZone())); + } + + private static boolean isUnknown(Number value) { + return value.intValue() == UNKNOWN_VALUE; + } + + /* + * Convert a QuantityType to the internal format used by the WolfSmartset API. + */ + @SuppressWarnings("unchecked") + public static Integer convertQuantityTypeToWolfSmartsetTemp(Object value) { + if (value instanceof QuantityType) { + QuantityType convertedTemp = ((QuantityType) value) + .toUnit(ImperialUnits.FAHRENHEIT); + if (convertedTemp != null) { + return Integer.valueOf(convertedTemp.intValue() * 10); + } + } + throw new IllegalArgumentException("temperature is not a QuantityType"); + } +} diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 0000000000000..0c22127c47c7c --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + WolfSmartset Binding + This is the binding for WolfSmartset smart systems. + + diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 0000000000000..33fa85740fd1f --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,47 @@ + + + + + + + + + password + + + + Specifies the refresh interval in minutes + 10 + true + + + + Specifies time in seconds to refresh states + 15 + true + + + + Enable/disable automatic discovery + true + + + + + + + System ID assigned to this system by WolfSmartset + + + + + + + Id assigned to this unit (e.g. rs:101) + + + + diff --git a/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..c24778e554186 --- /dev/null +++ b/bundles/org.openhab.binding.wolfsmartset/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,73 @@ + + + + + + + Represents an account at WolfSmartset + + + + + + + + + + An WolfSmartset system + systemId + + + + + + + + + + An WolfSmartset remote unit + + unitId + + + + + Number + + + + + + Contact + + + + + + Number:Temperature + + + + + + String + + + + + + DateTime + + + + + + Number:Pressure + + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 73f6869e16a80..47e550dd4978e 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -360,6 +360,7 @@ org.openhab.binding.windcentrale org.openhab.binding.wlanthermo org.openhab.binding.wled + org.openhab.binding.wolfsmartset org.openhab.binding.xmltv org.openhab.binding.xmppclient org.openhab.binding.yamahareceiver