| | P105 | Smart Mini Socket |
| EnergyMonitoring SmartPlug (Wi-Fi) | P110 | Energy Monitoring Smart Socket |
| | P115 | Energy Monitoring Mini Smart Socket |
+| Power Strip (Wi-Fi) | P300 | Smart Wi-Fi Power Strip - 3 sockets |
| Dimmable SmartBulb (Wi-Fi) | L510 | Dimmable White-Light Smart-Bulb (E27) |
| | L610 | Dimmable White-Light Smart-Spot (GU10) |
| MultiColor SmartBulb (Wi-Fi) | L530 | Multicolor Smart-Bulb (E27) |
| group | channel | type | description | things supporting this channel |
|-----------|----------------- |------------------------|------------------------------|------------------------------------------------------------------|
| actuator | output | Switch | Power device on or off | P100, P105, P110, P115, L510, L530, L610, L630, L900, L920, L930 |
+| | output1 | Switch | Power socket 1 on or off | P300 |
+| | output2 | Switch | Power socket 2 on or off | P300 |
+| | output3 | Switch | Power socket 3 on or off | P300 |
| | brightness | Dimmer | Brightness 0-100% | L510, L530, L610, L630, L900 |
| | colorTemperature | Number | White-Color-Temp 2500-6500K | L510, L530, L610, L630, L900 |
| | color | Color | Color | L530, L630, L900 |
tapocontrol:L900:myTapoBridge:myLightStrip "light-strip" (tapocontrol:bridge:myTapoBridge) [ ipAddress="192.168.178.153", pollingInterval=30 ]
Bridge tapocontrol:bridge:secondBridgeExample "Cloud-Login" [ username="youtoo@anyprovider.com", password="verysecret" ] {
- Thing tapocontrol:P110:secondBridgeExample:mySocket "My-Socket" [ ipAddress="192.168.101.51", pollingInterval=30 ]
+ Thing P110 mySocket "My-Socket" [ ipAddress="192.168.101.51", pollingInterval=30 ]
}
```
import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*;
import static org.openhab.binding.tapocontrol.internal.constants.TapoThingConstants.*;
-import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*;
+import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.jsonObjectToInt;
import java.net.InetAddress;
import java.util.HashMap;
+import java.util.Objects;
+import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tapocontrol.internal.device.TapoBridgeHandler;
import org.openhab.binding.tapocontrol.internal.device.TapoDevice;
import org.openhab.binding.tapocontrol.internal.helpers.PayloadBuilder;
import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;
+import org.openhab.binding.tapocontrol.internal.structures.TapoChild;
+import org.openhab.binding.tapocontrol.internal.structures.TapoChildData;
import org.openhab.binding.tapocontrol.internal.structures.TapoDeviceInfo;
import org.openhab.binding.tapocontrol.internal.structures.TapoEnergyData;
+import org.openhab.binding.tapocontrol.internal.structures.TapoSubRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.gson.Gson;
import com.google.gson.JsonObject;
/**
*/
@NonNullByDefault
public class TapoDeviceConnector extends TapoDeviceHttpApi {
+
private final Logger logger = LoggerFactory.getLogger(TapoDeviceConnector.class);
- private final String uid;
- private final TapoDevice device;
- private TapoDeviceInfo deviceInfo;
- private TapoEnergyData energyData;
- private Gson gson;
+
+ private TapoDeviceInfo deviceInfo = new TapoDeviceInfo();
+ private TapoEnergyData energyData = new TapoEnergyData();
+ private TapoChildData childData = new TapoChildData();
private long lastQuery = 0L;
private long lastSent = 0L;
private long lastLogin = 0L;
*/
public TapoDeviceConnector(TapoDevice device, TapoBridgeHandler bridgeThingHandler) {
super(device, bridgeThingHandler);
- this.device = device;
- this.gson = new Gson();
- this.deviceInfo = new TapoDeviceInfo();
- this.energyData = new TapoEnergyData();
- this.uid = device.getThingUID().getAsString();
}
/***********************************
/**
* send custom command to device
- *
+ *
* @param plBuilder Payloadbuilder with unencrypted payload
*/
public void sendCustomQuery(String queryMethod) {
/**
* send custom command to device
- *
+ *
* @param plBuilder Payloadbuilder with unencrypted payload
*/
public void sendCustomPayload(PayloadBuilder plBuilder) {
}
}
+ /**
+ * send "set_device_info" command to child's device
+ *
+ * @param index of the child
+ * @param childProperty to modify
+ * @param value for the property
+ */
+ public void sendChildCommand(Integer index, String childProperty, Object value) {
+ long now = System.currentTimeMillis();
+ if (now > this.lastSent + TAPO_SEND_MIN_GAP_MS) {
+ this.lastSent = now;
+ getChild(index).ifPresent(child -> {
+ child.setDeviceOn(Boolean.valueOf((Boolean) value));
+ TapoSubRequest request = new TapoSubRequest(child.getDeviceId(), DEVICE_CMD_SETINFO, child);
+ sendSecurePasstrhroug(GSON.toJson(request), request.method());
+ });
+ } else {
+ logger.debug("({}) command not sent because of min_gap: {}", uid, now + " <- " + lastSent);
+ }
+ }
+
/**
* send multiple "set_device_info" commands to device
*
}
/**
- * Query Info from Device adn refresh deviceInfo
+ * Query Info from Device and refresh deviceInfo
*/
public void queryInfo() {
queryInfo(false);
+ queryChildDevices();
}
/**
- * Query Info from Device adn refresh deviceInfo
- *
+ * Query Info from Device and refresh deviceInfo
+ *
* @param ignoreGap ignore gap to last query. query anyway
*/
public void queryInfo(boolean ignoreGap) {
}
}
+ /**
+ * Query Info from Child Devices and refresh deviceInfo
+ */
+ @Override
+ public void queryChildDevices() {
+ logger.trace("({}) DeviceConnetor_queryChildDevices from '{}'", uid, deviceURL);
+
+ /* create payload */
+ PayloadBuilder plBuilder = new PayloadBuilder();
+ plBuilder.method = DEVICE_CMD_CHILD_DEVICE_LIST;
+ String payload = plBuilder.getPayload();
+
+ sendSecurePasstrhroug(payload, DEVICE_CMD_CHILD_DEVICE_LIST);
+ }
+
/**
* Get energy usage from device
*/
/**
* SEND SECUREPASSTHROUGH
* encprypt payload and send to device
- *
+ *
* @param payload payload sent to device
* @param command command executed - this will handle result
*/
/**
* Handle SuccessResponse (setDeviceInfo)
- *
+ *
* @param responseBody String with responseBody from device
*/
@Override
}
/**
- *
+ *
* handle JsonResponse (getDeviceInfo)
- *
+ *
* @param responseBody String with responseBody from device
*/
@Override
/**
* handle JsonResponse (getEnergyData)
- *
+ *
* @param responseBody String with responseBody from device
*/
@Override
this.device.responsePasstrough(responseBody);
}
+ /**
+ * handle JsonResponse (getChildDeviceList)
+ *
+ * @param responseBody String with responseBody from device
+ */
+ @Override
+ protected void handleChildDevices(String responseBody) {
+ JsonObject jsnResult = getJsonFromResponse(responseBody);
+ if (jsnResult.has(CHILD_PROPERTY_START_INDEX)) {
+ this.childData = Objects.requireNonNull(GSON.fromJson(jsnResult, TapoChildData.class));
+ this.device.setChildData(childData);
+ } else {
+ this.childData = new TapoChildData();
+ }
+ this.device.responsePasstrough(responseBody);
+ }
+
/**
* handle custom response
- *
+ *
* @param responseBody String with responseBody from device
*/
@Override
/**
* handle error
- *
+ *
* @param te TapoErrorHandler
*/
@Override
/**
* get Json from response
- *
+ *
* @param responseBody
* @return JsonObject with result
*/
private JsonObject getJsonFromResponse(String responseBody) {
- JsonObject jsonObject = gson.fromJson(responseBody, JsonObject.class);
+ JsonObject jsonObject = GSON.fromJson(responseBody, JsonObject.class);
/* get errocode (0=success) */
if (jsonObject != null) {
Integer errorCode = jsonObjectToInt(jsonObject, "error_code");
if (errorCode == 0) {
/* decrypt response */
- jsonObject = gson.fromJson(responseBody, JsonObject.class);
+ jsonObject = GSON.fromJson(responseBody, JsonObject.class);
logger.trace("({}) received result: {}", uid, responseBody);
if (jsonObject != null) {
/* return result if set / else request was successful */
/**
* Check if device is online
- *
+ *
* @return true if device is online
*/
public Boolean isOnline() {
/**
* Check if device is online
- *
+ *
* @param raiseError if true
* @return true if device is online
*/
/**
* IP-Adress
- *
+ *
* @return String ipAdress
*/
public String getIP() {
/**
* PING IP Adress
- *
+ *
* @return true if ping successfull
*/
public Boolean pingDevice() {
return false;
}
}
+
+ private Optional<TapoChild> getChild(int position) {
+ return childData.getChildDeviceList().stream().filter(child -> child.getPosition() == position).findFirst();
+ }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
/**
*/
@NonNullByDefault
public class TapoDeviceHttpApi {
+ protected static final Gson GSON = new GsonBuilder()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+
private final Logger logger = LoggerFactory.getLogger(TapoDeviceHttpApi.class);
- private final String uid;
private final TapoCipher tapoCipher;
private final TapoBridgeHandler bridge;
- private Gson gson;
+ protected final String uid;
+ protected final TapoDevice device;
+
private String token = "";
private String cookie = "";
protected String deviceURL = "";
public TapoDeviceHttpApi(TapoDevice device, TapoBridgeHandler bridgeThingHandler) {
this.bridge = bridgeThingHandler;
this.tapoCipher = new TapoCipher();
- this.gson = new Gson();
+ this.device = device;
this.uid = device.getThingUID().getAsString();
- String ipAddress = device.getIpAddress();
- setDeviceURL(ipAddress);
+ setDeviceURL(device.getIpAddress());
}
/***********************************
************************************/
/**
* handle SuccessResponse (setDeviceInfo)
- *
+ *
* @param responseBody String with responseBody from device
*/
protected void handleSuccessResponse(String responseBody) {
/**
* handle JsonResponse (getDeviceInfo)
- *
+ *
* @param responseBody String with responseBody from device
*/
protected void handleDeviceResult(String responseBody) {
/**
* handle JsonResponse (getEnergyData)
- *
+ *
* @param responseBody String with responseBody from device
*/
protected void handleEnergyResult(String responseBody) {
/**
* handle custom response
- *
+ *
* @param responseBody String with responseBody from device
*/
protected void handleCustomResponse(String responseBody) {
}
+ /**
+ * handle JsonResponse (getChildDevices)
+ *
+ * @param responseBody String with responseBody from device
+ */
+ protected void handleChildDevices(String responseBody) {
+ }
+
/**
* handle error
- *
+ *
* @param te TapoErrorHandler
*/
protected void handleError(TapoErrorHandler tapoError) {
}
+ /**
+ * refresh the list of child devices
+ *
+ */
+ protected void queryChildDevices() {
+ }
+
/***********************************
*
* LOGIN FUNCTIONS
/**
* return encrypted key from 'handshake' request
- *
+ *
* @param response ContentResponse from "handshake" method
* @return
*/
private String getKeyFromResponse(ContentResponse response) {
String rBody = response.getContentAsString();
- JsonObject jsonObj = gson.fromJson(rBody, JsonObject.class);
+ JsonObject jsonObj = GSON.fromJson(rBody, JsonObject.class);
if (jsonObj != null) {
logger.trace("({}) received awnser: {}", uid, rBody);
return jsonObjectToString(jsonObj.getAsJsonObject("result"), "key");
/**
* return cookie from 'handshake' request
- *
+ *
* @param response ContentResponse from "handshake" metho
* @return
*/
/**
* Query Token from device
- *
+ *
* @return String with token returned from device
*/
protected String queryToken() {
/**
* get Token from "login"-request
- *
+ *
* @param response
* @return
*/
logger.trace("({}) received result: {}", uid, decryptedResponse);
/* get errocode (0=success) */
- JsonObject jsonObject = gson.fromJson(decryptedResponse, JsonObject.class);
+ JsonObject jsonObject = GSON.fromJson(decryptedResponse, JsonObject.class);
if (jsonObject != null) {
Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_JSON_DECODE_FAIL);
if (errorCode == 0) {
************************************/
/**
* SEND SYNCHRON HTTP-REQUEST
- *
+ *
* @param url url request is sent to
* @param payload payload (String) to send
* @return ContentResponse of request
/**
* SEND ASYNCHRONOUS HTTP-REQUEST
* (don't wait for awnser with programm code)
- *
+ *
* @param url string url request is sent to
* @param payload data-payload
* @param command command executed - this will handle RepsonseType
case DEVICE_CMD_CUSTOM:
handleCustomResponse(rBody);
break;
+ case DEVICE_CMD_CHILD_DEVICE_LIST:
+ handleChildDevices(rBody);
+ break;
}
} else {
getErrorCode(rBody);
/**
* return error code from response
- *
+ *
* @param response
* @return 0 if request was successfull
*/
/**
* return error code from responseBody
- *
+ *
* @param responseBody
* @return 0 if request was successfull
*/
protected Integer getErrorCode(String responseBody) {
try {
- JsonObject jsonObject = gson.fromJson(responseBody, JsonObject.class);
+ JsonObject jsonObject = GSON.fromJson(responseBody, JsonObject.class);
/* get errocode (0=success) */
Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_JSON_DECODE_FAIL);
if (errorCode == 0) {
/**
* Check for JsonObject "errorcode" and if this is > 0 (no Error)
- *
+ *
* @param responseBody
* @return true if is js errorcode > 0; false if there is no "errorcode"
*/
protected Boolean hasErrorCode(String responseBody) {
if (isValidJson(responseBody)) {
- JsonObject jsonObject = gson.fromJson(responseBody, JsonObject.class);
+ JsonObject jsonObject = GSON.fromJson(responseBody, JsonObject.class);
/* get errocode (0=success) */
Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_JSON_DECODE_FAIL);
if (errorCode > 0) {
/**
* Decrypt Response
- *
+ *
* @param responseBody encrypted string from response-body
* @return String decrypted responseBody
*/
protected String decryptResponse(String responseBody) {
try {
- JsonObject jsonObject = gson.fromJson(responseBody, JsonObject.class);
+ JsonObject jsonObject = GSON.fromJson(responseBody, JsonObject.class);
if (jsonObject != null) {
String encryptedResponse = jsonObjectToString(jsonObject.getAsJsonObject("result"), "response");
return tapoCipher.decode(encryptedResponse);
/**
* encrypt payload
- *
+ *
* @param payload
* @return encrypted payload
*/
************************************/
/**
* Logged In
- *
+ *
* @return true if logged in
*/
public Boolean loggedIn() {
/**
* Logged In
- *
+ *
* @param raiseError if true
* @return true if logged in
*/
/**
* Set new ipAddress
- *
+ *
* @param new ipAdress
*/
public void setDeviceURL(String ipAddress) {
/**
* Set new ipAdresss with token
- *
+ *
* @param ipAddress ipAddres of device
* @param token token from login-ressult
*/
/**
* Set new token
- *
+ *
* @param deviceURL
* @param token
*/
/**
* Set new cookie
- *
+ *
* @param cookie
*/
protected void setCookie(String cookie) {
public static final String DEVICE_CMD_GETINFO = "get_device_info";
public static final String DEVICE_CMD_SETINFO = "set_device_info";
public static final String DEVICE_CMD_GETENERGY = "get_energy_usage";
+ public static final String DEVICE_CMD_CHILD_DEVICE_LIST = "get_child_device_list";
+ public static final String DEVICE_CMD_CONTROL_CHILD = "control_child";
+ public static final String DEVICE_CMD_MULTIPLE_REQ = "multipleRequest";
public static final String DEVICE_CMD_CUSTOM = "custom_command";
}
*/
package org.openhab.binding.tapocontrol.internal.constants;
-import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
+import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.BINDING_ID;
import java.util.Collections;
import java.util.Set;
public static final String DEVICE_P105 = "P105";
public static final String DEVICE_P110 = "P110";
public static final String DEVICE_P115 = "P115";
+ public static final String DEVICE_P300 = "P300";
public static final String DEVICE_L510 = "L510";
public static final String DEVICE_L530 = "L530";
public static final String DEVICE_L610 = "L610";
/*** LIST OF SUPPORTED DEVICE DESCRIPTIONS ***/
public static final String DEVICE_DESCRIPTION_BRIDGE = "TapoControl Cloud-Login";
public static final String DEVICE_DESCRIPTION_SMART_PLUG = "SmartPlug";
+ public static final String DEVICE_DESCRIPTION_POWER_STRIP = "PowerStrip";
public static final String DEVICE_DESCRIPTION_WHITE_BULB = "White-Light-Bulb";
public static final String DEVICE_DESCRIPTION_COLOR_BULB = "Color-Light-Bulb";
public static final String DEVICE_DESCRIPTION_LIGHTSTRIP = "LightStrip";
public static final ThingTypeUID P105_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_P105);
public static final ThingTypeUID P110_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_P110);
public static final ThingTypeUID P115_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_P115);
+ public static final ThingTypeUID P300_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_P300);
public static final ThingTypeUID L510_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L510);
public static final ThingTypeUID L530_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L530);
public static final ThingTypeUID L610_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L610);
/*** SET OF SUPPORTED UIDS ***/
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_UIDS = Set.of(BRIDGE_THING_TYPE);
public static final Set<ThingTypeUID> SUPPORTED_SMART_PLUG_UIDS = Set.of(P100_THING_TYPE, P105_THING_TYPE,
- P110_THING_TYPE, P115_THING_TYPE);
+ P110_THING_TYPE, P115_THING_TYPE, P300_THING_TYPE);
public static final Set<ThingTypeUID> SUPPORTED_WHITE_BULB_UIDS = Set.of(L510_THING_TYPE, L610_THING_TYPE);
public static final Set<ThingTypeUID> SUPPORTED_COLOR_BULB_UIDS = Set.of(L530_THING_TYPE, L630_THING_TYPE);
public static final Set<ThingTypeUID> SUPPORTED_LIGHT_STRIP_UIDS = Set.of(L900_THING_TYPE, L920_THING_TYPE,
/*** THINGS WITH ENERGY DATA ***/
public static final Set<ThingTypeUID> SUPPORTED_ENERGY_DATA_UIDS = Set.of(P110_THING_TYPE, P115_THING_TYPE);
+ /*** THINGS WITH CHILDS DATA ***/
+ public static final Set<ThingTypeUID> SUPPORTED_CHILDS_DATA_UIDS = Set.of(P300_THING_TYPE);
+
/*** THINGS WITH CHANNEL GROUPS ***/
public static final Set<ThingTypeUID> CHANNEL_GROUP_THING_SET = Collections
.unmodifiableSet(Stream
public static final String ENERGY_PROPERTY_PAST7D = "past7d";
public static final String ENERGY_PROPERTY_PAST30D = "past30d";
public static final String ENERGY_PROPERTY_PAST1Y = "past1y";
+ // childs management
+ public static final String CHILD_PROPERTY_START_INDEX = "start_index";
/*** DEVICE SETTINGS ***/
public static final Integer BULB_MIN_COLORTEMP = 2500;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tapocontrol.internal.api.TapoDeviceConnector;
import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;
+import org.openhab.binding.tapocontrol.internal.structures.TapoChildData;
import org.openhab.binding.tapocontrol.internal.structures.TapoDeviceConfiguration;
import org.openhab.binding.tapocontrol.internal.structures.TapoDeviceInfo;
import org.openhab.binding.tapocontrol.internal.structures.TapoEnergyData;
/**
* CHECK SETTINGS
- *
+ *
* @return TapoErrorHandler with configuration-errors
*/
protected TapoErrorHandler checkSettings() {
/**
* Stop scheduler
- *
+ *
* @param scheduler ScheduledFeature<?> which schould be stopped
*/
protected void stopScheduler(@Nullable ScheduledFuture<?> scheduler) {
************************************/
/**
* return device Error
- *
+ *
* @return
*/
public TapoErrorHandler getError() {
/**
* set device error
- *
+ *
* @param tapoError TapoErrorHandler-Object
*/
public void setError(TapoErrorHandler tapoError) {
/***
* Check if ThingType is model
- *
+ *
* @param model
* @return
*/
/**
* CHECK IF RECEIVED DATA ARE FROM THE EXPECTED DEVICE
* Compare MAC-Adress
- *
+ *
* @param deviceInfo
* @return true if is the expected device
*/
/**
* query device Properties
- *
+ *
* @param ignoreGap ignore gap to last query. query anyway (force)
*/
public void queryDeviceInfo(boolean ignoreGap) {
if (SUPPORTED_ENERGY_DATA_UIDS.contains(getThing().getThingTypeUID())) {
connector.getEnergyUsage();
}
+ // query childs data
+ if (SUPPORTED_CHILDS_DATA_UIDS.contains(getThing().getThingTypeUID())) {
+ connector.queryChildDevices();
+ }
} else {
logger.debug("({}) tried to query DeviceInfo but not loggedIn", uid);
connect();
/**
* SET DEVICE INFOs to device
- *
+ *
* @param deviceInfo
*/
public void setDeviceInfo(TapoDeviceInfo deviceInfo) {
/**
* Set Device EnergyData to device
- *
+ *
* @param energyData
*/
public void setEnergyData(TapoEnergyData energyData) {
getTimeType(energyData.getTodayRuntime(), Units.MINUTE));
}
+ /**
+ * Set Device Child data to device
+ *
+ * @param energyData
+ */
+ public void setChildData(TapoChildData hostData) {
+ hostData.getChildDeviceList().forEach(child -> {
+ publishState(getChannelID(CHANNEL_GROUP_ACTUATOR, CHANNEL_OUTPUT + Integer.toString(child.getPosition())),
+ getOnOffType(child.getDeviceOn()));
+ });
+ }
+
/**
* Handle full responsebody received from connector
- *
+ *
* @param responseBody
*/
public void responsePasstrough(String responseBody) {
/**
* UPDATE PROPERTIES
- *
+ *
* If only one property must be changed, there is also a convenient method
* updateProperty(String name, String value).
- *
+ *
* @param TapoDeviceInfo
*/
protected void devicePropertiesChanged(TapoDeviceInfo deviceInfo) {
/**
* update channel state
- *
+ *
* @param channelID
* @param value
*/
/**
* Connect (login) to device
- *
+ *
*/
public Boolean connect() {
deviceError.reset();
************************************/
/**
* Get ChannelID including group
- *
+ *
* @param group String channel-group
* @param channel String channel-name
* @return String channelID
/**
* Get Channel from ChannelID
- *
+ *
* @param channelID String channelID
* @return String channel-name
*/
/**
* Constructor
- *
+ *
* @param thing Thing object representing device
*/
public TapoSmartPlug(Thing thing) {
/**
* handle command sent to device
- *
+ *
* @param channelUID channelUID command is sent to
* @param command command to be sent
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
- Boolean refreshInfo = false;
+ boolean refreshInfo = false;
+ String id = channelUID.getIdWithoutGroup();
/* perform actions */
if (command instanceof RefreshType) {
refreshInfo = true;
- } else if (command == OnOffType.ON) {
- connector.sendDeviceCommand(DEVICE_PROPERTY_ON, true);
- refreshInfo = true;
- } else if (command == OnOffType.OFF) {
- connector.sendDeviceCommand(DEVICE_PROPERTY_ON, false);
- refreshInfo = true;
+ } else if (command instanceof OnOffType) {
+ Boolean targetState = command == OnOffType.ON ? Boolean.TRUE : Boolean.FALSE;
+ if (CHANNEL_OUTPUT.equals(id)) { // Command is sent to the device output
+ connector.sendDeviceCommand(DEVICE_PROPERTY_ON, targetState);
+ refreshInfo = true;
+ } else if (id.startsWith(CHANNEL_OUTPUT)) { // Command is sent to a child's device output
+ Integer index = Integer.valueOf(id.replace(CHANNEL_OUTPUT, ""));
+ connector.sendChildCommand(index, DEVICE_PROPERTY_ON, targetState);
+ refreshInfo = true;
+ }
} else {
- logger.warn("({}) command type '{}' not supported for channel '{}'", uid, command.toString(),
- channelUID.getId());
+ logger.warn("({}) command type '{}' not supported for channel '{}'", uid, command, channelUID.getId());
}
/* refreshInfo */
/**
* UPDATE PROPERTIES
- *
+ *
* @param TapoDeviceInfo
*/
@Override
/**
* Get JSON Payload (STRING)
- *
+ *
* @return String JSON-Payload
*/
public String getPayload() {
/**
* Get JSON Payload (JSON-Object)
- *
+ *
* @return JsonObject JSON-Payload
*/
public JsonObject getJsonPayload() {
* remove all parameters
*/
public void flushParameters(String command) {
- this.parameters = new JsonObject();
+ parameters = new JsonObject();
}
}
--- /dev/null
+/**
+ * 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.tapocontrol.internal.structures;
+
+import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.MAC_DIVISION_CHAR;
+import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.formatMac;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Tapo Child Device Information class
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class TapoChild {
+ private String fwVer = "";
+ private String hwVer = "";
+ private String type = "";
+ private String model = "";
+ private String mac = "";
+ private String category = "";
+ private String deviceId = "";
+ private boolean overheatStatus = false;
+ private int bindCount = 0;
+ private long onTime = 0;
+ private int slotNumber = 0;
+ private int position = 0;
+ private String nickname = "";
+ private boolean deviceOn = false;
+ private String region = "";
+
+ /***********************************
+ *
+ * GET VALUES
+ *
+ ************************************/
+
+ public String getFirmwareVersion() {
+ return fwVer;
+ }
+
+ public String getHardwareVersion() {
+ return hwVer;
+ }
+
+ public Boolean isOff() {
+ return !deviceOn;
+ }
+
+ public Boolean isOn() {
+ return deviceOn;
+ }
+
+ public String getMAC() {
+ return formatMac(mac, MAC_DIVISION_CHAR);
+ }
+
+ public String getModel() {
+ return model.replace(" Series", "");
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public Number getOnTime() {
+ return onTime;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public String getRepresentationProperty() {
+ return getMAC();
+ }
+
+ public String getSerial() {
+ return deviceId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getFwVer() {
+ return fwVer;
+ }
+
+ public String getHwVer() {
+ return hwVer;
+ }
+
+ public String getMac() {
+ return mac;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getDeviceId() {
+ return deviceId;
+ }
+
+ public Boolean getOverheatStatus() {
+ return overheatStatus;
+ }
+
+ public Integer getBindCount() {
+ return bindCount;
+ }
+
+ public Integer getSlotNumber() {
+ return slotNumber;
+ }
+
+ public Integer getPosition() {
+ return position;
+ }
+
+ public Boolean getDeviceOn() {
+ return deviceOn;
+ }
+
+ public void setDeviceOn(Boolean deviceOn) {
+ this.deviceOn = deviceOn;
+ }
+}
--- /dev/null
+/**
+ * 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.tapocontrol.internal.structures;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Tapo-Child Structure Class
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class TapoChildData {
+ private int startIndex = 0;
+ private int sum = 0;
+ private List<TapoChild> childDeviceList = List.of();
+
+ public int getStartIndex() {
+ return startIndex;
+ }
+
+ public int getSum() {
+ return sum;
+ }
+
+ public List<TapoChild> getChildDeviceList() {
+ return childDeviceList;
+ }
+}
--- /dev/null
+/**
+ * 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.tapocontrol.internal.structures;
+
+import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * {@TapoSubRequest} holds data sent to device in order to act on a child
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public record TapoSubRequest(String method, Object params) {
+ private record ChildRequest(String device_id, @SerializedName("requestData") TapoSubRequest requestData) {
+ }
+
+ private record SubMultiple(List<TapoSubRequest> requests) {
+
+ private SubMultiple(String method, TapoChild params) {
+ this(List.of(new TapoSubRequest(method, params)));
+ }
+ }
+
+ public TapoSubRequest(String deviceId, String method, TapoChild params) {
+ this(DEVICE_CMD_CONTROL_CHILD, new ChildRequest(deviceId,
+ new TapoSubRequest(DEVICE_CMD_MULTIPLE_REQ, new SubMultiple(method, params))));
+ }
+}
thing-type.tapocontrol.P110.description = Tapo Smart Monitoring Wifi Plug
thing-type.tapocontrol.P115.label = P115 SmartPlug
thing-type.tapocontrol.P115.description = Tapo Smart Monitoring Wifi Plug
+thing-type.tapocontrol.P300.label = P300 Power Strip
+thing-type.tapocontrol.P300.description = Tapo Smart Wi-Fi Power Strip
thing-type.tapocontrol.bridge.label = Cloud-Login
thing-type.tapocontrol.bridge.description = Cloud Connector. Acts as device-bridge
channel-group-type.tapocontrol.lightEffect.description = Tapo Lightning Effects
channel-group-type.tapocontrol.lightStrip.label = Color Light Strip
channel-group-type.tapocontrol.lightStrip.description = Tapo Multicolor Smart Light Strip
+channel-group-type.tapocontrol.powerStrip.label = SmartPlug
+channel-group-type.tapocontrol.powerStrip.description = Tapo Smart Plug Power Outlet
+channel-group-type.tapocontrol.powerStrip.channel.output1.label = Output Switch 1
+channel-group-type.tapocontrol.powerStrip.channel.output1.description = Switches the power state on/off of the first socket
+channel-group-type.tapocontrol.powerStrip.channel.output2.label = Output Switch 2
+channel-group-type.tapocontrol.powerStrip.channel.output2.description = Switches the power state on/off of the second socket
+channel-group-type.tapocontrol.powerStrip.channel.output3.label = Output Switch 3
+channel-group-type.tapocontrol.powerStrip.channel.output3.description = Switches the power state on/off of the third socket
channel-group-type.tapocontrol.smartPlug.label = SmartPlug
channel-group-type.tapocontrol.smartPlug.description = Tapo Smart Plug Power Outlet
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="tapocontrol"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <!-- P300 THING-TYPE (POWER STRIP) -->
+ <thing-type id="P300">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="bridge"/>
+ </supported-bridge-type-refs>
+
+ <label>P300 Power Strip</label>
+ <description>Tapo Smart Wi-Fi Power Strip</description>
+ <channel-groups>
+ <channel-group id="actuator" typeId="powerStrip"/>
+ <channel-group id="device" typeId="deviceState"/>
+ </channel-groups>
+ <representation-property>macAddress</representation-property>
+
+ <config-description-ref uri="thing-type:tapo:device"/>
+ </thing-type>
+</thing:thing-descriptions>
</channels>
</channel-group-type>
+ <channel-group-type id="powerStrip">
+ <label>SmartPlug</label>
+ <description>Tapo Smart Plug Power Outlet</description>
+ <channels>
+ <channel id="output1" typeId="outputChannel">
+ <label>Output Switch 1</label>
+ <description>Switches the power state on/off of the first socket</description>
+ </channel>
+ <channel id="output2" typeId="outputChannel">
+ <label>Output Switch 2</label>
+ <description>Switches the power state on/off of the second socket</description>
+ </channel>
+ <channel id="output3" typeId="outputChannel">
+ <label>Output Switch 3</label>
+ <description>Switches the power state on/off of the third socket</description>
+ </channel>
+ </channels>
+ </channel-group-type>
+
<!--Light-Bulb Channel Type -->
<channel-group-type id="lightBulb">
<label>Light Bulb</label>