/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
--- /dev/null
+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
--- /dev/null
+# 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.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.openhab.addons.bundles</groupId>
+ <artifactId>org.openhab.addons.reactor.bundles</artifactId>
+ <version>3.2.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.openhab.binding.wolfsmartset</artifactId>
+
+ <name>openHAB Add-ons :: Bundles :: WolfSmartset Binding</name>
+
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.wolfsmartset-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
+ <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
+
+ <feature name="openhab-binding-wolfsmartset" description="Wolf Smartset Binding" version="${project.version}">
+ <feature>openhab-runtime-base</feature>
+ <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.wolfsmartset/${project.version}</bundle>
+ </feature>
+</features>
--- /dev/null
+/**
+ * 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<ThingTypeUID> 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<ThingTypeUID> 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<ThingTypeUID> 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<ThingTypeUID> 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<ThingTypeUID> 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<SubMenuEntryWithMenuItemTabView> EMPTY_UNITS = Collections
+ .<SubMenuEntryWithMenuItemTabView> emptyList();
+ public static final List<GetSystemListDTO> EMPTY_SYSTEMS = Collections.<GetSystemListDTO> emptyList();
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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<RequestQueueEntry> 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<GetSystemListDTO> getSystems() {
+ final String response = getSystemString();
+ List<GetSystemListDTO> 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<Long> 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<String, String> params = new HashMap<String, String>();
+ 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<Long> 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<String> requestGET(String url) throws WolfSmartsetCloudException {
+ return requestGET(url, new HashMap<String, String>());
+ }
+
+ private CompletableFuture<String> requestGET(String url, Map<String, String> 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<String, String> 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<String> 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<String> rateLimtedRequest(SupplyRequestFunctionalInterface buildRequest) {
+ // if no delay is set, return a completed CompletableFuture
+ CompletableFuture<String> 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<String> future;
+
+ public RequestQueueEntry(SupplyRequestFunctionalInterface buildRequest, CompletableFuture<String> 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);
+ }
+ }
+ }
+}
--- /dev/null
+/**
+ * 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);
+ }
+}
--- /dev/null
+/**
+ * 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;
+}
--- /dev/null
+/**
+ * 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;
+}
--- /dev/null
+/**
+ * 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;
+}
--- /dev/null
+/**
+ * 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<ThingTypeUID> 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<String, Object> 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();
+ }
+}
--- /dev/null
+/**
+ * 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<ThingTypeUID> 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<String, Object> 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();
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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<MenuItemDTO> menuItems = null;
+ @SerializedName("DynFaultMessageDevices")
+ @Expose
+ private List<Object> dynFaultMessageDevices = null;
+ @SerializedName("SystemHasWRSClassicDevices")
+ @Expose
+ private Boolean systemHasWRSClassicDevices;
+
+ public List<MenuItemDTO> getMenuItems() {
+ return menuItems;
+ }
+
+ public void setMenuItems(List<MenuItemDTO> menuItems) {
+ this.menuItems = menuItems;
+ }
+
+ public List<Object> getDynFaultMessageDevices() {
+ return dynFaultMessageDevices;
+ }
+
+ public void setDynFaultMessageDevices(List<Object> dynFaultMessageDevices) {
+ this.dynFaultMessageDevices = dynFaultMessageDevices;
+ }
+
+ public Boolean getSystemHasWRSClassicDevices() {
+ return systemHasWRSClassicDevices;
+ }
+
+ public void setSystemHasWRSClassicDevices(Boolean systemHasWRSClassicDevices) {
+ this.systemHasWRSClassicDevices = systemHasWRSClassicDevices;
+ }
+}
--- /dev/null
+/**
+ * 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<ValueDTO> values = null;
+ @SerializedName("IsNewJobCreated")
+ @Expose
+ private Boolean isNewJobCreated;
+
+ public String getLastAccess() {
+ return lastAccess;
+ }
+
+ public void setLastAccess(String lastAccess) {
+ this.lastAccess = lastAccess;
+ }
+
+ public List<ValueDTO> getValues() {
+ return values;
+ }
+
+ public void setValues(List<ValueDTO> values) {
+ this.values = values;
+ }
+
+ public Boolean getIsNewJobCreated() {
+ return isNewJobCreated;
+ }
+
+ public void setIsNewJobCreated(Boolean isNewJobCreated) {
+ this.isNewJobCreated = isNewJobCreated;
+ }
+}
--- /dev/null
+/**
+ * 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<Object> 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<Object> getSystemShares() {
+ return systemShares;
+ }
+
+ public void setSystemShares(List<Object> 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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<SubMenuEntryDTO> subMenuEntries = null;
+ @SerializedName("ParameterNode")
+ @Expose
+ private Boolean parameterNode;
+ @SerializedName("ImageName")
+ @Expose
+ private String imageName;
+ @SerializedName("TabViews")
+ @Expose
+ private List<Object> tabViews = null;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<SubMenuEntryDTO> getSubMenuEntries() {
+ return subMenuEntries;
+ }
+
+ public void setSubMenuEntries(List<SubMenuEntryDTO> 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<Object> getTabViews() {
+ return tabViews;
+ }
+
+ public void setTabViews(List<Object> tabViews) {
+ this.tabViews = tabViews;
+ }
+}
--- /dev/null
+/**
+ * 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<ParameterDescriptorDTO> parameterDescriptors;
+
+ @SerializedName("ViewType")
+ @Expose
+ public Long viewType;
+
+ @SerializedName("SvgSchemaDeviceId")
+ @Expose
+ public Long svgSchemaDeviceId;
+}
--- /dev/null
+/**
+ * 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;
+}
--- /dev/null
+/**
+ * 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<CurrentMessageDTO> currentMessages = null;
+ @SerializedName("HistoryMessages")
+ @Expose
+ private List<HistoryMessageDTO> historyMessages = null;
+ @SerializedName("DeviceFaultMessages")
+ @Expose
+ private List<Object> 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<CurrentMessageDTO> getCurrentMessages() {
+ return currentMessages;
+ }
+
+ public void setCurrentMessages(List<CurrentMessageDTO> currentMessages) {
+ this.currentMessages = currentMessages;
+ }
+
+ public List<HistoryMessageDTO> getHistoryMessages() {
+ return historyMessages;
+ }
+
+ public void setHistoryMessages(List<HistoryMessageDTO> historyMessages) {
+ this.historyMessages = historyMessages;
+ }
+
+ public List<Object> getDeviceFaultMessages() {
+ return deviceFaultMessages;
+ }
+
+ public void setDeviceFaultMessages(List<Object> deviceFaultMessages) {
+ this.deviceFaultMessages = deviceFaultMessages;
+ }
+}
--- /dev/null
+/**
+ * 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<Object> subMenuEntries = null;
+ @SerializedName("ParameterNode")
+ @Expose
+ private Boolean parameterNode;
+ @SerializedName("ImageName")
+ @Expose
+ private String imageName;
+ @SerializedName("TabViews")
+ @Expose
+ private List<MenuItemTabViewDTO> tabViews = null;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<Object> getSubMenuEntries() {
+ return subMenuEntries;
+ }
+
+ public void setSubMenuEntries(List<Object> 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<MenuItemTabViewDTO> getTabViews() {
+ return tabViews;
+ }
+
+ public void setTabViews(List<MenuItemTabViewDTO> tabViews) {
+ this.tabViews = tabViews;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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<GetSystemListDTO> cachedSystems = null;
+
+ private final Map<String, WolfSmartsetSystemBridgeHandler> systemHandlers = new ConcurrentHashMap<>();
+ private final Set<String> 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<Class<? extends ThingHandlerService>> 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<GetSystemListDTO> 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");
+ }
+ }
+}
--- /dev/null
+/**
+ * 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<String, WolfSmartsetUnitThingHandler> unitHandlers = new ConcurrentHashMap<>();
+
+ private @Nullable GetSystemListDTO savedSystem;
+ private @Nullable List<SubMenuEntryWithMenuItemTabView> savedUnits;
+ private Map<String, State> 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<Class<? extends ThingHandlerService>> 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<WolfSmartsetUnitThingHandler> 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<SubMenuEntryWithMenuItemTabView> getUnits() {
+ List<SubMenuEntryWithMenuItemTabView> 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<String, String> 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<SubMenuEntryWithMenuItemTabView> 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();
+ }
+}
--- /dev/null
+/**
+ * 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<String, State> stateCache = new ConcurrentHashMap<>();
+ private Map<Long, ParameterDescriptorDTO> 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();
+ }
+}
--- /dev/null
+/**
+ * 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<Temperature> to the internal format used by the WolfSmartset API.
+ */
+ @SuppressWarnings("unchecked")
+ public static Integer convertQuantityTypeToWolfSmartsetTemp(Object value) {
+ if (value instanceof QuantityType<?>) {
+ QuantityType<Temperature> convertedTemp = ((QuantityType<Temperature>) value)
+ .toUnit(ImperialUnits.FAHRENHEIT);
+ if (convertedTemp != null) {
+ return Integer.valueOf(convertedTemp.intValue() * 10);
+ }
+ }
+ throw new IllegalArgumentException("temperature is not a QuantityType");
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<binding:binding id="wolfsmartset" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
+
+ <name>WolfSmartset Binding</name>
+ <description>This is the binding for WolfSmartset smart systems.</description>
+
+</binding:binding>
--- /dev/null
+<config-description:config-descriptions
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
+
+ <config-description uri="thing-type:wolfsmartset:account">
+ <parameter name="username" type="text" required="false">
+ <label>Username</label>
+ </parameter>
+ <parameter name="password" type="text" required="false">
+ <label>Password</label>
+ <context>password</context>
+ </parameter>
+ <parameter name="refreshIntervalStructure" type="integer" min="2" required="false" unit="m">
+ <label>Structure Refresh Interval</label>
+ <description>Specifies the refresh interval in minutes</description>
+ <default>10</default>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="refreshIntervalValues" type="integer" min="2" required="false" unit="s">
+ <label>States Refresh Interval</label>
+ <description>Specifies time in seconds to refresh states</description>
+ <default>15</default>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="discoveryEnabled" type="boolean" required="false">
+ <label>Background Discovery</label>
+ <description>Enable/disable automatic discovery</description>
+ <default>true</default>
+ </parameter>
+ </config-description>
+
+ <config-description uri="thing-type:wolfsmartset:system">
+ <parameter name="systemId" type="text" required="true">
+ <label>System ID</label>
+ <description>System ID assigned to this system by WolfSmartset</description>
+ </parameter>
+ </config-description>
+
+ <config-description uri="thing-type:wolfsmartset:unit">
+ <parameter name="unitId" type="text" required="true">
+ <label>Unit Id</label>
+ <description>Id assigned to this unit (e.g. rs:101)</description>
+ </parameter>
+ </config-description>
+
+</config-description:config-descriptions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="wolfsmartset"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <!-- THING TYPE ACCOUNT -->
+ <bridge-type id="account">
+ <label>WolfSmartset Account</label>
+ <description>Represents an account at WolfSmartset</description>
+ <config-description-ref uri="thing-type:wolfsmartset:account"/>
+ </bridge-type>
+
+ <!-- THING TYPE SYSTEM -->
+ <bridge-type id="system">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="account"/>
+ </supported-bridge-type-refs>
+ <label>WolfSmartset System</label>
+ <description>An WolfSmartset system</description>
+ <representation-property>systemId</representation-property>
+ <config-description-ref uri="thing-type:wolfsmartset:system"/>
+ </bridge-type>
+
+ <!-- THING TYPE UNIT -->
+ <thing-type id="unit">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="system"/>
+ </supported-bridge-type-refs>
+ <label>WolfSmartset Unit</label>
+ <description>An WolfSmartset remote unit</description>
+
+ <representation-property>unitId</representation-property>
+ <config-description-ref uri="thing-type:wolfsmartset:unit"/>
+ </thing-type>
+
+ <channel-type id="number">
+ <item-type>Number</item-type>
+ <label>Number</label>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="contact">
+ <item-type>Contact</item-type>
+ <label>Contact</label>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="temperature">
+ <item-type>Number:Temperature</item-type>
+ <label>Temperature</label>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="string">
+ <item-type>String</item-type>
+ <label>String</label>
+ <state readOnly="true" pattern="%s"/>
+ </channel-type>
+
+ <channel-type id="datetime">
+ <item-type>DateTime</item-type>
+ <label>Date Time</label>
+ <state readOnly="true"/>
+ </channel-type>
+
+ <channel-type id="barometric-pressure">
+ <item-type>Number:Pressure</item-type>
+ <label>Pressure</label>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+</thing:thing-descriptions>
<module>org.openhab.binding.windcentrale</module>
<module>org.openhab.binding.wlanthermo</module>
<module>org.openhab.binding.wled</module>
+ <module>org.openhab.binding.wolfsmartset</module>
<module>org.openhab.binding.xmltv</module>
<module>org.openhab.binding.xmppclient</module>
<module>org.openhab.binding.yamahareceiver</module>