Skip to content

Commit

Permalink
[linkTap] PR Prep changes
Browse files Browse the repository at this point in the history
[linkTap] PR Prep changes

Signed-off-by: dag81 <[email protected]>
  • Loading branch information
dag81 committed Aug 20, 2024
1 parent 0c404fd commit 3709a3f
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 267 deletions.
7 changes: 7 additions & 0 deletions bundles/org.openhab.binding.linktap/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ https://www.eclipse.org/legal/epl-2.0/.
== Source Code

https://github.com/openhab/openhab-addons

== Third-party Content

jsoup
* License: MIT License
* Project: https://jsoup.org/
* Source: https://github.com/jhy/jsoup
30 changes: 8 additions & 22 deletions bundles/org.openhab.binding.linktap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ The currently supported capabilities include where supported by the gateway / de

A LinkTap gateway device, in order for openHAB to connect to the system, as a bridge.

## Connection options
## Connection Options

LinkTap supports MQTT and a direct interaction via HTTP.
This binding directly interact's with LinkTap's bridges using the Local HTTP API (HTTP).
The binding connects to the bridge's directly, and the Gateway is configured automatically to push updates to openHAB if
it has a HTTP configured server. (Note HTTPS is not supported).
it has a HTTP configured server.
(Note HTTPS is not supported).

Should the Gateway device's not be able to connect to the binding it automatically falls-back to a polling
implementation (15 second cycle). The gateway supports 1 Local HTTP API, for an ideal behaviour the Gateway should be able to
implementation (15 second cycle).
The gateway supports 1 Local HTTP API, for an ideal behaviour the Gateway should be able to
connect to OpenHab on an HTTP port from its IP, and only a single OpenHab instance should be connected to a Gateway.

It is recommended that you use **static IP's** for this binding, **for both openHAB and the Gateway device(s)**.
Expand Down Expand Up @@ -60,7 +62,7 @@ If the gateway cannot publish to openHAB, then the gateway is checked every 2 mi

## Binding Configuration

### Bridge configuration parameters
### Bridge Configuration Parameters

| Name | Type | Description | Recommended Values | Required | Advanced |
|------------|--------|-----------------------------------------------------------------------------------|--------------------|----------|----------|
Expand All @@ -71,7 +73,7 @@ If the gateway cannot publish to openHAB, then the gateway is checked every 2 mi

**NOTE** When enableMDNS is enabled, upon connection to the gateway option "Enable mDNS responder" is switched on

### LinkTap Device configuration parameters
### LinkTap Device Configuration Parameters

It is recommended to use the Device Id, for locating devices. This can be found in the LinkTap mobile application under
Settings->TapLinker / ValveLinker, e.g.
Expand All @@ -91,21 +93,6 @@ Settings->TapLinker / ValveLinker, e.g.
- a **deviceId** or a **deviceName must be provided** to communicate with the device
- **enableAlerts** allows the binding to **receive updates of Water Cut, and other alerts** conditions are detected for a LinkTap device.

## Thing Configuration

_Describe what is needed to manually configure a thing, either through the UI or via a thing-file._
_This should be mainly about its mandatory and optional configuration parameters._

_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._

### `sample` Thing Configuration

| Name | Type | Description | Default | Required | Advanced |
|-----------------|---------|---------------------------------------|---------|----------|----------|
| host | text | Hostname or IP address of the device | N/A | yes | no |
| password | text | Password to access the device | N/A | yes | no |
| refreshInterval | integer | Interval the device is polled in sec. | 600 | no | yes |

## Channels

There are 3 different Area of channels:
Expand Down Expand Up @@ -196,6 +183,5 @@ Number:Volume Tap1WateringCycleVol "Tap 1 - Current Cycle Volume
### Sitemap Configuration

```perl
Optional Sitemap configuration goes here.
Remove this section, if not needed.
To be added
```

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,4 @@ public String toString() {
return String.format("%d - %s", value, description);
}
}

