[wolfsmartset] Initial contribution (#10751)

Signed-off-by: Bo Biene <openhab.github@biene.eu>
This commit is contained in:
Bo Biene 2021-09-25 21:07:54 +02:00 committed by GitHub
parent 277c65f43a
commit 8440745ceb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 3882 additions and 0 deletions

View File

@ -326,6 +326,7 @@
/bundles/org.openhab.binding.windcentrale/ @marcelrv
/bundles/org.openhab.binding.wlanthermo/ @CSchlipp
/bundles/org.openhab.binding.wled/ @Skinah
/bundles/org.openhab.binding.wolfsmartset/ @BoBiene
/bundles/org.openhab.binding.xmltv/ @clinique
/bundles/org.openhab.binding.xmppclient/ @pavel-gololobov
/bundles/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz

View File

@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@ -0,0 +1,149 @@
# Wolf Smartset Binding
This binding communicates with the www.wolf-smartset.de API and provides values readonly.
Wolf systems are connected with official gateways (Wolf Link Home or Wolf Link Pro) https://www.wolf.eu/produkte/smarthome/
## Supported Things
- Account (``thing-type:wolfsmartset:account``)
* holding the credentials to connect to the wolf-smartset online portal.
- System (``thing-type:wolfsmartset:system``)
* represents one wolf system connected to the wolf-smartset online portal.
- Unit (``thing-type:wolfsmartset:unit``)
* unit is a part of the system with values and parameter
## Discovery
- System things (bridge) are discovered after Account thing (bridge) is set up
- Unit things are discovered after System things are set up
## Thing Configuration
### Account (bridge)
The account thing holds the credentials to connect to the wolf-smartset online portal.
| Parameter | Type | Defaut | Description |
|-----------------|---------|----------|---------------------------------------------------------------------|
| username | text | | username to authenticate to www.wolf-smartset.de |
| password | text | | password to authenticate to www.wolf-smartset.de |
| refreshIntervalStructure | integer | 10 | Specifies the refresh interval to refresh the Structure in minutes |
| refreshIntervalValues | integer | 15 | Specifies time in seconds to refresh values |
| discoveryEnabled | boolean | true | disable the Thing discovery |
### System (bridge)
The system thing represents one wolf system connected via a WOLF Link home or a WOLF Link pro to the wolf-smartset online portal.
You have access to your own or to shared systems.
| Parameter | Type | Defaut | Description |
|-----------------|---------|----------|---------------------------------------------------------------------|
| systemId | integer | | System ID assigned to the system by WolfSmartset |
### Unit
A system is divided into different units.
In the wolf-smartset portal, the system has an "Expert" section, each submenu item within the "Expert" section has multiple tabs.
Each of these tabs is treated as one unit.
| Parameter | Type | Defaut | Description |
|-----------------|---------|----------|---------------------------------------------------------------------|
| unitId | integer | | The BundleId assigned to the unit by WolfSmartset |
## Tested WOLF-Devices
| WOLF Equipment | openhab Version | Used gateway |
|-------------------|-----------------|---------------|
| CSZ (CGB and SM1) | 3.1 | WOLF Link Pro |
| CGB-2 | 3.1 | WOLF Link home|
## Channels
| channel | type | description |
|----------|--------|------------------------------|
| number | Number | a generic number |
| contact | Contact | a generic contact |
| temperature | Number:Temperature | a generic temperature |
| string | String | a generic String |
| datetime | DateTime | a generic DateTime |
## Full Example
### Things
````
Bridge wolfsmartset:account:account "Wolf Smartset Account" [ username="User", password="Password" ] {
Bridge system 32122305166 "WolfSmartset System CSZ" [ systemId="32122305166" ] {
Thing unitId uinit0 "CSZ Heizgerät" [ unitId="unit0" ] {
}
}
}
````
_You need to use the corrosponding systemId and unitId returned by the discovery_
### Items
````
"Number CSZHeizgerat_Raumtemperatur "Raumtemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900000"}
Number CSZHeizgerat_Flamme "Flamme" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900001"}
Number CSZHeizgerat_AnalogeFernbedienung "Analoge Fernbedienung" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900002"}
Number CSZHeizgerat_Raumsolltemperatur "Raumsolltemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900003"}
Number CSZHeizgerat_AusgangA1 "Ausgang A1" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900004"}
String CSZHeizgerat_ZeitprogrammdirekterHeizkreis "Zeitprogramm direkter Heizkreis" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900005"}
Number CSZHeizgerat_Ventil1 "Ventil 1" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900006"}
Number CSZHeizgerat_Ventil2 "Ventil 2" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900007"}
Number CSZHeizgerat_WiSoUmschaltung "Wi/So Umschaltung" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900008"}
Number CSZHeizgerat_Tagtemperatur "Tagtemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:1000900009"}
Number CSZHeizgerat_PWMPumpe "PWM Pumpe" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000010"}
Number CSZHeizgerat_Speichersolltemperatur "Speichersolltemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000011"}
Number CSZHeizgerat_Heizkurve "Heizkurve" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000012"}
Number CSZHeizgerat_Raumeinfluss "Raumeinfluss" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000013"}
Number CSZHeizgerat_TWVorlauf "TW-Vorlauf" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000014"}
Number CSZHeizgerat_Spartemperatur "Spartemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000015"}
Number CSZHeizgerat_Geblase "Gebläse" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000016"}
Number CSZHeizgerat_Vorlaufsolltemperatur "Vorlaufsolltemperatur" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000017"}
Group CSZHeizgerat "CSZ Heizgerät" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000018"}
Number CSZHeizgerat_ECOABS "ECO/ABS" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000019"}
Number CSZHeizgerat_Netzbetriebstunden "Netzbetriebstunden" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000020"}
Number CSZHeizgerat_TWAbgas "TW-Abgas" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000021"}
Number CSZHeizgerat_HGStatus "HG Status" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000022"}
Number CSZHeizgerat_EingangE1 "Eingang E1" { channel="wolfsmartset:unit:account:32122305166:uinit0:10009000023"}"
````
## Supported Heating-Devices
All devices able to be connected to www.wolf-smartset.de
### Related Documentation from WOLF
https://www.wolf.eu/fileadmin/Wolf_Daten/Dokumente/FAQ/3065655_201711.pdf
| Heating system | WOLF Link home | WOLF Link pro |
|-------------------------------------------|-----------------------|--------------------|
| Gas condensing boiler CGB-2, CGW-2, CGS-2 | ✅ | ✅ |
| Oil condensing boiler TOB | ✅ | ✅ |
| MGK-2 gas condensing boiler | ✅ | ✅ |
| split air/water heat pump BWL-1S | ✅ | ✅ |
| Oil condensing boiler COB | | ✅ |
| gas condensing boiler MGK | | ✅ |
| Gas condensing boilers CGB, CGW, CGS, FGB | | ✅ |
| Gas condensing boilers CGG-2, CGU-2 | | ✅ |
| Boiler controls R2, R3, R21 | | ✅ |
| Monobloc heat pumps BWW-1, BWL-1, BWS-1 | | ✅ |
| mixer module MM, MM-2 | ⬜ | ✅ |
| cascade module KM, KM-2 | ⬜ | ✅ |
| solar modules SM1, SM1-2, SM-2, SM2-2 | ⬜ | ✅ |
| Comfort apartment ventilation CWL Excellent | ⬜ | ✅ |
| Air handling units KG Top, CKL Pool``*`` | | ✅ |
| Air handling units CKL, CFL, CRL``*`` | | ✅ |
| Combined heat and power units | | ✅ |
Note:
⬜ possible in connection with a WOLF Link home compatible heater,
full functionality only for devices with current software version.
``*`` Modbus interface required in the device,
Special programming cannot be mapped.

