Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bundles/org.openhab.binding.tapocontrol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This binding adds support to control Tapo (Copyright © TP-Link Corporation Limi

## Supported Things

The following Tapo-Devices are supported. For precise channel-description look at `channels-table` below
The following Tapo-Devices are supported. For precise channel-description look at `channels-table` below:

| DeviceType | ThingType | Description |
|------------------------------------|-----------|-----------------------------------------------|
Expand Down Expand Up @@ -32,6 +32,7 @@ The following Tapo-Devices are supported. For precise channel-description look a

Before using Smart Plugs with openHAB the devices must be connected to the Wi-Fi network.
This can be done using the Tapo provided mobile app.
Some devices require Third-Party Compatibility to be disabled then re-enabled via the mobile app after initial installation or firmware upgrade.
You need to setup a bridge (Cloud-Login) to communicate with your devices.

**Note:** If the Tapo device is to be isolated from the internet e.g. on an IoT LAN, the P110 will not expose its energy and power data until it has successfully synchronised it's clock with an NTP server - at time of writing, this was `pool.ntp.org`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
*/
package org.openhab.binding.tapocontrol.internal.api;

import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.TAPO_CLOUD_URL;
import static org.openhab.binding.tapocontrol.internal.constants.TapoComConstants.*;
import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*;
import static org.openhab.binding.tapocontrol.internal.helpers.utils.JsonUtils.*;
import static org.openhab.binding.tapocontrol.internal.helpers.utils.JsonUtils.getObjectFromJson;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
Expand Down Expand Up @@ -169,6 +169,11 @@ public String getThingUID() {
return bridge.getUID().toString();
}

@Override
public TapoBridgeHandler getBridge() {
return bridge;
}

/************************
* Private Helpers
************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.tapocontrol.internal.devices.bridge.TapoBridgeHandler;
import org.openhab.binding.tapocontrol.internal.dto.TapoResponse;
import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;

Expand All @@ -40,6 +41,9 @@ public interface TapoConnectorInterface {
/* get base url of device */
public String getBaseUrl();

/* geth ThingUID of device */
/* get ThingUID of device */
public String getThingUID();

/* get bridge */
public TapoBridgeHandler getBridge();
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public void sendQueryCommand(String queryCommand, boolean ignoreGap) {

/**
* Send "set_device_info" command to device and query info immediately
*
*
* @param deviceDataClass clazz contains devicedata which should be sent
*/
public void sendDeviceCommand(Object deviceDataClass) {
Expand All @@ -181,7 +181,7 @@ public void sendDeviceCommand(Object deviceDataClass) {

/**
* Send command to device with params and query info immediately
*
*
* @param command command
* @param deviceDataClass clazz contains devicedata which should be sent
*/
Expand All @@ -191,7 +191,7 @@ public void sendDeviceCommand(String command, Object deviceDataClass) {

/**
* Send command to device with params
*
*
* @param command command
* @param deviceDataClass clazz contains devicedata which should be sent
* @param ignoreGap ignore gap to last query. query anyway
Expand All @@ -208,7 +208,7 @@ public void sendDeviceCommand(String command, Object deviceDataClass, boolean ig

/**
* Send command to device with params and query info immediately
*
*
* @param deviceDataClass clazz contains devicedata which should be sent
* @param multipleRequestSupported set to true if device supports multipleRequests
*/
Expand All @@ -218,7 +218,7 @@ public void sendCommandAndQuery(Object deviceDataClass, boolean multipleRequestS

/**
* Send command to device with params and query info immediately
*
*
* @param command command
* @param deviceDataClass clazz contains devicedata which should be sent
* @param multipleRequestSupported set to true if device supports multipleRequests
Expand All @@ -237,7 +237,7 @@ public void sendCommandAndQuery(String command, Object deviceDataClass, boolean

/**
* Send command to child device
*
*
* @param childData ChildDeviceData-Class
*/
public void sendChildCommand(TapoChildDeviceData childData) {
Expand All @@ -246,7 +246,7 @@ public void sendChildCommand(TapoChildDeviceData childData) {

/**
* Send command to child device
*
*
* @param childData ChildDeviceData-Class
* @param ignoreGap ignore gap to last query. query anyway
*/
Expand All @@ -271,7 +271,7 @@ public void sendMultipleRequest(List<TapoRequest> requests) {

/**
* send asynchronous multi-request igrnoring min-gap
*
*
* @param requests list of TapoRequests should be sent to device
* @param ignoreGap ignoreGap ignore gap to last query. query anyway
*/
Expand All @@ -288,7 +288,7 @@ public void sendMultipleRequest(List<TapoRequest> requests, boolean ignoreGap) {

/**
* send asynchron multi-request
*
*
* @param requests array of TapoRequest
*/
public void sendMultipleRequest(TapoRequest... requests) {
Expand All @@ -297,7 +297,7 @@ public void sendMultipleRequest(TapoRequest... requests) {

/**
* send asynchron request to protocol handler
*
*
* @param tapoRequest Request inherits TapoBaseRequestInterface
*/
public void sendAsyncRequest(TapoBaseRequestInterface tapoRequest) {
Expand All @@ -314,7 +314,7 @@ public void sendAsyncRequest(TapoBaseRequestInterface tapoRequest) {

/**
* Return class object from json formated string
*
*
* @param json json formatted string
* @param clazz class string should parsed to
*/
Expand All @@ -335,7 +335,7 @@ private <T> T getObjectFromJson(String json, Class<T> clazz) {

/**
* Return class object from JsonObject
*
*
* @param jso JsonOject
* @param clazz class string should parsed to
*/
Expand All @@ -345,7 +345,7 @@ private <T> T getObjectFromJson(JsonObject jso, Class<T> clazz) {

/**
* handle and decrypt response from device
*
*
* @param response TapoResponse was received
* @param command was sent to device belonging to response
*/
Expand All @@ -364,7 +364,7 @@ public void handleResponse(TapoResponse response, String command) {

/**
* Handle response got from single-request
*
*
* @param response response from request
* @param command command was sent
*/
Expand All @@ -381,7 +381,7 @@ private void handleSingleResponse(TapoResponse response, String command) {

/**
* Handle response got from multiple-request
*
*
* @param response response from request
*/
private void handleMultipleRespone(TapoResponse response) {
Expand All @@ -393,13 +393,13 @@ private void handleMultipleRespone(TapoResponse response) {

/**
* Parse responsedata from result to object and inform device about new data
*
*
* @param response response from request
* @param command command was sent
*/
private void handleQueryResult(TapoResponse response, String command) {
if (!response.hasError()) {
logger.trace("({}) queryResponse successfull '{}'", uid, response);
logger.trace("({}) queryResponse successful '{}'", uid, response);
queryResponse = response;
device.newDataResult(command);
} else {
Expand All @@ -410,15 +410,15 @@ private void handleQueryResult(TapoResponse response, String command) {

/**
* Handle SuccessResponse (setDeviceInfo)
*
*
* @param response response from request
*/
private void handleSuccessResponse(TapoResponse response) {
if (response.hasError()) {
logger.debug("({}) set deviceInfo not successful: {}", uid, response);
device.setError(new TapoErrorHandler(response.errorCode()));
} else {
logger.trace("({}) setcommand successfull '{}'", uid, response);
logger.trace("({}) setcommand successful '{}'", uid, response);
if (queryAfterCommand) {
sendQueryCommand(DEVICE_CMD_GETINFO, true);
}
Expand Down Expand Up @@ -479,15 +479,15 @@ public boolean isOnline(boolean raiseError) {

/**
* Get Dataobject from response
*
*
* @param clazz object class response should be transformed
*/
public <T> T getResponseData(Class<T> clazz) {
return getObjectFromJson(queryResponse.result(), clazz);
}

/**
* Ping to IP of device - return true if successfull
* Ping to IP of device - return true if successful
*/
public boolean pingDevice() {
try {
Expand Down Expand Up @@ -518,4 +518,9 @@ public String getBaseUrl() {
public String getThingUID() {
return device.getThingUID().toString();
}

@Override
public TapoBridgeHandler getBridge() {
return bridge;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static org.openhab.binding.tapocontrol.internal.helpers.utils.ByteUtils.byteArrayToHex;
import static org.openhab.binding.tapocontrol.internal.helpers.utils.JsonUtils.isValidJson;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -43,10 +44,17 @@
* Handler class for TAPO-KLAP-Protocol
*
* @author Christian Wild - Initial contribution
* @author David Henshaw - Improvements to handle tapo device error states in send
*/
@NonNullByDefault
public class KlapProtocol implements org.openhab.binding.tapocontrol.internal.api.protocol.TapoProtocolInterface {

private enum SuccessState {
failed,
retry,
success
};

private final Logger logger = LoggerFactory.getLogger(KlapProtocol.class);
protected final TapoConnectorInterface httpDelegator;
private KlapSession session;
Expand Down Expand Up @@ -151,30 +159,20 @@ private synchronized void sendRequestRetryable(TapoBaseRequestInterface tapoRequ

while (true) {
attemptCount++;
if (attemptCount > 1) {
try {
/* re-login using existing credentials */
session.reset();
if (!session.login()) {
logger.debug("({}) sendRequestRetryable login error", uid);
httpDelegator.handleError(new TapoErrorHandler(ERR_BINDING_LOGIN));
return;
}
logger.trace("({}) sendRequestRetryable re-login successful attempt {}", uid, attemptCount);
} catch (TapoErrorHandler e) {
logger.trace("({}) sendRequestRetryable error1 {}", uid, e.getMessage());
if (attemptCount < maxAttempts) {
continue;
}
httpDelegator.handleError(e);
return;
} catch (Exception ex) {
logger.trace("({}) sendRequestRetryable error2 {}", uid, ex.getMessage());
if (attemptCount < maxAttempts) {

// @@DGH
// if (attemptCount > 2) { // try to init comms via UDP
// sendUdpDiscoveryMessage(); // ignore any errors & retry login
// }

if (attemptCount > 1) { // re-login using existing credentials
switch (reLoginExistingCredentials(attemptCount, maxAttempts)) {
case SuccessState.success:
break; // move on to send message
case SuccessState.retry:
continue;
}
httpDelegator.handleError(new TapoErrorHandler(ERR_BINDING_LOGIN, ex.getMessage()));
return;
default:
return; // error
}
}
try {
Expand Down Expand Up @@ -236,12 +234,51 @@ private synchronized void sendRequestRetryable(TapoBaseRequestInterface tapoRequ
} catch (Exception e) {
String errorMessage = e.getMessage();
logger.debug("({}) sendRequestRetryable failed'{}'", uid, errorMessage);
httpDelegator.handleError(new TapoErrorHandler(new Exception(e), errorMessage));
return;
session.reset();
if (e.getCause() instanceof IOException) {
httpDelegator.handleError(new TapoErrorHandler(ERR_BINDING_SEND_REQUEST));
} else {
throw new TapoErrorHandler(ERR_BINDING_SEND_REQUEST, errorMessage);
}
}
} /* end of loop */
}

/**
* Try to re-login using the already-provided credentials
*
* @param attemptCount - how many times have we tried
* @param maxAttempts - how many attempts are allowed
* @return
*/
private SuccessState reLoginExistingCredentials(int attemptCount, int maxAttempts) {
/* re-login using existing credentials - return false if aborting */
try {
session.reset();
if (!session.login()) {
logger.debug("({}) sendRequestRetryable login error", uid);
httpDelegator.handleError(new TapoErrorHandler(ERR_BINDING_LOGIN));
return SuccessState.failed;
}
logger.trace("({}) sendRequestRetryable re-login successful attempt {}", uid, attemptCount);
return SuccessState.success;
} catch (TapoErrorHandler e) {
logger.trace("({}) sendRequestRetryable error1 {}", uid, e.getMessage());
if (attemptCount < maxAttempts) {
return SuccessState.retry;
}
httpDelegator.handleError(e);
return SuccessState.failed;
} catch (Exception ex) {
logger.trace("({}) sendRequestRetryable error2 {}", uid, ex.getMessage());
if (attemptCount < maxAttempts) {
return SuccessState.retry;
}
httpDelegator.handleError(new TapoErrorHandler(ERR_BINDING_LOGIN, ex.getMessage()));
return SuccessState.failed;
}
}

/************************
* RESPONSE HANDLERS
************************/
Expand Down
Loading
Loading