// Property name constants
public static final String DEVICE_PROP_DEVICE_NAME = "Device Name";
public static final String DEVICE_PROP_DEVICE_TYPE = "Device Type";
public static final String DEVICE_PROP_DEVICE_MAC_ID = "MAC Id";
public static final String DEVICE_PROP_DEVICE_FAMILY = "Device Family";
public static final String DEVICE_PROP_DEVICE_UUID = "UUID";
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
import static org.openhab.binding.linktap.internal.LinkTapBridgeHandler.MDNS_LOOKUP;
import static org.openhab.binding.linktap.internal.Utils.cleanPrintableChars;

import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -67,6 +69,8 @@ public class LinkTapBridgeDiscoveryService implements MDNSDiscoveryParticipant {
private static final String[] KEYS = new String[] { RAW_MODEL, RAW_ID, RAW_MAC, RAW_IP, RAW_ADMIN_URL, RAW_VENDOR,
RAW_VERSION };

private static final String TEXT_CHARSET = StandardCharsets.UTF_8.name();

@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return SUPPORTED_THING_TYPES;
Expand Down Expand Up @@ -119,7 +123,11 @@ public String getServiceType() {
logger.trace("[{}] GW ID: {}", itemId, cleanPrintableChars(nameSplit[1]));
}
}
logger.trace("[{}] GW Text {}", itemId, cleanPrintableChars(new String(service.getTextBytes())));
try {
logger.trace("[{}] GW Text {}", itemId,
cleanPrintableChars(new String(service.getTextBytes(), TEXT_CHARSET)));
} catch (UnsupportedEncodingException ignored) {
}