View File

@ -0,0 +1,17 @@
<?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>

View File

@ -0,0 +1,9 @@
<?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>

View File

@ -0,0 +1,91 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryWithMenuItemTabView;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link WolfSmartsetBindingConstants} class defines common constants that are
* used across the whole binding.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetBindingConstants {
public static final String BINDING_ID = "wolfsmartset";
// Account bridge
public static final String THING_TYPE_ACCOUNT = "account";
public static final ThingTypeUID UID_ACCOUNT_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_ACCOUNT);
public static final Set<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();
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal;
import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetAccountBridgeHandler;
import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetSystemBridgeHandler;
import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetUnitThingHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link WolfSmartsetHandlerFactory} is responsible for creating thing handlers
* for the account bridge, system bridge, and unit thing.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.wolfsmartset", service = ThingHandlerFactory.class)
public class WolfSmartsetHandlerFactory extends BaseThingHandlerFactory {
private final HttpClient httpClient;
@Activate
public WolfSmartsetHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SUPPORTED_ACCOUNT_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new WolfSmartsetAccountBridgeHandler((Bridge) thing, httpClient);
}
if (SUPPORTED_SYSTEM_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new WolfSmartsetSystemBridgeHandler((Bridge) thing);
}
if (SUPPORTED_UNIT_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new WolfSmartsetUnitThingHandler(thing);
}
return null;
}
}

