added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.bsblan</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

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/openhab2-addons

View File

@@ -0,0 +1,87 @@
# BSB-LAN Binding
This binding uses the REST API of [BSB-LPB-PPS-LAN](https://github.com/fredlcore/bsb_lan) to obtain data from the device.
## Supported Things
Currently the binding supports the following thing types:
| Thing Type | Description |
|-------------|----------------------------------------------------------------|
| bridge | Represents the BSB-LAN device. |
| parameter | Represents a single parameter available at the BSB-LAN device. |
## Discovery
There is no discovery implemented. You have to create your Things manually and specify the hostname/IP of the BSB-LAN device in the Bridge.
## Binding Configuration
The binding has no configuration options, all configuration is done at Thing level.
## Thing Configuration
### Bridge Thing Configuration
| Property | Default | Required | Type | Description |
|------------------|---------|----------|---------|--------------------------------------------------------------------------------------------|
| host | - | Yes | String | The hostname or IP address of the BSB-LAN device. |
| port | 80 | No | Integer | The port where the BSB-LAN device is listening. |
| passkey | - | No | String | The passkey required to access the BSB-LAN device. |
| username | - | No | String | The username required to access the BSB-LAN device (when using HTTP Basic Authentication). |
| password | - | No | String | The password required to access the BSB-LAN device (when using HTTP Basic Authentication). |
| refreshInterval | 60 | No | Integer | Specifies the refresh (poll) interval in seconds. Minimum value: 5s |
### Parameter Thing Configuration
| Property | Default | Required | Type | Description |
|-----------|---------|----------|---------|------------------------------------------------------------------------------------------|
| id | - | Yes | Integer | Specific parameter identifier (numeric value) |
| setId | value of `id` | No | Integer | Parameter identifier used for set requests (numeric value).<br />If not specified it falls back to the value of the `id` property. |
| setType | `SET` | No | String | Message type used for set requests. Possible values are: `INF` or `SET`.<br />If not specified or unknown it falls back to `SET`. |
Note: If you would also like to use the binding to set parameter values, ensure you have flashed the BSB-LAN adapter in write mode (see your `BSB_lan_config.h`)
## Channels
### Parameter Thing Channels
| Channel ID | Item Type | Description |
|--------------|-----------|------------------------------------------------------------------------------------|
| name | String | Name of the parameter as provided by the BSB-LAN device. |
| number-value | Number | Value of the parameter converted to a numerical value (if possible).<br />The value is published as `DecimalType(int)` for values of `datatype` `DT_ENUM` and `DecimalType(double)` otherwise. |
| string-value | String | Value of the parameter as provided by the BSB-LAN device. |
| switch-value | Switch | Value of the parameter.<br />`0` is interpreted as `OFF`, everything else as `ON`. |
| unit | String | Unit as provided by the BSB-LAN device (HTML unescaping applied). |
| description | String | Description as provided by the BSB-LAN device. |
| datatype | Number | Datatype as provided by the BSB-LAN device. Possible values are currently<br />`0` for `DT_VALS`: plain value<br />`1` for `DT_ENUM`: value (8/16 Bit) followed by space followed by text<br />`2` for `DT_BITS`: bit value followed by bitmask followed by text<br />`3` for `DT_WDAY`: weekday<br />`4` for `DT_HHMM`: hour:minute<br />`5` for `DT_DTTM`: date and time<br />`6` for `DT_DDMM`: day and month<br />`7` for `DT_STRN`: String<br />`8` for `DT_DWHM`: PPS time (day of week, hour:minute) |
## Full Example
bsblan.things:
```
Bridge bsblan:bridge:heating [host="192.168.1.100", refreshInterval=30, username="atari", password="800xl"] {
Thing parameter p700 [id=700]
Thing parameter p710 [id=710]
Thing parameter p8730 [id=8730]
}
```
bsblan.items:
```
Number BsbParameter700NumberValue { channel="bsblan:parameter:heating:p700:number-value" }
Number BsbParameter710NumberValue { channel="bsblan:parameter:heating:p710:number-value" }
String BsbParameter8730Description { channel="bsblan:parameter:heating:p8730:description" }
```
bsblan.sitemap:
```
sitemap bsblan label="BSB-LAN" {
Selection item=BsbParameter700NumberValue label="Operating Mode" mappings=[0="Protection", 1="Automatic", 2="Reduced", 3="Comfort"] icon="heating"
Setpoint item=BsbParameter710NumberValue label="Room Temperature Comfort Setpoint [%.1f °C]" icon="temperature" minValue=22.0 maxValue=25.0 step=0.5
Text item=BsbParameter8730Description label="Heating Circuit Pump [%s]"
}
```

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.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.bsblan</artifactId>
<name>openHAB Add-ons :: Bundles :: BSB-LAN Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.bsblan-${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-bsblan" description="BSB-LAN Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bsblan/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link BsbLanBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanBindingConstants {
private static final String BINDING_ID = "bsblan";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
public static final ThingTypeUID THING_TYPE_PARAMETER = new ThingTypeUID(BINDING_ID, "parameter");
// List of all channel ids
public static final String PARAMETER_CHANNEL_NAME = "name";
public static final String PARAMETER_CHANNEL_NUMBER_VALUE = "number-value";
public static final String PARAMETER_CHANNEL_STRING_VALUE = "string-value";
public static final String PARAMETER_CHANNEL_SWITCH_VALUE = "switch-value";
public static final String PARAMETER_CHANNEL_UNIT = "unit";
public static final String PARAMETER_CHANNEL_DESCRIPTION = "description";
public static final String PARAMETER_CHANNEL_DATATYPE = "datatype";
public static final Set<String> WRITEABLE_CHANNELS = new HashSet<String>() {
private static final long serialVersionUID = 1L;
{
add(PARAMETER_CHANNEL_NUMBER_VALUE);
add(PARAMETER_CHANNEL_STRING_VALUE);
add(PARAMETER_CHANNEL_SWITCH_VALUE);
}
};
public static final int MIN_REFRESH_INTERVAL = 5;
public static final int DEFAULT_REFRESH_INTERVAL = 60;
public static final int API_TIMEOUT = 10000;
public static final int DEFAULT_API_PORT = 80;
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal;
import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.handler.BsbLanBridgeHandler;
import org.openhab.binding.bsblan.internal.handler.BsbLanParameterHandler;
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.Component;
/**
* The {@link BsbLanHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.bsblan")
public class BsbLanHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<ThingTypeUID>() {
private static final long serialVersionUID = 1L;
{
add(THING_TYPE_PARAMETER);
add(THING_TYPE_BRIDGE);
}
};
@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 (thingTypeUID.equals(THING_TYPE_PARAMETER)) {
return new BsbLanParameterHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_BRIDGE)) {
return new BsbLanBridgeHandler((Bridge) thing);
}
return null;
}
}

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api;
import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiContentDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterQueryResponseDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterSetRequestDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterSetResponseDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterSetResultDTO;
import org.openhab.binding.bsblan.internal.configuration.BsbLanBridgeConfiguration;
import org.openhab.core.io.net.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class to call the BSB-LAN REST API.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanApiCaller {
private final Logger logger = LoggerFactory.getLogger(BsbLanApiCaller.class);
private final BsbLanBridgeConfiguration bridgeConfig;
public BsbLanApiCaller(BsbLanBridgeConfiguration config) {
bridgeConfig = config;
}
public @Nullable BsbLanApiParameterQueryResponseDTO queryParameter(Integer parameterId) {
Set<Integer> parameters = new HashSet<>();
parameters.add(parameterId);
return queryParameters(parameters);
}
public @Nullable BsbLanApiParameterQueryResponseDTO queryParameters(Set<Integer> parameterIds) {
// note: make the request even if parameterIds is empty as
// thing OFFLINE/ONLINE detection relies on a response
String apiPath = String.format("/JQ=%s", StringUtils.join(parameterIds, ","));
return makeRestCall(BsbLanApiParameterQueryResponseDTO.class, "GET", apiPath, null);
}
public boolean setParameter(Integer parameterId, String value, BsbLanApiParameterSetRequestDTO.Type type) {
// prepare request content
BsbLanApiParameterSetRequestDTO request = new BsbLanApiParameterSetRequestDTO();
request.parameter = parameterId.toString();
request.value = value;
request.type = type;
// make REST call and process response
BsbLanApiParameterSetResponseDTO setResponse = makeRestCall(BsbLanApiParameterSetResponseDTO.class, "POST",
"/JS", request);
if (setResponse == null) {
logger.debug("Failed to set parameter {} to '{}': no response received", parameterId, value);
return false;
}
BsbLanApiParameterSetResultDTO result = setResponse.getOrDefault(parameterId, null);
if (result == null) {
logger.debug("Failed to set parameter {} to '{}'': result is null", parameterId, value);
return false;
}
if (result.status == null) {
logger.debug("Failed to set parameter {} to '{}': status is null", parameterId, value);
return false;
}
if (result.status != BsbLanApiParameterSetResultDTO.Status.SUCCESS) {
logger.debug("Failed to set parameter {} to '{}': status = {}", parameterId, value, result.status);
return false;
}
return true;
}
private String createApiBaseUrl() {
final String host = StringUtils.trimToEmpty(bridgeConfig.host);
final String username = StringUtils.trimToEmpty(bridgeConfig.username);
final String password = StringUtils.trimToEmpty(bridgeConfig.password);
final String passkey = StringUtils.trimToEmpty(bridgeConfig.passkey);
StringBuilder url = new StringBuilder();
url.append("http://");
if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
url.append(username).append(":").append(password).append("@");
}
url.append(host);
if (bridgeConfig.port != 80) {
url.append(":").append(bridgeConfig.port);
}
if (StringUtils.isNotBlank(passkey)) {
url.append("/").append(passkey);
}
return url.toString();
}
/**
* @param responseType response class type
* @param httpMethod to execute
* @param apiPath to request
* @param content to add to request
* @return the object representation of the json response
*/
private <T> @Nullable T makeRestCall(Class<T> responseType, String httpMethod, String apiPath,
@Nullable BsbLanApiContentDTO request) {
try {
String url = createApiBaseUrl() + apiPath;
logger.trace("api request url = '{}'", url);
InputStream contentStream = null;
String contentType = null;
if (request != null) {
String content = BsbLanApiContentConverter.toJson(request);
logger.trace("api request content: '{}'", content);
if (StringUtils.isNotBlank(content)) {
contentStream = new ByteArrayInputStream(content.getBytes(Charset.forName("UTF-8")));
contentType = "application/json";
}
}
String response = HttpUtil.executeUrl(httpMethod, url, contentStream, contentType, API_TIMEOUT);
if (response == null) {
logger.debug("no response returned");
return null;
}
logger.trace("api response content: '{}'", response);
return BsbLanApiContentConverter.fromJson(response, responseType);
} catch (IOException | IllegalStateException e) {
logger.debug("Error executing bsb-lan api request: {}", e.getMessage());
return null;
}
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiContentDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
/**
* Utility class to create JSON content.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanApiContentConverter {
private static final Logger LOGGER = LoggerFactory.getLogger(BsbLanApiContentConverter.class);
private static final Gson GSON = new Gson();
public static String toJson(BsbLanApiContentDTO request) {
return GSON.toJson(request);
}
public static <T> @Nullable T fromJson(String content, Class<T> resultType) {
try {
T result = GSON.fromJson(content, resultType);
if (result == null) {
LOGGER.debug("result null after json parsing (response = {})", content);
}
return result;
} catch (JsonSyntaxException e) {
LOGGER.debug("Parsing JSON API response failed: {}", e.getMessage());
return null;
}
}
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api.dto;
/**
* The {@link BsbLanApiContentDTO} reflects a request sent to the BSB-LAN device.
*
* @author Peter Schraffl - Initial contribution
*/
public interface BsbLanApiContentDTO {
// empty for now
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link BsbLanApiParameterDTO} is responsible for storing parameter info.
*
* @author Peter Schraffl - Initial contribution
*/
public class BsbLanApiParameterDTO {
public enum DataType {
@SerializedName("0")
DT_VALS(0), // plain value
@SerializedName("1")
DT_ENUM(1), // value (8/16 Bit) followed by space followed by text
@SerializedName("2")
DT_BITS(2), // bit value followed by bitmask followed by text
@SerializedName("3")
DT_WDAY(3), // weekday
@SerializedName("4")
DT_HHMM(4), // hour:minute
@SerializedName("5")
DT_DTTM(5), // date and time
@SerializedName("6")
DT_DDMM(6), // day and month
@SerializedName("7")
DT_STRN(7), // string
@SerializedName("8")
DT_DWHM(8); // PPS time (day of week, hour:minute)
private final int value;
DataType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
@SerializedName("name")
public String name;
@SerializedName("value")
public String value;
@SerializedName("unit")
public String unit;
@SerializedName("desc")
public String description;
@SerializedName("dataType")
public DataType dataType;
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api.dto;
import java.util.HashMap;
/**
* The {@link BsbLanApiParameterQueryResponseDTO} reflects the response received
* when querying parameters.
*
* @author Peter Schraffl - Initial contribution
*/
@SuppressWarnings("serial")
public class BsbLanApiParameterQueryResponseDTO extends HashMap<Integer, BsbLanApiParameterDTO>
implements BsbLanApiContentDTO {
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link BsbLanApiParameterSetRequestDTO} reflects the request sent
* when setting a parameter.
*
* @author Peter Schraffl - Initial contribution
*/
public class BsbLanApiParameterSetRequestDTO implements BsbLanApiContentDTO {
public enum Type {
@SerializedName("0")
INF("INF"),
@SerializedName("1")
SET("SET");
private final String value;
Type(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static Type getTypeWithFallback(String value) {
if (value != null) {
for (Type t : Type.values()) {
if (t.value.toLowerCase().equals(value.toLowerCase())) {
return t;
}
}
}
// fallback to SET
return Type.SET;
}
}
// Although specifying the parameter as int (which would be nicer) also seems to work,
// we use a String here as this is the way it is noted in the documentation.
@SerializedName("Parameter")
public String parameter;
@SerializedName("Value")
public String value;
@SerializedName("Type")
public Type type;
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api.dto;
import java.util.HashMap;
/**
* The {@link BsbLanApiParameterSetResponseDTO} reflects the response received
* when setting a parameter.
*
* @author Peter Schraffl - Initial contribution
*/
@SuppressWarnings("serial")
public class BsbLanApiParameterSetResponseDTO extends HashMap<Integer, BsbLanApiParameterSetResultDTO>
implements BsbLanApiContentDTO {
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link BsbLanApiParameterSetResponseDTO} reflects the response received
* when setting a parameter.
*
* @author Peter Schraffl - Initial contribution
*/
public class BsbLanApiParameterSetResultDTO {
public enum Status {
@SerializedName("0")
ERROR(0),
@SerializedName("1")
SUCCESS(1),
@SerializedName("2")
READ_ONLY(2);
private final int value;
Status(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
@SerializedName("status")
public Status status;
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.configuration;
/**
* The {@link BsbLanBridgeConfiguration} is the class used to match the
* bridge configuration.
*
* @author Peter Schraffl - Initial contribution
*/
public class BsbLanBridgeConfiguration {
/**
* Hostname or IP address of the device
*/
public String host;
/**
* HTTP port where device is listening
*/
public Integer port;
/**
* For "security" feature of BSB-LAN devices
*/
public String passkey;
/**
* HTTP Basic Authentication User
*/
public String username;
/**
* HTTP Basic Authentication Password
*/
public String password;
/**
* Value refresh interval
*/
public Integer refreshInterval;
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.configuration;
/**
* The {@link BsbLanParameterConfiguration} is the class used to match the
* thing configuration.
*
* @author Peter Schraffl - Initial contribution
*/
public class BsbLanParameterConfiguration {
/**
* Parameter Id (ProgNr) to query
*/
public Integer id;
/**
* Parameter Id (ProgNr) used for change requests.
*/
public Integer setId;
/**
* Command type used for change requests (INF or SET)
*/
public String setType;
}

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.api.BsbLanApiCaller;
import org.openhab.binding.bsblan.internal.configuration.BsbLanBridgeConfiguration;
import org.openhab.core.thing.Bridge;
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.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Basic Handler class for all BSB-LAN services.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public abstract class BsbLanBaseThingHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(BsbLanBaseThingHandler.class);
private @Nullable BsbLanBridgeHandler bridgeHandler;
public BsbLanBaseThingHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateChannel(channelUID.getId());
} else {
setChannel(channelUID.getId(), command);
}
}
@Override
public void initialize() {
BsbLanBridgeHandler bridgeHandler = getBridgeHandler();
if (bridgeHandler == null) {
logger.debug("Initializing '{}': thing is only supported within a bridge", getDescription());
updateStatus(ThingStatus.OFFLINE);
return;
}
logger.trace("Initializing '{}' thing", getDescription());
bridgeHandler.registerThing(this);
}
protected synchronized @Nullable BsbLanBridgeHandler getBridgeHandler() {
if (this.bridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof BsbLanBridgeHandler) {
this.bridgeHandler = (BsbLanBridgeHandler) handler;
}
}
return this.bridgeHandler;
}
/**
* Update all channels
*/
protected void updateChannels() {
for (Channel channel : getThing().getChannels()) {
updateChannel(channel.getUID().getId());
}
}
protected @Nullable BsbLanApiCaller getApiCaller() {
// use a local variable to avoid the build warning "Potential null pointer access"
BsbLanBridgeHandler localBridgeHandler = bridgeHandler;
if (localBridgeHandler == null) {
return null;
}
return new BsbLanApiCaller(localBridgeHandler.getBridgeConfiguration());
}
/**
* Update the channel from the last data
*
* @param channelId the id identifying the channel to be updated
*/
protected abstract void updateChannel(String channelId);
/**
* Set new value for channel
*
* @param channelId the id identifying the channel
*/
protected abstract void setChannel(String channelId, Command command);
/**
* return an internal description for logging
*
* @return the description of the thing
*/
protected abstract String getDescription();
/**
* do whatever a thing must do to update the values
* this function is called from the bridge in a given interval
*
* @param bridgeConfiguration the connected bridge configuration
*/
public abstract void refresh(BsbLanBridgeConfiguration bridgeConfiguration);
}

View File

@@ -0,0 +1,166 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.handler;
import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.*;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.api.BsbLanApiCaller;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterQueryResponseDTO;
import org.openhab.binding.bsblan.internal.configuration.BsbLanBridgeConfiguration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Bridge for BSB-LAN devices.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(BsbLanBridgeHandler.class);
private final Set<BsbLanBaseThingHandler> things = new HashSet<>();
private BsbLanBridgeConfiguration bridgeConfig = new BsbLanBridgeConfiguration();
private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable ScheduledFuture<?> debouncedInit;
private @Nullable BsbLanApiParameterQueryResponseDTO cachedParameterQueryResponse;
public BsbLanBridgeHandler(Bridge bridge) {
super(bridge);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
public void registerThing(final BsbLanBaseThingHandler parameter) {
this.things.add(parameter);
// To avoid having to wait up to refreshInterval seconds until values are updated
// for the added thing, we trigger a debounced refresh to shorten the delay.
// Alternatively the thing itself could make an additional REST call
// on initialization but this would flood the device when lots of parameters are setup.
// use a local variable to avoid the build warning "Potential null pointer access"
ScheduledFuture<?> localDebouncedInit = debouncedInit;
if (localDebouncedInit == null || localDebouncedInit.isCancelled() || localDebouncedInit.isDone()) {
debouncedInit = scheduler.schedule(this::doRefresh, 2, TimeUnit.SECONDS);
}
}
@Override
public void initialize() {
bridgeConfig = getConfigAs(BsbLanBridgeConfiguration.class);
// validate 'host' configuration
if (StringUtils.isBlank(bridgeConfig.host)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Parameter 'host' is mandatory and must be configured");
return;
}
// validate 'refreshInterval' configuration
if (bridgeConfig.refreshInterval != null && bridgeConfig.refreshInterval < MIN_REFRESH_INTERVAL) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
String.format("Parameter 'refreshInterval' must be at least %d seconds", MIN_REFRESH_INTERVAL));
return;
}
if (bridgeConfig.port == null) {
bridgeConfig.port = DEFAULT_API_PORT;
}
// all checks succeeded, start refreshing
startAutomaticRefresh(bridgeConfig);
}
@Override
public void dispose() {
// use a local variable to avoid the build warning "Potential null pointer access"
ScheduledFuture<?> localRefreshJob = refreshJob;
if (localRefreshJob != null) {
localRefreshJob.cancel(true);
}
// use a local variable to avoid the build warning "Potential null pointer access"
ScheduledFuture<?> localDebouncedInit = debouncedInit;
if (localDebouncedInit != null) {
localDebouncedInit.cancel(true);
}
things.clear();
}
public @Nullable BsbLanApiParameterQueryResponseDTO getCachedParameterQueryResponse() {
return cachedParameterQueryResponse;
}
public BsbLanBridgeConfiguration getBridgeConfiguration() {
return bridgeConfig;
}
private void doRefresh() {
logger.trace("Refreshing parameter values");
BsbLanApiCaller apiCaller = new BsbLanApiCaller(bridgeConfig);
// refresh all parameters
Set<Integer> parameterIds = things.stream().filter(thing -> thing instanceof BsbLanParameterHandler)
.map(thing -> (BsbLanParameterHandler) thing).map(thing -> thing.getParameterId())
.collect(Collectors.toSet());
cachedParameterQueryResponse = apiCaller.queryParameters(parameterIds);
// InetAddress.isReachable(...) check returned false on RPi although the device is reachable (worked on
// Windows).
// Therefore we check status depending on the response.
if (cachedParameterQueryResponse == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Did not receive a response from BSB-LAN device. Check your configuration and if device is online.");
// continue processing, so things can go to OFFLINE too
} else {
// response received, tread device as reachable, refresh state now
updateStatus(ThingStatus.ONLINE);
}
for (BsbLanBaseThingHandler parameter : things) {
parameter.refresh(bridgeConfig);
}
}
/**
* Start the job refreshing the data
*/
private void startAutomaticRefresh(BsbLanBridgeConfiguration config) {
// use a local variable to avoid the build warning "Potential null pointer access"
ScheduledFuture<?> localRefreshJob = refreshJob;
if (localRefreshJob == null || localRefreshJob.isCancelled()) {
int interval = (config.refreshInterval != null) ? config.refreshInterval.intValue()
: DEFAULT_REFRESH_INTERVAL;
refreshJob = scheduler.scheduleWithFixedDelay(this::doRefresh, 0, interval, TimeUnit.SECONDS);
}
}
}

View File

@@ -0,0 +1,172 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.handler;
import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.api.BsbLanApiCaller;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterQueryResponseDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterSetRequestDTO.Type;
import org.openhab.binding.bsblan.internal.configuration.BsbLanBridgeConfiguration;
import org.openhab.binding.bsblan.internal.configuration.BsbLanParameterConfiguration;
import org.openhab.binding.bsblan.internal.helper.BsbLanParameterConverter;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BsbLanParameterHandler} is responsible for updating the data, which are
* sent to one of the channels.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanParameterHandler extends BsbLanBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(BsbLanParameterHandler.class);
private BsbLanParameterConfiguration parameterConfig = new BsbLanParameterConfiguration();
public BsbLanParameterHandler(Thing thing) {
super(thing);
}
public Integer getParameterId() {
return parameterConfig.id;
}
@Override
protected String getDescription() {
return "BSB-LAN Parameter";
}
@Override
public void refresh(BsbLanBridgeConfiguration bridgeConfiguration) {
updateChannels();
}
@Override
public void initialize() {
parameterConfig = getConfigAs(BsbLanParameterConfiguration.class);
super.initialize();
// validate 'setId' configuration -> fallback to value of 'id' if invalid or not specified
if (parameterConfig.setId == null || parameterConfig.setId <= 0) {
parameterConfig.setId = parameterConfig.id;
}
// validate 'setType' configuration -> fallback to 'SET' if invalid or not specified
parameterConfig.setType = Type.getTypeWithFallback(parameterConfig.setType).toString();
// it will take up to refreshInterval seconds until we receive a value and thing goes online
// see notes in {@link BsbLanBridgeHandler#registerThing(BsbLanBaseThingHandler)}
updateStatus(ThingStatus.UNKNOWN);
}
/**
* Update the channel from the last data retrieved
*
* @param channelId the id identifying the channel to be updated
*/
@Override
protected void updateChannel(String channelId) {
BsbLanApiParameterQueryResponseDTO data = null;
BsbLanBridgeHandler bridgeHandler = getBridgeHandler();
if (bridgeHandler != null) {
data = bridgeHandler.getCachedParameterQueryResponse();
}
updateChannel(channelId, data);
}
private void updateChannel(String channelId, @Nullable BsbLanApiParameterQueryResponseDTO data) {
if (data == null) {
logger.debug("no data available while updating channel '{}' of parameter {}", channelId,
parameterConfig.id);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.BRIDGE_OFFLINE,
"No data received from BSB-LAN device");
return;
}
BsbLanApiParameterDTO parameter = data.getOrDefault(parameterConfig.id, null);
if (parameter == null) {
logger.debug("parameter {} is not part of response data while updating channel '{}' ", parameterConfig.id,
channelId);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
String.format("No data received for parameter %s", parameterConfig.id));
return;
}
updateStatus(ThingStatus.ONLINE);
if (!isLinked(channelId)) {
return;
}
State state = BsbLanParameterConverter.getState(channelId, parameter);
if (state == null) {
return;
}
updateState(channelId, state);
}
/**
* Update the channel from the last data retrieved
*
* @param channelId the id identifying the channel to be updated
* @param command the value to be set
*/
@Override
protected void setChannel(String channelId, Command command) {
logger.trace("Received command '{}' for channel '{}'", command, channelId);
if (!WRITEABLE_CHANNELS.contains(channelId)) {
logger.warn("Channel '{}' is read only. Ignoring command", channelId);
return;
}
String value = BsbLanParameterConverter.getValue(channelId, command);
if (value == null) {
logger.warn("Channel '{}' is read only or conversion failed. Ignoring command", channelId);
return;
}
BsbLanApiCaller api = getApiCaller();
if (api == null) {
logger.debug("Failed to set parameter {} (API unavailable)", parameterConfig.setId);
return;
}
boolean success = api.setParameter(parameterConfig.setId, value,
Type.getTypeWithFallback(parameterConfig.setType));
if (!success) {
logger.debug("Failed to set parameter {} to '{}' for channel '{}'", parameterConfig.setId, value,
channelId);
}
// refresh value
BsbLanApiParameterQueryResponseDTO queryResponse = api.queryParameter(parameterConfig.id);
if (queryResponse == null) {
logger.debug("Failed to refresh parameter {} after set request", parameterConfig.id);
return;
}
updateChannel(channelId, queryResponse);
}
}

View File

@@ -0,0 +1,163 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.helper;
import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*;
import org.apache.commons.lang.StringEscapeUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterDTO;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BsbLanParameterHandler} is responsible for updating the data, which are
* sent to one of the channels.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanParameterConverter {
private static final Logger LOGGER = LoggerFactory.getLogger(BsbLanParameterConverter.class);
public static @Nullable State getState(String channelId, BsbLanApiParameterDTO parameter) {
switch (channelId) {
case PARAMETER_CHANNEL_NAME:
return getStateForNameChannel(parameter);
case PARAMETER_CHANNEL_DESCRIPTION:
return getStateForDescriptionChannel(parameter);
case PARAMETER_CHANNEL_DATATYPE:
return getStateForDatatypeChannel(parameter);
case PARAMETER_CHANNEL_NUMBER_VALUE:
return getStateForNumberValueChannel(parameter);
case PARAMETER_CHANNEL_STRING_VALUE:
return getStateForStringValueChannel(parameter);
case PARAMETER_CHANNEL_SWITCH_VALUE:
return getStateForSwitchValueChannel(parameter);
case PARAMETER_CHANNEL_UNIT:
return getStateForUnitChannel(parameter);
}
LOGGER.debug("unsupported channel '{}' while updating state", channelId);
return null;
}
private static State getStateForNameChannel(BsbLanApiParameterDTO parameter) {
return new StringType(parameter.name);
}
private static State getStateForDescriptionChannel(BsbLanApiParameterDTO parameter) {
return new StringType(parameter.description);
}
private static State getStateForUnitChannel(BsbLanApiParameterDTO parameter) {
String value = StringEscapeUtils.unescapeHtml(parameter.unit);
return new StringType(value);
}
private static State getStateForDatatypeChannel(BsbLanApiParameterDTO parameter) {
int value = parameter.dataType.getValue();
return new DecimalType(value);
}
private static @Nullable State getStateForNumberValueChannel(BsbLanApiParameterDTO parameter) {
try {
switch (parameter.dataType) {
// parse enum data type as integer
case DT_ENUM:
return new DecimalType(Integer.parseInt(parameter.value));
default:
return new DecimalType(Double.parseDouble(parameter.value));
}
} catch (NumberFormatException e) {
// silently ignore - there is not "tryParse"
}
return null;
}
private static State getStateForStringValueChannel(BsbLanApiParameterDTO parameter) {
return new StringType(parameter.value);
}
private static State getStateForSwitchValueChannel(BsbLanApiParameterDTO parameter) {
// treat "0" as OFF and everything else as ON
return parameter.value.equals("0") ? OnOffType.OFF : OnOffType.ON;
}
/**
* Converts a Command back to a value which is sent to the BSB-LAN device afterwards.
*
* @param channelId
* @param command
* @return null if conversion fails or channel is readonly.
*/
public static @Nullable String getValue(String channelId, Command command) {
switch (channelId) {
case PARAMETER_CHANNEL_NUMBER_VALUE:
return getValueForNumberValueChannel(command);
case PARAMETER_CHANNEL_STRING_VALUE:
return getValueForStringValueChannel(command);
case PARAMETER_CHANNEL_SWITCH_VALUE:
return getValueForSwitchValueChannel(command);
default:
LOGGER.debug("Channel '{}' is read only. Ignoring command", channelId);
return null;
}
}
private static @Nullable String getValueForNumberValueChannel(Command command) {
// check if numeric
if (command.toString().matches("-?\\d+(\\.\\d+)?")) {
return command.toString();
}
LOGGER.warn("Command '{}' is not a valid number value", command);
return null;
}
private static String getValueForStringValueChannel(Command command) {
// special OnOffType handling
if (command.equals(OnOffType.ON)) {
return "1";
} else if (command.equals(OnOffType.OFF)) {
return "0";
}
return command.toString();
}
private static @Nullable String getValueForSwitchValueChannel(Command command) {
if (command.equals(OnOffType.ON)) {
return "1";
} else if (command.equals(OnOffType.OFF)) {
return "0";
}
LOGGER.warn("Command '{}' is not a valid switch value", command);
return null;
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="bsblan" 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>BSB-LAN Binding</name>
<description>Binding for BSB-LAN Gateway.</description>
<author>Peter Schraffl</author>
</binding:binding>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="bsblan"
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">
<bridge-type id="bridge">
<label>BSB-LAN Bridge</label>
<description>A bridge to connect a BSB-LAN device</description>
<config-description>
<parameter name="host" type="text" required="true" groupName="network">
<context>network-address</context>
<label>Host</label>
<description>The hostname or IP address of the BSB-LAN device.</description>
</parameter>
<parameter name="port" type="integer" required="false" groupName="network">
<label>Port</label>
<description>The port BSB-LAN device.</description>
<default>80</default>
</parameter>
<parameter name="passkey" type="text" required="false" groupName="network">
<label>Passkey</label>
<description>The passkey required to access the BSB-LAN device.</description>
</parameter>
<parameter name="username" type="text" required="false" groupName="network">
<label>Username</label>
<description>The username required to access the BSB-LAN device (when using HTTP Basic Authentication).</description>
</parameter>
<parameter name="password" type="text" required="false" groupName="network">
<context>password</context>
<label>Password</label>
<description>The password required to access the BSB-LAN device (when using HTTP Basic Authentication).</description>
</parameter>
<parameter name="refreshInterval" type="integer" required="false" min="5">
<label>Refresh Interval</label>
<description>Specifies the refresh interval in seconds.</description>
<default>60</default>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="bsblan"
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">
<!-- Parameter Thing Type -->
<thing-type id="parameter">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Parameter</label>
<description>Represents a single parameter available at the BSB-LAN device and identified by a numeric id.</description>
<channels>
<channel id="name" typeId="parameter_name"/>
<channel id="number-value" typeId="parameter_number_value"/>
<channel id="string-value" typeId="parameter_string_value"/>
<channel id="switch-value" typeId="parameter_switch_value"/>
<channel id="unit" typeId="parameter_unit"/>
<channel id="description" typeId="parameter_description"/>
<channel id="datatype" typeId="parameter_datatype"/>
</channels>
<config-description>
<parameter name="id" type="integer" required="true">
<label>Parameter ID</label>
<description>Specific parameter identifier</description>
</parameter>
<parameter name="setId" type="integer" required="false" groupName="Change Requests">
<label>Parameter Set-ID</label>
<description>Parameter identifier used for change requests. Defaults to the value of Parameter ID</description>
<advanced>true</advanced>
</parameter>
<parameter name="setType" type="text" required="false" groupName="Change Requests">
<label>Message Type</label>
<description>Message type used for change requests. Defaults to SET.</description>
<default>SET</default>
<advanced>true</advanced>
<options>
<option value="INF">INF</option>
<option value="SET">SET</option>
</options>
</parameter>
</config-description>
</thing-type>
<channel-type id="parameter_name">
<item-type>String</item-type>
<label>Name</label>
<description>Name of the parameter</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="parameter_number_value">
<item-type>Number</item-type>
<label>Value</label>
<description>Value of the parameter</description>
<state readOnly="false" pattern="%.1f °C"></state>
</channel-type>
<channel-type id="parameter_string_value">
<item-type>String</item-type>
<label>Value</label>
<description>Value of the parameter</description>
<state readOnly="false"></state>
</channel-type>
<channel-type id="parameter_switch_value">
<item-type>Switch</item-type>
<label>Value</label>
<description>Value of the parameter</description>
<state readOnly="false"></state>
</channel-type>
<channel-type id="parameter_unit">
<item-type>String</item-type>
<label>Unit</label>
<description>Unit of the parameter</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="parameter_description">
<item-type>String</item-type>
<label>Description</label>
<description>Description of the parameter</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="parameter_datatype">
<item-type>Number</item-type>
<label>Data Type</label>
<description>Data type of the parameter</description>
<state readOnly="true"></state>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link BsbLanHandlerFactoryTests} class implements tests
* for {@link BsbLanHandlerFactory}.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanHandlerFactoryTests {
public static final ThingTypeUID UNKNOWN_THING_TYPE_UID = new ThingTypeUID("bsblan", "unknown");
/**
* Test if factory knows all thing types.
*/
@Test
public void supportsThingType() {
BsbLanHandlerFactory factory = new BsbLanHandlerFactory();
assertFalse(factory.supportsThingType(UNKNOWN_THING_TYPE_UID));
assertTrue(factory.supportsThingType(BsbLanBindingConstants.THING_TYPE_BRIDGE));
assertTrue(factory.supportsThingType(BsbLanBindingConstants.THING_TYPE_PARAMETER));
}
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterQueryResponseDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterSetRequestDTO;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterSetRequestDTO.Type;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link BsbLanApiContentConverterTests} class implements tests
* for {@link BsbLanApiContentConverter}.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanApiContentConverterTests {
@Test
public void parseBsbLanApiParameterQueryResponse() {
String content = "{\r\n" + "\"700\": {\r\n" + "\"name\": \"Betriebsart\",\r\n" + "\"value\": \"0\",\r\n"
+ "\"unit\": \"\",\r\n" + "\"desc\": \"Schutzbetrieb\",\r\n" + "\"dataType\": 1\r\n" + "}\r\n" + "}";
BsbLanApiParameterQueryResponseDTO r = BsbLanApiContentConverter.fromJson(content,
BsbLanApiParameterQueryResponseDTO.class);
assertNotNull(r);
assertTrue(r.containsKey(700));
BsbLanApiParameterDTO p = r.get(700);
assertEquals("Betriebsart", p.name);
assertEquals("0", p.value);
assertEquals("", p.unit);
assertEquals("Schutzbetrieb", p.description);
assertEquals(BsbLanApiParameterDTO.DataType.DT_ENUM, p.dataType);
}
@Test
public void serializeBsbLanApiParameterSetRequest() {
BsbLanApiParameterSetRequestDTO request = new BsbLanApiParameterSetRequestDTO();
request.parameter = "1234";
request.value = "Hello World";
request.type = Type.SET;
String serializedRequest = BsbLanApiContentConverter.toJson(request);
// verify serialized content
JsonParser parser = new JsonParser();
JsonObject json = parser.parse(serializedRequest).getAsJsonObject();
// Although specifying the parameter as int (which would be nicer) also seems to work,
// we use a String here as this is the way it is noted in the documentation.
// So ensure there is a 'Parameter' and it is serialized as string.
assertEquals("1234", json.get("Parameter").getAsString());
// ensure there is a 'Value' and it is serialized as string
assertEquals("Hello World", json.get("Value").getAsString());
// ensure there is a 'Type' and it is serialized as number
assertEquals(1, json.get("Type").getAsInt());
}
}

View File

@@ -0,0 +1,254 @@
/**
* Copyright (c) 2010-2020 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.bsblan.internal.helper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterDTO;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
/**
* The {@link BsbLanParameterConverterTests} class implements tests
* for {@link BsbLanParameterConverter}.
*
* @author Peter Schraffl - Initial contribution
*/
@NonNullByDefault
public class BsbLanParameterConverterTests {
@Test
public void testGetStatesForStringParameter() {
BsbLanApiParameterDTO parameter = new BsbLanApiParameterDTO();
parameter.dataType = BsbLanApiParameterDTO.DataType.DT_STRN;
parameter.description = "Test-Description";
parameter.name = "Test-Name";
parameter.unit = "Test-Unit";
parameter.value = "Test-Value";
State state = null;
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DATATYPE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(BsbLanApiParameterDTO.DataType.DT_STRN.getValue()), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DESCRIPTION, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Description"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NAME, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Name"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_UNIT, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Unit"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NUMBER_VALUE, parameter);
assertNull(state);
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_STRING_VALUE, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Value"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_SWITCH_VALUE, parameter);
assertNotNull(state);
assertEquals(OnOffType.ON, state);
}
@Test
public void testGetStatesForEnumParameterValue1() {
BsbLanApiParameterDTO parameter = new BsbLanApiParameterDTO();
parameter.dataType = BsbLanApiParameterDTO.DataType.DT_ENUM;
parameter.description = "Test-Description";
parameter.name = "Test-Name";
parameter.unit = "Test-Unit";
parameter.value = "1";
State state = null;
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DATATYPE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(BsbLanApiParameterDTO.DataType.DT_ENUM.getValue()), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DESCRIPTION, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Description"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NAME, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Name"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_UNIT, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Unit"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NUMBER_VALUE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(1), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_STRING_VALUE, parameter);
assertNotNull(state);
assertEquals(new StringType("1"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_SWITCH_VALUE, parameter);
assertNotNull(state);
assertEquals(OnOffType.ON, state);
}
@Test
public void testGetStatesForEnumParameterValue0() {
BsbLanApiParameterDTO parameter = new BsbLanApiParameterDTO();
parameter.dataType = BsbLanApiParameterDTO.DataType.DT_ENUM;
parameter.description = "Test-Description";
parameter.name = "Test-Name";
parameter.unit = "Test-Unit";
parameter.value = "0";
State state = null;
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DATATYPE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(BsbLanApiParameterDTO.DataType.DT_ENUM.getValue()), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DESCRIPTION, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Description"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NAME, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Name"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_UNIT, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Unit"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NUMBER_VALUE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(0), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_STRING_VALUE, parameter);
assertNotNull(state);
assertEquals(new StringType("0"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_SWITCH_VALUE, parameter);
assertNotNull(state);
assertEquals(OnOffType.OFF, state);
}
@Test
public void testGetStatesForValueParameterValue() {
BsbLanApiParameterDTO parameter = new BsbLanApiParameterDTO();
parameter.dataType = BsbLanApiParameterDTO.DataType.DT_VALS;
parameter.description = "Test-Description";
parameter.name = "Test-Name";
parameter.unit = "Test-Unit";
parameter.value = "22.5";
State state = null;
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DATATYPE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(BsbLanApiParameterDTO.DataType.DT_VALS.getValue()), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_DESCRIPTION, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Description"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NAME, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Name"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_UNIT, parameter);
assertNotNull(state);
assertEquals(new StringType("Test-Unit"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_NUMBER_VALUE, parameter);
assertNotNull(state);
assertEquals(new DecimalType(22.5), state.as(DecimalType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_STRING_VALUE, parameter);
assertNotNull(state);
assertEquals(new StringType("22.5"), state.as(StringType.class));
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_SWITCH_VALUE, parameter);
assertNotNull(state);
assertEquals(OnOffType.ON, state);
}
@Test
public void testGetStatesEscapesHtml() {
BsbLanApiParameterDTO parameter = new BsbLanApiParameterDTO();
parameter.dataType = BsbLanApiParameterDTO.DataType.DT_VALS;
parameter.description = "Test-Description";
parameter.name = "Test-Name";
parameter.unit = "&deg;C";
parameter.value = "22.5";
State state = null;
state = BsbLanParameterConverter.getState(PARAMETER_CHANNEL_UNIT, parameter);
assertNotNull(state);
assertEquals(new StringType("°C"), state.as(StringType.class));
}
@Test
public void testGetValueForReadonlyChannels() {
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_DATATYPE, OnOffType.ON));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_DESCRIPTION, OnOffType.ON));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NAME, OnOffType.ON));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_UNIT, OnOffType.ON));
}
@Test
public void testGetValueForNumberValueChannel() {
assertNull("1", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NUMBER_VALUE, OnOffType.ON));
assertNull("0", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NUMBER_VALUE, OnOffType.OFF));
assertEquals("42", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NUMBER_VALUE, new DecimalType(42)));
assertEquals("22.5", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NUMBER_VALUE, new DecimalType(22.5)));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NUMBER_VALUE,
new StringType("Not a number value")));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_NUMBER_VALUE, new StringType("")));
}
@Test
public void testGetValueForSwitchValueChannel() {
assertEquals("1", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, OnOffType.ON));
assertEquals("0", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, OnOffType.OFF));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, new DecimalType(1)));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, new DecimalType(0)));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, new DecimalType(42)));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, new DecimalType(22.5)));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE,
new StringType("Not a number value")));
assertNull(BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_SWITCH_VALUE, new StringType("")));
}
@Test
public void testGetValueForStringValueChannel() {
assertEquals("1", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_STRING_VALUE, OnOffType.ON));
assertEquals("0", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_STRING_VALUE, OnOffType.OFF));
assertEquals("42", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_STRING_VALUE, new DecimalType(42)));
assertEquals("22.5", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_STRING_VALUE, new DecimalType(22.5)));
assertEquals("A string value",
BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_STRING_VALUE, new StringType("A string value")));
assertEquals("", BsbLanParameterConverter.getValue(PARAMETER_CHANNEL_STRING_VALUE, new StringType("")));
}
}