added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.tplinksmarthome-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-tplinksmarthome" description="TP-Link Smart Home Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.tplinksmarthome/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,294 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.model.GetRealtime;
import org.openhab.binding.tplinksmarthome.internal.model.GetSysinfo;
import org.openhab.binding.tplinksmarthome.internal.model.GsonUtil;
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
import org.openhab.binding.tplinksmarthome.internal.model.Realtime;
import org.openhab.binding.tplinksmarthome.internal.model.SetBrightness;
import org.openhab.binding.tplinksmarthome.internal.model.SetLedOff;
import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState;
import org.openhab.binding.tplinksmarthome.internal.model.SetSwitchState;
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightState;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightState.LightOnOff;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightState.LightStateBrightness;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightState.LightStateColor;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightState.LightStateColorTemperature;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightStateResponse;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import com.google.gson.Gson;
/**
* Class to construct the tp-link json commands and convert retrieved results into data objects.
*
* @author Christian Fischer - Initial contribution
* @author Hilbrand Bouwkamp - Rewritten to use gson to parse json
*/
@NonNullByDefault
public class Commands {
private static final String CONTEXT = "{\"context\":{\"child_ids\":[\"%s\"]},";
private static final String SYSTEM_GET_SYSINFO = "\"system\":{\"get_sysinfo\":{}}";
private static final String GET_SYSINFO = "{" + SYSTEM_GET_SYSINFO + "}";
private static final String REALTIME = "\"emeter\":{\"get_realtime\":{}}";
private static final String GET_REALTIME_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO + ", " + REALTIME + "}";
private static final String GET_REALTIME_BULB_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO
+ ", \"smartlife.iot.common.emeter\":{\"get_realtime\":{}}}";
private final Gson gson = GsonUtil.createGson();
private final Gson gsonWithExpose = GsonUtil.createGsonWithExpose();
/**
* Returns the json to get the energy and sys info data from the device.
*
* @return The json string of the command to send to the device
*/
public static String getRealtimeAndSysinfo() {
return GET_REALTIME_AND_SYSINFO;
}
/**
* Returns the json to get the energy and sys info data from the bulb.
*
* @return The json string of the command to send to the bulb
*/
public static String getRealtimeBulbAndSysinfo() {
return GET_REALTIME_BULB_AND_SYSINFO;
}
/**
* Returns the json to get the energy and sys info data from an outlet device.
*
* @param id optional id of the device
* @return The json string of the command to send to the device
*/
public static String getRealtimeWithContext(String id) {
return String.format(CONTEXT, id) + REALTIME + "}";
}
/**
* Returns the json response of the get_realtime command to the data object.
*
* @param realtimeResponse the json string
* @return The data object containing the energy data from the json string
*/
@SuppressWarnings("null")
public Realtime getRealtimeResponse(String realtimeResponse) {
GetRealtime getRealtime = gson.fromJson(realtimeResponse, GetRealtime.class);
return getRealtime == null ? new Realtime() : getRealtime.getRealtime();
}
/**
* Returns the json to get the sys info data from the device.
*
* @return The json string of the command to send to the device
*/
public static String getSysinfo() {
return GET_SYSINFO;
}
/**
* Returns the json response of the get_sysinfo command to the data object.
*
* @param getSysinfoReponse the json string
* @return The data object containing the state data from the json string
*/
@SuppressWarnings("null")
public Sysinfo getSysinfoReponse(String getSysinfoReponse) {
GetSysinfo getSysinfo = gson.fromJson(getSysinfoReponse, GetSysinfo.class);
return getSysinfo == null ? new Sysinfo() : getSysinfo.getSysinfo();
}
/**
* Returns the json for the set_relay_state command to switch on or off.
*
* @param onOff the switch state to set
* @param childId optional child id if multiple children are supported by a single device
* @return The json string of the command to send to the device
*/
public String setRelayState(OnOffType onOff, @Nullable String childId) {
SetRelayState relayState = new SetRelayState();
relayState.setRelayState(onOff);
if (childId != null) {
relayState.setChildId(childId);
}
return gsonWithExpose.toJson(relayState);
}
/**
* Returns the json response of the set_relay_state command to the data object.
*
* @param relayStateResponse the json string
* @return The data object containing the state data from the json string
*/
public @Nullable SetRelayState setRelayStateResponse(String relayStateResponse) {
return gsonWithExpose.fromJson(relayStateResponse, SetRelayState.class);
}
/**
* Returns the json for the set_switch_state command to switch a dimmer on or off.
*
* @param onOff the switch state to set
* @return The json string of the command to send to the device
*/
public String setSwitchState(OnOffType onOff) {
SetSwitchState switchState = new SetSwitchState();
switchState.setSwitchState(onOff);
return gsonWithExpose.toJson(switchState);
}
/**
* Returns the json response of the set_switch_state command to the data object.
*
* @param switchStateResponse the json string
* @return The data object containing the state data from the json string
*/
public @Nullable SetSwitchState setSwitchStateResponse(String switchStateResponse) {
return gsonWithExpose.fromJson(switchStateResponse, SetSwitchState.class);
}
/**
* Returns the json for the set_brightness command to set the brightness value.
*
* @param brightness the brightness value to set
* @return The json string of the command to send to the device
*/
public String setDimmerBrightness(int brightness) {
SetBrightness setBrightness = new SetBrightness();
setBrightness.setBrightness(brightness);
return gsonWithExpose.toJson(setBrightness);
}
/**
* Returns the json response of the set_brightness command to the data object.
*
* @param dimmerBrightnessResponse the json string
* @return The data object containing the state data from the json string
*/
public @Nullable HasErrorResponse setDimmerBrightnessResponse(String dimmerBrightnessResponse) {
return gsonWithExpose.fromJson(dimmerBrightnessResponse, SetBrightness.class);
}
/**
* Returns the json for the set_light_state command to switch a bulb on or off.
*
* @param onOff the switch state to set
* @param transitionPeriod the transition period for the action to take place
* @return The json string of the command to send to the device
*/
public String setLightState(OnOffType onOff, int transitionPeriod) {
TransitionLightState transitionLightState = new TransitionLightState();
LightOnOff lightState = new LightOnOff();
lightState.setOnOff(onOff);
lightState.setTransitionPeriod(transitionPeriod);
transitionLightState.setLightState(lightState);
return gson.toJson(transitionLightState);
}
/**
* Returns the json for the set_led_off command to switch the led of the device on or off.
*
* @param onOff the led state to set
* @param childId optional child id if multiple children are supported by a single device
* @return The json string of the command to send to the device
*/
public String setLedOn(OnOffType onOff, @Nullable String childId) {
SetLedOff sLOff = new SetLedOff();
sLOff.setLed(onOff);
if (childId != null) {
sLOff.setChildId(childId);
}
return gsonWithExpose.toJson(sLOff);
}
/**
* Returns the json response for the set_led_off command to the data object.
*
* @param setLedOnResponse the json string
* @return The data object containing the data from the json string
*/
public @Nullable SetLedOff setLedOnResponse(String setLedOnResponse) {
return gsonWithExpose.fromJson(setLedOnResponse, SetLedOff.class);
}
/**
* Returns the json for the set_light_State command to set the brightness.
*
* @param brightness the brightness value
* @param transitionPeriod the transition period for the action to take place
* @return The json string of the command to send to the device
*/
public String setBrightness(int brightness, int transitionPeriod) {
TransitionLightState transitionLightState = new TransitionLightState();
LightStateBrightness lightState = new LightStateBrightness();
lightState.setOnOff(brightness == 0 ? OnOffType.OFF : OnOffType.ON);
lightState.setBrightness(brightness);
lightState.setTransitionPeriod(transitionPeriod);
transitionLightState.setLightState(lightState);
return gson.toJson(transitionLightState);
}
/**
* Returns the json for the set_light_State command to set the color.
*
* @param hsb the color to set
* @param transitionPeriod the transition period for the action to take place
* @return The json string of the command to send to the device
*/
public String setColor(HSBType hsb, int transitionPeriod) {
TransitionLightState transitionLightState = new TransitionLightState();
LightStateColor lightState = new LightStateColor();
int brightness = hsb.getBrightness().intValue();
lightState.setOnOff(brightness == 0 ? OnOffType.OFF : OnOffType.ON);
lightState.setBrightness(brightness);
lightState.setHue(hsb.getHue().intValue());
lightState.setSaturation(hsb.getSaturation().intValue());
lightState.setTransitionPeriod(transitionPeriod);
transitionLightState.setLightState(lightState);
return gson.toJson(transitionLightState);
}
/**
* Returns the json for the set_light_State command to set the color temperature.
*
* @param colorTemperature the color temperature to set
* @param transitionPeriod the transition period for the action to take place
* @return The json string of the command to send to the device
*/
public String setColorTemperature(int colorTemperature, int transitionPeriod) {
TransitionLightState transitionLightState = new TransitionLightState();
LightStateColorTemperature lightState = new LightStateColorTemperature();
lightState.setOnOff(OnOffType.ON);
lightState.setColorTemperature(colorTemperature);
lightState.setTransitionPeriod(transitionPeriod);
transitionLightState.setLightState(lightState);
return gson.toJson(transitionLightState);
}
/**
* Returns the json response for the set_light_state command.
*
* @param response the json string
* @return The data object containing the state data from the json string
*/
public @Nullable TransitionLightStateResponse setTransitionLightStateResponse(String response) {
return gson.fromJson(response, TransitionLightStateResponse.class);
}
}

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class acts as and interface to the physical device.
*
* @author Christian Fischer - Initial contribution
* @author Hilbrand Bouwkamp - Reorganized code an put connection in single class
*/
@NonNullByDefault
public class Connection {
public static final int TP_LINK_SMART_HOME_PORT = 9999;
private static final int SOCKET_TIMEOUT_MILLISECONDS = 3_000;
private final Logger logger = LoggerFactory.getLogger(Connection.class);
private @Nullable String ipAddress;
/**
* Initializes a connection to the given ip address.
*
* @param ipAddress ip address of the connection
*/
public Connection(@Nullable final String ipAddress) {
this.ipAddress = ipAddress;
}
/**
* Set the ip address to connect to.
*
* @param ipAddress The ip address to connect to
*/
public void setIpAddress(final String ipAddress) {
this.ipAddress = ipAddress;
}
/**
* Sends the command, which is a json string, encrypted to the device and decrypts the json result and returns it
*
* @param command json command to send to the device
* @return decrypted returned json result from the device
* @throws IOException exception in case device not reachable
*/
public synchronized String sendCommand(final String command) throws IOException {
logger.trace("Executing command: {}", command);
try (Socket socket = createSocket(); final OutputStream outputStream = socket.getOutputStream()) {
outputStream.write(CryptUtil.encryptWithLength(command));
final String response = readReturnValue(socket);
logger.trace("Command response: {}", response);
return response;
}
}
/**
* Reads and decrypts result returned from the device.
*
* @param socket socket to read result from
* @return decrypted result
* @throws IOException exception in case device not reachable
*/
private String readReturnValue(final Socket socket) throws IOException {
try (InputStream is = socket.getInputStream()) {
return CryptUtil.decryptWithLength(is);
}
}
/**
* Wrapper around socket creation to make mocking possible.
*
* @return new Socket instance
* @throws UnknownHostException exception in case the host could not be determined
* @throws IOException exception in case device not reachable
*/
protected Socket createSocket() throws UnknownHostException, IOException {
if (ipAddress == null) {
throw new IOException("Ip address not set. Wait for discovery or manually trigger discovery process.");
}
final Socket socket = new Socket(ipAddress, TP_LINK_SMART_HOME_PORT);
socket.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS);
return socket;
}
}

View File