View File

@ -0,0 +1,610 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.api;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.wolfsmartset.internal.dto.CreateSession2DTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetParameterValuesDTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemStateListDTO;
import org.openhab.binding.wolfsmartset.internal.dto.LoginResponseDTO;
import org.openhab.binding.wolfsmartset.internal.dto.ReadFaultMessagesDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
/**
* The {@link WolfSmartsetCloudConnector} class is used for connecting to the Wolf Smartset cloud service
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetApi {
private static final int MAX_QUEUE_SIZE = 1000; // maximum queue size
private static final int REQUEST_TIMEOUT_SECONDS = 10;
private static final DateTimeFormatter SESSION_TIME_STAMP = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final String WOLF_API_URL = "https://www.wolf-smartset.com/portal/";
private Instant blockRequestsUntil = Instant.now();
private String username;
private String password;
private String serviceToken = "";
private @Nullable CreateSession2DTO session = null;
private int loginFailedCounter = 0;
private HttpClient httpClient;
private int delay = 500; // in ms
private final ScheduledExecutorService scheduler;
private final Gson gson = new GsonBuilder().serializeNulls().create();
private final LinkedBlockingQueue<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);
}
}
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Will be thrown for cloud errors
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetCloudException extends Exception {
/**
* required variable to avoid IncorrectMultilineIndexException warning
*/
private static final Long serialVersionUID = -1280858607995252321L;
public WolfSmartsetCloudException() {
super();
}
public WolfSmartsetCloudException(@Nullable String message) {
super(message);
}
public WolfSmartsetCloudException(@Nullable String message, @Nullable Exception e) {
super(message, e);
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link WolfSmartsetAccountConfiguration} class contains fields mapping
* to the account thing configuration parameters.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetAccountConfiguration {
public @Nullable String username = "";
public @Nullable String password = "";
/**
* Time in seconds between information refresh
*/
public @Nullable Integer refreshIntervalStructure;
/**
* Time in seconds to wait after successful update, command or action before refresh
*/
public @Nullable Integer refreshIntervalValues;
/*
* Enable/disable automatic discovery
*/
public @Nullable Boolean discoveryEnabled;
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link WolfSmartsetSystemConfiguration} class contains fields mapping
* to the system thing configuration parameters.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetSystemConfiguration {
/**
* System ID assigned by WolfSmartset
*/
public @Nullable String systemId;
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link WolfSmartsetUnitConfiguration} class contains fields mapping
* to the unit thing configuration parameters.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetUnitConfiguration {
/**
* Id of remote unit
*/
public @Nullable String unitId;
}

