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.bundles org.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.lifx org.openhab.binding.linky org.openhab.binding.linuxinput + org.openhab.binding.liquidcheck org.openhab.binding.lirc org.openhab.binding.livisismarthome org.openhab.binding.logreader