@@ -0,0 +1,117 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Util class to encypt and decrypt data to be sent to and from the smart plug.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public final class CryptUtil {
private static final int KEY = 171;
private CryptUtil() {
// Util class
}
/**
* Decrypt the byte array of the specified length. The length is necessary because the byte array might be larger
* than the actual content.
*
* @param data byte array containing the data
* @param length number of bytes in the byte array to read
* @return decrypted String of the byte array
* @throws IOException exception in case device not reachable
*/
public static String decrypt(byte[] data, int length) throws IOException {
try (ByteArrayInputStream is = new ByteArrayInputStream(data)) {
return decrypt(is, length);
}
}
/**
* Decrypt the byte data in the input stream. In the first 4 bytes the length of the data in the byte array is
* coded.
*
* @param inputStream input stream containing length and data
* @return decrypted String of the inputstream
* @throws IOException exception in case device not reachable
*/
public static String decryptWithLength(InputStream inputStream) throws IOException {
try (DataInputStream is = new DataInputStream(inputStream)) {
return decrypt(is, is.readInt());
}
}
/**
* Decrypt the byte data in the input stream with the length as given.
*
* @param inputStream input stream
* @param length the number of bytes to read
* @return decrypted String of the inputstream
* @throws IOException exception in case device not reachable
*/
private static String decrypt(InputStream inputStream, int length) throws IOException {
final byte[] decryptedData = new byte[length];
int in;
int key = KEY;
int i = 0;
while (i < length && (in = inputStream.read()) != -1) {
final int nextKey = in;
decryptedData[i++] = (byte) (in ^ key);
key = nextKey;
}
return new String(decryptedData, StandardCharsets.UTF_8);
}
/**
* Encrypts the string into a byte array with the first for bytes specifying the length of the given String. The
* length is not encrypted.
*
* @param string String to encrypt
* @return byte array with length and encrypted string
*/
public static byte[] encryptWithLength(String string) {
ByteBuffer bb = ByteBuffer.allocate(4 + string.length());
bb.putInt(string.length());
bb.put(encrypt(string));
return bb.array();
}
/**
* Encrypts the string into a byte array.
*
* @param string String to encrypt
* @return byte array with encrypted string
*/
public static byte[] encrypt(String string) {
byte[] buffer = new byte[string.length()];
byte key = (byte) KEY;
for (int i = 0; i < string.length(); i++) {
buffer[i] = (byte) (string.charAt(i) ^ key);
key = buffer[i];
}
return buffer;
}
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.DeviceType;
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
/**
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public final class PropertiesCollector {
private PropertiesCollector() {
// Util class
}
/**
* Collect all properties of the thing from the {@link Sysinfo} object.
*
* @param thingType thing to get the properties for
* @param ipAddress ip address of the device
* @param sysinfo system info data returned from the device
* @return map of properties
*/
public static Map<String, Object> collectProperties(TPLinkSmartHomeThingType thingType, String ipAddress,
Sysinfo sysinfo) {
final Map<String, Object> properties = new TreeMap<>();
putNonNull(properties, CONFIG_IP, ipAddress);
if (thingType.getDeviceType() == DeviceType.RANGE_EXTENDER) {
collectPropertiesRangeExtender(properties, sysinfo);
} else {
collectProperties(properties, sysinfo);
if (thingType.getDeviceType() == DeviceType.BULB) {
collectPropertiesBulb(properties, sysinfo);
} else {
collectPropertiesOther(properties, sysinfo);
}
}
return properties;
}
/**
* Collect generic properties.
*
* @param properties properties object to store properties in
* @param sysinfo system info data returned from the device
*/
private static void collectProperties(Map<String, Object> properties, Sysinfo sysinfo) {
putNonNull(properties, CONFIG_DEVICE_ID, sysinfo.getDeviceId());
putNonNull(properties, PROPERTY_MODEL, sysinfo.getModel());
putNonNull(properties, PROPERTY_HARDWARE_VERSION, sysinfo.getHwVer());
putNonNull(properties, PROPERTY_SOFWARE_VERSION, sysinfo.getSwVer());
putNonNull(properties, PROPERTY_HARDWARE_ID, sysinfo.getHwId());
putNonNull(properties, PROPERTY_OEM_ID, sysinfo.getOemId());
}
/**
* Collect Smart Bulb specific properties.
*
* @param properties properties object to store properties in
* @param sysinfo system info data returned from the device
*/
private static void collectPropertiesBulb(Map<String, Object> properties, Sysinfo sysinfo) {
putNonNull(properties, PROPERTY_TYPE, sysinfo.getType());
putNonNull(properties, PROPERTY_MAC, sysinfo.getMac());
putNonNull(properties, PROPERTY_PROTOCOL_NAME, sysinfo.getProtocolName());
putNonNull(properties, PROPERTY_PROTOCOL_VERSION, sysinfo.getProtocolVersion());
}
/**
* Collect Smart Range Extender specific properties.
*
* @param properties properties object to store properties in
* @param sysinfo system info data returned from the device
*/
private static void collectPropertiesRangeExtender(Map<String, Object> properties, Sysinfo sysinfo) {
final Sysinfo system = sysinfo.getSystem();
collectProperties(properties, system);
putNonNull(properties, PROPERTY_TYPE, system.getType());
putNonNull(properties, PROPERTY_MAC, system.getMac());
putNonNull(properties, PROPERTY_DEVICE_NAME, system.getDevName());
putNonNull(properties, PROPERTY_FEATURE, sysinfo.getPlug().getFeature());
}
/**
* Collect Smart Switch specific properties.
*
* @param properties properties object to store properties in
* @param sysinfo system info data returned from the device
*/
private static void collectPropertiesOther(Map<String, Object> properties, Sysinfo sysinfo) {
putNonNull(properties, PROPERTY_TYPE, sysinfo.getType());
putNonNull(properties, PROPERTY_MAC, sysinfo.getMac());
putNonNull(properties, PROPERTY_DEVICE_NAME, sysinfo.getDevName());
putNonNull(properties, PROPERTY_FIRMWARE_ID, sysinfo.getFwId());
putNonNull(properties, PROPERTY_FEATURE, sysinfo.getFeature());
}
private static void putNonNull(Map<String, Object> properties, String key, @Nullable String value) {
if (value != null) {
properties.put(key, value);
}
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Service to get the actual ip of a TP-Link Smart Home device as registered on the network.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public interface TPLinkIpAddressService {
/**
* Returns the last known ip address of the given device id. If no ip address known null is returned.
*
* @param deviceId id of the device to get the ip address of
* @return ip address or null
*/
@Nullable
String getLastKnownIpAddress(String deviceId);
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* This class defines common constants, which are used across the whole binding.
*
* @author Christian Fischer - Initial contribution
* @author Hilbrand Bouwkamp - Added channel and property keys
*/
@NonNullByDefault
public final class TPLinkSmartHomeBindingConstants {
public static final String BINDING_ID = "tplinksmarthome";
// List of all switch channel ids
public static final String CHANNEL_SWITCH = "switch";
// List of all plug channel ids
public static final String CHANNEL_LED = "led";
// List of all bulb channel ids
public static final String CHANNEL_BRIGHTNESS = "brightness";
public static final String CHANNEL_COLOR = "color";
public static final String CHANNEL_COLOR_TEMPERATURE = "colorTemperature";
public static final String CHANNEL_COLOR_TEMPERATURE_ABS = "colorTemperatureAbs";
public static final int COLOR_TEMPERATURE_1_MIN = 2700;
public static final int COLOR_TEMPERATURE_1_MAX = 6500;
public static final int COLOR_TEMPERATURE_2_MIN = 2500;
public static final int COLOR_TEMPERATURE_2_MAX = 9000;
public static final Set<String> CHANNELS_BULB_SWITCH = Stream.of(CHANNEL_BRIGHTNESS, CHANNEL_COLOR,
CHANNEL_COLOR_TEMPERATURE, CHANNEL_COLOR_TEMPERATURE_ABS, CHANNEL_SWITCH).collect(Collectors.toSet());
// List of all energy channel ids
public static final String CHANNEL_ENERGY_POWER = "power";
public static final String CHANNEL_ENERGY_TOTAL = "energyUsage";
public static final String CHANNEL_ENERGY_VOLTAGE = "voltage";
public static final String CHANNEL_ENERGY_CURRENT = "current";
public static final Set<String> CHANNELS_ENERGY = Stream
.of(CHANNEL_ENERGY_POWER, CHANNEL_ENERGY_TOTAL, CHANNEL_ENERGY_VOLTAGE, CHANNEL_ENERGY_CURRENT)
.collect(Collectors.toSet());
// List of all misc channel ids
public static final String CHANNEL_RSSI = "rssi";
// List of all group channel ids
public static final String CHANNEL_SWITCH_GROUP = "group";
public static final String CHANNEL_OUTLET_GROUP_PREFIX = "outlet";
// List of configuration keys
public static final String CONFIG_IP = "ipAddress";
public static final String CONFIG_DEVICE_ID = "deviceId";
public static final String CONFIG_REFRESH = "refresh";
// Only for bulbs
public static final String CONFIG_TRANSITION_PERIOD = "transitionPeriod";
// List of property keys
public static final String PROPERTY_TYPE = "type";
public static final String PROPERTY_MODEL = "model";
public static final String PROPERTY_DEVICE_NAME = "device name";
public static final String PROPERTY_MAC = "mac";
public static final String PROPERTY_HARDWARE_VERSION = "hardware version";
public static final String PROPERTY_SOFWARE_VERSION = "sofware version";
public static final String PROPERTY_HARDWARE_ID = "hardware id";
public static final String PROPERTY_FIRMWARE_ID = "firmware id";
public static final String PROPERTY_OEM_ID = "oem id";
public static final String PROPERTY_FEATURE = "feature";
public static final String PROPERTY_PROTOCOL_NAME = "protocol name";
public static final String PROPERTY_PROTOCOL_VERSION = "protocol version";
public static final int FORCED_REFRESH_BOUNDERY_SECONDS = 60;
public static final int FORCED_REFRESH_BOUNDERY_SWITCHED_SECONDS = 5;
private TPLinkSmartHomeBindingConstants() {
// Constants class
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
/**
* Data class representing the user configurable settings of the device
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class TPLinkSmartHomeConfiguration {
/**
* IP Address of the device.
*/
public String ipAddress;
/**
* The id of the device;
*/
public String deviceId;
/**
* Refresh rate for the device in seconds.
*/
public int refresh;
/**
* Transition period of light bulb state changes in seconds.
*/
public int transitionPeriod;
}

View File

@@ -0,0 +1,207 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CONFIG_DEVICE_ID;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.*;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link TPLinkSmartHomeDiscoveryService} detects new Smart Home Bulbs, Plugs and Switches by sending a UDP network
* broadcast and parsing the answer into a thing.
*
* @author Christian Fischer - Initial contribution
* @author Hilbrand Bouwkamp - Complete make-over, reorganized code and code cleanup.
*/
@Component(service = { DiscoveryService.class,
TPLinkIpAddressService.class }, configurationPid = "discovery.tplinksmarthome")
@NonNullByDefault
public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService implements TPLinkIpAddressService {
private static final String BROADCAST_IP = "255.255.255.255";
private static final int DISCOVERY_TIMEOUT_SECONDS = 8;
private static final int UDP_PACKET_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(DISCOVERY_TIMEOUT_SECONDS - 1);
private static final long REFRESH_INTERVAL_MINUTES = 1;
private final Logger logger = LoggerFactory.getLogger(TPLinkSmartHomeDiscoveryService.class);
private final Commands commands = new Commands();
private final Map<String, String> idInetAddressCache = new ConcurrentHashMap<>();
private final DatagramPacket discoverPacket;
private final byte[] buffer = new byte[2048];
private @NonNullByDefault({}) DatagramSocket discoverSocket;
private @NonNullByDefault({}) ScheduledFuture<?> discoveryJob;
public TPLinkSmartHomeDiscoveryService() throws UnknownHostException {
super(SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS);
final InetAddress broadcast = InetAddress.getByName(BROADCAST_IP);
final byte[] discoverbuffer = CryptUtil.encrypt(Commands.getSysinfo());
discoverPacket = new DatagramPacket(discoverbuffer, discoverbuffer.length, broadcast,
Connection.TP_LINK_SMART_HOME_PORT);
}
@Override
public @Nullable String getLastKnownIpAddress(String deviceId) {
return idInetAddressCache.get(deviceId);
}
@Override
protected void startBackgroundDiscovery() {
discoveryJob = scheduler.scheduleWithFixedDelay(this::startScan, 0, REFRESH_INTERVAL_MINUTES, TimeUnit.MINUTES);
}
@Override
protected void stopBackgroundDiscovery() {
stopScan();
if (discoveryJob != null && !discoveryJob.isCancelled()) {
discoveryJob.cancel(true);
discoveryJob = null;
}
}
@Override
protected void startScan() {
logger.debug("Start scan for TP-Link Smart devices.");
synchronized (this) {
try {
idInetAddressCache.clear();
discoverSocket = sendDiscoveryPacket();
// Runs until the socket call gets a time out and throws an exception. When a time out is triggered it
// means no data was present and nothing new to discover.
while (true) {
if (discoverSocket == null) {
break;
}
final DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
discoverSocket.receive(packet);
logger.debug("TP-Link Smart device discovery returned package with length {}", packet.getLength());
if (packet.getLength() > 0) {
detectThing(packet);
}
}
} catch (SocketTimeoutException e) {
logger.debug("Discovering poller timeout...");
} catch (IOException e) {
logger.debug("Error during discovery: {}", e.getMessage());
} finally {
closeDiscoverSocket();
removeOlderResults(getTimestampOfLastScan());
}
}
}
@Override
protected void stopScan() {
logger.debug("Stop scan for TP-Link Smart devices.");
closeDiscoverSocket();
super.stopScan();
}
/**
* Opens a {@link DatagramSocket} and sends a packet for discovery of TP-Link Smart Home devices.
*
* @return Returns the new socket
* @throws IOException exception in case sending the packet failed
*/
protected DatagramSocket sendDiscoveryPacket() throws IOException {
final DatagramSocket ds = new DatagramSocket(null);
ds.setBroadcast(true);
ds.setSoTimeout(UDP_PACKET_TIMEOUT_MS);
ds.send(discoverPacket);
logger.trace("Discovery package sent.");
return ds;
}
/**
* Closes the discovery socket and cleans the value. No need for synchronization as this method is called from a
* synchronized context.
*/
private void closeDiscoverSocket() {
if (discoverSocket != null) {
discoverSocket.close();
discoverSocket = null;
}
}
/**
* Detected a device (thing) and get process the data from the device and report it discovered.
*
* @param packet containing data of detected device
* @throws IOException in case decrypting of the data failed
*/
private void detectThing(DatagramPacket packet) throws IOException {
final String ipAddress = packet.getAddress().getHostAddress();
final String rawData = CryptUtil.decrypt(packet.getData(), packet.getLength());
final Sysinfo sysinfoRaw = commands.getSysinfoReponse(rawData);
final Sysinfo sysinfo = sysinfoRaw.getActualSysinfo();
logger.trace("Detected TP-Link Smart Home device: {}", rawData);
final String deviceId = sysinfo.getDeviceId();
logger.debug("TP-Link Smart Home device '{}' with id {} found on {} ", sysinfo.getAlias(), deviceId, ipAddress);
idInetAddressCache.put(deviceId, ipAddress);
final Optional<TPLinkSmartHomeThingType> thingType = getThingTypeUID(sysinfo.getModel());
if (thingType.isPresent()) {
final ThingTypeUID thingTypeUID = thingType.get().thingTypeUID();
final ThingUID thingUID = new ThingUID(thingTypeUID,
deviceId.substring(deviceId.length() - 6, deviceId.length()));
final Map<String, Object> properties = PropertiesCollector.collectProperties(thingType.get(), ipAddress,
sysinfoRaw);
final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withLabel(sysinfo.getAlias()).withRepresentationProperty(CONFIG_DEVICE_ID)
.withProperties(properties).build();
thingDiscovered(discoveryResult);
} else {
logger.debug("Detected, but ignoring unsupported TP-Link Smart Home device model '{}'", sysinfo.getModel());
}
}
/**
* Finds the {@link ThingTypeUID} based on the model value returned by the device.
*
* @param model model value returned by the device
* @return {@link ThingTypeUID} or null if device not recognized
*/
private Optional<TPLinkSmartHomeThingType> getThingTypeUID(String model) {
final String modelLC = model.toLowerCase(Locale.ENGLISH);
return SUPPORTED_THING_TYPES_LIST.stream().filter(type -> modelLC.startsWith(type.thingTypeUID().getId()))
.findFirst();
}
}

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.device.BulbDevice;
import org.openhab.binding.tplinksmarthome.internal.device.DimmerDevice;
import org.openhab.binding.tplinksmarthome.internal.device.EnergySwitchDevice;
import org.openhab.binding.tplinksmarthome.internal.device.PowerStripDevice;
import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice;
import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice;
import org.openhab.binding.tplinksmarthome.internal.device.SwitchDevice;
import org.openhab.binding.tplinksmarthome.internal.handler.SmartHomeHandler;
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;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link TPLinkSmartHomeHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Christian Fischer - Initial contribution
* @author Hilbrand Bouwkamp - Specific handlers for different type of devices.
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.tplinksmarthome")
public class TPLinkSmartHomeHandlerFactory extends BaseThingHandlerFactory {
private @NonNullByDefault({}) TPLinkIpAddressService ipAddressService;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Nullable
@Override
protected ThingHandler createHandler(Thing thing) {
final ThingTypeUID thingTypeUID = thing.getThingTypeUID();
final TPLinkSmartHomeThingType type = TPLinkSmartHomeThingType.THING_TYPE_MAP.get(thingTypeUID);
if (type == null) {
return null;
}
final SmartHomeDevice device;
switch (type.getDeviceType()) {
case BULB:
if (TPLinkSmartHomeThingType.isBulbDeviceWithTemperatureColor1(thingTypeUID)) {
device = new BulbDevice(thingTypeUID, COLOR_TEMPERATURE_1_MIN, COLOR_TEMPERATURE_1_MAX);
} else if (TPLinkSmartHomeThingType.isBulbDeviceWithTemperatureColor2(thingTypeUID)) {
device = new BulbDevice(thingTypeUID, COLOR_TEMPERATURE_2_MIN, COLOR_TEMPERATURE_2_MAX);
} else {
device = new BulbDevice(thingTypeUID);
}
break;
case DIMMER:
device = new DimmerDevice();
break;
case PLUG:
if (HS110.is(thingTypeUID)) {
device = new EnergySwitchDevice();
} else {
device = new SwitchDevice();
}
break;
case SWITCH:
device = new SwitchDevice();
break;
case RANGE_EXTENDER:
device = new RangeExtenderDevice();
break;
case STRIP:
device = new PowerStripDevice(type);
break;
default:
return null;
}
return new SmartHomeHandler(thing, device, type, ipAddressService);
}
@Reference
protected void setTPLinkIpAddressCache(TPLinkIpAddressService ipAddressCache) {
this.ipAddressService = ipAddressCache;
}
protected void unsetTPLinkIpAddressCache(TPLinkIpAddressService ipAddressCache) {
this.ipAddressService = null;
}
}

View File

@@ -0,0 +1,198 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* ThingType enum with all supported TP-Link Smart Home devices.
*
* @author Hilbrand Bouwkamp - Initial contribution
*
*/
@NonNullByDefault
public enum TPLinkSmartHomeThingType {
// Bulb Thing Type UIDs
KB100("kb100", DeviceType.BULB),
KB130("kb130", DeviceType.BULB),
LB100("lb100", DeviceType.BULB),
LB110("lb110", DeviceType.BULB),
LB120("lb120", DeviceType.BULB),
LB130("lb130", DeviceType.BULB),
LB200("lb200", DeviceType.BULB),
LB230("lb230", DeviceType.BULB),
KL50("kl50", DeviceType.BULB),
KL60("kl60", DeviceType.BULB),
KL110("kl110", DeviceType.BULB),
KL120("kl120", DeviceType.BULB),
KL130("kl130", DeviceType.BULB),
// Plug Thing Type UIDs
HS100("hs100", DeviceType.PLUG),
HS103("hs103", DeviceType.PLUG),
HS105("hs105", DeviceType.PLUG),
HS110("hs110", DeviceType.PLUG),
KP100("kp100", DeviceType.PLUG),
KP105("kp105", DeviceType.PLUG),
// Switch Thing Type UIDs
HS200("hs200", DeviceType.SWITCH),
HS210("hs210", DeviceType.SWITCH),
// Dimmer Thing Type UIDs
HS220("hs220", DeviceType.DIMMER),
// Power Strip Thing Type UIDs.
HS107("hs107", DeviceType.STRIP, 2),
HS300("hs300", DeviceType.STRIP, 6),
KP200("kp200", DeviceType.STRIP, 2),
KP303("kp303", DeviceType.STRIP, 3),
KP400("kp400", DeviceType.STRIP, 2),
// Range Extender Thing Type UIDs
RE270K("re270", DeviceType.RANGE_EXTENDER),
RE370K("re370", DeviceType.RANGE_EXTENDER);
/**
* All supported Smart Home devices in a list.
*/
public static final List<TPLinkSmartHomeThingType> SUPPORTED_THING_TYPES_LIST = Arrays
.asList(TPLinkSmartHomeThingType.values());
/**
* All {@link ThingTypeUID}s of all supported Smart Home devices.
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = SUPPORTED_THING_TYPES_LIST.stream()
.map(TPLinkSmartHomeThingType::thingTypeUID).collect(Collectors.toSet());
/**
* A map of all {@link TPLinkSmartHomeThingType} mapped to {@link ThingTypeUID}.
*/
public static final Map<ThingTypeUID, TPLinkSmartHomeThingType> THING_TYPE_MAP = SUPPORTED_THING_TYPES_LIST.stream()
.collect(Collectors.toMap(TPLinkSmartHomeThingType::thingTypeUID, Function.identity()));
private static final List<TPLinkSmartHomeThingType> BULB_WITH_TEMPERATURE_COLOR_1 = Stream.of(LB120, KL120)
.collect(Collectors.toList());
private static final List<TPLinkSmartHomeThingType> BULB_WITH_TEMPERATURE_COLOR_2 = Stream
.of(KB130, KL130, LB130, LB230).collect(Collectors.toList());
private final ThingTypeUID thingTypeUID;
private final DeviceType type;
private final int sockets;
TPLinkSmartHomeThingType(final String name, final DeviceType type) {
this(name, type, 0);
}
TPLinkSmartHomeThingType(final String name, final DeviceType type, int sockets) {
thingTypeUID = new ThingTypeUID(TPLinkSmartHomeBindingConstants.BINDING_ID, name);
this.type = type;
this.sockets = sockets;
}
/**
* @return Returns the type of the device.
*/
public DeviceType getDeviceType() {
return type;
}
/**
* @return The {@link ThingTypeUID} of this device.
*/
public ThingTypeUID thingTypeUID() {
return thingTypeUID;
}
/**
* @return Returns the number of sockets. Only for Strip devices.
*/
public int getSockets() {
return sockets;
}
/**
* Returns true if the given {@link ThingTypeUID} matches a device that is a bulb with color temperature ranges 1
* (2700 to 6500k).
*
* @param thingTypeUID if the check
* @return true if it's a bulb device with color temperature range 1
*/
public static boolean isBulbDeviceWithTemperatureColor1(ThingTypeUID thingTypeUID) {
return isDevice(thingTypeUID, BULB_WITH_TEMPERATURE_COLOR_1);
}
/**
* Returns true if the given {@link ThingTypeUID} matches a device that is a bulb with color temperature ranges 2
* (2500 to 9000k).
*
* @param thingTypeUID if the check
* @return true if it's a bulb device with color temperature range 2
*/
public static boolean isBulbDeviceWithTemperatureColor2(ThingTypeUID thingTypeUID) {
return isDevice(thingTypeUID, BULB_WITH_TEMPERATURE_COLOR_2);
}
private static boolean isDevice(ThingTypeUID thingTypeUID, List<TPLinkSmartHomeThingType> thingTypes) {
return thingTypes.stream().anyMatch(t -> t.is(thingTypeUID));
}
/**
* Returns true if the given {@link ThingTypeUID} matches the {@link ThingTypeUID} in this enum.
*
* @param otherThingTypeUID to check
* @return true if matches
*/
public boolean is(ThingTypeUID otherThingTypeUID) {
return thingTypeUID.equals(otherThingTypeUID);
}
/**
* Enum indicating the type of the device.
*/
public enum DeviceType {
/**
* Light Bulb device.
*/
BULB,
/**
* Dimmer device.
*/
DIMMER,
/**
* Plug device.
*/
PLUG,
/**
* Wi-Fi range extender device with plug.
*/
RANGE_EXTENDER,
/**
* Power strip device.
*/
STRIP,
/**
* Switch device.
*/
SWITCH
}
}

View File

@@ -0,0 +1,162 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
import org.openhab.binding.tplinksmarthome.internal.model.LightState;
import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightStateResponse;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* TP-Link Smart Home Light Bulb.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class BulbDevice extends SmartHomeDevice {
protected Commands commands = new Commands();
private final int colorTempMin;
private final int colorTempMax;
private final int colorTempRangeFactor;
public BulbDevice(ThingTypeUID thingTypeUID) {
this(thingTypeUID, 0, 0);
}
public BulbDevice(ThingTypeUID thingTypeUID, int colorTempMin, int colorTempMax) {
this.colorTempMin = colorTempMin;
this.colorTempMax = colorTempMax;
colorTempRangeFactor = (colorTempMax - colorTempMin) / 100;
}
@Override
public String getUpdateCommand() {
return Commands.getRealtimeBulbAndSysinfo();
}
@Override
public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException {
final String channelId = channelUid.getId();
final int transitionPeriod = configuration.transitionPeriod;
final HasErrorResponse response;
if (command instanceof OnOffType) {
response = handleOnOffType(channelId, (OnOffType) command, transitionPeriod);
} else if (command instanceof HSBType) {
response = handleHSBType(channelId, (HSBType) command, transitionPeriod);
} else if (command instanceof DecimalType) {
response = handleDecimalType(channelId, (DecimalType) command, transitionPeriod);
} else {
return false;
}
checkErrors(response);
return response != null;
}
private @Nullable HasErrorResponse handleOnOffType(String channelID, OnOffType onOff, int transitionPeriod)
throws IOException {
if (CHANNELS_BULB_SWITCH.contains(channelID)) {
return commands.setTransitionLightStateResponse(
connection.sendCommand(commands.setLightState(onOff, transitionPeriod)));
}
return null;
}
private @Nullable HasErrorResponse handleDecimalType(String channelID, DecimalType command, int transitionPeriod)
throws IOException {
if (CHANNEL_COLOR.equals(channelID) || CHANNEL_BRIGHTNESS.equals(channelID)) {
return commands.setTransitionLightStateResponse(
connection.sendCommand(commands.setBrightness(command.intValue(), transitionPeriod)));
} else if (CHANNEL_COLOR_TEMPERATURE.equals(channelID)) {
return handleColorTemperature(convertPercentageToKelvin(command.intValue()), transitionPeriod);
} else if (CHANNEL_COLOR_TEMPERATURE_ABS.equals(channelID)) {
return handleColorTemperature(guardColorTemperature(command.intValue()), transitionPeriod);
}
return null;
}
private @Nullable TransitionLightStateResponse handleColorTemperature(int colorTemperature, int transitionPeriod)
throws IOException {
return commands.setTransitionLightStateResponse(
connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod)));
}
@Nullable
private HasErrorResponse handleHSBType(String channelID, HSBType command, int transitionPeriod) throws IOException {
if (CHANNEL_COLOR.equals(channelID)) {
return commands.setTransitionLightStateResponse(
connection.sendCommand(commands.setColor(command, transitionPeriod)));
}
return null;
}
@Override
public State updateChannel(ChannelUID channelUid, DeviceState deviceState) {
final LightState lightState = deviceState.getSysinfo().getLightState();
final State state;
switch (channelUid.getId()) {
case CHANNEL_BRIGHTNESS:
state = lightState.getBrightness();
break;
case CHANNEL_COLOR:
state = new HSBType(lightState.getHue(), lightState.getSaturation(), lightState.getBrightness());
break;
case CHANNEL_COLOR_TEMPERATURE:
state = new PercentType(convertKelvinToPercentage(lightState.getColorTemp()));
break;
case CHANNEL_COLOR_TEMPERATURE_ABS:
state = new DecimalType(guardColorTemperature(lightState.getColorTemp()));
break;
case CHANNEL_SWITCH:
state = lightState.getOnOff();
break;
case CHANNEL_ENERGY_POWER:
state = new DecimalType(deviceState.getRealtime().getPower());
break;
default:
state = UnDefType.UNDEF;
break;
}
return state;
}
private int convertPercentageToKelvin(int percentage) {
return guardColorTemperature(colorTempMin + colorTempRangeFactor * percentage);
}
private int convertKelvinToPercentage(int colorTemperature) {
return (guardColorTemperature(colorTemperature) - colorTempMin) / colorTempRangeFactor;
}
private int guardColorTemperature(int colorTemperature) {
return Math.max(colorTempMin, Math.min(colorTempMax, colorTemperature));
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.model.Realtime;
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
/**
* Data class to store device state.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class DeviceState {
private final Commands commands = new Commands();
private final Realtime realtime;
private final Sysinfo sysinfo;
/**
* Initializes the device state given the json response from the device.
*
* @param state device state as json string
*/
public DeviceState(String state) {
sysinfo = commands.getSysinfoReponse(state);
realtime = commands.getRealtimeResponse(state);
}
/**
* @return returns the device energy data (if present)
*/
public Realtime getRealtime() {
return realtime;
}
/**
* @return returns the device state
*/
public Sysinfo getSysinfo() {
return sysinfo;
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CHANNEL_BRIGHTNESS;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
/**
* TP-Link Smart Home device with a dimmer (HS220).
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class DimmerDevice extends SwitchDevice {
@Override
protected @Nullable HasErrorResponse setOnOffState(ChannelUID channelUid, OnOffType onOff) throws IOException {
return commands.setSwitchStateResponse(connection.sendCommand(commands.setSwitchState(onOff)));
}
@Override
public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException {
return CHANNEL_BRIGHTNESS.equals(channelUid.getId()) ? handleBrightnessChannel(channelUid, command)
: super.handleCommand(channelUid, command);
}
/**
* Handle the brightness channel. Because the device has different commands for setting the device on/off and
* setting the brightness the on/off command must be send to the device as well when the brightness.
*
* @param channelUid uid of the channel to handle
* @param command command to the send
* @return returns true if the command was handled
* @throws IOException throws an {@link IOException} if the command handling failed
*/
private boolean handleBrightnessChannel(ChannelUID channelUid, Command command) throws IOException {
HasErrorResponse response = null;
if (command instanceof OnOffType) {
response = setOnOffState(channelUid, (OnOffType) command);
} else if (command instanceof PercentType) {
PercentType percentCommand = (PercentType) command;
// Don't send value 0 as brightness value as it will give an error from the device.
if (percentCommand.intValue() > 0) {
response = commands.setDimmerBrightnessResponse(
connection.sendCommand(commands.setDimmerBrightness(percentCommand.intValue())));
checkErrors(response);
if (response == null) {
return false;
}
response = setOnOffState(channelUid, OnOffType.ON);
} else {
response = setOnOffState(channelUid, OnOffType.OFF);
}
}
checkErrors(response);
return response != null;
}
@Override
public State updateChannel(ChannelUID channelUid, DeviceState deviceState) {
if (CHANNEL_BRIGHTNESS.equals(channelUid.getId())) {
return deviceState.getSysinfo().getRelayState() == OnOffType.OFF ? PercentType.ZERO
: new PercentType(deviceState.getSysinfo().getBrightness());
} else {
return super.updateChannel(channelUid, deviceState);
}
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.model.Realtime;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* TP-Link Smart Home Switch device that also can measure energy usage.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class EnergySwitchDevice extends SwitchDevice {
@Override
public String getUpdateCommand() {
return Commands.getRealtimeAndSysinfo();
}
@Override
public State updateChannel(ChannelUID channelUid, DeviceState deviceState) {
final State state;
final String matchChannelId = channelUid.isInGroup() ? channelUid.getIdWithoutGroup() : channelUid.getId();
if (CHANNELS_ENERGY.contains(matchChannelId)) {
state = updateEnergyChannel(matchChannelId, deviceState.getRealtime());
} else {
state = super.updateChannel(channelUid, deviceState);
}
return state;
}
/**
* Gets the state for an energy channel.
*
* @param channelId Id of the energy channel to get the state
* @param realtime data object containing the data from the device
* @return state object for the given channel
*/
protected State updateEnergyChannel(String channelId, Realtime realtime) {
final State value;
switch (channelId) {
case CHANNEL_ENERGY_CURRENT:
value = new QuantityType<>(realtime.getCurrent(), SmartHomeUnits.AMPERE);
break;
case CHANNEL_ENERGY_TOTAL:
value = new QuantityType<>(realtime.getTotal(), SmartHomeUnits.KILOWATT_HOUR);
break;
case CHANNEL_ENERGY_VOLTAGE:
value = new QuantityType<>(realtime.getVoltage(), SmartHomeUnits.VOLT);
break;
case CHANNEL_ENERGY_POWER:
value = new QuantityType<>(realtime.getPower(), SmartHomeUnits.WATT);
break;
default:
value = UnDefType.UNDEF;
break;
}
return value;
}
}

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType;
import org.openhab.binding.tplinksmarthome.internal.model.Realtime;
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo.Outlet;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TP-Link Smart Home device with multiple sockets, like the HS107 and HS300.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class PowerStripDevice extends EnergySwitchDevice {
private final Logger logger = LoggerFactory.getLogger(PowerStripDevice.class);
private final List<@Nullable Realtime> realTimeCacheList;
private final List<@Nullable String> childIds;
public PowerStripDevice(final TPLinkSmartHomeThingType type) {
final int nrOfSockets = type.getSockets();
realTimeCacheList = new ArrayList<>(nrOfSockets);
childIds = new ArrayList<>(Collections.nCopies(nrOfSockets, ""));
}
@Override
public String getUpdateCommand() {
return Commands.getSysinfo();
}
@Override
public void refreshedDeviceState(@Nullable final DeviceState deviceState) {
if (deviceState != null) {
for (int i = 0; i < childIds.size(); i++) {
childIds.set(i, deviceState.getSysinfo().getChildren().get(i).getId());
realTimeCacheList.add(i, refreshCache(i));
}
}
}
@Override
protected State getOnOffState(final DeviceState deviceState) {
// Global On/Off state is determined by the combined state of all sockets. If 1 socket is on, the state is on.
return OnOffType
.from(deviceState.getSysinfo().getChildren().stream().anyMatch(e -> OnOffType.ON.equals(e.getState())));
}
@Override
public State updateChannel(final ChannelUID channelUid, final DeviceState deviceState) {
final int idx = channelToIndex(channelUid);
if (idx >= 0 && idx < childIds.size()) {
final Outlet outlet = deviceState.getSysinfo().getChildren().get(idx);
final String baseChannel = channelUid.getIdWithoutGroup();
if (CHANNEL_SWITCH.equals(baseChannel)) {
return outlet.getState();
} else if (CHANNELS_ENERGY.contains(baseChannel)) {
final Realtime realTime = realTimeCacheList.get(idx);
return realTime == null ? UnDefType.UNDEF : updateEnergyChannel(baseChannel, realTime);
}
} else {
if (idx >= 0) {
logger.debug("For channel update the index '{}' could be mapped to a channel. passed channel: {}", idx,
channelUid);
}
}
return super.updateChannel(channelUid, deviceState);
}
@Override
protected @Nullable String getChildId(final ChannelUID channelUid) {
final int idx = channelToIndex(channelUid);
return idx >= 0 && idx < childIds.size() ? childIds.get(idx) : null;
}
private int channelToIndex(final ChannelUID channelUid) {
final String groupId = channelUid.getGroupId();
return (groupId != null && groupId.startsWith(CHANNEL_OUTLET_GROUP_PREFIX)
? Integer.parseInt(groupId.substring(CHANNEL_OUTLET_GROUP_PREFIX.length()))
: 0) - 1;
}
private @Nullable Realtime refreshCache(final int idx) {
try {
final String childId = childIds.get(idx);
return childId == null ? null
: commands.getRealtimeResponse(connection.sendCommand(Commands.getRealtimeWithContext(childId)));
} catch (final IOException e) {
return null;
} catch (final RuntimeException e) {
logger.debug(
"Obtaining realtime data for channel-'{}' unexpectedly crashed. If this keeps happening please report: ",
idx, e);
return null;
}
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.State;
/**
* TP-Link Smart Home range extender with smart plug.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class RangeExtenderDevice extends SwitchDevice {
@Override
protected State getOnOffState(DeviceState deviceState) {
return deviceState.getSysinfo().getPlug().getRelayStatus();
}
@Override
protected @Nullable SetRelayState setOnOffState(ChannelUID channelUid, OnOffType onOff) throws IOException {
// It's unknown what the command is to send to the device so it's not supported.
return null;
}
}

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.Connection;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration;
import org.openhab.binding.tplinksmarthome.internal.model.ErrorResponse;
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
/**
* Abstract class as base for Smart Home device implementations.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public abstract class SmartHomeDevice {
protected final Commands commands = new Commands();
protected @NonNullByDefault({}) Connection connection;
protected @NonNullByDefault({}) TPLinkSmartHomeConfiguration configuration;
/**
* Checks if the response object contains errors and if so throws an {@link IOException} when an error code was set.
*
* @param response The response to check for errors.
* @throws IOException if an error code was set in the response object
*/
protected void checkErrors(@Nullable HasErrorResponse response) throws IOException {
final ErrorResponse errorResponse = response == null ? null : response.getErrorResponse();
if (errorResponse != null && errorResponse.getErrorCode() != 0) {
throw new IOException("Error (" + errorResponse.getErrorCode() + "): " + errorResponse.getErrorMessage());
}
}
/**
* Sets connection and configuration values.
*
* @param connection The connection to the device
* @param configuration The global configuration
*/
public void initialize(Connection connection, TPLinkSmartHomeConfiguration configuration) {
this.connection = connection;
this.configuration = configuration;
}
/**
* @return the json string to send to the device to get the state of the device.
*/
public abstract String getUpdateCommand();
/**
* Handle the command for the given channel
*
* @param channelUID The channel the command is for
* @param command The command to be send to the device
* @return Returns true if the commands successfully was send to the device
* @throws IOException In case of communications error or the device returned an error
*/
public abstract boolean handleCommand(ChannelUID channelUID, Command command) throws IOException;
/**
* Returns the {@link State} value for the given value extracted from the deviceState data.
*
* @param channelUid channel to get state for
* @param deviceState state object containing the state
* @return {@link State} value for the given channel
*/
public abstract State updateChannel(ChannelUID channelUid, DeviceState deviceState);
/**
* Called with the new device state after the new device state is retrieved from the device.
*
* @param deviceState new device state
*/
public void refreshedDeviceState(@Nullable DeviceState deviceState) {
}
}

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* TP-Link Smart Home device with a switch, like Smart Plugs and Switches.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class SwitchDevice extends SmartHomeDevice {
@Override
public String getUpdateCommand() {
return Commands.getSysinfo();
}
@Override
public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException {
return command instanceof OnOffType && handleOnOffType(channelUid, (OnOffType) command);
}
/**
* Returns the switch state.
*
* @param deviceState data object containing the state
* @return the switch state
*/
protected State getOnOffState(DeviceState deviceState) {
return deviceState.getSysinfo().getRelayState();
}
private boolean handleOnOffType(ChannelUID channelUid, OnOffType onOff) throws IOException {
final HasErrorResponse response;
final String baseChannelId = getBaseChannel(channelUid);
if (CHANNEL_SWITCH.contentEquals(baseChannelId)) {
response = setOnOffState(channelUid, onOff);
} else if (CHANNEL_LED.contentEquals(baseChannelId)) {
response = commands
.setLedOnResponse(connection.sendCommand(commands.setLedOn(onOff, getChildId(channelUid))));
} else {
response = null;
}
checkErrors(response);
return response != null;
}
/**
* Sends the {@link OnOffType} command to the device and returns the returned answer.
*
* @param channelUid channel Id to use to determine child id
* @param onOff command to the send
* @return state returned by the device
* @throws IOException exception in case device not reachable
*/
protected @Nullable HasErrorResponse setOnOffState(ChannelUID channelUid, OnOffType onOff) throws IOException {
return commands
.setRelayStateResponse(connection.sendCommand(commands.setRelayState(onOff, getChildId(channelUid))));
}
@Override
public State updateChannel(ChannelUID channelUid, DeviceState deviceState) {
final String baseChannelId = getBaseChannel(channelUid);
if (CHANNEL_SWITCH.equals(baseChannelId)) {
return getOnOffState(deviceState);
} else if (CHANNEL_LED.equals(baseChannelId)) {
return deviceState.getSysinfo().getLedOff();
}
return UnDefType.UNDEF;
}
/**
* Returns the child Id for the given channel if the device supports children and it's a channel for a specific
* child.
*
* @param channelUid channel Id to get the child id for
* @return null or child id
*/
protected @Nullable String getChildId(ChannelUID channelUid) {
return null;
}
private String getBaseChannel(ChannelUID channelUid) {
return channelUid.isInGroup() ? channelUid.getIdWithoutGroup() : channelUid.getId();
}
}

View File

@@ -0,0 +1,260 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.handler;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.util.StringUtil;
import org.openhab.binding.tplinksmarthome.internal.Connection;
import org.openhab.binding.tplinksmarthome.internal.TPLinkIpAddressService;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.DeviceType;
import org.openhab.binding.tplinksmarthome.internal.device.DeviceState;
import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
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.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler class for TP-Link Smart Home devices.
*
* @author Christian Fischer - Initial contribution
* @author Hilbrand Bouwkamp - Rewrite to generic TP-Link Smart Home Handler
*/
@NonNullByDefault
public class SmartHomeHandler extends BaseThingHandler {
private static final Duration ONE_SECOND = Duration.ofSeconds(1);
private final Logger logger = LoggerFactory.getLogger(SmartHomeHandler.class);
private final SmartHomeDevice smartHomeDevice;
private final TPLinkIpAddressService ipAddressService;
private final int forceRefreshThreshold;
private @NonNullByDefault({}) TPLinkSmartHomeConfiguration configuration;
private @NonNullByDefault({}) Connection connection;
private @NonNullByDefault({}) ScheduledFuture<?> refreshJob;
private @NonNullByDefault({}) ExpiringCache<@Nullable DeviceState> cache;
/**
* Cache to avoid refresh is called multiple time in 1 second.
*/
private @NonNullByDefault({}) ExpiringCache<@Nullable DeviceState> fastCache;
/**
* Constructor.
*
* @param thing The thing to handle
* @param smartHomeDevice Specific Smart Home device handler
* @param type The device type
* @param ipAddressService Cache keeping track of ip addresses of tp link devices
*/
public SmartHomeHandler(Thing thing, SmartHomeDevice smartHomeDevice, TPLinkSmartHomeThingType type,
TPLinkIpAddressService ipAddressService) {
super(thing);
this.smartHomeDevice = smartHomeDevice;
this.ipAddressService = ipAddressService;
this.forceRefreshThreshold = type.getDeviceType() == DeviceType.SWITCH
|| type.getDeviceType() == DeviceType.DIMMER ? FORCED_REFRESH_BOUNDERY_SWITCHED_SECONDS
: FORCED_REFRESH_BOUNDERY_SECONDS;
}
@Override
public void handleCommand(ChannelUID channelUid, Command command) {
try {
if (command instanceof RefreshType) {
updateChannelState(channelUid, fastCache.getValue());
} else if (smartHomeDevice.handleCommand(channelUid, command)) {
// After a command always refresh the cache to make sure the cache has the latest data
updateChannelState(channelUid, forceCacheUpdate());
} else {
logger.debug("Command {} is not supported for channel: {}", command, channelUid.getId());
}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
@Override
public void dispose() {
if (refreshJob != null && !refreshJob.isCancelled()) {
refreshJob.cancel(true);
refreshJob = null;
}
}
@Override
public void initialize() {
configuration = getConfigAs(TPLinkSmartHomeConfiguration.class);
if (StringUtil.isBlank(configuration.ipAddress) && StringUtil.isBlank(configuration.deviceId)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No ip address or the device id configured.");
return;
}
logger.debug("Initializing TP-Link Smart device on ip '{}' or deviceId '{}' ", configuration.ipAddress,
configuration.deviceId);
connection = createConnection(configuration);
smartHomeDevice.initialize(connection, configuration);
cache = new ExpiringCache<>(Duration.ofSeconds(configuration.refresh), this::refreshCache);
// If refresh > threshold fast cache invalidates after 1 second, else it behaves just as the 'normal' cache
fastCache = configuration.refresh > forceRefreshThreshold
? new ExpiringCache<>(ONE_SECOND, this::forceCacheUpdate)
: cache;
updateStatus(ThingStatus.UNKNOWN);
// While config.xml defines refresh as min 1, this check is used to run a test that doesn't start refresh.
if (configuration.refresh > 0) {
startAutomaticRefresh(configuration);
}
}
/**
* Creates new Connection. Methods makes mocking of the connection in tests possible.
*
* @param config configuration to be used by the connection
* @return new Connection object
*/
Connection createConnection(TPLinkSmartHomeConfiguration config) {
return new Connection(config.ipAddress);
}
/**
* Invalidates the cache to force an update. It returns the refreshed cached value.
*
* @return the refreshed value
*/
private @Nullable DeviceState forceCacheUpdate() {
cache.invalidateValue();
return cache.getValue();
}
private @Nullable DeviceState refreshCache() {
try {
updateIpAddress();
final DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand()));
updateDeviceId(deviceState.getSysinfo().getDeviceId());
smartHomeDevice.refreshedDeviceState(deviceState);
if (getThing().getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
}
return deviceState;
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
return null;
} catch (RuntimeException e) {
logger.debug("Obtaining new device data unexpectedly crashed. If this keeps happening please report: ", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DISABLED, e.getMessage());
return null;
}
}
/**
* Checks if the current configured ip addres is still the same as by which the device is registered on the network.
* If there is a different ip address for this device it will update the configuration with this ip and start using
* this ip address.
*/
private void updateIpAddress() {
if (configuration.deviceId == null) {
// The device id is needed to get the ip address so if not known no need to continue.
return;
}
String lastKnownIpAddress = ipAddressService.getLastKnownIpAddress(configuration.deviceId);
if (lastKnownIpAddress != null && !lastKnownIpAddress.equals(configuration.ipAddress)) {
Configuration editConfig = editConfiguration();
editConfig.put(CONFIG_IP, lastKnownIpAddress);
updateConfiguration(editConfig);
configuration.ipAddress = lastKnownIpAddress;
connection.setIpAddress(lastKnownIpAddress);
}
}
/**
* Updates the device id configuration if it's not set or throws an {@link IllegalArgumentException} if the
* configured device id doesn't match with the id reported by the device.
*
* @param actualDeviceId The id of the device as actual returned by the device.
* @throws IllegalArgumentException if the configured device id doesn't match with the id reported by the device
* itself.
*/
private void updateDeviceId(String actualDeviceId) {
if (StringUtil.isBlank(configuration.deviceId)) {
Configuration editConfig = editConfiguration();
editConfig.put(CONFIG_DEVICE_ID, actualDeviceId);
updateConfiguration(editConfig);
configuration.deviceId = actualDeviceId;
} else if (!StringUtil.isBlank(actualDeviceId) && !actualDeviceId.equals(configuration.deviceId)) {
throw new IllegalArgumentException(
String.format("The configured device '%s' doesn't match with the id the device reports: '%s'.",
configuration.deviceId, actualDeviceId));
}
}
/**
* Starts the background refresh thread.
*/
private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) {
if (refreshJob == null || refreshJob.isCancelled()) {
refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, config.refresh, config.refresh,
TimeUnit.SECONDS);
}
}
void refreshChannels() {
logger.trace("Update Channels for:{}", thing.getUID());
getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), cache.getValue()));
}
/**
* Updates the state from the device data for the channel given the data..
*
* @param channelUID channel to update
* @param deviceState the state object containing the value to set of the channel
*
*/
private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState deviceState) {
if (!isLinked(channelUID)) {
return;
}
String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId();
final State state;
if (deviceState == null) {
state = UnDefType.UNDEF;
} else if (CHANNEL_RSSI.equals(channelId)) {
state = new QuantityType<>(deviceState.getSysinfo().getRssi(), SmartHomeUnits.DECIBEL_MILLIWATTS);
} else {
state = smartHomeDevice.updateChannel(channelUID, deviceState);
}
updateState(channelUID, state);
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import com.google.gson.annotations.Expose;
/**
* Class to be extended by state classes that support context. i.e. has multiple children that can be controlled.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class ContextState {
public static class Context {
@Expose
private String[] childIds = new String[1];
public void setChildId(String childId) {
this.childIds[0] = childId;
}
@Override
public String toString() {
return " child_ids:[" + childIds[0] + "]";
}
}
@Expose
private Context context;
public void setChildId(String childId) {
context = new Context();
context.setChildId(childId);
}
@Override
public String toString() {
return context == null ? "" : context.toString();
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import com.google.gson.annotations.Expose;
/**
* Base class for responses containing the common error response fields.
* Only getter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class ErrorResponse implements HasErrorResponse {
@Expose(serialize = false)
private int errCode;
@Expose(serialize = false)
private String errMsg;
/**
* @return the error code
*/
public int getErrorCode() {
return errCode;
}
/**
* @return the error message
*/
public String getErrorMessage() {
return errMsg;
}
@Override
public ErrorResponse getErrorResponse() {
return this;
}
@Override
public String toString() {
return "{err_code:" + errCode + ", err_msg:'" + errMsg + "'}";
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* Data class for getting the tp-Link Smart Plug energy state.
* Only getter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class GetRealtime extends ContextState {
public static class EMeter {
private Realtime getRealtime = new Realtime();
public Realtime getRealtime() {
return getRealtime;
}
@Override
public String toString() {
return "get_realtime:{" + getRealtime + "}";
}
}
@SerializedName(value = "emeter", alternate = "smartlife.iot.common.emeter")
private EMeter emeter = new EMeter();
public Realtime getRealtime() {
return emeter.getRealtime();
}
@Override
public String toString() {
return "GetRealtime {emeter:{" + emeter + "}}";
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Data class for getting the tp-Link Smart Plug state.
* Only getter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class GetSysinfo {
public static class System {
private Sysinfo getSysinfo = new Sysinfo();
public Sysinfo getGetSysinfo() {
return getSysinfo;
}
@Override
public String toString() {
return "get_sysinfo:{" + getSysinfo + "}";
}
}
private System system = new System();
public Sysinfo getSysinfo() {
return system.getGetSysinfo();
}
@Override
public String toString() {
return "GetSysinfo {system:{" + system + "}}";
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;
/**
* Util class to create a {@link Gson} object.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public final class GsonUtil {
private GsonUtil() {
// Util class
}
/**
* Creates a new {@link Gson} object.
*
* @return new {@link Gson} object.
*/
public static Gson createGson() {
return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
}
/**
* Creates a new {@link Gson} object that uses the {@link Expose} annotation..
*
* @return new {@link Gson} object.
*/
public static Gson createGsonWithExpose() {
return new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
}
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
/**
* Interface for data objects that have an error code in their response.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@FunctionalInterface
public interface HasErrorResponse {
/**
* @return returns the object containing the error response
*/
ErrorResponse getErrorResponse();
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
/**
* Data class for reading the retrieved state of a Smart Home light bulb.
* Mostly only getter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class LightState extends ErrorResponse {
private int brightness;
private int colorTemp;
private int hue;
private int ignoreDefault;
private String mode;
private int onOff;
private int saturation;
public PercentType getBrightness() {
return onOff > 0 ? new PercentType(brightness) : PercentType.ZERO;
}
public int getColorTemp() {
return colorTemp;
}
public DecimalType getHue() {
return new DecimalType(hue);
}
public int getIgnoreDefault() {
return ignoreDefault;
}
public String getMode() {
return mode;
}
public OnOffType getOnOff() {
return onOff == 1 ? OnOffType.ON : OnOffType.OFF;
}
public PercentType getSaturation() {
return new PercentType(saturation);
}
public void setOnOff(OnOffType onOff) {
this.onOff = onOff == OnOffType.ON ? 1 : 0;
}
@Override
public String toString() {
return "brightness:" + brightness + ", color_temp:" + colorTemp + ", hue:" + hue + ", ignore_default:"
+ ignoreDefault + ", mode:" + mode + ", on_off:" + onOff + ", saturation:" + saturation
+ super.toString();
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
/**
* Data class for reading tp-Link Smart Plug energy monitoring.
* Only getter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class Realtime extends ErrorResponse {
private static final int MILLIWATT_TO_WATT = 1000;
private static final int MILLIAMP_TO_AMP = 1000;
private static final int WATTHOUR_TO_KILOWATTHOUR = 1000;
private static final int MILLIVOLT_TO_VOLT = 1000;
private double current;
private double power;
private double total;
private double voltage;
// JSON names used for v2 hardware
private double currentMa;
private double powerMw;
private double totalWh;
private double voltageMv;
public double getCurrent() {
return currentMa > 0.0 ? currentMa / MILLIAMP_TO_AMP : current;
}
public double getPower() {
return powerMw > 0.0 ? powerMw / MILLIWATT_TO_WATT : power;
}
public double getTotal() {
return totalWh > 0.0 ? totalWh / WATTHOUR_TO_KILOWATTHOUR : total;
}
public double getVoltage() {
return voltageMv > 0.0 ? voltageMv / MILLIVOLT_TO_VOLT : voltage;
}
@Override
public String toString() {
return "current:" + getCurrent() + ", power:" + getPower() + ", total:" + getTotal() + ", voltage:"
+ getVoltage() + super.toString();
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* Data class to set the Brightness of a Smart Home dimmer (HS220).
* Setter methods for data and getter for error response.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class SetBrightness implements HasErrorResponse {
public static class Brightness extends ErrorResponse {
@Expose(deserialize = false)
private int brightness;
@Override
public String toString() {
return "brightness:" + brightness + super.toString();
}
}
public static class Dimmer {
@Expose
private Brightness setBrightness = new Brightness();
@Override
public String toString() {
return "set_brightness:{" + setBrightness + "}";
}
}
@Expose
@SerializedName("smartlife.iot.dimmer")
private Dimmer dimmer = new Dimmer();
@Override
public ErrorResponse getErrorResponse() {
return dimmer.setBrightness;
}
public void setBrightness(int brightness) {
dimmer.setBrightness.brightness = brightness;
}
@Override
public String toString() {
return "smartlife.iot.dimmer:{" + dimmer + "}";
}
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.openhab.core.library.types.OnOffType;
import com.google.gson.annotations.Expose;
/**
* Data class to set the led of a Smart Home device on or off.
* Only setter methods as the object is only used to send values.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class SetLedOff extends ContextState implements HasErrorResponse {
public static class LedOff extends ErrorResponse {
@Expose
private int off;
public void setOff(int off) {
this.off = off;
}
@Override
public String toString() {
return "off:" + off + super.toString();
}
}
public static class System {
@Expose
private LedOff setLedOff = new LedOff();
@Override
public String toString() {
return "set_led_off:{" + setLedOff + "}";
}
}
@Expose
private System system = new System();
@Override
public ErrorResponse getErrorResponse() {
return system.setLedOff;
}
public void setLed(OnOffType onOff) {
system.setLedOff.setOff(onOff == OnOffType.OFF ? 1 : 0);
}
@Override
public String toString() {
return "SetLedOff {system:{" + system + "}";
}
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.openhab.core.library.types.OnOffType;
import com.google.gson.annotations.Expose;
/**
* Data class for setting the TP-Link Smart Plug state and retrieving the result.
* Only setter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class SetRelayState extends ContextState implements HasErrorResponse {
public static class RelayState extends ErrorResponse {
@Expose(deserialize = false)
private int state;
@Override
public String toString() {
return "state:" + state;
}
}
public static class System {
@Expose
private RelayState setRelayState = new RelayState();
public void setRelayState(OnOffType onOff) {
setRelayState.state = onOff == OnOffType.ON ? 1 : 0;
}
@Override
public String toString() {
return "set_relay_state:{" + setRelayState + "}";
}
}
@Expose
private System system = new System();
@Override
public ErrorResponse getErrorResponse() {
return system.setRelayState;
}
public void setRelayState(OnOffType onOff) {
system.setRelayState(onOff);
}
@Override
public String toString() {
return "SetRelayState {system:{" + system + "}";
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.openhab.core.library.types.OnOffType;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* Data class for setting the TP-Link Smart Dimmer (HS220) state and retrieving the result.
* Only setter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class SetSwitchState implements HasErrorResponse {
public static class State extends ErrorResponse {
@Expose(deserialize = false)
private int state;
@Override
public String toString() {
return "state:" + state;
}
}
public static class Dimmer {
@Expose
private State setSwitchState = new State();
@Override
public String toString() {
return "set_switch_state:{" + setSwitchState + "}";
}
}
@Expose
@SerializedName("smartlife.iot.dimmer")
private Dimmer dimmer = new Dimmer();
@Override
public ErrorResponse getErrorResponse() {
return dimmer.setSwitchState;
}
public void setSwitchState(OnOffType onOff) {
dimmer.setSwitchState.state = onOff == OnOffType.ON ? 1 : 0;
}
@Override
public String toString() {
return "SetSwitchState {smartlife.iot.dimmer:{" + dimmer + "}";
}
}

View File

@@ -0,0 +1,325 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import java.util.ArrayList;
import java.util.List;
import org.openhab.core.library.types.OnOffType;
import com.google.gson.annotations.SerializedName;
/**
* Data class for reading TP-Link Smart Home device state.
* Only getter methods as the values are set by gson based on the retrieved json.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class Sysinfo extends ErrorResponse {
public static class CtrlProtocols {
private String name;
private String version;
public String getName() {
return name;
}
public String getVersion() {
return version;
}
@Override
public String toString() {
return "name:" + name + ", version:" + version;
}
}
/**
* With default light state state. The default light state is set when the device is off. If the device is on the
* state is in the parent fields.
*/
public static class WithDefaultLightState extends LightState {
private LightState dftOnState;
public LightState getLightState() {
if (dftOnState == null) {
return this;
} else {
dftOnState.setOnOff(getOnOff());
return dftOnState;
}
}
@Override
public String toString() {
return super.toString() + ", dftOnState:{" + dftOnState + "}";
}
}
/**
* Status of the plug as set in the range extender products.
*/
public static class Plug {
private String feature;
private String relayStatus;
public String getFeature() {
return feature;
}
public OnOffType getRelayStatus() {
return "ON".equals(relayStatus) ? OnOffType.ON : OnOffType.OFF;
}
}
/**
* Status of a single outlet on power strip.
*/
public static class Outlet {
private String alias;
private String id;
private long onTime;
private int state;
public String getAlias() {
return alias;
}
public String getId() {
return id;
}
public long getOnTime() {
return onTime;
}
public OnOffType getState() {
return state == 1 ? OnOffType.ON : OnOffType.OFF;
}
}
/**
* Status of the range extended Wi-Fi.
*/
public static class RangeextenderWireless {
private int w2gRssi;
public int getW2gRssi() {
return w2gRssi;
}
}
private String swVer;
private String hwVer;
private String model;
@SerializedName("deviceId")
private String deviceId;
@SerializedName("hwId")
private String hwId;
@SerializedName("oemId")
private String oemId;
private String alias;
private String activeMode;
private int rssi;
@SerializedName(value = "type", alternate = "mic_type")
private String type;
@SerializedName(value = "mac", alternate = { "mic_mac", "ethernet_mac" })
private String mac;
// switch and plug specific system info
@SerializedName("fwId")
private String fwId;
private String devName;
private String iconHash;
private int relayState; // 0 is off, 1 is on
private long onTime;
private String feature; // HS100 -> TIM, HS110 -> TIM:ENE
// Disabled updating as it's a different type for different devices.
// private int updating;
private int ledOff;
private double latitude;
private double longitude;
// powerstrip/multiple plugs support.
private int childNum;
private List<Outlet> children = new ArrayList<>();
// dimmer specific system info
private int brightness;
// bulb specific system info
private boolean isFactory;
private String discoVer;
private CtrlProtocols ctrlProtocols;
private WithDefaultLightState lightState = new WithDefaultLightState();
// range extender specific system info
private String ledStatus;
private Plug plug = new Plug();
private Sysinfo system;
@SerializedName("rangeextender.wireless")
private RangeextenderWireless reWireless;
public String getSwVer() {
return swVer;
}
public String getHwVer() {
return hwVer;
}
public String getType() {
return type;
}
public String getModel() {
return model;
}
public String getMac() {
return mac;
}
public String getDeviceId() {
return deviceId;
}
public String getHwId() {
return hwId;
}
public String getFwId() {
return fwId;
}
public String getOemId() {
return oemId;
}
public String getAlias() {
return alias;
}
public String getDevName() {
return devName;
}
public String getIconHash() {
return iconHash;
}
public OnOffType getRelayState() {
return relayState == 1 ? OnOffType.ON : OnOffType.OFF;
}
public int getBrightness() {
return brightness;
}
public long getOnTime() {
return onTime;
}
public String getActiveMode() {
return activeMode;
}
public String getFeature() {
return feature;
}
public int getRssi() {
// for range extender use the 2g rssi.
return reWireless == null ? rssi : reWireless.getW2gRssi();
}
public OnOffType getLedOff() {
return ledOff == 1 ? OnOffType.OFF : OnOffType.ON;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public boolean isFactory() {
return isFactory;
}
public String getDiscoVer() {
return discoVer;
}
public String getProtocolName() {
return ctrlProtocols == null ? null : ctrlProtocols.getName();
}
public String getProtocolVersion() {
return ctrlProtocols == null ? null : ctrlProtocols.getVersion();
}
public LightState getLightState() {
return lightState.getLightState();
}
public OnOffType getLedStatus() {
return "ON".equals(ledStatus) ? OnOffType.OFF : OnOffType.ON;
}
public Plug getPlug() {
return plug;
}
public int getChildNum() {
return childNum;
}
public List<Outlet> getChildren() {
return children;
}
public Sysinfo getSystem() {
return system;
}
/**
* Returns the {@link Sysinfo} object independent of the device. The range extender devices have the system
* information in another place as the other devices. This method returns the object independent of how the device
* returns it.
*
* @return device independent {@link Sysinfo} object.
*/
public Sysinfo getActualSysinfo() {
return system == null ? this : system;
}
public RangeextenderWireless getReWireless() {
return reWireless;
}
@Override
public String toString() {
return "Sysinfo [swVer=" + swVer + ", hwVer=" + hwVer + ", model=" + model + ", deviceId=" + deviceId
+ ", hwId=" + hwId + ", oemId=" + oemId + ", alias=" + alias + ", activeMode=" + activeMode + ", rssi="
+ rssi + ", type=" + type + ", mac=" + mac + ", fwId=" + fwId + ", devName=" + devName + ", iconHash="
+ iconHash + ", relayState=" + relayState + ", brightness=" + brightness + ", onTime=" + onTime
+ ", feature=" + feature + ", ledOff=" + ledOff + ", latitude=" + latitude + ", longitude=" + longitude
+ ", isFactory=" + isFactory + ", discoVer=" + discoVer + ", ctrlProtocols=" + ctrlProtocols
+ ", lightState=" + lightState + ", ledStatus=" + ledStatus + ", plug=" + plug + ", system=" + system
+ ", reWireless=" + reWireless + "]";
}
}

View File

@@ -0,0 +1,125 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import com.google.gson.annotations.SerializedName;
/**
* Data class to setting different kind of states on Smart Home light bulbs.
* Only setter methods as the object is only used to send values.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class TransitionLightState {
/**
* Color sets hue, saturation and brightness.
* color temperature present to send with default 0 value to bulb.
*/
public static class LightStateColor extends LightStateBrightness {
private int colorTemp;
private int hue;
private int saturation;
public void setHue(int hue) {
this.hue = hue;
}
public void setSaturation(int saturation) {
this.saturation = saturation;
}
@Override
public String toString() {
return "colorTemp" + colorTemp + ", hue:" + hue + ", saturation:" + saturation + ", " + super.toString();
}
}
/**
* Color Temperature doesn't set brightness therefore separate class.
* hue and saturation present to send with default 0 value to bulb.
*/
public static class LightStateColorTemperature extends LightOnOff {
private int colorTemp;
private int hue;
private int saturation;
public void setColorTemperature(int colorTemp) {
this.colorTemp = colorTemp;
}
@Override
public String toString() {
return "colorTemp" + colorTemp + ", hue:" + hue + ", saturation:" + saturation + ", " + super.toString();
}
}
public static class LightStateBrightness extends LightOnOff {
private int brightness;
public void setBrightness(int brightness) {
this.brightness = brightness;
}
@Override
public String toString() {
return "brightness:" + brightness + ", " + super.toString();
}
}
public static class LightOnOff {
private int onOff;
private int ignoreDefault = 1;
private String mode = "normal";
private int transitionPeriod;
public void setOnOff(OnOffType onOff) {
this.onOff = onOff == OnOffType.ON ? 1 : 0;
}
public void setTransitionPeriod(int transitionPeriod) {
this.transitionPeriod = transitionPeriod;
}
@Override
public String toString() {
return "onOff:" + onOff + ", ignoreDefault:" + ignoreDefault + ", mode:" + mode + ", transitionPeriod:"
+ transitionPeriod;
}
}
public static class LightingService {
private LightOnOff transitionLightState;
@Override
public String toString() {
return "transitionLightState:{" + transitionLightState + "}";
}
}
@NonNullByDefault
@SerializedName("smartlife.iot.smartbulb.lightingservice")
private LightingService service = new LightingService();
public void setLightState(LightOnOff lightState) {
service.transitionLightState = lightState;
}
@Override
public String toString() {
return "TransitionLightState {service:{" + service + "}";
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import com.google.gson.annotations.SerializedName;
/**
* Data class for getting the response from the Light bulb. This class is similar to {@link TransitionLightState} but
* has no subclasses for the light state. The other class has and makes it difficult to use to deserialize by gson.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class TransitionLightStateResponse implements HasErrorResponse {
public static class LightingService {
private LightState transitionLightState;
@Override
public String toString() {
return "LightingService:{" + transitionLightState + "}";
}
}
@SerializedName("smartlife.iot.smartbulb.lightingservice")
private LightingService service = new LightingService();
@Override
public ErrorResponse getErrorResponse() {
return service.transitionLightState;
}
@Override
public String toString() {
return "TransitionLightStateResponse {service:{" + service + "}";
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="tplinksmarthome" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>TP-Link Smart Home Binding</name>
<description>This binding integrates the TP-Link Wi-Fi Smart Home devices.</description>
<author>Hilbrand Bouwkamp, Christian Fischer</author>
</binding:binding>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:device:bulb">
<parameter name="ipAddress" type="text">
<context>network-address</context>
<label>IP Address</label>
<description>IP Address of the device.</description>
</parameter>
<parameter name="deviceId" type="text">
<label>Device Id</label>
<description>The id of the device.</description>
</parameter>
<parameter name="transitionPeriod" type="integer" min="0">
<label>Transition Period</label>
<description>Time the transition to the new state takes in milliseconds.</description>
<unitLabel>milliseconds</unitLabel>
<default>0</default>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Rate</label>
<description>Refresh of device state in seconds.</description>
<unitLabel>seconds</unitLabel>
<default>30</default>
</parameter>
</config-description>
<config-description uri="thing-type:device:plug">
<parameter name="ipAddress" type="text">
<context>network-address</context>
<label>IP Address</label>
<description>IP Address of the device.</description>
</parameter>
<parameter name="deviceId" type="text">
<label>Device Id</label>
<description>The id of the device.</description>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Rate</label>
<description>Refresh of device state in seconds.</description>
<unitLabel>seconds</unitLabel>
<default>30</default>
</parameter>
</config-description>
<config-description uri="thing-type:device:switch">
<parameter name="ipAddress" type="text">
<context>network-address</context>
<label>IP Address</label>
<description>IP Address of the device.</description>
</parameter>
<parameter name="deviceId" type="text">
<label>Device Id</label>
<description>The id of the device.</description>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Rate</label>
<description>Refresh of device state in seconds.</description>
<unitLabel>seconds</unitLabel>
<default>1</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs100">
<label>HS100</label>
<description>TP-Link HS100 Smart Wi-Fi Plug</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs103">
<label>HS103</label>
<description>TP-Link HS103 Smart Wi-Fi Plug Lite</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs105">
<label>HS105</label>
<description>TP-Link HS105 Smart Wi-Fi Plug</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs107">
<label>HS107</label>
<description>TP-Link HS107 Smart Wi-Fi Plug, 2-Outlets</description>
<category>PowerOutlet</category>
<channel-groups>
<channel-group id="groupSwitch" typeId="switch-group"/>
<channel-group id="outlet1" typeId="switch-outlet">
<label>Outlet 1</label>
</channel-group>
<channel-group id="outlet2" typeId="switch-outlet">
<label>Outlet 2</label>
</channel-group>
</channel-groups>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs110">
<label>HS110</label>
<description>TP-Link HS110 Smart Wi-Fi Plug with Energy Monitoring</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
<channel id="power" typeId="power"/>
<channel id="energyUsage" typeId="energy-usage"/>
<channel id="current" typeId="current"/>
<channel id="voltage" typeId="voltage"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs200">
<label>HS200</label>
<description>TP-Link HS200 Smart Wi-Fi Switch</description>
<category>WallSwitch</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:switch"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs210">
<label>HS210</label>
<description>TP-Link HS210 Smart Wi-Fi Light Switch 3-Way Kit</description>
<category>WallSwitch</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:switch"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs220">
<label>HS220</label>
<description>TP-Link HS220 Smart Wi-Fi Light Switch, Dimmer</description>
<category>WallSwitch</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:switch"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="hs300">
<label>HS300</label>
<description>TP-Link HS300 Smart Wi-Fi Power Strip</description>
<category>PowerOutlet</category>
<channel-groups>
<channel-group id="group" typeId="switch-group"/>
<channel-group id="outlet1" typeId="energy-outlet">
<label>Outlet 1</label>
</channel-group>
<channel-group id="outlet2" typeId="energy-outlet">
<label>Outlet 2</label>
</channel-group>
<channel-group id="outlet3" typeId="energy-outlet">
<label>Outlet 3</label>
</channel-group>
<channel-group id="outlet4" typeId="energy-outlet">
<label>Outlet 4</label>
</channel-group>
<channel-group id="outlet5" typeId="energy-outlet">
<label>Outlet 5</label>
</channel-group>
<channel-group id="outlet6" typeId="energy-outlet">
<label>Outlet 6</label>
</channel-group>
</channel-groups>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kb100">
<label>KB100</label>
<description>TP-Link KB100 Kasa Smart Light Bulb</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kb130">
<label>KB130</label>
<description>TP-Link KB130 Kasa Multi-color Smart Light Bulb</description>
<category>Lightbulb</category>
<channels>
<channel id="color" typeId="system.color"/>
<channel id="colorTemperature" typeId="system.color-temperature"/>
<channel id="colorTemperatureAbs" typeId="colorTemperatureAbs2"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kl110">
<label>KL110</label>
<description>TP-Link KL110 Smart Wi-Fi LED Bulb with Brightness</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kl120">
<label>KL120</label>
<description>TP-Link KL120 Smart Wi-Fi LED Bulb with Tunable White Light Brightness</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="colorTemperature" typeId="system.color-temperature"/>
<channel id="colorTemperatureAbs" typeId="colorTemperatureAbs1"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kl130">
<label>KL130</label>
<description>TP-Link KL130 Smart Wi-Fi LED Bulb with Color Changing Hue</description>
<category>Lightbulb</category>
<channels>
<channel id="color" typeId="system.color"/>
<channel id="colorTemperature" typeId="system.color-temperature"/>
<channel id="colorTemperatureAbs" typeId="colorTemperatureAbs2"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kl50">
<label>KL50</label>
<description>Kasa Filament Smart Bulb, Soft White</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kl60">
<label>KL60</label>
<description>Kasa Filament Smart Bulb, Warm Amber</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kp100">
<label>KP100</label>
<description>TP-Link KP100 Kasa Wi-Fi Smart Plug - Slim Edition</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kp105">
<label>KP105</label>
<description>TP-Link KP105 Kasa Wi-Fi Smart Plug - Slim Edition</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kp200">
<label>KP200</label>
<description>TP-Link KP200 Smart Wi-Fi Power Outlet, 2-Sockets</description>
<category>PowerOutlet</category>
<channel-groups>
<channel-group id="groupSwitch" typeId="switch-group"/>
<channel-group id="outlet1" typeId="switch-outlet">
<label>Outlet 1</label>
</channel-group>
<channel-group id="outlet2" typeId="switch-outlet">
<label>Outlet 2</label>
</channel-group>
</channel-groups>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kp303">
<label>KP303</label>
<description>TP-Link HS300 Smart Wi-Fi Power Strip, 3-Outlets</description>
<category>PowerOutlet</category>
<channel-groups>
<channel-group id="group" typeId="switch-group"/>
<channel-group id="outlet1" typeId="switch-outlet">
<label>Outlet 1</label>
</channel-group>
<channel-group id="outlet2" typeId="switch-outlet">
<label>Outlet 2</label>
</channel-group>
<channel-group id="outlet3" typeId="switch-outlet">
<label>Outlet 3</label>
</channel-group>
</channel-groups>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="kp400">
<label>KP400</label>
<description>TP-Link KP400 Smart Outdoor Plug</description>
<category>PowerOutlet</category>
<channel-groups>
<channel-group id="groupSwitch" typeId="switch-group"/>
<channel-group id="outlet1" typeId="switch-outlet">
<label>Outlet 1</label>
</channel-group>
<channel-group id="outlet2" typeId="switch-outlet">
<label>Outlet 2</label>
</channel-group>
</channel-groups>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="lb100">
<label>LB100</label>
<description>TP-Link LB100 Smart Wi-Fi LED Bulb with Dimmable Light</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="lb110">
<label>LB110</label>
<description>TP-Link LB110 Smart Wi-Fi LED Bulb with Dimmable Light</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="lb120">
<label>LB120</label>
<description>TP-Link LB120 Smart Wi-Fi LED Bulb with Tunable White Light</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="colorTemperature" typeId="system.color-temperature"/>
<channel id="colorTemperatureAbs" typeId="colorTemperatureAbs1"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="lb130">
<label>LB130</label>
<description>TP-Link LB130 Smart Wi-Fi LED Bulb with Color Changing Hue</description>
<category>Lightbulb</category>
<channels>
<channel id="color" typeId="system.color"/>
<channel id="colorTemperature" typeId="system.color-temperature"/>
<channel id="colorTemperatureAbs" typeId="colorTemperatureAbs2"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="lb200">
<label>LB200</label>
<description>TP-Link LB200 Smart Wi-Fi LED Bulb with Dimmable Light</description>
<category>Lightbulb</category>
<channels>
<channel id="brightness" typeId="system.brightness"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="lb230">
<label>LB230</label>
<description>TP-Link LB230 Smart Wi-Fi LED Bulb with Color Changing Hue</description>
<category>Lightbulb</category>
<channels>
<channel id="color" typeId="system.color"/>
<channel id="colorTemperature" typeId="system.color-temperature"/>
<channel id="colorTemperatureAbs" typeId="colorTemperatureAbs2"/>
<channel id="power" typeId="power"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:bulb"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="re270">
<label>RE270K</label>
<description>TP-Link AC750 Wi-Fi Range Extender with Smart Plug</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="switch-readonly"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<thing-type id="re370">
<label>RE370K</label>
<description>TP-Link AC1200 Wi-Fi Range Extender with Smart Plug</description>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="switch-readonly"/>
<channel id="rssi" typeId="rssi"/>
</channels>
<representation-property>deviceId</representation-property>
<config-description-ref uri="thing-type:device:plug"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tplinksmarthome"
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">
<!-- Primary Channel types -->
<channel-type id="switch-readonly">
<item-type>Switch</item-type>
<label>Switch</label>
<description>Shows the switch state of the Smart Home device.</description>
<category>Switch</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="led" advanced="true">
<item-type>Switch</item-type>
<label>Switch Led</label>
<description>Switch the Smart Home device led on or off.</description>
<category>Switch</category>
</channel-type>
<!-- Absolute Temperature Channel -->
<channel-type id="colorTemperatureAbs1" advanced="true">
<item-type>Number</item-type>
<label>Color Temperature</label>
<description>This channel supports adjusting the color temperature from 2700K to 6500K.</description>
<category>ColorLight</category>
<state min="2700" max="6500" pattern="%d K"/>
</channel-type>
<channel-type id="colorTemperatureAbs2" advanced="true">
<item-type>Number</item-type>
<label>Color Temperature</label>
<description>This channel supports adjusting the color temperature from 2500K to 9000K.</description>
<category>ColorLight</category>
<state min="2500" max="9000" pattern="%d K"/>
</channel-type>
<!-- Energy Channel types -->
<channel-type id="power">
<item-type>Number:Power</item-type>
<label>Power</label>
<description>Actual power usage.</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"></state>
</channel-type>
<channel-type id="energy-usage">
<item-type>Number:Energy</item-type>
<label>Energy Usage</label>
<description>Actual energy usage.</description>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"></state>
</channel-type>
<channel-type id="current" advanced="true">
<item-type>Number:ElectricCurrent</item-type>
<label>Current</label>
<description>Actual current usage.</description>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"></state>
</channel-type>
<channel-type id="voltage" advanced="true">
<item-type>Number:ElectricPotential</item-type>
<label>Voltage</label>
<description>Actual voltage usage.</description>
<category>Energy</category>
<state readOnly="true" pattern="%.0f %unit%"></state>
</channel-type>
<!-- Misc Channel types -->
<channel-type id="rssi" advanced="true">
<item-type>Number:Power</item-type>
<label>Signal</label>
<description>Wi-Fi signal strength indicator.</description>
<category>QualityOfService</category>
<state readOnly="true" pattern="%d %unit%"></state>
</channel-type>
<!-- Channel Groups types -->
<channel-group-type id="switch-group">
<label>Outlet Group</label>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="led" typeId="led"/>
<channel id="rssi" typeId="rssi"/>
</channels>
</channel-group-type>
<channel-group-type id="switch-outlet">
<label>Outlet</label>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
</channels>
</channel-group-type>
<channel-group-type id="energy-outlet">
<label>Outlet</label>
<category>PowerOutlet</category>
<channels>
<channel id="switch" typeId="system.power"/>
<channel id="power" typeId="power"/>
<channel id="energyUsage" typeId="energy-usage"/>
<channel id="current" typeId="current"/>
<channel id="voltage" typeId="voltage"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingUID;
/**
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public final class ChannelUIDConstants {
public static final ChannelUID CHANNEL_UID_BRIGHTNESS = createChannel(LB130, CHANNEL_BRIGHTNESS);
public static final ChannelUID CHANNEL_UID_COLOR = createChannel(LB130, CHANNEL_COLOR);
public static final ChannelUID CHANNEL_UID_COLOR_TEMPERATURE = createChannel(LB130, CHANNEL_COLOR_TEMPERATURE);
public static final ChannelUID CHANNEL_UID_COLOR_TEMPERATURE_ABS = createChannel(LB130,
CHANNEL_COLOR_TEMPERATURE_ABS);
public static final ChannelUID CHANNEL_UID_ENERGY_CURRENT = createChannel(HS110, CHANNEL_ENERGY_CURRENT);
public static final ChannelUID CHANNEL_UID_ENERGY_POWER = createChannel(HS110, CHANNEL_ENERGY_POWER);
public static final ChannelUID CHANNEL_UID_ENERGY_TOTAL = createChannel(HS110, CHANNEL_ENERGY_TOTAL);
public static final ChannelUID CHANNEL_UID_ENERGY_VOLTAGE = createChannel(HS110, CHANNEL_ENERGY_VOLTAGE);
public static final ChannelUID CHANNEL_UID_LED = createChannel(HS100, CHANNEL_LED);
public static final ChannelUID CHANNEL_UID_OTHER = createChannel(HS100, "OTHER");
public static final ChannelUID CHANNEL_UID_RSSI = createChannel(HS100, CHANNEL_RSSI);
public static final ChannelUID CHANNEL_UID_SWITCH = createChannel(HS100, CHANNEL_SWITCH);
private static final String ID = "1234";
private ChannelUIDConstants() {
// Util class
}
private static ChannelUID createChannel(TPLinkSmartHomeThingType thingType, String channelId) {
return new ChannelUID(new ThingUID(thingType.thingTypeUID(), ID), channelId);
}
public static ChannelUID createChannel(TPLinkSmartHomeThingType thingType, String groupId, String channelId) {
return new ChannelUID(new ThingUID(thingType.thingTypeUID(), ID), groupId, channelId);
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.junit.Test;
/**
* Test class for {@link CryptUtil} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class CryptUtilTest {
private static final String TEST_STRING = "This is just a message";
/**
* Test round trip of encrypt and decrypt that should return the same value.
*
* @throws IOException exception in case device not reachable
*/
@Test
public void testCrypt() throws IOException {
assertEquals("Crypting should result in same string", TEST_STRING,
CryptUtil.decrypt(CryptUtil.encrypt(TEST_STRING), TEST_STRING.length()));
}
/**
* Test round trip of encrypt and decrypt with length that should return the same value.
*
* @throws IOException exception in case device not reachable
*/
@Test
public void testCryptWithLength() throws IOException {
try (final ByteArrayInputStream is = new ByteArrayInputStream(CryptUtil.encryptWithLength(TEST_STRING))) {
assertEquals("Crypting should result in same string", TEST_STRING, CryptUtil.decryptWithLength(is));
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.tplinksmarthome.internal.model.GetSysinfo;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
/**
* Test class for {@link PropertiesCollector} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class PropertiesCollectorTest {
/**
* Tests if properties for a bulb device are correctly parsed.
*
* @throws IOException exception in case device not reachable
*/
@Test
public void testBulbProperties() throws IOException {
assertProperties("bulb_get_sysinfo_response_on", TPLinkSmartHomeThingType.LB130, 11);
}
/**
* Tests if properties for a switch device are correctly parsed.
*
* @throws IOException exception in case device not reachable
*/
@Test
public void testSwitchProperties() throws IOException {
assertProperties("plug_get_sysinfo_response", TPLinkSmartHomeThingType.HS100, 12);
}
/**
* Tests if properties for a range extender device are correctly parsed.
*
* @throws IOException exception in case device not reachable
*/
@Test
public void testRangeExtenderProperties() throws IOException {
assertProperties("rangeextender_get_sysinfo_response", TPLinkSmartHomeThingType.RE270K, 11);
}
private void assertProperties(String responseFile, TPLinkSmartHomeThingType thingType, int expectedSize)
throws IOException {
final Map<String, Object> props = PropertiesCollector.collectProperties(thingType, "localhost",
ModelTestUtil.jsonFromFile(responseFile, GetSysinfo.class).getSysinfo());
assertEquals("Number of properties not as expected for properties: " + props, expectedSize, props.size());
props.entrySet().stream().forEach(
entry -> assertNotNull("Property '" + entry.getKey() + "' should not be null", entry.getValue()));
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
import org.openhab.core.config.discovery.DiscoveryListener;
import org.openhab.core.config.discovery.DiscoveryResult;
/**
* Test class for {@link TPLinkSmartHomeDiscoveryService} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@RunWith(value = Parameterized.class)
public class TPLinkSmartHomeDiscoveryServiceTest {
private static final List<Object[]> TESTS = Arrays.asList(
new Object[][] { { "bulb_get_sysinfo_response_on", 11 }, { "rangeextender_get_sysinfo_response", 11 } });
@Mock
private DatagramSocket discoverSocket;
@Mock
private DiscoveryListener discoveryListener;
private TPLinkSmartHomeDiscoveryService discoveryService;
private final String filename;
private final int propertiesSize;
public TPLinkSmartHomeDiscoveryServiceTest(String filename, int propertiesSize) {
this.filename = filename;
this.propertiesSize = propertiesSize;
}
@Parameters(name = "{0}")
public static List<Object[]> data() {
return TESTS;
}
@Before
public void setUp() throws IOException {
initMocks(this);
discoveryService = new TPLinkSmartHomeDiscoveryService() {
@Override
protected DatagramSocket sendDiscoveryPacket() throws IOException {
return discoverSocket;
}
};
doAnswer(new Answer<Void>() {
private int cnt;
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
if (cnt++ > 0) {
throw new SocketTimeoutException("Only test 1 thing discovery");
}
DatagramPacket packet = (DatagramPacket) invocation.getArguments()[0];
packet.setAddress(InetAddress.getLocalHost());
packet.setData(CryptUtil.encrypt(ModelTestUtil.readJson(filename)));
return null;
}
}).when(discoverSocket).receive(any());
discoveryService.addDiscoveryListener(discoveryListener);
}
/**
* Test if startScan method finds a device with expected properties.
*/
@Test
public void testScan() {
discoveryService.startScan();
ArgumentCaptor<DiscoveryResult> discoveryResultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
verify(discoveryListener).thingDiscovered(any(), discoveryResultCaptor.capture());
DiscoveryResult discoveryResult = discoveryResultCaptor.getValue();
assertEquals("Check if correct binding id found", TPLinkSmartHomeBindingConstants.BINDING_ID,
discoveryResult.getBindingId());
assertEquals("Check if expected number of properties found", propertiesSize,
discoveryResult.getProperties().size());
}
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mock;
import org.openhab.binding.tplinksmarthome.internal.device.BulbDevice;
import org.openhab.binding.tplinksmarthome.internal.device.DimmerDevice;
import org.openhab.binding.tplinksmarthome.internal.device.EnergySwitchDevice;
import org.openhab.binding.tplinksmarthome.internal.device.PowerStripDevice;
import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice;
import org.openhab.binding.tplinksmarthome.internal.device.SwitchDevice;
import org.openhab.binding.tplinksmarthome.internal.handler.SmartHomeHandler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
/**
* Test class for {@link TPLinkSmartHomeHandlerFactory}.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@RunWith(value = Parameterized.class)
public class TPLinkSmartHomeHandlerFactoryTest {
private static final String SMART_HOME_DEVICE_FIELD = "smartHomeDevice";
private final TPLinkSmartHomeHandlerFactory factory = new TPLinkSmartHomeHandlerFactory();
// @formatter:off
private static final List<Object[]> TESTS = Arrays.asList(new Object[][] {
{ "hs100", SwitchDevice.class },
{ "hs110", EnergySwitchDevice.class },
{ "hs200", SwitchDevice.class },
{ "hs220", DimmerDevice.class },
{ "hs300", PowerStripDevice.class },
{ "lb100", BulbDevice.class },
{ "lb120", BulbDevice.class },
{ "lb130", BulbDevice.class },
{ "lb230", BulbDevice.class },
{ "kl110", BulbDevice.class },
{ "kl120", BulbDevice.class },
{ "kl130", BulbDevice.class },
{ "re270", RangeExtenderDevice.class },
{ "unknown", null },
});
// @formatter:on
@Mock
Thing thing;
private final String name;
private final Class<?> clazz;
public TPLinkSmartHomeHandlerFactoryTest(String name, Class<?> clazz) {
this.name = name;
this.clazz = clazz;
}
@Parameters(name = "{0} - {1}")
public static List<Object[]> data() {
return TESTS;
}
@Before
public void setUp() {
initMocks(this);
}
@Test
public void testCorrectClass()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
when(thing.getThingTypeUID()).thenReturn(new ThingTypeUID(TPLinkSmartHomeBindingConstants.BINDING_ID, name));
SmartHomeHandler handler = (SmartHomeHandler) factory.createHandler(thing);
if (clazz == null) {
assertNull(name + " should not return any handler but null", handler);
} else {
assertNotNull(name + " should no return null handler", handler);
Field smartHomeDeviceField = SmartHomeHandler.class.getDeclaredField(SMART_HOME_DEVICE_FIELD);
smartHomeDeviceField.setAccessible(true);
assertSame(name + " should return expected device class", clazz,
smartHomeDeviceField.get(handler).getClass());
}
}
}

View File

@@ -0,0 +1,171 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.*;
import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.LB130;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.types.UnDefType;
/**
* Test class for {@link BulbDevice} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class BulbDeviceTest extends DeviceTestBase<BulbDevice> {
private static final String DEVICE_OFF = "bulb_get_sysinfo_response_off";
public BulbDeviceTest() throws IOException {
super(new BulbDevice(LB130.thingTypeUID(), COLOR_TEMPERATURE_2_MIN, COLOR_TEMPERATURE_2_MAX),
"bulb_get_sysinfo_response_on");
}
@Override
public void setUp() throws IOException {
super.setUp();
setSocketReturnAssert("bulb_transition_light_state_response");
}
@Test
public void testHandleCommandBrightness() throws IOException {
assertInput("bulb_transition_light_state_brightness");
assertTrue("Brightness channel should be handled",
device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(33)));
}
@Test
public void testHandleCommandBrightnessOnOff() throws IOException {
assertInput("bulb_transition_light_state_on");
assertTrue("Brightness channel with OnOff state should be handled",
device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON));
}
@Test
public void testHandleCommandColor() throws IOException {
assertInput("bulb_transition_light_state_color");
assertTrue("Color channel should be handled", device.handleCommand(CHANNEL_UID_COLOR, new HSBType("55,44,33")));
}
public void testHandleCommandColorBrightness() throws IOException {
assertInput("bulb_transition_light_state_brightness");
assertTrue("Color channel with Percentage state (=brightness) should be handled",
device.handleCommand(CHANNEL_UID_COLOR, new PercentType(33)));
}
public void testHandleCommandColorOnOff() throws IOException {
assertInput("bulb_transition_light_state_on");
assertTrue("Color channel with OnOff state should be handled",
device.handleCommand(CHANNEL_UID_COLOR, OnOffType.ON));
}
@Test
public void testHandleCommandColorTemperature() throws IOException {
assertInput("bulb_transition_light_state_color_temp");
assertTrue("Color temperature channel should be handled",
device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, new PercentType(40)));
}
@Test
public void testHandleCommandColorTemperatureAbs() throws IOException {
assertInput("bulb_transition_light_state_color_temp");
assertTrue("Color temperature channel should be handled",
device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE_ABS, new DecimalType(5100)));
}
@Test
public void testHandleCommandColorTemperatureOnOff() throws IOException {
assertInput("bulb_transition_light_state_on");
assertTrue("Color temperature channel with OnOff state should be handled",
device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, OnOffType.ON));
}
@Test
public void testHandleCommandSwitch() throws IOException {
assertInput("bulb_transition_light_state_on");
assertTrue("Switch channel should be handled", device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON));
}
@Test
public void testUpdateChannelBrightnessOn() {
assertEquals("Brightness should be on", new PercentType(92),
device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState));
}
@Test
public void testUpdateChannelBrightnessOff() throws IOException {
deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF));
assertEquals("Brightness should be off", PercentType.ZERO,
device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState));
}
@Test
public void testUpdateChannelColorOn() {
assertEquals("Color should be on", new HSBType("7,44,92"),
device.updateChannel(CHANNEL_UID_COLOR, deviceState));
}
@Test
public void testUpdateChannelColorOff() throws IOException {
deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF));
assertEquals("Color should be off", new HSBType("7,44,0"),
device.updateChannel(CHANNEL_UID_COLOR, deviceState));
}
@Test
public void testUpdateChannelSwitchOn() {
assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState));
}
@Test
public void testUpdateChannelSwitchOff() throws IOException {
deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF));
assertSame("Switch should be off", OnOffType.OFF, device.updateChannel(CHANNEL_UID_SWITCH, deviceState));
}
@Test
public void testUpdateChannelColorTemperature() {
assertEquals("Color temperature should be set", new PercentType(2),
device.updateChannel(CHANNEL_UID_COLOR_TEMPERATURE, deviceState));
}
@Test
public void testUpdateChannelColorTemperatureAbs() {
assertEquals("Color temperature should be set", new DecimalType(2630),
device.updateChannel(CHANNEL_UID_COLOR_TEMPERATURE_ABS, deviceState));
}
@Test
public void testUpdateChannelOther() {
assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF,
device.updateChannel(CHANNEL_UID_OTHER, deviceState));
}
@Test
public void testUpdateChannelPower() {
assertEquals("Power values should be set", new DecimalType(10.8),
device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState));
}
}

View File

@@ -0,0 +1,130 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Before;
import org.mockito.Mock;
import org.openhab.binding.tplinksmarthome.internal.Connection;
import org.openhab.binding.tplinksmarthome.internal.CryptUtil;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
/**
* Base class for tests that test classes extending {@link SmartHomeDevice} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class DeviceTestBase<T extends SmartHomeDevice> {
protected final T device;
protected final Connection connection;
protected final TPLinkSmartHomeConfiguration configuration = new TPLinkSmartHomeConfiguration();
protected @NonNullByDefault({}) DeviceState deviceState;
private final String deviceStateFilename;
@Mock
private @NonNullByDefault({}) Socket socket;
@Mock
private @NonNullByDefault({}) OutputStream outputStream;
/**
* Constructor.
*
* @param device Device under test
* @param deviceStateFilename name of the file to read the device state json from to use in tests
*
* @throws IOException exception in case device not reachable
*/
protected DeviceTestBase(T device, String deviceStateFilename) throws IOException {
this.device = device;
this.deviceStateFilename = deviceStateFilename;
configuration.ipAddress = "localhost";
configuration.refresh = 30;
configuration.transitionPeriod = 10;
connection = new Connection(configuration.ipAddress) {
@Override
protected Socket createSocket() throws IOException {
return socket;
}
};
device.initialize(connection, configuration);
}
@Before
public void setUp() throws IOException {
initMocks(this);
when(socket.getOutputStream()).thenReturn(outputStream);
deviceState = new DeviceState(ModelTestUtil.readJson(deviceStateFilename));
}
/**
* Sets the answer to return when the socket.getInputStream() is requested. If multiple files are given they will
* returned in order each time a call to socket.getInputStream() is done.
*
* @param responseFilenames names of the files to read that contains the answer. It's the unencrypted json string
* @throws IOException exception in case device not reachable
*/
protected void setSocketReturnAssert(String... responseFilenames) throws IOException {
AtomicInteger index = new AtomicInteger();
doAnswer(i -> {
String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
return new ByteArrayInputStream(CryptUtil.encryptWithLength(stateResponse));
}).when(socket).getInputStream();
}
/**
* Asserts the value passed to outputstream.write, which is the call that would be made to the actual device. This
* checks if the value sent to the device is what is expected to be sent to the device. If multiple files are given
* they will be used to check in order each time a call outputstream.write is done.
*
* @param filenames names of the files containing the reference json
* @throws IOException exception in case device not reachable
*/
protected void assertInput(String... filenames) throws IOException {
assertInput(Function.identity(), Function.identity(), filenames);
}
protected void assertInput(Function<String, String> jsonProcessor, Function<String, String> expectedProcessor,
String... filenames) throws IOException {
AtomicInteger index = new AtomicInteger();
doAnswer(arg -> {
String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()]));
byte[] input = (byte[]) arg.getArguments()[0];
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(input)) {
String expectedString = expectedProcessor.apply(CryptUtil.decryptWithLength(inputStream));
assertEquals(filenames[index.get()], json, expectedString);
}
index.incrementAndGet();
return null;
}).when(outputStream).write(any());
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.*;
import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.types.UnDefType;
/**
* Test class for {@link DimmerDevice}.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class DimmerDeviceTest extends DeviceTestBase<DimmerDevice> {
private static final PercentType BRIGHTNESS_VALUE = new PercentType(50);
public DimmerDeviceTest() throws IOException {
super(new DimmerDevice(), "hs220_get_sysinfo_response_on");
}
@Test
public void testHandleCommandBrightnessOnOff() throws IOException {
assertInput("dimmer_set_switch_state_on");
setSocketReturnAssert("dimmer_set_switch_state_on");
assertTrue("Brightness channel as OnOffType type should be handled",
device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON));
}
@Test
public void testHandleCommandBrightnessZero() throws IOException {
assertInput("dimmer_set_switch_state_off");
setSocketReturnAssert("dimmer_set_switch_state_response");
assertTrue("Brightness channel with percentage 0 should be handled",
device.handleCommand(CHANNEL_UID_BRIGHTNESS, PercentType.ZERO));
}
@Test
public void testHandleCommandBrightness() throws IOException {
assertInput("dimmer_set_brightness", "dimmer_set_switch_state_on");
setSocketReturnAssert("dimmer_set_brightness_response", "dimmer_set_switch_state_on");
assertTrue("Brightness channel should be handled",
device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(17)));
}
@Test
public void testUpdateChannelSwitch() throws IOException {
deviceState = new DeviceState(ModelTestUtil.readJson("hs220_get_sysinfo_response_off"));
assertSame("Dimmer device should be off", OnOffType.OFF,
((PercentType) device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)).as(OnOffType.class));
}
@Test
public void testUpdateChannelBrightness() {
assertEquals("Dimmer brightness should be set", BRIGHTNESS_VALUE,
device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState));
}
@Test
public void testUpdateChannelOther() {
assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF,
device.updateChannel(CHANNEL_UID_OTHER, deviceState));
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
/**
* Test class to test if text read from the device is correctly decoded to handle special characters.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class EncodingTest extends DeviceTestBase<SwitchDevice> {
public EncodingTest() throws IOException {
super(new SwitchDevice(), "encoding_test");
}
@Test
public void testCorrectDecodingOfText() throws IOException {
assertThat("Alias incorrectly decoded", deviceState.getSysinfo().getAlias(), is("MyßmärtPlug"));
}
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.*;
import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* Test class for {@link EnergySwitchDevice} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@RunWith(value = Parameterized.class)
@NonNullByDefault
public class EnergySwitchDeviceTest {
private static final List<Object[]> TESTS = Arrays
.asList(new Object[][] { { "plug_get_realtime_response", }, { "plug_get_realtime_response_v2", } });
private final EnergySwitchDevice device = new EnergySwitchDevice();
private final DeviceState deviceState;
public EnergySwitchDeviceTest(String name) throws IOException {
deviceState = new DeviceState(ModelTestUtil.readJson(name));
}
@Parameters(name = "{0}")
public static List<Object[]> data() {
return TESTS;
}
@Test
public void testUpdateChannelEnergyCurrent() {
assertEquals("Energy current should have valid state value", new QuantityType<>(1 + " A"),
device.updateChannel(CHANNEL_UID_ENERGY_CURRENT, deviceState));
}
@Test
public void testUpdateChannelEnergyTotal() {
assertEquals("Energy total should have valid state value", new QuantityType<>(10 + " kWh"),
device.updateChannel(CHANNEL_UID_ENERGY_TOTAL, deviceState));
}
@Test
public void testUpdateChannelEnergyVoltage() {
State state = device.updateChannel(CHANNEL_UID_ENERGY_VOLTAGE, deviceState);
assertEquals("Energy voltage should have valid state value", 230, ((QuantityType<?>) state).intValue());
assertEquals("Channel patten to format voltage correctly", "230 V", state.format("%.0f %unit%"));
}
@Test
public void testUpdateChanneEnergyPower() {
assertEquals("Energy power should have valid state value", new QuantityType<>(20 + " W"),
device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState));
}
@Test
public void testUpdateChannelOther() {
assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF,
device.updateChannel(CHANNEL_UID_OTHER, deviceState));
}
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.*;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.HS300;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.ChannelUID;
/**
* Test class for {@link PowerStripDevice} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class PowerStripDeviceTest extends DeviceTestBase<PowerStripDevice> {
private static final ChannelUID CHANNEL_OUTLET_1 = ChannelUIDConstants.createChannel(HS300,
CHANNEL_OUTLET_GROUP_PREFIX + '1', CHANNEL_SWITCH);
private static final ChannelUID CHANNEL_OUTLET_2 = ChannelUIDConstants.createChannel(HS300,
CHANNEL_OUTLET_GROUP_PREFIX + '2', CHANNEL_SWITCH);
private static final ChannelUID CHANNEL_ENERGY_CURRENT_OUTLET_2 = ChannelUIDConstants.createChannel(HS300,
CHANNEL_OUTLET_GROUP_PREFIX + '2', CHANNEL_ENERGY_CURRENT);
private static final String[] REALTIME_INPUTS = IntStream.range(0, 6).mapToObj(i -> "hs300_get_realtime")
.collect(Collectors.toList()).toArray(new String[0]);
private static final String[] REALTIME_RESPONSES = IntStream.range(0, 6).mapToObj(i -> "plug_get_realtime_response")
.collect(Collectors.toList()).toArray(new String[0]);
public PowerStripDeviceTest() throws IOException {
super(new PowerStripDevice(HS300), "hs300_get_sysinfo_response");
}
@Override
@Before
public void setUp() throws IOException {
super.setUp();
final AtomicInteger inputCounter = new AtomicInteger(0);
final Function<String, String> inputWrapper = s -> s.replace("001", "00" + inputCounter.incrementAndGet());
assertInput(inputWrapper, Function.identity(), REALTIME_INPUTS);
setSocketReturnAssert(REALTIME_RESPONSES);
device.refreshedDeviceState(deviceState);
}
@Test
public void testHandleCommandSwitchChannel2() throws IOException {
Function<String, String> normalize = s -> ModelTestUtil.GSON
.toJson(ModelTestUtil.GSON.fromJson(s, SetRelayState.class));
assertInput(normalize, normalize, "hs300_set_relay_state");
setSocketReturnAssert("hs300_set_relay_state_response");
assertTrue("Outlet channel 2 should be handled", device.handleCommand(CHANNEL_OUTLET_2, OnOffType.ON));
}
@Test
public void testUpdateChannelOutlet1() {
assertSame("Outlet 1 should be on", OnOffType.ON, device.updateChannel(CHANNEL_OUTLET_1, deviceState));
}
@Test
public void testUpdateChannelOutlet2() {
assertSame("Outlet 2 should be off", OnOffType.OFF, device.updateChannel(CHANNEL_OUTLET_2, deviceState));
}
@Test
public void testUpdateChannelEnergyCurrent() {
assertEquals("Energy current should have valid state value", 1,
((QuantityType<?>) device.updateChannel(CHANNEL_ENERGY_CURRENT_OUTLET_2, deviceState)).intValue());
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.*;
import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.UnDefType;
/**
* Test class for {@link RangeExtenderDevice} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class RangeExtenderDeviceTest extends DeviceTestBase<RangeExtenderDevice> {
public RangeExtenderDeviceTest() throws IOException {
super(new RangeExtenderDevice(), "rangeextender_get_sysinfo_response");
}
@Test
public void testHandleCommandSwitch() throws IOException {
assertFalse("Switch channel not yet supported so should not be handled",
device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON));
}
@Test
public void testUpdateChannelSwitch() {
assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState));
}
@Test
public void testUpdateChannelLed() {
assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_LED, deviceState));
}
@Test
public void testUpdateChannelOther() {
assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF,
device.updateChannel(CHANNEL_UID_OTHER, deviceState));
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.device;
import static org.junit.Assert.*;
import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.UnDefType;
/**
* Test class for {@link SwitchDevice} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class SwitchDeviceTest extends DeviceTestBase<SwitchDevice> {
public SwitchDeviceTest() throws IOException {
super(new SwitchDevice(), "plug_get_sysinfo_response");
}
@Test
public void testHandleCommandSwitch() throws IOException {
assertInput("plug_set_relay_state_on");
setSocketReturnAssert("plug_set_relay_state_on");
assertTrue("Switch channel should be handled", device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON));
}
@Test
public void testHandleCommandLed() throws IOException {
assertInput("plug_set_led_on");
setSocketReturnAssert("plug_set_led_on");
assertTrue("Led channel should be handled", device.handleCommand(CHANNEL_UID_LED, OnOffType.ON));
}
@Test
public void testUpdateChannelSwitch() {
assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState));
}
@Test
public void testUpdateChannelLed() {
assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_LED, deviceState));
}
@Test
public void testUpdateChannelOther() {
assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF,
device.updateChannel(CHANNEL_UID_OTHER, deviceState));
}
}

View File

@@ -0,0 +1,148 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.handler;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_SWITCH;
import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants;
import org.openhab.binding.tplinksmarthome.internal.Commands;
import org.openhab.binding.tplinksmarthome.internal.Connection;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeDiscoveryService;
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType;
import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice;
import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
/**
* Tests cases for {@link SmartHomeHandler} class.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public class SmartHomeHandlerTest {
private @NonNullByDefault({}) SmartHomeHandler handler;
@Mock
private @NonNullByDefault({}) Connection connection;
@Mock
private @NonNullByDefault({}) ThingHandlerCallback callback;
@Mock
private @NonNullByDefault({}) Thing thing;
@Mock
private @NonNullByDefault({}) SmartHomeDevice smartHomeDevice;
@Mock
private @NonNullByDefault({}) TPLinkSmartHomeDiscoveryService discoveryService;
private final Configuration configuration = new Configuration();
@Before
public void setUp() throws IOException {
initMocks(this);
configuration.put(CONFIG_IP, "localhost");
configuration.put(CONFIG_REFRESH, 1);
when(thing.getConfiguration()).thenReturn(configuration);
when(smartHomeDevice.getUpdateCommand()).thenReturn(Commands.getSysinfo());
when(connection.sendCommand(Commands.getSysinfo()))
.thenReturn(ModelTestUtil.readJson("plug_get_sysinfo_response"));
handler = new SmartHomeHandler(thing, smartHomeDevice, TPLinkSmartHomeThingType.HS100, discoveryService) {
@Override
Connection createConnection(TPLinkSmartHomeConfiguration config) {
return connection;
}
};
when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true);
when(callback.isChannelLinked(any())).thenReturn(true);
handler.setCallback(callback);
}
@After
public void after() {
handler.dispose();
}
@Test
public void testInitializeShouldCallTheCallback() throws InterruptedException {
handler.initialize();
ArgumentCaptor<ThingStatusInfo> statusInfoCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
verify(callback).statusUpdated(eq(thing), statusInfoCaptor.capture());
ThingStatusInfo thingStatusInfo = statusInfoCaptor.getValue();
assertEquals("Device should be unknown", ThingStatus.UNKNOWN, thingStatusInfo.getStatus());
}
@Test
public void testHandleCommandRefreshType() {
handler.initialize();
assertHandleCommandRefreshType(-53);
}
@Test
public void testHandleCommandRefreshTypeRangeExtender() throws IOException {
when(connection.sendCommand(Commands.getSysinfo()))
.thenReturn(ModelTestUtil.readJson("rangeextender_get_sysinfo_response"));
handler.initialize();
assertHandleCommandRefreshType(-70);
}
private void assertHandleCommandRefreshType(int expectedRssi) {
handler.initialize();
ChannelUID channelUID = ChannelUIDConstants.CHANNEL_UID_RSSI;
handler.handleCommand(channelUID, RefreshType.REFRESH);
ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture());
assertEquals("State of RSSI channel should be set", new QuantityType<>(expectedRssi + " dBm"),
stateCaptor.getValue());
}
@Test
public void testHandleCommandOther() throws InterruptedException {
handler.initialize();
ChannelUID channelUID = ChannelUIDConstants.CHANNEL_UID_SWITCH;
Mockito.doReturn(OnOffType.ON).when(smartHomeDevice).updateChannel(eq(channelUID), any());
handler.handleCommand(channelUID, RefreshType.REFRESH);
ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture());
assertSame("State of channel switch should be set", OnOffType.ON, stateCaptor.getValue());
}
@Test
public void testRefreshChannels() {
handler.initialize();
handler.refreshChannels();
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.tplinksmarthome.internal.model;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.Gson;
/**
* Util class for reading test resources.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@NonNullByDefault
public final class ModelTestUtil {
public static final Gson GSON = GsonUtil.createGson();
private ModelTestUtil() {
// Util class
}
/**
* Util method to read a json file into it's data class.
*
* @param <T> Type of the class the json data represents.
* @param gson gson class
* @param filename filename of the json file to read. The file is read relative to the directory of this class
* @param clazz Data class expected to be read from the json file
* @return instance of clazz with read data from json file
* @throws IOException when file could not be read.
*/
public static <T> T jsonFromFile(String filename, Class<T> clazz) throws IOException {
return GSON.fromJson(readJson(filename), clazz);
}
/**
* Util method to read a json file. It normalizes the string by removing returns, tabs and spaces. It's not very
* smart as it removes spaces inside json values as well. But this method is mainly intended be able to compare 2
* json strings.
*
* @param filename filename of the json file to read. The file is read relative to the directory of this class
* @return read json string
* @throws IOException when file could not be read.
*/
public static String readJson(String filename) throws IOException {
return IOUtils
.toString(ModelTestUtil.class.getResourceAsStream(filename + ".json"), StandardCharsets.UTF_8.name())
.replaceAll("[\n\r\t ]", "");
}
}

View File

@@ -0,0 +1 @@
{"smartlife.iot.common.emeter":{"get_realtime":{}}}

View File

@@ -0,0 +1,75 @@
{
"system": {
"get_sysinfo": {
"active_mode": "schedule",
"alias": "Downstairs Light",
"ctrl_protocols": {
"name": "Linkie",
"version": "1.0"
},
"description": "Smart Wi-Fi LED Bulb with Dimmable Light",
"dev_state": "normal",
"deviceId": "80120B3D03E0B639CDF33E3CB1466490187FEF32",
"disco_ver": "1.0",
"err_code": 0,
"heapsize": 309908,
"hwId": "111E35908497A05512E259BB76801E10",
"hw_ver": "1.0",
"is_color": 0,
"is_dimmable": 1,
"is_factory": false,
"is_variable_color_temp": 0,
"light_state": {
"dft_on_state": {
"brightness": 92,
"color_temp": 2700,
"hue": 7,
"mode": "normal",
"saturation": 44
},
"on_off": 0
},
"mic_mac": "50C7BF7BE306",
"mic_type": "IOT.SMARTBULB",
"model": "LB110(EU)",
"oemId": "A68E15472071CB761E5CCFB388A1D8AE",
"preferred_state": [
{
"brightness": 100,
"color_temp": 2700,
"hue": 0,
"index": 0,
"saturation": 0
},
{
"brightness": 58,
"color_temp": 2700,
"hue": 0,
"index": 1,
"saturation": 0
},
{
"brightness": 25,
"color_temp": 2700,
"hue": 0,
"index": 2,
"saturation": 0
},
{
"brightness": 1,
"color_temp": 2700,
"hue": 0,
"index": 3,
"saturation": 0
}
],
"rssi": -61,
"sw_ver": "1.5.5 Build 170623 Rel.090105"
}
},
"smartlife.iot.common.emeter": {
"get_realtime": {
"power_mw": 10800
}
}
}

View File

@@ -0,0 +1,72 @@
{
"system": {
"get_sysinfo": {
"active_mode": "none",
"alias": "Living Room Side Table",
"ctrl_protocols": {
"name": "Linkie",
"version": "1.0"
},
"description": "Smart Wi-Fi LED Bulb with Color Changing",
"dev_state": "normal",
"deviceId": "DEVID_HERE",
"disco_ver": "1.0",
"heapsize": 347000,
"hwId": "HWID_HERE",
"hw_ver": "1.0",
"is_color": 1,
"is_dimmable": 1,
"is_factory": false,
"is_variable_color_temp": 1,
"light_state": {
"brightness": 92,
"color_temp": 2630,
"hue": 7,
"mode": "normal",
"on_off": 1,
"saturation": 44
},
"mic_mac": "MAC_HERE",
"mic_type": "IOT.SMARTBULB",
"model": "LB130(US)",
"oemId": "EOMiID_HERE",
"preferred_state": [
{
"brightness": 50,
"color_temp": 2700,
"hue": 0,
"index": 0,
"saturation": 0
},
{
"brightness": 100,
"color_temp": 0,
"hue": 0,
"index": 1,
"saturation": 75
},
{
"brightness": 100,
"color_temp": 0,
"hue": 120,
"index": 2,
"saturation": 75
},
{
"brightness": 100,
"color_temp": 0,
"hue": 240,
"index": 3,
"saturation": 75
}
],
"rssi": -55,
"sw_ver": "1.1.2 Build 160927 Rel.111100"
}
},
"smartlife.iot.common.emeter": {
"get_realtime": {
"power_mw": 10800
}
}
}

View File

@@ -0,0 +1,11 @@
{
"smartlife.iot.smartbulb.lightingservice": {
"transition_light_state": {
"brightness": 33,
"on_off": 1,
"ignore_default": 1,
"mode": "normal",
"transition_period": 10
}
}
}

View File

@@ -0,0 +1,14 @@
{
"smartlife.iot.smartbulb.lightingservice": {
"transition_light_state": {
"color_temp": 0,
"hue": 55,
"saturation": 44,
"brightness": 33,
"on_off": 1,
"ignore_default": 1,
"mode": "normal",
"transition_period": 10
}
}
}

View File

@@ -0,0 +1,13 @@
{
"smartlife.iot.smartbulb.lightingservice": {
"transition_light_state": {
"color_temp": 5100,
"hue": 0,
"saturation": 0,
"on_off": 1,
"ignore_default": 1,
"mode": "normal",
"transition_period": 10
}
}
}

View File

@@ -0,0 +1,10 @@
{
"smartlife.iot.smartbulb.lightingservice": {
"transition_light_state": {
"on_off": 1,
"ignore_default": 1,
"mode": "normal",
"transition_period": 10
}
}
}

View File

@@ -0,0 +1,13 @@
{
"smartlife.iot.smartbulb.lightingservice": {
"transition_light_state": {
"on_off": 1,
"mode": "normal",
"hue": 120,
"saturation": 65,
"color_temp": 0,
"brightness": 10,
"err_code": 0
}
}
}

View File

@@ -0,0 +1 @@
{"smartlife.iot.dimmer":{"set_brightness":{"brightness":17}}}

View File

@@ -0,0 +1 @@
{"smartlife.iot.dimmer":{"set_brightness":{"err_code":0}}}

View File

@@ -0,0 +1 @@
{"smartlife.iot.dimmer":{"set_switch_state":{"state":0}}}

View File

@@ -0,0 +1 @@
{"smartlife.iot.dimmer":{"set_switch_state":{"state":1}}}

View File

@@ -0,0 +1 @@
{"smartlife.iot.dimmer":{"set_switch_state":{"err_code":0}}}

View File

@@ -0,0 +1,29 @@
{
"system": {
"get_sysinfo": {
"err_code": 0,
"sw_ver": "1.0.8 Build 151101 Rel.24452",
"hw_ver": "1.0",
"model": "HS100(EU)",
"deviceId": "DEVICEID_HERE",
"hwId": "HWID_HERE",
"oemId": "OEMID_HERE",
"alias": "MyßmärtPlug",
"active_mode": "schedule",
"rssi": -53,
"type": "smartplug",
"mac": "MAC_ADD_HERE",
"fwId": "FWID_HERE",
"dev_name": "Wi-Fi Smart Plug",
"icon_hash": "",
"relay_state": 1,
"on_time": 451,
"feature": "TIM",
"updating": 0,
"led_off": 0,
"latitude": 0.0,
"longitude": 0.0,
"is_factory": false
}
}
}

View File

@@ -0,0 +1,50 @@
{
"system": {
"get_sysinfo": {
"sw_ver": "1.4.8 Build 180109 Rel.171240",
"hw_ver": "1.0",
"mic_type": "IOT.SMARTPLUGSWITCH",
"model": "HS220(US)",
"mac": "00:00:00:00:00:00",
"dev_name": "Smart Wi-Fi Dimmer",
"alias": "dimmer",
"relay_state": 0,
"brightness": 50,
"on_time": 0,
"active_mode": "none",
"feature": "TIM",
"updating": 0,
"icon_hash": "",
"rssi": -10,
"led_off": 0,
"longitude_i": 0,
"latitude_i": 0,
"hwId": "00000000000000000000000000000000",
"fwId": "00000000000000000000000000000000",
"deviceId": "00000000000000000000000000000000",
"oemId": "00000000000000000000000000000000",
"preferred_state": [
{
"index": 0,
"brightness": 100
},
{
"index": 1,
"brightness": 75
},
{
"index": 2,
"brightness": 50
},
{
"index": 3,
"brightness": 25
}
],
"next_action": {
"type": -1
},
"err_code": 0
}
}
}

View File

@@ -0,0 +1,50 @@
{
"system": {
"get_sysinfo": {
"sw_ver": "1.4.8 Build 180109 Rel.171240",
"hw_ver": "1.0",
"mic_type": "IOT.SMARTPLUGSWITCH",
"model": "HS220(US)",
"mac": "00:00:00:00:00:00",
"dev_name": "Smart Wi-Fi Dimmer",
"alias": "dimmer",
"relay_state": 1,
"brightness": 50,
"on_time": 0,
"active_mode": "none",
"feature": "TIM",
"updating": 0,
"icon_hash": "",
"rssi": -10,
"led_off": 0,
"longitude_i": 0,
"latitude_i": 0,
"hwId": "00000000000000000000000000000000",
"fwId": "00000000000000000000000000000000",
"deviceId": "00000000000000000000000000000000",
"oemId": "00000000000000000000000000000000",
"preferred_state": [
{
"index": 0,
"brightness": 100
},
{
"index": 1,
"brightness": 75
},
{
"index": 2,
"brightness": 50
},
{
"index": 3,
"brightness": 25
}
],
"next_action": {
"type": -1
},
"err_code": 0
}
}
}

View File

@@ -0,0 +1 @@
{"context": {"child_ids": ["00000000000000000000000000000001"]}, "emeter": {"get_realtime": {}}}

View File

@@ -0,0 +1 @@
{"context": {"child_ids": ["00000000000000000000000000000001"]}, "emeter": {"get_realtime": {}}}

Some files were not shown because too many files have changed in this diff Show More