View File

@ -0,0 +1,154 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.discovery;
import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetAccountBridgeHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WolfSmartsetAccountDiscoveryService} is responsible for discovering the WolfSmartset
* systems that are associated with the WolfSmartset Account
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetAccountDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(WolfSmartsetAccountDiscoveryService.class);
private @NonNullByDefault({}) WolfSmartsetAccountBridgeHandler bridgeHandler;
private @Nullable Future<?> discoveryJob;
public WolfSmartsetAccountDiscoveryService() {
super(SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS, 8, true);
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof WolfSmartsetAccountBridgeHandler) {
this.bridgeHandler = (WolfSmartsetAccountBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void activate() {
super.activate(null);
}
@Override
public void deactivate() {
super.deactivate();
}
@Override
public Set<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();
}
}

View File

@ -0,0 +1,161 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.discovery;
import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryWithMenuItemTabView;
import org.openhab.binding.wolfsmartset.internal.handler.WolfSmartsetSystemBridgeHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WolfSmartsetAccountDiscoveryService} is responsible for discovering the WolfSmartset Units
* that are associated with the WolfSmartset System
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetSystemDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(WolfSmartsetSystemDiscoveryService.class);
private @NonNullByDefault({}) WolfSmartsetSystemBridgeHandler bridgeHandler;
private @Nullable Future<?> discoveryJob;
public WolfSmartsetSystemDiscoveryService() {
super(SUPPORTED_SYSTEM_AND_UNIT_THING_TYPES_UIDS, 8, true);
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof WolfSmartsetSystemBridgeHandler) {
this.bridgeHandler = (WolfSmartsetSystemBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void activate() {
super.activate(null);
}
@Override
public void deactivate() {
super.deactivate();
}
@Override
public Set<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();
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class CreateSession2DTO {
@SerializedName("CultureInfoCode")
@Expose
private String cultureInfoCode;
@SerializedName("IsPasswordReset")
@Expose
private Boolean isPasswordReset;
@SerializedName("IsProfessional")
@Expose
private Boolean isProfessional;
@SerializedName("IsProfessionalPasswordReset")
@Expose
private Boolean isProfessionalPasswordReset;
@SerializedName("BrowserSessionId")
@Expose
private Integer browserSessionId;
public String getCultureInfoCode() {
return cultureInfoCode;
}
public void setCultureInfoCode(String cultureInfoCode) {
this.cultureInfoCode = cultureInfoCode;
}
public Boolean getIsPasswordReset() {
return isPasswordReset;
}
public void setIsPasswordReset(Boolean isPasswordReset) {
this.isPasswordReset = isPasswordReset;
}
public Boolean getIsProfessional() {
return isProfessional;
}
public void setIsProfessional(Boolean isProfessional) {
this.isProfessional = isProfessional;
}
public Boolean getIsProfessionalPasswordReset() {
return isProfessionalPasswordReset;
}
public void setIsProfessionalPasswordReset(Boolean isProfessionalPasswordReset) {
this.isProfessionalPasswordReset = isProfessionalPasswordReset;
}
public Integer getBrowserSessionId() {
return browserSessionId;
}
public void setBrowserSessionId(Integer browserSessionId) {
this.browserSessionId = browserSessionId;
}
}

View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class CurrentMessageDTO {
@SerializedName("Id")
@Expose
private Integer id;
@SerializedName("ErrorCode")
@Expose
private Integer errorCode;
@SerializedName("Description")
@Expose
private String description;
@SerializedName("OccurTimeLocal")
@Expose
private String occurTimeLocal;
@SerializedName("Active")
@Expose
private Boolean active;
@SerializedName("Index")
@Expose
private Integer index;
@SerializedName("Device")
@Expose
private String device;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getOccurTimeLocal() {
return occurTimeLocal;
}
public void setOccurTimeLocal(String occurTimeLocal) {
this.occurTimeLocal = occurTimeLocal;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
}

View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class GatewayStateDTO {
@SerializedName("GatewayId")
@Expose
private Integer gatewayId;
@SerializedName("IsOnline")
@Expose
private Boolean isOnline;
@SerializedName("GatewayOfflineCause")
@Expose
private Integer gatewayOfflineCause;
@SerializedName("IsLocked")
@Expose
private Boolean isLocked;
@SerializedName("IsDeleted")
@Expose
private Boolean isDeleted;
@SerializedName("IsBusy")
@Expose
private Boolean isBusy;
@SerializedName("ImageName")
@Expose
private String imageName;
public Integer getGatewayId() {
return gatewayId;
}
public void setGatewayId(Integer gatewayId) {
this.gatewayId = gatewayId;
}
public Boolean getIsOnline() {
return isOnline;
}
public void setIsOnline(Boolean isOnline) {
this.isOnline = isOnline;
}
public Integer getGatewayOfflineCause() {
return gatewayOfflineCause;
}
public void setGatewayOfflineCause(Integer gatewayOfflineCause) {
this.gatewayOfflineCause = gatewayOfflineCause;
}
public Boolean getIsLocked() {
return isLocked;
}
public void setIsLocked(Boolean isLocked) {
this.isLocked = isLocked;
}
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
public Boolean getIsBusy() {
return isBusy;
}
public void setIsBusy(Boolean isBusy) {
this.isBusy = isBusy;
}
public String getImageName() {
return imageName;
}
public void setImageName(String imageName) {
this.imageName = imageName;
}
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class GetGuiDescriptionForGatewayDTO {
@SerializedName("MenuItems")
@Expose
private List<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;
}
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class GetParameterValuesDTO {
@SerializedName("LastAccess")
@Expose
private String lastAccess;
@SerializedName("Values")
@Expose
private List<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;
}
}

View File

@ -0,0 +1,186 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import javax.annotation.Generated;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class GetSystemListDTO {
@SerializedName("Id")
@Expose
private Integer id;
@SerializedName("GatewayId")
@Expose
private Integer gatewayId;
@SerializedName("IsForeignSystem")
@Expose
private Boolean isForeignSystem;
@SerializedName("AccessLevel")
@Expose
private Integer accessLevel;
@SerializedName("GatewayUsername")
@Expose
private String gatewayUsername;
@SerializedName("Name")
@Expose
private String name;
@SerializedName("SystemShares")
@Expose
private List<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;
}
}

View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class GetSystemStateListDTO {
@SerializedName("SystemId")
@Expose
private Integer systemId;
@SerializedName("GatewayState")
@Expose
private GatewayStateDTO gatewayState;
@SerializedName("AccessLevel")
@Expose
private Integer accessLevel;
@SerializedName("IsSystemShareDeleted")
@Expose
private Boolean isSystemShareDeleted;
@SerializedName("IsSystemShareRejected")
@Expose
private Boolean isSystemShareRejected;
@SerializedName("IsSystemDeleted")
@Expose
private Boolean isSystemDeleted;
@SerializedName("FirstActiveAlertCode")
@Expose
private Integer firstActiveAlertCode;
public Integer getSystemId() {
return systemId;
}
public void setSystemId(Integer systemId) {
this.systemId = systemId;
}
public GatewayStateDTO getGatewayState() {
return gatewayState;
}
public void setGatewayState(GatewayStateDTO gatewayState) {
this.gatewayState = gatewayState;
}
public Integer getAccessLevel() {
return accessLevel;
}
public void setAccessLevel(Integer accessLevel) {
this.accessLevel = accessLevel;
}
public Boolean getIsSystemShareDeleted() {
return isSystemShareDeleted;
}
public void setIsSystemShareDeleted(Boolean isSystemShareDeleted) {
this.isSystemShareDeleted = isSystemShareDeleted;
}
public Boolean getIsSystemShareRejected() {
return isSystemShareRejected;
}
public void setIsSystemShareRejected(Boolean isSystemShareRejected) {
this.isSystemShareRejected = isSystemShareRejected;
}
public Boolean getIsSystemDeleted() {
return isSystemDeleted;
}
public void setIsSystemDeleted(Boolean isSystemDeleted) {
this.isSystemDeleted = isSystemDeleted;
}
public Integer getFirstActiveAlertCode() {
return firstActiveAlertCode;
}
public void setFirstActiveAlertCode(Integer firstActiveAlertCode) {
this.firstActiveAlertCode = firstActiveAlertCode;
}
}

View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class HistoryMessageDTO {
@SerializedName("Id")
@Expose
private Integer id;
@SerializedName("ErrorCode")
@Expose
private Integer errorCode;
@SerializedName("Description")
@Expose
private String description;
@SerializedName("OccurTimeLocal")
@Expose
private String occurTimeLocal;
@SerializedName("Active")
@Expose
private Boolean active;
@SerializedName("Index")
@Expose
private Integer index;
@SerializedName("Device")
@Expose
private String device;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getOccurTimeLocal() {
return occurTimeLocal;
}
public void setOccurTimeLocal(String occurTimeLocal) {
this.occurTimeLocal = occurTimeLocal;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class LoginResponseDTO {
@SerializedName("access_token")
@Expose
private String accessToken;
@SerializedName("expires_in")
@Expose
private Integer expiresIn;
@SerializedName("token_type")
@Expose
private String tokenType;
@SerializedName("refresh_token")
@Expose
private String refreshToken;
@SerializedName("scope")
@Expose
private String scope;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public Integer getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Integer expiresIn) {
this.expiresIn = expiresIn;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}

View File

@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class MenuItemDTO {
@SerializedName("Name")
@Expose
private String name;
@SerializedName("SubMenuEntries")
@Expose
private List<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;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
public class MenuItemTabViewDTO {
@SerializedName("IsExpertView")
@Expose
public Boolean isExpertView;
@SerializedName("TabName")
@Expose
public String tabName;
@SerializedName("GuiId")
@Expose
public Long guiId;
@SerializedName("BundleId")
@Expose
public Long bundleId;
@SerializedName("ParameterDescriptors")
@Expose
public List<ParameterDescriptorDTO> parameterDescriptors;
@SerializedName("ViewType")
@Expose
public Long viewType;
@SerializedName("SvgSchemaDeviceId")
@Expose
public Long svgSchemaDeviceId;
}

View File

@ -0,0 +1,115 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
public class ParameterDescriptorDTO {
@SerializedName("ValueId")
@Expose
public Long valueId;
@SerializedName("SortId")
@Expose
public Long sortId;
@SerializedName("SubBundleId")
@Expose
public Long subBundleId;
@SerializedName("ParameterId")
@Expose
public Long parameterId;
@SerializedName("IsReadOnly")
@Expose
public Boolean isReadOnly;
@SerializedName("NoDataPoint")
@Expose
public Boolean noDataPoint;
@SerializedName("IsExpertProtectable")
@Expose
public Boolean isExpertProtectable;
@SerializedName("Name")
@Expose
public String name;
@SerializedName("Group")
@Expose
public String group;
@SerializedName("ControlType")
@Expose
public Integer controlType;
@SerializedName("Value")
@Expose
public String value;
@SerializedName("ValueState")
@Expose
public Long valueState;
@SerializedName("HasDependentParameter")
@Expose
public Boolean hasDependentParameter;
@SerializedName("ProtGrp")
@Expose
public String protGrp;
@SerializedName("Unit")
@Expose
public String unit;
@SerializedName("Decimals")
@Expose
public Long decimals;
@SerializedName("MinValueCondition")
@Expose
public String minValueCondition;
@SerializedName("MaxValueCondition")
@Expose
public String maxValueCondition;
@SerializedName("MinValue")
@Expose
public Long minValue;
@SerializedName("MaxValue")
@Expose
public Long maxValue;
@SerializedName("StepWidth")
@Expose
public double stepWidth;
@SerializedName("NamePrefix")
@Expose
public String namePrefix;
@SerializedName("TurnOffValue")
@Expose
public Long turnOffValue;
}

View File

@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class ReadFaultMessagesDTO {
@SerializedName("MessagesChanged")
@Expose
private Boolean messagesChanged;
@SerializedName("LastRead")
@Expose
private String lastRead;
@SerializedName("CurrentMessages")
@Expose
private List<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;
}
}

View File

@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class SubMenuEntryDTO {
@SerializedName("Name")
@Expose
private String name;
@SerializedName("SubMenuEntries")
@Expose
private List<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;
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
/**
* Link the SubMenuEntryDTO with the MenuItemTabViewDTO
*
* @author Bo Biene - Initial contribution
*/
public class SubMenuEntryWithMenuItemTabView {
public SubMenuEntryDTO subMenuEntryDTO;
public MenuItemTabViewDTO menuItemTabViewDTO;
public SubMenuEntryWithMenuItemTabView(SubMenuEntryDTO subMenuEntryDTO, MenuItemTabViewDTO menuItemTabViewDTO) {
this.subMenuEntryDTO = subMenuEntryDTO;
this.menuItemTabViewDTO = menuItemTabViewDTO;
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.dto;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* generated with https://www.jsonschema2pojo.org/
*
* @author Bo Biene - Initial contribution
*/
@Generated("jsonschema2pojo")
public class ValueDTO {
@SerializedName("ValueId")
@Expose
private Long valueId;
@SerializedName("Value")
@Expose
private String value;
@SerializedName("State")
@Expose
private Integer state;
public Long getValueId() {
return valueId;
}
public void setValueId(Long valueId) {
this.valueId = valueId;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
}

View File

@ -0,0 +1,276 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.handler;
import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.CONFIG_SYSTEM_ID;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.wolfsmartset.internal.api.WolfSmartsetApi;
import org.openhab.binding.wolfsmartset.internal.api.WolfSmartsetCloudException;
import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetAccountConfiguration;
import org.openhab.binding.wolfsmartset.internal.discovery.WolfSmartsetAccountDiscoveryService;
import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WolfSmartsetAccountBridgeHandler} is responsible for managing
* communication with the WolfSmartset API.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetAccountBridgeHandler extends BaseBridgeHandler {
private static final int REFRESH_STARTUP_DELAY_SECONDS = 3;
private static final int REFRESH_INTERVAL_SECONDS = 1;
private static final int DEFAULT_REFRESH_INTERVAL_CONFIGURATION_MINUTES = 10;
private static final int DEFAULT_REFRESH_INTERVAL_VALUES_SECONDS = 15;
private final Logger logger = LoggerFactory.getLogger(WolfSmartsetAccountBridgeHandler.class);
private final HttpClient httpClient;
private @NonNullByDefault({}) WolfSmartsetApi api;
private int refreshIntervalStructureMinutes;
private int refreshIntervalValuesSeconds;
private boolean discoveryEnabled;
private @Nullable List<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");
}
}
}

View File

@ -0,0 +1,268 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.handler;
import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetSystemConfiguration;
import org.openhab.binding.wolfsmartset.internal.discovery.WolfSmartsetSystemDiscoveryService;
import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
import org.openhab.binding.wolfsmartset.internal.dto.GetSystemStateListDTO;
import org.openhab.binding.wolfsmartset.internal.dto.ReadFaultMessagesDTO;
import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryWithMenuItemTabView;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WolfSmartsetSystemBridgeHandler} is the handler for an WolfSmartset system.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetSystemBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(WolfSmartsetSystemBridgeHandler.class);
private @NonNullByDefault({}) String systemId;
private final Map<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();
}
}

View File

@ -0,0 +1,268 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.handler;
import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.*;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetUnitConfiguration;
import org.openhab.binding.wolfsmartset.internal.dto.GetParameterValuesDTO;
import org.openhab.binding.wolfsmartset.internal.dto.MenuItemTabViewDTO;
import org.openhab.binding.wolfsmartset.internal.dto.ParameterDescriptorDTO;
import org.openhab.binding.wolfsmartset.internal.dto.SubMenuEntryDTO;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WolfSmartsetUnitThingHandler} is responsible for updating the channels associated
* with an WolfSmartset unit.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public class WolfSmartsetUnitThingHandler extends BaseThingHandler {
public static final String CAPABILITY_ADC = "adc";
public static final String CAPABILITY_CO2 = "co2";
public static final String CAPABILITY_DRY_CONTACT = "dryContact";
public static final String CAPABILITY_HUMIDITY = "humidity";
public static final String CAPABILITY_OCCUPANCY = "occupancy";
public static final String CAPABILITY_TEMPERATURE = "temperature";
public static final String CAPABILITY_UNKNOWN = "unknown";
private final Logger logger = LoggerFactory.getLogger(WolfSmartsetUnitThingHandler.class);
private @NonNullByDefault({}) String unitId;
private @Nullable Instant lastRefreshTime;
private Map<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();
}
}

