[liquidcheck] Initial contribution (#13287)

* Add new binding liquidcheck

Signed-off-by: Marcel Goerentz <m.goerentz@t-online.de>
This commit is contained in:
Marcel Goerentz 2023-05-14 00:19:56 +02:00 committed by GitHub
parent 939c9caa1b
commit ab16c94ace
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1530 additions and 0 deletions

View File

@ -175,6 +175,7 @@
/bundles/org.openhab.binding.lifx/ @wborn
/bundles/org.openhab.binding.linky/ @clinique @lolodomo
/bundles/org.openhab.binding.linuxinput/ @t-8ch
/bundles/org.openhab.binding.liquidcheck/ @marcelGoerentz
/bundles/org.openhab.binding.lirc/ @kabili207
/bundles/org.openhab.binding.livisismarthome/ @Novanic
/bundles/org.openhab.binding.logreader/ @paulianttila

View File

@ -871,6 +871,11 @@
<artifactId>org.openhab.binding.linuxinput</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.liquidcheck</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.lirc</artifactId>

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,64 @@
# LiquidCheck Binding
This binding is for the Liquid-Check device from SI-Elektronik GmbH which can be used to measure level and content of tanks.
## Supported Things
`liquidCheckDevice`:
The Liquid-Check device in Hardwareversion B and Firmwareversion 1.60 has been tested and is working.
You can access the measured data, raw data, the settings as properties and command a measurement.
## Discovery
This binding discovers the devices via a ping and request method.
It uses every Ethernet/WLAN interface that is connected to the openHAB server.
Therefore the discovery has to be manually triggered.
## Thing Configuration
You only need to set the IP address of the device or use the discovery method.
If the maximum content has not been set the fill-indicator channel will not contain valid values.
### `liquidCheckDevice` Thing Configuration
| Name | Type | Description | Default | Required | Advanced |
|------------------|---------|------------------------------------------|---------|----------|----------|
| hostname | text | Hostname or IP address of the device | N/A | yes | no |
| maxContent | integer | Maximal content of the container | 1 | no | no |
| refreshInterval | integer | Interval the device is polled in seconds | 60 | no | yes |
| connectionTimeout| integer | Timeout after a request has been sent | 5 | no | yes |
## Channels
| Channel | Type | Read/Write | Description |
|----------------|-----------------------------|------------|---------------------------------------|
| content | Number:Volume | R | This is the measured content |
| level | Number:Length | R | This is the measured level |
| raw-content | Number:Volume | R | This is the measured raw content data |
| raw-level | Number:Length | R | This is the measured raw level data |
| fill-indicator | Number:Dimensionless | R | This is the fill level in percentage |
| measure | Switch | W | This starts a measurement |
| pump-runs | Number | R | This is the total runs number |
| pump-runtime | Number:Time | R | This is the total runtime in sec. |
## Full Example
### Thing
```java
Thing liquidcheck:liquidCheckDevice:myDevice "Label" @ "Location" [hostname="XXX.XXX.XXX.XXX", maxContent=9265, refreshInterval=600, connectionTimeout=5]
```
### Items
```java
Number:Volume ContentLiquidCheck "Content" {liquidcheck:liquidCheckDevice:myDevice:content}
Number:Length LevelLiquidCheck "Level" {liquidcheck:liquidCheckDevice:myDevice:level}
Number:Volume RawContentLiquidCheck "Raw Content" {liquidcheck:liquidCheckDevice:myDevice:raw-content}
Number:Length RawLevelLiquidCheck "Raw Level" {liquidcheck:liquidCheckDevice:myDevice:raw-level}
Number:Dimensionless FillIndicator "Fill Indicator" {liquidcheck:liquidCheckDevice:myDevice:fill-indicator}
Switch MeasureLiquidCheck "Measure" {liquidcheck:liquidCheckDevice:myDevice:measure}
Number PumpRuns "Pump runs" {liquidcheck:liquidCheckDevice:myDevice:pump-runs}
Number PumpRuntime "Pump runtime" {liquidcheck:liquidCheckDevice:myDevice:pump-runtime}
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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>4.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.liquidcheck</artifactId>
<name>openHAB Add-ons :: Bundles :: LiquidCheck Binding</name>
</project>

View File

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

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link LiquidCheckBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class LiquidCheckBindingConstants {
private static final String BINDING_ID = "liquidcheck";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_LIQUID_CHECK = new ThingTypeUID(BINDING_ID, "liquidCheckDevice");
// List of all Channel ids
public static final String CONTENT_CHANNEL = "content";
public static final String RAW_CONTENT_CHANNEL = "raw-content";
public static final String LEVEL_CHANNEL = "level";
public static final String RAW_LEVEL_CHANNEL = "raw-level";
public static final String FILL_INDICATOR_CHANNEL = "fill-indicator";
public static final String PUMP_TOTAL_RUNS_CHANNEL = "pump-runs";
public static final String PUMP_TOTAL_RUNTIME_CHANNEL = "pump-runtime";
public static final String MEASURE_CHANNEL = "measure";
// List of all Property ids
public static final String PROPERTY_NAME = "name";
public static final String PROPERTY_SECURITY_CODE = "securityCode";
public static final String PROPERTY_IP = "ip";
public static final String PROPERTY_HOSTNAME = "hostname";
public static final String PROPERTY_SSID = "ssid";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LIQUID_CHECK);
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link LiquidCheckConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class LiquidCheckConfiguration {
public String hostname = "";
public int refreshInterval = 60;
public int maxContent = 1;
public byte connectionTimeout = 5;
}

View File

@ -0,0 +1,173 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.CONTENT_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.FILL_INDICATOR_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.LEVEL_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.MEASURE_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PUMP_TOTAL_RUNS_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PUMP_TOTAL_RUNTIME_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.RAW_CONTENT_CHANNEL;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.RAW_LEVEL_CHANNEL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
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.openhab.binding.liquidcheck.internal.httpclient.LiquidCheckHttpClient;
import org.openhab.binding.liquidcheck.internal.json.CommData;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
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.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
/**
* The {@link LiquidCheckHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class LiquidCheckHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LiquidCheckHandler.class);
private final HttpClient httpClient;
private Map<String, String> oldProps = new HashMap<>();
private LiquidCheckConfiguration config = new LiquidCheckConfiguration();
private @Nullable LiquidCheckHttpClient client;
private @Nullable ScheduledFuture<?> polling;
public LiquidCheckHandler(Thing thing, HttpClient httpClient) {
super(thing);
this.httpClient = httpClient;
}
@Override
@SuppressWarnings("null")
public void handleCommand(ChannelUID channelUID, Command command) {
if (channelUID.getId().equals(MEASURE_CHANNEL)) {
if (command instanceof OnOffType) {
try {
LiquidCheckHttpClient client = this.client;
if (client != null && client.isConnected()) {
String response = client.measureCommand();
CommData commandResponse = new Gson().fromJson(response, CommData.class);
if (commandResponse != null && !commandResponse.header.name.equals("")) {
if (!"success".equals(commandResponse.context.status)) {
logger.warn("Starting the measurement was not successful!");
}
} else {
logger.debug("The object commandResponse is null!");
}
}
} catch (TimeoutException | ExecutionException | JsonSyntaxException e) {
logger.warn("This went wrong in handleCommand: {}", e.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
updateState(channelUID, OnOffType.OFF);
}
}
}
@Override
public void initialize() {
config = getConfigAs(LiquidCheckConfiguration.class);
oldProps = thing.getProperties();
updateStatus(ThingStatus.UNKNOWN);
var client = new LiquidCheckHttpClient(config, httpClient);
this.client = client;
PollingForData pollingRunnable = new PollingForData(client);
polling = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, config.refreshInterval, TimeUnit.SECONDS);
}
@Override
public void dispose() {
ScheduledFuture<?> polling = this.polling;
if (null != polling) {
polling.cancel(true);
this.polling = null;
}
}
private class PollingForData implements Runnable {
private final LiquidCheckHttpClient client;
public PollingForData(LiquidCheckHttpClient client) {
this.client = client;
}
@Override
public void run() {
try {
String jsonString = client.pollData();
CommData response = new Gson().fromJson(jsonString, CommData.class);
if (response != null && !response.header.messageId.equals("")) {
Map<String, String> properties = response.createPropertyMap();
if (!oldProps.equals(properties)) {
oldProps = properties;
updateProperties(properties);
}
updateState(CONTENT_CHANNEL, new QuantityType<>(response.payload.measure.content, Units.LITRE));
updateState(LEVEL_CHANNEL, new QuantityType<>(response.payload.measure.level, SIUnits.METRE));
updateState(RAW_CONTENT_CHANNEL,
new QuantityType<>(response.payload.measure.raw.content, Units.LITRE));
updateState(RAW_LEVEL_CHANNEL,
new QuantityType<>(response.payload.measure.raw.level, SIUnits.METRE));
updateState(PUMP_TOTAL_RUNS_CHANNEL, new DecimalType(response.payload.system.pump.totalRuns));
updateState(PUMP_TOTAL_RUNTIME_CHANNEL,
new QuantityType<>(response.payload.system.pump.totalRuntime, Units.SECOND));
if (config.maxContent > 1) {
double fillIndicator = response.payload.measure.content / config.maxContent * 100;
updateState(FILL_INDICATOR_CHANNEL, new QuantityType<>(fillIndicator, Units.PERCENT));
}
if (!thing.getStatus().equals(ThingStatus.ONLINE)) {
updateStatus(ThingStatus.ONLINE);
}
} else {
logger.debug("Json is null");
}
} catch (TimeoutException | ExecutionException | JsonSyntaxException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}

View File

@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.core.io.net.http.HttpClientFactory;
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 LiquidCheckHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.liquidcheck")
public class LiquidCheckHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LIQUID_CHECK);
private final HttpClient httpClient;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Activate
public LiquidCheckHandlerFactory(final @Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_LIQUID_CHECK.equals(thingTypeUID)) {
return new LiquidCheckHandler(thing, httpClient);
}
return null;
}
}

View File

@ -0,0 +1,194 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.discovery;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.SUPPORTED_THING_TYPES_UIDS;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.THING_TYPE_LIQUID_CHECK;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.liquidcheck.internal.json.CommData;
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.config.discovery.DiscoveryService;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
/**
* The {@link LiquidCheckDiscoveryService} class defines discovery service for the LiquidCheckBinding
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.liquidcheck")
public class LiquidCheckDiscoveryService extends AbstractDiscoveryService {
private static final int DISCOVER_TIMEOUT_SECONDS = 300;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final HttpClient httpClient;
@Activate
public LiquidCheckDiscoveryService(@Reference HttpClientFactory httpClientFactory) {
super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
httpClient = httpClientFactory.getCommonHttpClient();
}
/**
* Method for starting the scan
*/
@Override
protected void startScan() {
scheduler.execute(liquidCheckDiscoveryRunnable());
}
/**
* Method to stop the scan
*/
@Override
protected synchronized void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}
/**
* Method for creating a Runnable to start a scan
*
* @return the Runnable
*/
protected Runnable liquidCheckDiscoveryRunnable() {
return () -> {
try {
List<InetAddress> addresses = getIPv4Addresses();
List<InetAddress> hosts = findActiveHosts(addresses);
for (InetAddress host : hosts) {
Request request = httpClient.newRequest("http://" + host.getHostAddress() + "/infos.json")
.method(HttpMethod.GET).followRedirects(false);
try {
ContentResponse response = request.send();
if (response.getStatus() == 200) {
CommData json = null;
try {
json = new Gson().fromJson(response.getContentAsString(), CommData.class);
} catch (JsonSyntaxException e) {
logger.debug("Json Syntax Exception!");
}
if (null != json) {
buildDiscoveryResult(json,
InetAddress.getByName(json.payload.wifi.station.hostname).isReachable(50));
} else {
logger.debug("Response Object is null!");
}
}
} catch (TimeoutException e) {
logger.debug("TimeOut: {}", e.getMessage());
} catch (ExecutionException e) {
logger.debug("ExecutionException: {}", e.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
} catch (IOException e) {
logger.debug("Message: {}", e.getMessage());
}
};
}
/**
* This Method retrieves all IPv4 addresses of the server
*
* @return A list of all available IPv4 addresses that are registered
* @throws SocketException
*/
private List<InetAddress> getIPv4Addresses() throws SocketException {
Iterator<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces().asIterator();
List<InetAddress> addresses = new ArrayList<>();
// Get IPv4 addresses from all network interfaces
if (null != networkInterfaces) {
while (networkInterfaces.hasNext()) {
NetworkInterface currentNetworkInterface = networkInterfaces.next();
Iterator<InetAddress> inetAddresses = currentNetworkInterface.getInetAddresses().asIterator();
while (inetAddresses.hasNext()) {
InetAddress currentAddress = inetAddresses.next();
if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) {
addresses.add(currentAddress);
}
}
}
}
return addresses;
}
/**
* This method will find any active host in the network and return a list of them
*
* @param addresses
* @return List of hosts
* @throws UnknownHostException
* @throws IOException
*/
private List<InetAddress> findActiveHosts(List<InetAddress> addresses) throws UnknownHostException, IOException {
List<InetAddress> hosts = new ArrayList<>();
for (InetAddress inetAddress : addresses) {
String[] addressStrings = inetAddress.getHostAddress().split("[.]");
String subnet = addressStrings[0] + "." + addressStrings[1] + "." + addressStrings[2];
int timeout = 50;
for (int i = 0; i < 255; i++) {
String host = subnet + "." + i;
if (!inetAddress.getHostAddress().equals(host)) {
if (InetAddress.getByName(host).isReachable(timeout)) {
hosts.add(InetAddress.getByName(host));
}
}
}
}
return hosts;
}
/**
* This method builds a thing based on the response from the device
*
* @param response
*/
private void buildDiscoveryResult(CommData response, Boolean isHostname) {
ThingUID thingUID = new ThingUID(THING_TYPE_LIQUID_CHECK, response.payload.device.uuid);
DiscoveryResult dResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(response.createPropertyMap(isHostname)).withLabel(response.payload.device.name).build();
thingDiscovered(dResult);
}
}

View File

@ -0,0 +1,99 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.httpclient;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
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.openhab.binding.liquidcheck.internal.LiquidCheckConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link LiquidCheckHttpClient} sets up the jetty client for the connection to the device.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class LiquidCheckHttpClient {
private final Logger logger = LoggerFactory.getLogger(LiquidCheckHttpClient.class);
private final HttpClient client;
private final LiquidCheckConfiguration config;
public boolean isClosed = false;
/**
* The Constructor of the LiquidCheckHttpClient class will set up a jetty client
*
* @param config
*/
public LiquidCheckHttpClient(LiquidCheckConfiguration config, HttpClient client) {
this.config = config;
this.client = client;
}
/**
* The pollData method will poll the data from device
*
* @return String with the response of the request
* @throws InterruptedException
* @throws TimeoutException
* @throws ExecutionException
*/
public String pollData() throws InterruptedException, TimeoutException, ExecutionException {
String uri = "http://" + config.hostname + "/infos.json";
Request request = client.newRequest(uri).method(HttpMethod.GET)
.timeout(config.connectionTimeout, TimeUnit.SECONDS).followRedirects(false);
logger.debug("Polling for data");
ContentResponse response = request.send();
return response.getContentAsString();
}
/**
* The measureCommand method will start a measurement
*
* @return String with response of the request
* @throws InterruptedException
* @throws TimeoutException
* @throws ExecutionException
*/
public String measureCommand() throws InterruptedException, TimeoutException, ExecutionException {
String uri = "http://" + config.hostname + "/command";
Request request = client.newRequest(uri);
request.method(HttpMethod.POST);
request.header(HttpHeader.CONTENT_TYPE, "applicaton/json");
request.content(new StringContentProvider(
"{\"header\":{\"namespace\":\"Device.Control\",\"name\":\"StartMeasure\",\"messageId\":\"1\",\"payloadVersion\":\"1\"},\"payload\":null}"));
ContentResponse response = request.send();
return response.getContentAsString();
}
/**
* The isConnected method will return the state of the http client
*
* @return
*/
public boolean isConnected() {
String state = this.client.getState();
return "STARTED".equals(state);
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link AccessPoint} is used for serializing and deserializing of JSONs.
* It contains the data for ssid, bssid and the rssi value.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class AccessPoint {
public String ssid = "";
public String bssid = "";
public int rssi = 0;
}

View File

@ -0,0 +1,64 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PROPERTY_HOSTNAME;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PROPERTY_IP;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PROPERTY_NAME;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PROPERTY_SECURITY_CODE;
import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.PROPERTY_SSID;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.Thing;
/**
* The {@link CommData} is used for serializing and deserializing of JSONs.
* It contains the complete communication data with header and payload or context.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class CommData {
public Header header = new Header();
public Payload payload = new Payload();
public Context context = new Context();
public Map<String, String> createPropertyMap() {
Map<String, String> properties = new HashMap<>();
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, payload.device.firmware);
properties.put(Thing.PROPERTY_HARDWARE_VERSION, payload.device.hardware);
properties.put(PROPERTY_NAME, payload.device.name);
properties.put(Thing.PROPERTY_VENDOR, payload.device.manufacturer);
properties.put(Thing.PROPERTY_SERIAL_NUMBER, payload.device.uuid);
properties.put(PROPERTY_SECURITY_CODE, payload.device.security.code);
properties.put(PROPERTY_IP, payload.wifi.station.ip);
properties.put(Thing.PROPERTY_MAC_ADDRESS, payload.wifi.station.mac);
properties.put(PROPERTY_SSID, payload.wifi.accessPoint.ssid);
return properties;
}
public Map<String, Object> createPropertyMap(boolean isHostname) {
Map<String, Object> properties = new HashMap<>(createPropertyMap());
if (isHostname) {
properties.put(PROPERTY_HOSTNAME, payload.wifi.station.hostname);
} else {
properties.put(PROPERTY_HOSTNAME, payload.wifi.station.ip);
}
return properties;
}
}

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Context} is used for serializing and deserializing of JSONs.
* It contains the status message.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Context {
public String status = "";
}

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Device} is used for serializing and deserializing of JSONs.
* It contains the device related data like firmware, hardware, name, the model class,
* manufacturer, uuid and the security class.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Device {
public String firmware = "";
public String hardware = "";
public String name = "";
public Model model = new Model();
public String manufacturer = "";
public String uuid = "";
public Security security = new Security();
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Expansion} is used for serializing and deserializing of JSONs.
* It contains the Expansion related data like boardType, oneWire and board.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Expansion {
public int boardType = 0;
public String oneWire = "";
public String board = "";
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.Expose;
/**
* The {@link Header} class is used for serializing and deserializing of JSONs.
* It contains the data lika namespace, name, messageId, payloadVersion and authorization.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Header {
@Expose
public String namespace = "";
@Expose
public String name = "";
@Expose
public String messageId = "";
@Expose
public String payloadVersion = "";
public String authorization = "";
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link LiquidCheckSystem} is used for serializing and deserializing of JSONs.
* It contains the error counter, the uptime and the pump class.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class LiquidCheckSystem {
public int error = 0;
public int uptime = 0;
public Pump pump = new Pump();
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Measure} is used for serializing and deserializing of JSONs.
* It contains the measured data like level, content and the raw class and the age of the data.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Measure {
public double level = 0.0;
public double content = 0.0;
public Raw raw = new Raw();
public int age = 0;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Model} is used for serializing and deserializing of JSONs.
* It contains the name und the number of the model.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Model {
public String name = "";
public int number = 0;
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Payload} is used for serializing and deserializing of JSONs.
* It contains the complete data set within the Measure class, the Expansion class, the Device class, the
* LiquidCheckSystem class and the wifi class.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Payload {
public Measure measure = new Measure();
public Expansion expansion = new Expansion();
public Device device = new Device();
public LiquidCheckSystem system = new LiquidCheckSystem();
public Wifi wifi = new Wifi();
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Pump} is used for serializing and deserializing of JSONs.
* It contains the pump data like total runs and total runtime.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Pump {
public int totalRuns = 0;
public int totalRuntime = 0;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Raw} is used for serializing and deserializing of JSONs.
* It contains the raw data measured and calculated by the sensor.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Raw {
public double level = 0.0000;
public double content = 0.0000;
}

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Security} is used for serializing and deserializing of JSONs.
* It contains the security code for cloud connection of the device.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Security {
public String code = "";
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Station} is used for serializing and deserializing of JSONs.
* It contains the station related data like hostname, ip address, gateway, netmask and mac address.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Station {
public String hostname = "";
public String ip = "";
public String gateway = "";
public String netmask = "";
public String mac = "";
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Wifi} is used for serializing and deserializing of JSONs.
* It conatains the wifi related data within the Station class and AccessPoint class.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Wifi {
public Station station = new Station();
public AccessPoint accessPoint = new AccessPoint();
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="liquidcheck" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>binding</type>
<name>LiquidCheck Binding</name>
<description>This is the binding for LiquidCheck devices.</description>
<connection>local</connection>
</addon:addon>

View File

@ -0,0 +1,38 @@
# add-on
addon.liquidcheck.name = LiquidCheck Binding
addon.liquidcheck.description = This is the binding for LiquidCheck devices.
# thing types
thing-type.liquidcheck.liquidCheckDevice.label = Liquid Check Device
# thing types config
thing-type.config.liquidcheck.liquidCheckDevice.connectionTimeout.label = Connection Timeout
thing-type.config.liquidcheck.liquidCheckDevice.connectionTimeout.description = After the given amount of seconds without a response, the connection will be seen as timed out.
thing-type.config.liquidcheck.liquidCheckDevice.hostname.label = Hostname
thing-type.config.liquidcheck.liquidCheckDevice.hostname.description = Hostname or IP address of the device.
thing-type.config.liquidcheck.liquidCheckDevice.maxContent.label = Maximal Content
thing-type.config.liquidcheck.liquidCheckDevice.maxContent.description = Maximal content in the container.
thing-type.config.liquidcheck.liquidCheckDevice.refreshInterval.label = Refresh Interval
thing-type.config.liquidcheck.liquidCheckDevice.refreshInterval.description = Interval the device is polled in seconds.
# channel types
channel-type.liquidcheck.content-channel.label = Content
channel-type.liquidcheck.content-channel.description = Content in the container
channel-type.liquidcheck.fill-indicator-channel.label = Fill Indicator
channel-type.liquidcheck.fill-indicator-channel.description = Indicates the fill level based on max content and measured content
channel-type.liquidcheck.level-channel.label = Level
channel-type.liquidcheck.level-channel.description = Level in the container
channel-type.liquidcheck.measure-channel.label = Measure
channel-type.liquidcheck.measure-channel.description = Triggers a measurement
channel-type.liquidcheck.pump-runs-channel.label = Pump Total Runs
channel-type.liquidcheck.pump-runs-channel.description = Number of pump starts in total
channel-type.liquidcheck.pump-runtime-channel.label = Pump Total Runtime
channel-type.liquidcheck.pump-runtime-channel.description = Seconds the pump has runned since manufacturing
channel-type.liquidcheck.raw-content-channel.label = Content Raw Data
channel-type.liquidcheck.raw-content-channel.description = Content in the container as measured by the device
channel-type.liquidcheck.raw-level-channel.label = Level Raw Data
channel-type.liquidcheck.raw-level-channel.description = Level in the container as measured by the device

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="liquidcheck"
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 id="liquidCheckDevice">
<label>Liquid Check Device</label>
<category>Sensor</category>
<channels>
<channel id="content" typeId="content-channel"/>
<channel id="level" typeId="level-channel"/>
<channel id="fill-indicator" typeId="fill-indicator-channel"/>
<channel id="raw-content" typeId="raw-content-channel"/>
<channel id="raw-level" typeId="raw-level-channel"/>
<channel id="pump-runs" typeId="pump-runs-channel"/>
<channel id="pump-runtime" typeId="pump-runtime-channel"/>
<channel id="measure" typeId="measure-channel"/>
</channels>
<config-description>
<parameter name="hostname" type="text" required="true">
<context>network-address</context>
<label>Hostname</label>
<description>Hostname or IP address of the device.</description>
</parameter>
<parameter name="maxContent" type="integer" unit="l" min="1">
<label>Maximal Content</label>
<description>Maximal content in the container.</description>
<default>1</default>
</parameter>
<parameter name="refreshInterval" type="integer" unit="s" min="1" max="3600">
<label>Refresh Interval</label>
<description>Interval the device is polled in seconds.</description>
<default>60</default>
<advanced>true</advanced>
</parameter>
<parameter name="connectionTimeout" type="integer" unit="s" min="1" max="120">
<label>Connection Timeout</label>
<description>After the given amount of seconds without a response, the connection will be seen as timed out.</description>
<default>5</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<channel-type id="content-channel">
<item-type>Number:Volume</item-type>
<label>Content</label>
<description>Content in the container</description>
<state readOnly="true" pattern="%d l"/>
</channel-type>
<channel-type id="raw-content-channel">
<item-type>Number:Volume</item-type>
<label>Content Raw Data</label>
<description>Content in the container as measured by the device</description>
<state readOnly="true" pattern="%.4f l"/>
</channel-type>
<channel-type id="level-channel">
<item-type>Number:Length</item-type>
<label>Level</label>
<description>Level in the container</description>
<state readOnly="true" pattern="%.2f m"/>
</channel-type>
<channel-type id="raw-level-channel">
<item-type>Number:Length</item-type>
<label>Level Raw Data</label>
<description>Level in the container as measured by the device</description>
<state readOnly="true" pattern="%.4f m"/>
</channel-type>
<channel-type id="fill-indicator-channel">
<item-type>Number:Dimensionless</item-type>
<label>Fill Indicator</label>
<description>Indicates the fill level based on max content and measured content</description>
<state readOnly="true" pattern="%.1f %%"/>
</channel-type>
<channel-type id="measure-channel">
<item-type>Switch</item-type>
<label>Measure</label>
<description>Triggers a measurement</description>
</channel-type>
<channel-type id="pump-runs-channel">
<item-type>Number</item-type>
<label>Pump Total Runs</label>
<description>Number of pump starts in total</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="pump-runtime-channel">
<item-type>Number:Time</item-type>
<label>Pump Total Runtime</label>
<description>Seconds the pump has run since manufacturing</description>
<state readOnly="true" pattern="%d s"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2023 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.liquidcheck.internal.json;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
/**
* The {@link ResponseTest} will test the correct parsing from json responses for the CommData class.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class ResponseTest {
@DisplayName("Test response from polling")
@Test
@SuppressWarnings("null")
public void pollingResponseTest() {
CommData response = new CommData();
try {
List<String> lines = Files.readAllLines(Paths.get("src/test/resources/PollingResponseExample.json"),
StandardCharsets.UTF_8);
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line);
}
String json = sb.toString();
response = new Gson().fromJson(json, CommData.class);
} catch (Exception e) {
return;
}
if (response != null && !"".equals(response.header.messageId)) {
assertThat(response, is(notNullValue()));
assertThat(response.header.namespace, is(equalTo("Device")));
assertThat(response.header.name, is(equalTo("Response")));
assertThat(response.header.messageId, is(equalTo("499C7D21-F9579A3C")));
assertThat(response.header.payloadVersion, is(equalTo("1")));
assertThat(response.header.authorization, is(equalTo("1C9DC262BE70-00038BC8-TX0K103HIXCXVLTBMVKVXFF")));
assertThat(response.payload.measure.level, is(equalTo(2.23)));
assertThat(response.payload.measure.content, is(equalTo(9265.0)));
assertThat(response.payload.measure.age, is(equalTo(1981)));
assertThat(response.payload.measure.raw.level, is(equalTo(2.2276)));
assertThat(response.payload.measure.raw.content, is(equalTo(9255.3193)));
assertThat(response.payload.expansion.boardType, is(equalTo(-1)));
assertThat(response.payload.expansion.board, is(nullValue()));
assertThat(response.payload.expansion.oneWire, is(nullValue()));
assertThat(response.payload.device.firmware, is(equalTo("1.60")));
assertThat(response.payload.device.hardware, is(equalTo("B5")));
assertThat(response.payload.device.name, is(equalTo("Liquid-Check")));
assertThat(response.payload.device.manufacturer, is(equalTo("SI-Elektronik GmbH")));
assertThat(response.payload.device.uuid, is(equalTo("0ba64a0c-7a88b168-0001")));
assertThat(response.payload.device.model.name, is(equalTo("")));
assertThat(response.payload.device.model.number, is(equalTo(1)));
assertThat(response.payload.device.security.code, is(equalTo("gkzQ5uGo6ElSdUsDWKQu2A==")));
assertThat(response.payload.system.error, is(equalTo(0)));
assertThat(response.payload.system.uptime, is(equalTo(232392)));
assertThat(response.payload.system.pump.totalRuns, is(equalTo(351)));
assertThat(response.payload.system.pump.totalRuntime, is(equalTo(1249)));
assertThat(response.payload.wifi.station.hostname, is(equalTo("Liquid-Check")));
assertThat(response.payload.wifi.station.ip, is(equalTo("192.168.2.102")));
assertThat(response.payload.wifi.station.gateway, is(equalTo("192.168.2.1")));
assertThat(response.payload.wifi.station.netmask, is(equalTo("255.255.255.0")));
assertThat(response.payload.wifi.station.mac, is(equalTo("1C:9D:C2:62:BE:70")));
assertThat(response.payload.wifi.accessPoint.ssid, is(equalTo("WLAN-267994")));
assertThat(response.payload.wifi.accessPoint.bssid, is(equalTo("4C:09:D4:2B:C3:97")));
assertThat(response.payload.wifi.accessPoint.rssi, is(equalTo(-45)));
}
}
@DisplayName("Test response from measurement command")
@Test
public void commandResponseTest() {
CommData response = new CommData();
try {
List<String> lines = Files.readAllLines(Paths.get("src/test/resources/CommandResponseExample.json"),
StandardCharsets.UTF_8);
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line);
}
String json = sb.toString();
response = new Gson().fromJson(json, CommData.class);
} catch (Exception e) {
return;
}
if (response != null && !"".equals(response.header.messageId)) {
assertThat(response, is(notNullValue()));
assertThat(response.header.namespace, is(equalTo("Device.Control")));
assertThat(response.header.name, is(equalTo("StartMeasure.Response")));
assertThat(response.header.messageId, is(equalTo("6D6A415C-A116FF36")));
assertThat(response.header.payloadVersion, is(equalTo("1")));
assertThat(response.header.authorization, is(equalTo("1C9DC262BE70-001092EA-4D4KU4ID5ZCXPNTQJ3V8HD")));
assertThat(response.context.status, is(equalTo("success")));
}
}
}

View File

@ -0,0 +1,12 @@
{
"header":{
"namespace":"Device.Control",
"name":"StartMeasure.Response",
"messageId":"6D6A415C-A116FF36",
"payloadVersion":"1",
"authorization":"1C9DC262BE70-001092EA-4D4KU4ID5ZCXPNTQJ3V8HD"
},
"context":{
"status" : "success"
}
}

View File

@ -0,0 +1,62 @@
{
"header":{
"namespace":"Device",
"name":"Response",
"messageId":"499C7D21-F9579A3C",
"payloadVersion":"1",
"authorization":"1C9DC262BE70-00038BC8-TX0K103HIXCXVLTBMVKVXFF"
},
"payload":{
"measure":{
"level":2.23,
"content":9265,
"raw":{
"level":2.2276,
"content":9255.3193
},
"age":1981
},
"expansion":{
"boardType":-1,
"oneWire":null,
"board":null
},
"device":{
"firmware":"1.60",
"hardware":"B5",
"name":"Liquid-Check",
"model":{
"name":"",
"number":1
},
"manufacturer":"SI-Elektronik GmbH",
"uuid":"0ba64a0c-7a88b168-0001",
"security":{
"code":"gkzQ5uGo6ElSdUsDWKQu2A=="
}
},
"system":{
"error":0,
"uptime":232392,
"pump":{
"totalRuns":351,
"totalRuntime":1249
}
},
"wifi":{
"station":{
"hostname":"Liquid-Check",
"ip":"192.168.2.102",
"gateway":"192.168.2.1",
"netmask":"255.255.255.0",
"mac":"1C:9D:C2:62:BE:70"
},
"accessPoint":{
"ssid":"WLAN-267994",
"bssid":"4C:09:D4:2B:C3:97",
"rssi":-45
}
}
}
}

View File

@ -209,6 +209,7 @@
<module>org.openhab.binding.lifx</module>
<module>org.openhab.binding.linky</module>
<module>org.openhab.binding.linuxinput</module>
<module>org.openhab.binding.liquidcheck</module>
<module>org.openhab.binding.lirc</module>
<module>org.openhab.binding.livisismarthome</module>
<module>org.openhab.binding.logreader</module>