for (int i = 0; i < service.getInet4Addresses().length; ++i) {
logger.trace("[{}] GW IPv4 Address: {}", itemId,
Expand Down Expand Up @@ -192,7 +200,12 @@ public String getServiceType() {

public Properties extractProps(ServiceInfo serviceInfo) {
final Properties result = new Properties();
final String data = new String(serviceInfo.getTextBytes());
String data = "";
try {
data = new String(serviceInfo.getTextBytes(), TEXT_CHARSET);
} catch (UnsupportedEncodingException uee) {
logger.warn("Missing character set to decode MDNS Text");
}
final int[] keyIndexes = new int[7];

for (int i = 0; i < KEYS.length; ++i) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public class LinkTapBridgeHandler extends BaseBridgeHandler {
public static final LookupWrapper<@Nullable String> MDNS_LOOKUP = new LookupWrapper<>();
private final Object schedulerLock = new Object();
private @Nullable ScheduledFuture<?> backgroundGwPollingScheduler;
private volatile LinkTapBridgeConfiguration config = new LinkTapBridgeConfiguration();

private volatile long lastGwCommandRecvTs = 0L;

Expand Down Expand Up @@ -134,6 +135,7 @@ private void cancelGwPolling() {
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
config = getConfigAs(LinkTapBridgeConfiguration.class);
scheduler.execute(this::connect);
}

Expand Down Expand Up @@ -164,14 +166,12 @@ private void deregisterBridge(final LinkTapBridgeHandler ref) {
}

public String getHost() {
final LinkTapBridgeConfiguration config = getConfigAs(LinkTapBridgeConfiguration.class);
return config.host;
}

private boolean registerBridge(final LinkTapBridgeHandler ref) {
final WebServerApi api = WebServerApi.getInstance();
api.setHttpClient(httpClientProvider.getHttpClient());
final LinkTapBridgeConfiguration config = getConfigAs(LinkTapBridgeConfiguration.class);
try {
final InetAddress ip = InetAddress.getByName(new URL("http://" + config.host).getHost());
final String newHost = ip.getHostAddress();
Expand Down Expand Up @@ -283,7 +283,6 @@ public String sendApiRequest(final TLGatewayFrame req) {
}

private void connect() {
final LinkTapBridgeConfiguration config = getConfigAs(LinkTapBridgeConfiguration.class);
// Check if we can resolve the remote host, if so then it can be mapped back to a bridge handler.
// If not further communications would fail - so its offline.
if (!registerBridge(this)) {
Expand Down Expand Up @@ -312,12 +311,22 @@ private void connect() {
GW_ID_LOOKUP.registerItem(gwId, this, () -> {
});
}
if (Thread.currentThread().isInterrupted()) {
return;
}
getGatewayConfiguration();

if (Thread.currentThread().isInterrupted()) {
return;
}

@NotNull
final String hostname = getHostname(config);

String localServerAddr = "";
try (Socket socket = new Socket()) {
try {
socket.connect(new InetSocketAddress(config.host, 80));
socket.connect(new InetSocketAddress(hostname, 80), 1500);
} catch (IOException e) {
logger.warn("Failed to connect to remote device due to exception", e);
return;
Expand All @@ -331,39 +340,42 @@ private void connect() {
final String servletEp = BindingServlet.getServletAddress(localServerAddr);
final Optional<String> servletEpOpt = (!servletEp.isEmpty()) ? Optional.of(servletEp) : Optional.empty();

@NotNull
final String hostname = getHostname(config);

api.configureBridge(hostname, Optional.of(Boolean.TRUE), servletEpOpt);
updateStatus(ThingStatus.ONLINE);
if (Thread.currentThread().isInterrupted()) {
return;
}
startGwPolling();
} catch (InterruptedException ignored) {
} catch (NotTapLinkGatewayException e) {
deregisterBridge(this);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Target Host is not a LinkTap Gateway");
} catch (TransientCommunicationIssueException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Cannot connect to LinkTap Gateway");
} catch (UnknownHostException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Unknown host");
}
}

@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
}

protected @NotNull String getHostname() {
final LinkTapBridgeConfiguration config = getConfigAs(LinkTapBridgeConfiguration.class);
protected @NotNull String getHostname() throws UnknownHostException {
return getHostname(config);
}

private @NotNull String getHostname(final LinkTapBridgeConfiguration config) {
private @NotNull String getHostname(final LinkTapBridgeConfiguration config) throws UnknownHostException {
@NotNull
String hostname = config.host;
final String mdnsLookup = MDNS_LOOKUP.getItem(config.host);
if (mdnsLookup != null) {
hostname = mdnsLookup;
}
return hostname;
final InetAddress address = InetAddress.getByName(hostname);
return address.getHostAddress();
}

public Map<String, String> getMetadataProperities(final @Nullable HandshakeReq handshake) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ public LinkTapHandler(Thing thing, Storage<String> strStore) {
@Override
protected void runStartInit() {
try {
final LinkTapDeviceConfiguration config = getConfigAs(LinkTapDeviceConfiguration.class);
if (config.enableAlerts) {
sendRequest(new AlertStateReq(0, true));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ public abstract class PollingDeviceHandler extends BaseThingHandler implements I

private final Logger logger = LoggerFactory.getLogger(PollingDeviceHandler.class);

protected Logger getLogger() {
return logger;
}

protected static final String MARKER_INVALID_DEVICE_KEY = "---INVALID---";
protected String registeredDeviceId = EMPTY_STRING;

Expand All @@ -70,6 +66,8 @@ protected Logger getLogger() {
private final Object readBackPollLock = new Object();
private @Nullable ScheduledFuture<?> readBackPollSf = null;

protected volatile LinkTapDeviceConfiguration config = new LinkTapDeviceConfiguration();

protected void requestReadbackPoll() {
synchronized (readBackPollLock) {
cancelReadbackPoll();
Expand Down Expand Up @@ -124,7 +122,7 @@ private void cancelStatusPolling() {
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);

config = getConfigAs(LinkTapDeviceConfiguration.class);
final LinkTapBridgeHandler bridge = (LinkTapBridgeHandler) getBridgeHandler();
if (bridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Bridge is not selected / set");
Expand Down Expand Up @@ -228,8 +226,6 @@ public String sendRequest(TLGatewayFrame frame) throws InvalidParameterException

@NotNull
public String getValidatedIdString() {
final LinkTapDeviceConfiguration config = getConfigAs(LinkTapDeviceConfiguration.class);

BridgeHandler bridgeHandler = getBridgeHandler();
if (bridgeHandler instanceof LinkTapBridgeHandler vesyncBridgeHandler) {
final String devId = config.id;
Expand Down
Loading

0 comments on commit 3709a3f

Please sign in to comment.