View File

@ -0,0 +1,109 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wolfsmartset.internal.handler;
import java.time.ZonedDateTime;
import java.util.Date;
import javax.measure.Unit;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* The {@link WolfSmartsetUtils} contains utility methods used by the
* thing handler and the bridge handler.
*
* @author Bo Biene - Initial contribution
*/
@NonNullByDefault
public final class WolfSmartsetUtils {
private static final int UNKNOWN_VALUE = -5002;
/*
* Checks to see if a bridge is online.
*/
public static boolean isBridgeOnline(@Nullable Bridge bridge) {
boolean bridgeStatus = false;
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
bridgeStatus = true;
}
return bridgeStatus;
}
/*
* Set the state to the passed value. If value is null, set the state to UNDEF
*/
public static State undefOrOnOff(@Nullable Boolean value) {
return value == null ? UnDefType.UNDEF : (value.booleanValue() ? OnOffType.ON : OnOffType.OFF);
}
public static State undefOrString(@Nullable String value) {
return value == null ? UnDefType.UNDEF : new StringType(value);
}
public static State undefOrDecimal(@Nullable Number value) {
return (value == null || isUnknown(value)) ? UnDefType.UNDEF : new DecimalType(value.doubleValue());
}
public static State undefOrQuantity(@Nullable Number value, Unit<?> unit) {
return (value == null || isUnknown(value)) ? UnDefType.UNDEF : new QuantityType<>(value, unit);
}
public static State undefOrTemperature(@Nullable Number value) {
return (value == null || isUnknown(value)) ? UnDefType.UNDEF
: new QuantityType<>(value.doubleValue() / 10.0, ImperialUnits.FAHRENHEIT);
}
public static State undefOrPoint(@Nullable String value) {
return value == null ? UnDefType.UNDEF : new PointType(value);
}
public static State undefOrDate(@Nullable Date date, TimeZoneProvider timeZoneProvider) {
return date == null ? UnDefType.UNDEF
: new DateTimeType(ZonedDateTime.ofInstant(date.toInstant(), timeZoneProvider.getTimeZone()));
}
private static boolean isUnknown(Number value) {
return value.intValue() == UNKNOWN_VALUE;
}
/*
* Convert a QuantityType<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");
}
}

View File

@ -0,0 +1,9 @@
<?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>

View File

@ -0,0 +1,47 @@
<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>

View File

@ -0,0 +1,73 @@
<?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>

View File

@ -360,6 +360,7 @@
<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>