diff --git a/CODEOWNERS b/CODEOWNERS index 50a78212abfdc..0fb93050ec65b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -314,6 +314,7 @@ /bundles/org.openhab.binding.tibber/ @kjoglum /bundles/org.openhab.binding.tivo/ @mlobstein /bundles/org.openhab.binding.touchwand/ @roieg +/bundles/org.openhab.binding.tplinkrouter/ @olivierkeke /bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand /bundles/org.openhab.binding.tr064/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 930b8d50a74cb..7621f4776a58c 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1561,6 +1561,11 @@ org.openhab.binding.touchwand ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.tplinkrouter + ${project.version} + org.openhab.addons.bundles org.openhab.binding.tplinksmarthome diff --git a/bundles/org.openhab.binding.tplinkrouter/NOTICE b/bundles/org.openhab.binding.tplinkrouter/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/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.tplinkrouter/README.md b/bundles/org.openhab.binding.tplinkrouter/README.md new file mode 100644 index 0000000000000..641413f599456 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/README.md @@ -0,0 +1,76 @@ +# tplinkrouter Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures (only PNG is supported currently), a video, etc. to give an impression of what can be done with this binding._ +_You can place such resources into a `doc` folder next to this README.md._ + +_Put each sentence in a separate line to improve readability of diffs._ + +## Supported Things + +_Please describe the different supported things / devices including their ThingTypeUID within this section._ +_Which different types are supported, which models were tested etc.?_ +_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._ + +- `bridge`: Short description of the Bridge, if any +- `sample`: Short description of the Thing with the ThingTypeUID `sample` + +## Discovery + +_Describe the available auto-discovery features here._ +_Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it._ +_In this section, you should link to this file and provide some information about the options._ +_The file could e.g. look like:_ + +``` +# Configuration for the tplinkrouter Binding +# +# Default secret key for the pairing of the tplinkrouter Thing. +# It has to be between 10-40 (alphanumeric) characters. +# This may be changed by the user for security reasons. +secret=openHABSecret +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/OH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## 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 | +|-----------------|---------|---------------------------------------|---------|----------|----------| +| hostname | 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 + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_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._ + +| Channel | Type | Read/Write | Description | +|---------|--------|------------|-----------------------------| +| control | Switch | RW | This is the control channel | + +## Full Example + +_Provide a full usage example based on textual configuration files._ +_*.things, *.items examples are mandatory as textual configuration is well used by many users._ +_*.sitemap examples are optional._ + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/bundles/org.openhab.binding.tplinkrouter/pom.xml b/bundles/org.openhab.binding.tplinkrouter/pom.xml new file mode 100644 index 0000000000000..1942268b47419 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.3.0-SNAPSHOT + + + org.openhab.binding.tplinkrouter + + openHAB Add-ons :: Bundles :: TpLinkRouter Binding + + diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/feature/feature.xml b/bundles/org.openhab.binding.tplinkrouter/src/main/feature/feature.xml new file mode 100644 index 0000000000000..fbc5b4128f96f --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/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.tplinkrouter/${project.version} + + diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterBindingConstants.java b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterBindingConstants.java new file mode 100644 index 0000000000000..3b7e04bc07df6 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterBindingConstants.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2022 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.tplinkrouter.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link TpLinkRouterBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +public class TpLinkRouterBindingConstants { + + private static final String BINDING_ID = "tplinkrouter"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_TDW9970 = new ThingTypeUID(BINDING_ID, "TD-W9970"); + + // List of all Channel ids + public static final String WIFI_STATUS = "wifi#Status"; + public static final String WIFI_SSID = "wifi#SSID"; + public static final String WIFI_BANDWIDTH = "wifi#bandWidth"; + public static final String WIFI_QSS = "wifi#QSS"; + public static final String WIFI_SECMODE = "wifi#SecMode"; + public static final String WIFI_KEY = "wifi#Key"; +} diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterConfiguration.java b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterConfiguration.java new file mode 100644 index 0000000000000..60da26d467bc3 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterConfiguration.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2022 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.tplinkrouter.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link TpLinkRouterConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +public class TpLinkRouterConfiguration { + + /** + * Sample configuration parameters. Replace with your own. + */ + public String hostname = ""; + public int port = 23; + public String username = ""; + public String password = ""; + public int refreshInterval = 600; +} diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterHandler.java b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterHandler.java new file mode 100644 index 0000000000000..bd27729aa19e1 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterHandler.java @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2010-2022 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.tplinkrouter.internal; + +import static org.openhab.binding.tplinkrouter.internal.TpLinkRouterBindingConstants.*; + +import java.io.IOException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.Channel; +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.openhab.core.types.RefreshType; +import org.openhab.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link TpLinkRouterHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +public class TpLinkRouterHandler extends BaseThingHandler implements TpLinkRouterTelenetListener { + + private static final Integer RECONNECT_DELAY = 60000; // 1 minute + + private final Logger logger = LoggerFactory.getLogger(TpLinkRouterHandler.class); + + private TpLinkRouterConfiguration config = new TpLinkRouterConfiguration(); + private final TpLinkRouterTelnetConnector connector = new TpLinkRouterTelnetConnector(); + private final BlockingQueue commandQueue = new ArrayBlockingQueue<>(1); + private @Nullable ScheduledFuture scheduledFuture; + + public TpLinkRouterHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (WIFI_STATUS.equals(channelUID.getId())) { + try { + commandQueue.put(new ChannelUIDCommand(channelUID, command)); + } catch (InterruptedException e) { + logger.error("Got exception", e); + Thread.currentThread().interrupt(); + } + if (command instanceof RefreshType) { + connector.sendCommand("wlctl show"); + } + if (command == OnOffType.ON) { + connector.sendCommand("wlctl set --switch on"); + } else if (command == OnOffType.OFF) { + connector.sendCommand("wlctl set --switch off"); + } + + // Note: if communication with thing fails for some reason, + // indicate that by setting the status with detail information: + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + // "Could not control device at IP address x.x.x.x"); + } + } + + @Override + public void initialize() { + config = getConfigAs(TpLinkRouterConfiguration.class); + updateStatus(ThingStatus.UNKNOWN); + scheduler.execute(this::createConnection); + } + + @Override + public void dispose() { + ScheduledFuture scheduledFutureLocal = scheduledFuture; + if (scheduledFutureLocal != null) { + scheduledFutureLocal.cancel(true); + } + commandQueue.clear(); + connector.dispose(); + super.dispose(); + } + + private void createConnection() { + connector.dispose(); + try { + connector.connect(this, config, this.getThing().getUID().getAsString()); + } catch (IOException e) { + logger.debug("Error while connecting, will retry in {} ms", RECONNECT_DELAY); + scheduler.schedule(this::createConnection, RECONNECT_DELAY, TimeUnit.MILLISECONDS); + } + } + + @Override + public void receivedLine(String line) { + logger.debug("Received line: {}", line); + Pattern pattern = Pattern.compile("(\\w+)=(.+)"); + Matcher matcher = pattern.matcher(line); + if (matcher.find()) { + String label = matcher.group(1); + String value = matcher.group(2); + switch (label) { + case "Status": + if ("Disabled".equals(value)) { + updateState(WIFI_STATUS, OnOffType.OFF); + } else if ("Enabled".equals(value)) { + updateState(WIFI_STATUS, OnOffType.ON); + } else { + logger.warn("Unsupported value {} for label {}", value, label); + } + break; + case "SSID": + updateState(WIFI_SSID, StringType.valueOf(value)); + break; + case "bandWidth": + updateState(WIFI_BANDWIDTH, StringType.valueOf(value)); + break; + case "QSS": + updateState(WIFI_QSS, StringType.valueOf(value)); + break; + case "SecMode": + updateState(WIFI_SECMODE, StringType.valueOf(value)); + break; + case "Key": + updateState(WIFI_KEY, StringType.valueOf(value)); + break; + } + } else if ("cmd:SUCC".equals(line)) { + ChannelUIDCommand channelUIDCommand = commandQueue.poll(); + if (channelUIDCommand != null && channelUIDCommand.getCommand() instanceof State) { + updateState(channelUIDCommand.getChannelUID(), (State) channelUIDCommand.getCommand()); + } + } else if ("Login incorrect. Try again.".equals(line)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Login or password incorrect"); + } + } + + @Override + public void onReaderThreadStopped() { + updateStatus(ThingStatus.UNINITIALIZED); + logger.debug("try to reconnect in {} ms", RECONNECT_DELAY); + scheduler.schedule(this::createConnection, RECONNECT_DELAY, TimeUnit.MILLISECONDS); + } + + @Override + public void onReaderThreadInterrupted() { + updateStatus(ThingStatus.UNINITIALIZED); + } + + @Override + public void onReaderThreadStarted() { + scheduledFuture = scheduler.scheduleWithFixedDelay(() -> { + Channel wifiStateChannel = this.getThing().getChannel(WIFI_STATUS); + if (wifiStateChannel != null) { + this.handleCommand(wifiStateChannel.getUID(), RefreshType.REFRESH); + } + }, 0, config.refreshInterval, TimeUnit.SECONDS); + updateStatus(ThingStatus.ONLINE); + } + + @Override + public void onCommunicationUnavailable() { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Connection not available. Check if there is not another open connection."); + } +} + +/** + * Stores a command with associated channel + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +class ChannelUIDCommand { + private final ChannelUID channelUID; + private final Command command; + + public ChannelUIDCommand(ChannelUID channelUID, Command command) { + this.channelUID = channelUID; + this.command = command; + } + + public ChannelUID getChannelUID() { + return channelUID; + } + + public Command getCommand() { + return command; + } +} diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterHandlerFactory.java b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterHandlerFactory.java new file mode 100644 index 0000000000000..d11b4c11fb142 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterHandlerFactory.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2022 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.tplinkrouter.internal; + +import static org.openhab.binding.tplinkrouter.internal.TpLinkRouterBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +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.Component; + +/** + * The {@link TpLinkRouterHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.tplinkrouter", service = ThingHandlerFactory.class) +public class TpLinkRouterHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_TDW9970); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_TDW9970.equals(thingTypeUID)) { + return new TpLinkRouterHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterTelenetListener.java b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterTelenetListener.java new file mode 100644 index 0000000000000..86459c52ceaa2 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterTelenetListener.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2022 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.tplinkrouter.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link TpLinkRouterTelenetListener} defines listener for telnet events. + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +public interface TpLinkRouterTelenetListener { + + /** + * The telnet client has received a line. + * + * @param line the received line + */ + void receivedLine(String line); + + /** + * The telnet client has successfully connected to the receiver. + */ + void onReaderThreadStopped(); + + void onReaderThreadInterrupted(); + + void onReaderThreadStarted(); + + void onCommunicationUnavailable(); +} diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterTelnetConnector.java b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterTelnetConnector.java new file mode 100644 index 0000000000000..35089f378f06f --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/java/org/openhab/binding/tplinkrouter/internal/TpLinkRouterTelnetConnector.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2010-2022 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.tplinkrouter.internal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.InterruptedIOException; +import java.io.OutputStreamWriter; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link TpLinkRouterTelnetConnector} is responsible of the telnet connection. + * + * @author Olivier Marceau - Initial contribution + */ +@NonNullByDefault +public class TpLinkRouterTelnetConnector { + + private static final Integer TIMEOUT = 60000; // 1 minute + + private final Logger logger = LoggerFactory.getLogger(TpLinkRouterTelnetConnector.class); + + private @Nullable Thread telnetClientThread; + private @Nullable Socket socket; // use raw socket since commons net usage seems discouraged + private @Nullable OutputStreamWriter out; + + public void connect(TpLinkRouterTelenetListener listener, TpLinkRouterConfiguration config, String thingUID) + throws IOException { + logger.debug("Connecting to {}", config.hostname); + + Socket socketLocal = new Socket(); + socketLocal.connect(new InetSocketAddress(config.hostname, config.port)); + socketLocal.setSoTimeout(TIMEOUT); + socketLocal.setKeepAlive(true); + + InputStreamReader inputStream = new InputStreamReader(socketLocal.getInputStream()); + this.out = new OutputStreamWriter(socketLocal.getOutputStream()); + this.socket = socketLocal; + loginAttempt(listener, inputStream, config); + Thread clientThread = new Thread(() -> listenInputStream(listener, inputStream, config)); + clientThread.setName("OH-binding-" + thingUID); + this.telnetClientThread = clientThread; + clientThread.start(); + logger.debug("TP-Link router telnet client connected to {}", config.hostname); + } + + public void dispose() { + logger.debug("disposing connector"); + Thread clientThread = telnetClientThread; + if (clientThread != null) { + clientThread.interrupt(); + } + Socket socketLocal = socket; + if (socketLocal != null) { + try { + socketLocal.close(); + } catch (IOException e) { + logger.error("Error while disconnecting telnet client", e); + } + } + } + + public void sendCommand(String command) { + logger.debug("sending command: {}", command); + OutputStreamWriter output = out; + if (output != null) { + try { + output.write(command + '\n'); + output.flush(); + } catch (IOException e) { + logger.debug("Error sending command", e); + } + } else { + logger.debug("Cannot send command, no telnet connection"); + } + } + + private void listenInputStream(TpLinkRouterTelenetListener listener, InputStreamReader inputStream, + TpLinkRouterConfiguration config) { + listener.onReaderThreadStarted(); + BufferedReader in = new BufferedReader(inputStream); + try { + while (!Thread.currentThread().isInterrupted()) { + try { + String line = in.readLine(); + if (line != null && !line.isBlank()) { + listener.receivedLine(line); + if ("CLI exited after timing out".equals(line)) { + OutputStreamWriter output = out; + if (output != null) { + output.write("\n"); // trigger a "username:" prompt + output.flush(); + loginAttempt(listener, inputStream, config); + } + } + } + } catch (SocketTimeoutException e) { + logger.trace("Socket timeout"); + } + } + } catch (InterruptedIOException e) { + logger.debug("Error in telnet connection ", e); + } catch (IOException e) { + if (!Thread.currentThread().isInterrupted()) { + logger.debug("Error in telnet connection ", e); + listener.onReaderThreadStopped(); + } + } + if (Thread.currentThread().isInterrupted()) { + logger.debug("Interrupted client thread"); + listener.onReaderThreadInterrupted(); + } + } + + private void loginAttempt(TpLinkRouterTelenetListener listener, InputStreamReader inputStreamReader, + TpLinkRouterConfiguration config) throws IOException { + int charInt; + StringBuilder word = new StringBuilder(); + OutputStreamWriter output = out; + if (output != null) { + try { + while ((charInt = inputStreamReader.read()) != -1) { + word.append((char) charInt); + logger.trace("received char: {}", (char) charInt); + if (word.toString().contains("username:")) { + logger.debug("Sending username"); + output.write(config.username + '\n'); + output.flush(); + word = new StringBuilder(); + } + if (word.toString().contains("password:")) { + logger.debug("Sending password"); + output.write(config.password + '\n'); + output.flush(); + break; + } + } + } catch (SocketTimeoutException e) { + listener.onCommunicationUnavailable(); + } + } + } +} diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 0000000000000..905242ab0c8c9 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + TpLinkRouter Binding + This is the binding for controlling a TP-Link router with telnet. + + diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/thing/channel-group-types.xml b/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/thing/channel-group-types.xml new file mode 100644 index 0000000000000..edcae6b49f888 --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/thing/channel-group-types.xml @@ -0,0 +1,48 @@ + + + + + + Wifi channels + + + + Wifi status + + + + Wifi SSID + + + + Wifi bandwidth + + + + Wifi QSS + + + + Wifi SecMode + + + + Wifi Key + + + + + + Switch + + Switch Type for TpLinkrRouter Binding + + + String + + + + diff --git a/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..e9092db1aa51a --- /dev/null +++ b/bundles/org.openhab.binding.tplinkrouter/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,47 @@ + + + + + + + + + Sample thing for TpLinkRouter Binding + + + + + + + + network-address + + Hostname or IP address of the device + + + username + + Password to access the device + + + password + + Password to access the device + + + + Interval the device is polled in sec. + 60 + true + + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index e1ab1accb9c8d..36186a88e0526 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -1,628 +1,1232 @@ - - + + 4.0.0 - + + org.openhab.addons + org.openhab.addons.reactor + 3.3.0-SNAPSHOT + - + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + pom - + openHAB Add-ons :: Bundles - + + + org.openhab.automation.groovyscripting + org.openhab.automation.jrubyscripting + org.openhab.automation.jsscripting + org.openhab.automation.jythonscripting + org.openhab.automation.pidcontroller + org.openhab.automation.pwm + + org.openhab.io.homekit + org.openhab.io.hueemulation + org.openhab.io.imperihome + org.openhab.io.metrics + org.openhab.io.neeo + org.openhab.io.openhabcloud + + org.openhab.transform.bin2json + org.openhab.transform.exec + org.openhab.transform.javascript + org.openhab.transform.jinja + org.openhab.transform.jsonpath + org.openhab.transform.map + org.openhab.transform.regex + org.openhab.transform.scale + org.openhab.transform.xpath + org.openhab.transform.xslt + + org.openhab.binding.adorne + org.openhab.binding.ahawastecollection + org.openhab.binding.airq + org.openhab.binding.airquality + org.openhab.binding.airvisualnode + org.openhab.binding.alarmdecoder + org.openhab.binding.allplay + org.openhab.binding.amazondashbutton + org.openhab.binding.amazonechocontrol + org.openhab.binding.ambientweather + org.openhab.binding.amplipi + org.openhab.binding.androiddebugbridge + org.openhab.binding.anel + org.openhab.binding.astro + org.openhab.binding.atlona + org.openhab.binding.autelis + org.openhab.binding.automower + org.openhab.binding.avmfritz + org.openhab.binding.benqprojector + org.openhab.binding.bigassfan + org.openhab.binding.bluetooth + org.openhab.binding.bluetooth.airthings + org.openhab.binding.bluetooth.am43 + org.openhab.binding.bluetooth.bluegiga + org.openhab.binding.bluetooth.bluez + org.openhab.binding.bluetooth.blukii + org.openhab.binding.bluetooth.daikinmadoka + org.openhab.binding.bluetooth.enoceanble + org.openhab.binding.bluetooth.generic + org.openhab.binding.bluetooth.govee + org.openhab.binding.bluetooth.roaming + org.openhab.binding.bluetooth.ruuvitag + org.openhab.binding.bmwconnecteddrive + org.openhab.binding.boschindego + org.openhab.binding.boschshc + org.openhab.binding.bosesoundtouch + org.openhab.binding.broadlinkthermostat + org.openhab.binding.bsblan + org.openhab.binding.bticinosmarther + org.openhab.binding.buienradar + org.openhab.binding.caddx + org.openhab.binding.cbus + org.openhab.binding.chromecast + org.openhab.binding.cm11a + org.openhab.binding.comfoair + org.openhab.binding.coolmasternet + org.openhab.binding.coronastats + org.openhab.binding.daikin + org.openhab.binding.dali + org.openhab.binding.danfossairunit + org.openhab.binding.darksky + org.openhab.binding.dbquery + org.openhab.binding.deconz + org.openhab.binding.denonmarantz + org.openhab.binding.deutschebahn + org.openhab.binding.digiplex + org.openhab.binding.digitalstrom + org.openhab.binding.dlinksmarthome + org.openhab.binding.dmx + org.openhab.binding.dominoswiss + org.openhab.binding.doorbird + org.openhab.binding.draytonwiser + org.openhab.binding.dscalarm + org.openhab.binding.dsmr + org.openhab.binding.dwdpollenflug + org.openhab.binding.dwdunwetter + org.openhab.binding.ecobee + org.openhab.binding.ecotouch + org.openhab.binding.ekey + org.openhab.binding.electroluxair + org.openhab.binding.elerotransmitterstick + org.openhab.binding.energenie + org.openhab.binding.enigma2 + org.openhab.binding.enocean + org.openhab.binding.enphase + org.openhab.binding.enturno + org.openhab.binding.epsonprojector + org.openhab.binding.etherrain + org.openhab.binding.evohome + org.openhab.binding.exec + org.openhab.binding.feed + org.openhab.binding.feican + org.openhab.binding.fmiweather + org.openhab.binding.folderwatcher + org.openhab.binding.folding + org.openhab.binding.foobot + org.openhab.binding.freebox + org.openhab.binding.fronius + org.openhab.binding.fsinternetradio + org.openhab.binding.ftpupload + org.openhab.binding.gardena + org.openhab.binding.gce + org.openhab.binding.generacmobilelink + org.openhab.binding.goecharger + org.openhab.binding.gpio + org.openhab.binding.globalcache + org.openhab.binding.gpstracker + org.openhab.binding.gree + org.openhab.binding.groheondus + org.openhab.binding.guntamatic + org.openhab.binding.haassohnpelletstove + org.openhab.binding.harmonyhub + org.openhab.binding.haywardomnilogic + org.openhab.binding.hccrubbishcollection + org.openhab.binding.hdanywhere + org.openhab.binding.hdpowerview + org.openhab.binding.helios + org.openhab.binding.heliosventilation + org.openhab.binding.heos + org.openhab.binding.herzborg + org.openhab.binding.homeconnect + org.openhab.binding.homematic + org.openhab.binding.homewizard + org.openhab.binding.hpprinter + org.openhab.binding.http + org.openhab.binding.hue + org.openhab.binding.hydrawise + org.openhab.binding.hyperion + org.openhab.binding.iammeter + org.openhab.binding.iaqualink + org.openhab.binding.icalendar + org.openhab.binding.icloud + org.openhab.binding.ihc + org.openhab.binding.innogysmarthome + org.openhab.binding.insteon + org.openhab.binding.ipcamera + org.openhab.binding.ipobserver + org.openhab.binding.intesis + org.openhab.binding.ipp + org.openhab.binding.irobot + org.openhab.binding.irtrans + org.openhab.binding.ism8 + org.openhab.binding.jablotron + org.openhab.binding.jeelink + org.openhab.binding.kaleidescape + org.openhab.binding.keba + org.openhab.binding.km200 + org.openhab.binding.knx + org.openhab.binding.kodi + org.openhab.binding.konnected + org.openhab.binding.kostalinverter + org.openhab.binding.kvv + org.openhab.binding.lametrictime + org.openhab.binding.lcn + org.openhab.binding.leapmotion + org.openhab.binding.lghombot + org.openhab.binding.lgtvserial + org.openhab.binding.lgwebos + org.openhab.binding.lifx + org.openhab.binding.linky + org.openhab.binding.linuxinput + org.openhab.binding.lirc + org.openhab.binding.logreader + org.openhab.binding.loxone + org.openhab.binding.luftdateninfo + org.openhab.binding.lutron + org.openhab.binding.luxtronikheatpump + org.openhab.binding.magentatv + org.openhab.binding.mail + org.openhab.binding.max + org.openhab.binding.mcp23017 + org.openhab.binding.mecmeter + org.openhab.binding.melcloud + org.openhab.binding.meteoalerte + org.openhab.binding.meteoblue + org.openhab.binding.meteostick + org.openhab.binding.miele + org.openhab.binding.mielecloud + org.openhab.binding.mihome + org.openhab.binding.miio + org.openhab.binding.mikrotik + org.openhab.binding.millheat + org.openhab.binding.milight + org.openhab.binding.minecraft + org.openhab.binding.modbus + org.openhab.binding.modbus.e3dc + org.openhab.binding.modbus.sbc + org.openhab.binding.modbus.studer + org.openhab.binding.modbus.sunspec + org.openhab.binding.modbus.stiebeleltron + org.openhab.binding.modbus.helioseasycontrols + org.openhab.binding.monopriceaudio + org.openhab.binding.mpd + org.openhab.binding.mqtt + org.openhab.binding.mqtt.espmilighthub + org.openhab.binding.mqtt.generic + org.openhab.binding.mqtt.homeassistant + org.openhab.binding.mqtt.homie + org.openhab.binding.mycroft + org.openhab.binding.myq + org.openhab.binding.mystrom + org.openhab.binding.nanoleaf + org.openhab.binding.neato + org.openhab.binding.neeo + org.openhab.binding.neohub + org.openhab.binding.nest + org.openhab.binding.netatmo + org.openhab.binding.network + org.openhab.binding.networkupstools + org.openhab.binding.nibeheatpump + org.openhab.binding.nibeuplink + org.openhab.binding.nikobus + org.openhab.binding.nikohomecontrol + org.openhab.binding.novafinedust + org.openhab.binding.ntp + org.openhab.binding.nuki + org.openhab.binding.nuvo + org.openhab.binding.nzwateralerts + org.openhab.binding.oceanic + org.openhab.binding.ojelectronics + org.openhab.binding.omnikinverter + org.openhab.binding.omnilink + org.openhab.binding.onebusaway + org.openhab.binding.onewiregpio + org.openhab.binding.onewire + org.openhab.binding.onkyo + org.openhab.binding.opengarage + org.openhab.binding.opensprinkler + org.openhab.binding.openthermgateway + org.openhab.binding.openuv + org.openhab.binding.openweathermap + org.openhab.binding.openwebnet + org.openhab.binding.oppo + org.openhab.binding.orbitbhyve + org.openhab.binding.orvibo + org.openhab.binding.paradoxalarm + org.openhab.binding.pentair + org.openhab.binding.phc + org.openhab.binding.pilight + org.openhab.binding.pioneeravr + org.openhab.binding.pixometer + org.openhab.binding.pjlinkdevice + org.openhab.binding.playstation + org.openhab.binding.plclogo + org.openhab.binding.plugwise + org.openhab.binding.plugwiseha + org.openhab.binding.powermax + org.openhab.binding.proteusecometer + org.openhab.binding.publictransportswitzerland + org.openhab.binding.pulseaudio + org.openhab.binding.pushbullet + org.openhab.binding.pushover + org.openhab.binding.pushsafer + org.openhab.binding.qbus + org.openhab.binding.radiothermostat + org.openhab.binding.regoheatpump + org.openhab.binding.revogi + org.openhab.binding.remoteopenhab + org.openhab.binding.renault + org.openhab.binding.resol + org.openhab.binding.rfxcom + org.openhab.binding.rme + org.openhab.binding.robonect + org.openhab.binding.roku + org.openhab.binding.rotel + org.openhab.binding.russound + org.openhab.binding.sagercaster + org.openhab.binding.samsungtv + org.openhab.binding.satel + org.openhab.binding.semsportal + org.openhab.binding.senechome + org.openhab.binding.seneye + org.openhab.binding.sensebox + org.openhab.binding.sensibo + org.openhab.binding.serial + org.openhab.binding.serialbutton + org.openhab.binding.shelly + org.openhab.binding.silvercrestwifisocket + org.openhab.binding.siemensrds + org.openhab.binding.sinope + org.openhab.binding.sleepiq + org.openhab.binding.smaenergymeter + org.openhab.binding.smartmeter + org.openhab.binding.smhi + org.openhab.binding.smartthings + org.openhab.binding.sncf + org.openhab.binding.snmp + org.openhab.binding.solaredge + org.openhab.binding.solarlog + org.openhab.binding.solarwatt + org.openhab.binding.somfymylink + org.openhab.binding.somfytahoma + org.openhab.binding.sonnen + org.openhab.binding.sonos + org.openhab.binding.sonyaudio + org.openhab.binding.sonyprojector + org.openhab.binding.souliss + org.openhab.binding.spotify + org.openhab.binding.squeezebox + org.openhab.binding.surepetcare + org.openhab.binding.synopanalyzer + org.openhab.binding.systeminfo + org.openhab.binding.tacmi + org.openhab.binding.tado + org.openhab.binding.tankerkoenig + org.openhab.binding.tapocontrol + org.openhab.binding.telegram + org.openhab.binding.teleinfo + org.openhab.binding.tellstick + org.openhab.binding.tesla + org.openhab.binding.tibber + org.openhab.binding.tivo + org.openhab.binding.touchwand + org.openhab.binding.tplinkrouter + org.openhab.binding.tplinksmarthome + org.openhab.binding.tr064 + org.openhab.binding.tradfri + org.openhab.binding.twitter + org.openhab.binding.unifi + org.openhab.binding.unifiedremote + org.openhab.binding.upnpcontrol + org.openhab.binding.upb + org.openhab.binding.urtsi + org.openhab.binding.valloxmv + org.openhab.binding.vdr + org.openhab.binding.vektiva + org.openhab.binding.velbus + org.openhab.binding.velux + org.openhab.binding.venstarthermostat + org.openhab.binding.ventaair + org.openhab.binding.verisure + org.openhab.binding.vigicrues + org.openhab.binding.vitotronic + org.openhab.binding.volvooncall + org.openhab.binding.warmup + org.openhab.binding.weathercompany + org.openhab.binding.weatherunderground + org.openhab.binding.webthing + org.openhab.binding.wemo + org.openhab.binding.wifiled + org.openhab.binding.windcentrale + org.openhab.binding.wlanthermo + org.openhab.binding.wled + org.openhab.binding.wolfsmartset + org.openhab.binding.xmltv + org.openhab.binding.xmppclient + org.openhab.binding.yamahamusiccast + org.openhab.binding.yamahareceiver + org.openhab.binding.yioremote + org.openhab.binding.yeelight + org.openhab.binding.zoneminder + org.openhab.binding.zway + + org.openhab.persistence.dynamodb + org.openhab.persistence.influxdb + org.openhab.persistence.jdbc + org.openhab.persistence.jpa + org.openhab.persistence.mapdb + org.openhab.persistence.mongodb + org.openhab.persistence.rrd4j + + org.openhab.voice.googlestt + org.openhab.voice.googletts + org.openhab.voice.mactts + org.openhab.voice.marytts + org.openhab.voice.picotts + org.openhab.voice.pollytts + org.openhab.voice.porcupineks + org.openhab.voice.voicerss + org.openhab.voice.watsonstt + + - + + target/dependency + + - + + + org.lastnpe.eea + eea-all + ${eea.version} + + + + org.openhab.core.bom + org.openhab.core.bom.compile + pom + provided + + + org.openhab.core.bom + org.openhab.core.bom.openhab-core + pom + provided + + + commons-net + commons-net + + + + + org.openhab.core.bom + org.openhab.core.bom.test + pom + test + + + + org.apache.karaf.features + framework + ${karaf.version} + kar + true + + + * + * + + + + + + org.apache.karaf.features + standard + ${karaf.version} + features + xml + provided + + - + + + + + org.apache.maven.plugins + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + true + + + + org.apache.karaf.tooling + karaf-maven-plugin + ${karaf.version} + true + + 80 + true + true + false + true + true + + + + compile + + features-generate-descriptor + + generate-resources + + ${feature.directory} + + + + karaf-feature-verification + + verify + + verify + + + + mvn:org.apache.karaf.features/framework/${karaf.version}/xml/features + mvn:org.apache.karaf.features/standard/${karaf.version}/xml/features + + file:${project.build.directory}/feature/feature.xml + + org.apache.karaf.features:framework + ${oh.java.version} + + framework + + + openhab-* + + false + true + first + + + + + + - + + + biz.aQute.bnd + bnd-maven-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + embed-dependencies + + unpack-dependencies + + + runtime + jar + javax.activation,org.apache.karaf.features,org.lastnpe.eea + ${dep.noembedding} + ${project.build.directory}/classes + true + true + true + jar + + + + unpack-eea + + unpack + + + + + org.lastnpe.eea + eea-all + ${eea.version} + true + + + + + + + + - + + + + no-embed-dependencies + + + noEmbedDependencies.profile + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + embed-dependencies + none + + + + + + + - +