diff --git a/CODEOWNERS b/CODEOWNERS
index 2e4a2c55e..210ce9a62 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -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
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 206ff34f0..420d5cd44 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -871,6 +871,11 @@
org.openhab.binding.linuxinput${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.liquidcheck
+ ${project.version}
+ org.openhab.addons.bundlesorg.openhab.binding.lirc
diff --git a/bundles/org.openhab.binding.liquidcheck/NOTICE b/bundles/org.openhab.binding.liquidcheck/NOTICE
new file mode 100644
index 000000000..38d625e34
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/NOTICE
@@ -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
diff --git a/bundles/org.openhab.binding.liquidcheck/README.md b/bundles/org.openhab.binding.liquidcheck/README.md
new file mode 100644
index 000000000..3eeb556e5
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/README.md
@@ -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}
+```
diff --git a/bundles/org.openhab.binding.liquidcheck/pom.xml b/bundles/org.openhab.binding.liquidcheck/pom.xml
new file mode 100644
index 000000000..8fc8fd32b
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 4.0.0-SNAPSHOT
+
+
+ org.openhab.binding.liquidcheck
+
+ openHAB Add-ons :: Bundles :: LiquidCheck Binding
+
+
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/feature/feature.xml b/bundles/org.openhab.binding.liquidcheck/src/main/feature/feature.xml
new file mode 100644
index 000000000..ef7b1adc1
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.liquidcheck/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckBindingConstants.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckBindingConstants.java
new file mode 100644
index 000000000..9d48b823d
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckBindingConstants.java
@@ -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 SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LIQUID_CHECK);
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckConfiguration.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckConfiguration.java
new file mode 100644
index 000000000..200146476
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckConfiguration.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckHandler.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckHandler.java
new file mode 100644
index 000000000..82709680a
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckHandler.java
@@ -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 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 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();
+ }
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckHandlerFactory.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckHandlerFactory.java
new file mode 100644
index 000000000..d3998bfb3
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/LiquidCheckHandlerFactory.java
@@ -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 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/discovery/LiquidCheckDiscoveryService.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/discovery/LiquidCheckDiscoveryService.java
new file mode 100644
index 000000000..13091d770
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/discovery/LiquidCheckDiscoveryService.java
@@ -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 addresses = getIPv4Addresses();
+ List 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 getIPv4Addresses() throws SocketException {
+ Iterator networkInterfaces = NetworkInterface.getNetworkInterfaces().asIterator();
+ List addresses = new ArrayList<>();
+ // Get IPv4 addresses from all network interfaces
+ if (null != networkInterfaces) {
+ while (networkInterfaces.hasNext()) {
+ NetworkInterface currentNetworkInterface = networkInterfaces.next();
+ Iterator 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 findActiveHosts(List addresses) throws UnknownHostException, IOException {
+ List 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);
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/httpclient/LiquidCheckHttpClient.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/httpclient/LiquidCheckHttpClient.java
new file mode 100644
index 000000000..2ffa6e2b2
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/httpclient/LiquidCheckHttpClient.java
@@ -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);
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/AccessPoint.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/AccessPoint.java
new file mode 100644
index 000000000..c54dbc6b5
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/AccessPoint.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/CommData.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/CommData.java
new file mode 100644
index 000000000..1dde132c4
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/CommData.java
@@ -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 createPropertyMap() {
+ Map 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 createPropertyMap(boolean isHostname) {
+ Map 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Context.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Context.java
new file mode 100644
index 000000000..49f4b46c1
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Context.java
@@ -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 = "";
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Device.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Device.java
new file mode 100644
index 000000000..3949d7f69
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Device.java
@@ -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();
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Expansion.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Expansion.java
new file mode 100644
index 000000000..fdbeb5ceb
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Expansion.java
@@ -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 = "";
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Header.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Header.java
new file mode 100644
index 000000000..d04319e62
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Header.java
@@ -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 = "";
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/LiquidCheckSystem.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/LiquidCheckSystem.java
new file mode 100644
index 000000000..bd4c3a083
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/LiquidCheckSystem.java
@@ -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();
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Measure.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Measure.java
new file mode 100644
index 000000000..26511f40d
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Measure.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Model.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Model.java
new file mode 100644
index 000000000..33852d92f
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Model.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Payload.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Payload.java
new file mode 100644
index 000000000..9a56106ba
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Payload.java
@@ -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();
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Pump.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Pump.java
new file mode 100644
index 000000000..543985bc5
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Pump.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Raw.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Raw.java
new file mode 100644
index 000000000..a5e776ddc
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Raw.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Security.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Security.java
new file mode 100644
index 000000000..75a04a529
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Security.java
@@ -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 = "";
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Station.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Station.java
new file mode 100644
index 000000000..5dcadd596
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Station.java
@@ -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 = "";
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Wifi.java b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Wifi.java
new file mode 100644
index 000000000..4880553f9
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/java/org/openhab/binding/liquidcheck/internal/json/Wifi.java
@@ -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();
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644
index 000000000..56623ca9f
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/addon/addon.xml
@@ -0,0 +1,11 @@
+
+
+
+ binding
+ LiquidCheck Binding
+ This is the binding for LiquidCheck devices.
+ local
+
+
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/i18n/liquidcheck.properties b/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/i18n/liquidcheck.properties
new file mode 100644
index 000000000..c711f1dce
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/i18n/liquidcheck.properties
@@ -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
diff --git a/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 000000000..fa4c133a7
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+ Sensor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ network-address
+
+ Hostname or IP address of the device.
+
+
+
+ Maximal content in the container.
+ 1
+
+
+
+ Interval the device is polled in seconds.
+ 60
+ true
+
+
+
+ After the given amount of seconds without a response, the connection will be seen as timed out.
+ 5
+ true
+
+
+
+
+
+ Number:Volume
+
+ Content in the container
+
+
+
+ Number:Volume
+
+ Content in the container as measured by the device
+
+
+
+ Number:Length
+
+ Level in the container
+
+
+
+ Number:Length
+
+ Level in the container as measured by the device
+
+
+
+ Number:Dimensionless
+
+ Indicates the fill level based on max content and measured content
+
+
+
+ Switch
+
+ Triggers a measurement
+
+
+ Number
+
+ Number of pump starts in total
+
+
+
+ Number:Time
+
+ Seconds the pump has run since manufacturing
+
+
+
diff --git a/bundles/org.openhab.binding.liquidcheck/src/test/java/org/openhab/binding/liquidcheck/internal/json/ResponseTest.java b/bundles/org.openhab.binding.liquidcheck/src/test/java/org/openhab/binding/liquidcheck/internal/json/ResponseTest.java
new file mode 100644
index 000000000..f61cfef97
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/test/java/org/openhab/binding/liquidcheck/internal/json/ResponseTest.java
@@ -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 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 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")));
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/test/resources/CommandResponseExample.json b/bundles/org.openhab.binding.liquidcheck/src/test/resources/CommandResponseExample.json
new file mode 100644
index 000000000..e7b03a5af
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/test/resources/CommandResponseExample.json
@@ -0,0 +1,12 @@
+{
+ "header":{
+ "namespace":"Device.Control",
+ "name":"StartMeasure.Response",
+ "messageId":"6D6A415C-A116FF36",
+ "payloadVersion":"1",
+ "authorization":"1C9DC262BE70-001092EA-4D4KU4ID5ZCXPNTQJ3V8HD"
+ },
+ "context":{
+ "status" : "success"
+ }
+}
diff --git a/bundles/org.openhab.binding.liquidcheck/src/test/resources/PollingResponseExample.json b/bundles/org.openhab.binding.liquidcheck/src/test/resources/PollingResponseExample.json
new file mode 100644
index 000000000..68bd17128
--- /dev/null
+++ b/bundles/org.openhab.binding.liquidcheck/src/test/resources/PollingResponseExample.json
@@ -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
+ }
+ }
+ }
+}
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index ab2a843d0..6ee1c3186 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -209,6 +209,7 @@
org.openhab.binding.lifxorg.openhab.binding.linkyorg.openhab.binding.linuxinput
+ org.openhab.binding.liquidcheckorg.openhab.binding.lircorg.openhab.binding.livisismarthomeorg.openhab.binding.logreader