Skip to content

Commit

Permalink
[liquidcheck] Initial contribution (#13287)
Browse files Browse the repository at this point in the history
* Add new binding liquidcheck

Signed-off-by: Marcel Goerentz <m.goerentz@t-online.de>
  • Loading branch information
marcelGoerentz authored May 13, 2023
1 parent 939c9ca commit ab16c94
Show file tree
Hide file tree
Showing 34 changed files with 1,530 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.liquidcheck/NOTICE
Original file line number Diff line number Diff line change
@@ -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
64 changes: 64 additions & 0 deletions bundles/org.openhab.binding.liquidcheck/README.md
Original file line number Diff line number Diff line change
@@ -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}
```
17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.liquidcheck/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
}
Loading

0 comments on commit ab16c94

Please sign in to comment.