]> git.basschouten.com Git - openhab-addons.git/commitdiff
[tplinksmarthome] Added KL400, KL430 lightstrip support (#12000)
authorHilbrand Bouwkamp <hilbrand@h72.nl>
Thu, 10 Feb 2022 18:58:24 +0000 (19:58 +0100)
committerGitHub <noreply@github.com>
Thu, 10 Feb 2022 18:58:24 +0000 (19:58 +0100)
Closes #8709

Additional:
- Added action to send and receive json commands to a tplink device. This can be used for test purposes or to run commands not available through channels.

Fixes:
- Power channel of a bulb is defined as QuantityType in xml, therefor it should create the state using QuantityType
- Retry getting values 5 times before setting the device offline. Reduced socket time out to 2 seconds as it normally should react quickly and if it times out it tries again.

Also-by: Dustin Masters <ceo@dustinsoftware.com>
Signed-off-by: Hilbrand Bouwkamp <hilbrand@h72.nl>
25 files changed:
bundles/org.openhab.binding.tplinksmarthome/README.md
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/LightStripDevice.java [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/TPLinkSmartHomeActions.java [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLightState.java [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/i18n/tplinksmarthome.properties
bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/thing/KL400.xml [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/thing/KL430.xml [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java
bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java
bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/LightStripDeviceTest.java [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_brightness.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_brightness_response.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_color.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_color_response.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_colortemperature.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_colortemperature_response.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_on.json [new file with mode: 0644]
bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_on_response.json [new file with mode: 0644]

index 591748c1d525639fb2f037fca238c5b6cb44ec95..cd820ce4102530e657638978310d520a3c7638c5 100644 (file)
@@ -242,6 +242,24 @@ Switching, Brightness and Color is done using the `color` channel.
 
 Switching, Brightness and Color is done using the `color` channel.
 
+### KL400 Kasa Smart LED Light Strip
+
+* Power On/Off
+* Fine-tune colors
+* Adjust light appearance from soft white (2500k) to daylight (9000k)
+* Adjust the brightness
+* Wi-Fi signal strength (RSSI)
+
+### KL430 Kasa Smart LED Light Strip, 16 Color Zones
+
+* Power On/Off
+* Fine-tune colors
+* Adjust light appearance from soft white (2500k) to daylight (9000k)
+* Adjust the brightness
+* Wi-Fi signal strength (RSSI)
+
+Switching, Brightness and Color is done using the `color` channel.
+
 ### KP100 Kasa Wi-Fi Smart Plug - Slim Edition
 
 * Power On/Off
@@ -363,9 +381,9 @@ All devices support some of the following channels:
 |---------------------|--------------------------|------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
 | switch              | Switch                   | Power the device on or off.                    | EP10, EP40, HS100, HS103, HS105, HS107, HS110, HS200, HS210, HS300, KP100, KP105, KP115, KP200, KP303, KP400, KS230, RE270K, RE370K             |
 | brightness          | Dimmer                   | Set the brightness of device or dimmer.        | ES20M, HS220, KB100, KL50, KL60, KL110, KL120, KP405, LB100, LB110, LB120, LB200                                                                |
-| colorTemperature    | Dimmer                   | Set the color temperature in percentage.       | KB130, KL120, KL125, KL130, KL135, LB120, LB130, LB230                                                                                          |
-| colorTemperatureAbs | Number                   | Set the color temperature in Kelvin.           | KB130, KL120, KL125, KL130, KL135, LB120, LB130, LB230                                                                                          |
-| color               | Color                    | Set the color of the light.                    | KB130, KL125, KL130, KL135, LB130, LB230                                                                                                        |
+| colorTemperature    | Dimmer                   | Set the color temperature in percentage.       | KB130, KL120, KL125, KL130, KL135, KL400, KL430, LB120, LB130, LB230                                                                            |
+| colorTemperatureAbs | Number                   | Set the color temperature in Kelvin.           | KB130, KL120, KL125, KL130, KL135, KL400, KL430, LB120, LB130, LB230                                                                            |
+| color               | Color                    | Set the color of the light.                    | KB130, KL125, KL130, KL135, KL400, KL430, LB130, LB230                                                                                          |
 | power               | Number:Power             | Actual energy usage in Watt.                   | HS110, HS300, KLxxx, KP115, KP125, LBxxx,                                                                                                       |
 | eneryUsage          | Number:Energy            | Energy Usage in kWh.                           | HS110, HS300, KP115, KP125                                                                                                                      |
 | current             | Number:ElectricCurrent   | Actual current usage in Ampere.                | HS110, HS300, KP115, KP125                                                                                                                      |
index 55786b412da8ffb8820c86ffc2433f9b809a8bcc..8207e11806e2a7e4c15444d054a970a9e71cfedc 100644 (file)
@@ -21,6 +21,7 @@ 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.SetLightState;
 import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState;
 import org.openhab.binding.tplinksmarthome.internal.model.SetSwitchState;
 import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
@@ -79,7 +80,7 @@ public class Commands {
      * @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) {
+    public static String getRealtimeWithContext(final String id) {
         return String.format(CONTEXT, id) + REALTIME + "}";
     }
 
@@ -90,8 +91,8 @@ public class Commands {
      * @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);
+    public Realtime getRealtimeResponse(final String realtimeResponse) {
+        final GetRealtime getRealtime = gson.fromJson(realtimeResponse, GetRealtime.class);
         return getRealtime == null ? new Realtime() : getRealtime.getRealtime();
     }
 
@@ -111,8 +112,8 @@ public class Commands {
      * @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);
+    public Sysinfo getSysinfoReponse(final String getSysinfoReponse) {
+        final GetSysinfo getSysinfo = gson.fromJson(getSysinfoReponse, GetSysinfo.class);
         return getSysinfo == null ? new Sysinfo() : getSysinfo.getSysinfo();
     }
 
@@ -123,8 +124,8 @@ public class Commands {
      * @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();
+    public String setRelayState(final OnOffType onOff, @Nullable final String childId) {
+        final SetRelayState relayState = new SetRelayState();
         relayState.setRelayState(onOff);
         if (childId != null) {
             relayState.setChildId(childId);
@@ -138,7 +139,7 @@ public class Commands {
      * @param relayStateResponse the json string
      * @return The data object containing the state data from the json string
      */
-    public @Nullable SetRelayState setRelayStateResponse(String relayStateResponse) {
+    public @Nullable SetRelayState setRelayStateResponse(final String relayStateResponse) {
         return gsonWithExpose.fromJson(relayStateResponse, SetRelayState.class);
     }
 
@@ -148,8 +149,8 @@ public class Commands {
      * @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();
+    public String setSwitchState(final OnOffType onOff) {
+        final SetSwitchState switchState = new SetSwitchState();
         switchState.setSwitchState(onOff);
         return gsonWithExpose.toJson(switchState);
     }
@@ -160,7 +161,7 @@ public class Commands {
      * @param switchStateResponse the json string
      * @return The data object containing the state data from the json string
      */
-    public @Nullable SetSwitchState setSwitchStateResponse(String switchStateResponse) {
+    public @Nullable SetSwitchState setSwitchStateResponse(final String switchStateResponse) {
         return gsonWithExpose.fromJson(switchStateResponse, SetSwitchState.class);
     }
 
@@ -170,8 +171,8 @@ public class Commands {
      * @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();
+    public String setDimmerBrightness(final int brightness) {
+        final SetBrightness setBrightness = new SetBrightness();
         setBrightness.setBrightness(brightness);
         return gsonWithExpose.toJson(setBrightness);
     }
@@ -182,26 +183,10 @@ public class Commands {
      * @param dimmerBrightnessResponse the json string
      * @return The data object containing the state data from the json string
      */
-    public @Nullable HasErrorResponse setDimmerBrightnessResponse(String dimmerBrightnessResponse) {
+    public @Nullable HasErrorResponse setDimmerBrightnessResponse(final 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.
      *
@@ -209,8 +194,8 @@ public class Commands {
      * @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();
+    public String setLedOn(final OnOffType onOff, @Nullable final String childId) {
+        final SetLedOff sLOff = new SetLedOff();
         sLOff.setLed(onOff);
         if (childId != null) {
             sLOff.setChildId(childId);
@@ -224,10 +209,21 @@ public class Commands {
      * @param setLedOnResponse the json string
      * @return The data object containing the data from the json string
      */
-    public @Nullable SetLedOff setLedOnResponse(String setLedOnResponse) {
+    public @Nullable SetLedOff setLedOnResponse(final String setLedOnResponse) {
         return gsonWithExpose.fromJson(setLedOnResponse, SetLedOff.class);
     }
 
+    /**
+     * Returns the json for the transition_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 setTransitionLightState(final OnOffType onOff, final int transitionPeriod) {
+        return setTransitionLightState(new LightOnOff(), onOff, transitionPeriod);
+    }
+
     /**
      * Returns the json for the set_light_State command to set the brightness.
      *
@@ -235,14 +231,10 @@ public class Commands {
      * @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);
+    public String setTransitionLightStateBrightness(final int brightness, final int transitionPeriod) {
+        final LightStateBrightness lightState = new LightStateBrightness();
         lightState.setBrightness(brightness);
-        lightState.setTransitionPeriod(transitionPeriod);
-        transitionLightState.setLightState(lightState);
-        return gson.toJson(transitionLightState);
+        return setTransitionLightState(lightState, OnOffType.from(brightness != 0), transitionPeriod);
     }
 
     /**
@@ -252,17 +244,13 @@ public class Commands {
      * @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);
+    public String setTransitionLightStateColor(final HSBType hsb, final int transitionPeriod) {
+        final LightStateColor lightState = new LightStateColor();
+        final int brightness = hsb.getBrightness().intValue();
         lightState.setBrightness(brightness);
         lightState.setHue(hsb.getHue().intValue());
         lightState.setSaturation(hsb.getSaturation().intValue());
-        lightState.setTransitionPeriod(transitionPeriod);
-        transitionLightState.setLightState(lightState);
-        return gson.toJson(transitionLightState);
+        return setTransitionLightState(lightState, OnOffType.from(brightness != 0), transitionPeriod);
     }
 
     /**
@@ -272,13 +260,18 @@ public class Commands {
      * @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);
+    public String setColorTemperature(final int colorTemperature, final int transitionPeriod) {
+        final LightStateColorTemperature lightState = new LightStateColorTemperature();
         lightState.setColorTemperature(colorTemperature);
-        lightState.setTransitionPeriod(transitionPeriod);
-        transitionLightState.setLightState(lightState);
+        return setTransitionLightState(lightState, OnOffType.ON, transitionPeriod);
+    }
+
+    private String setTransitionLightState(final LightOnOff lightOnOff, final OnOffType onOff,
+            final int transitionPeriod) {
+        final TransitionLightState transitionLightState = new TransitionLightState();
+        transitionLightState.setLightState(lightOnOff);
+        lightOnOff.setOnOff(onOff);
+        lightOnOff.setTransitionPeriod(transitionPeriod);
         return gson.toJson(transitionLightState);
     }
 
@@ -288,7 +281,82 @@ public class Commands {
      * @param response the json string
      * @return The data object containing the state data from the json string
      */
-    public @Nullable TransitionLightStateResponse setTransitionLightStateResponse(String response) {
+    public @Nullable TransitionLightStateResponse setTransitionLightStateResponse(final String response) {
         return gson.fromJson(response, TransitionLightStateResponse.class);
     }
+
+    // ---------------------------------------------------------------
+
+    /**
+     * Returns the json for the set_light_state command to switch a light strip on or off.
+     *
+     * @param onOff the switch state to set
+     * @param transition the transition period for the action to take place
+     * @return The json string of the command to send to the device
+     */
+    public String setLightStripState(final OnOffType onOff, final int transition) {
+        return setLightStripState(new SetLightState.LightOnOff(), onOff, transition);
+    }
+
+    /**
+     * Returns the json for the set_light_State command to set the brightness.
+     *
+     * @param brightness the brightness value
+     * @param transition the transition period for the action to take place
+     * @return The json string of the command to send to the device
+     */
+    public String setLightStripBrightness(final int brightness, final int transition) {
+        final SetLightState.Brightness lightState = new SetLightState.Brightness();
+        lightState.setBrightness(brightness);
+        return setLightStripState(lightState, OnOffType.from(brightness != 0), transition);
+    }
+
+    /**
+     * Returns the json for the set_light_State command to set the color.
+     *
+     * @param hsb the color to set
+     * @param transition the transition period for the action to take place
+     * @return The json string of the command to send to the device
+     */
+    public String setLightStripColor(final HSBType hsb, final int transition) {
+        final SetLightState.Color lightState = new SetLightState.Color();
+        final int brightness = hsb.getBrightness().intValue();
+        lightState.setHue(hsb.getHue().intValue());
+        lightState.setSaturation(hsb.getSaturation().intValue());
+        lightState.setBrightness(brightness);
+        return setLightStripState(lightState, OnOffType.from(brightness != 0), transition);
+    }
+
+    /**
+     * Returns the json for the set_light_State command to set the color temperature.
+     *
+     * @param colorTemperature the color temperature to set
+     * @param transition the transition period for the action to take place
+     * @return The json string of the command to send to the device
+     */
+    public String setLightStripColorTemperature(final int colorTemperature, final int transition) {
+        final SetLightState.ColorTemperature lightState = new SetLightState.ColorTemperature();
+        lightState.setColorTemp(colorTemperature);
+        return setLightStripState(lightState, OnOffType.ON, transition);
+    }
+
+    private String setLightStripState(final SetLightState.LightOnOff lightOnOff, final OnOffType onOff,
+            final int transition) {
+        final SetLightState setLightState = new SetLightState();
+        setLightState.setContext(new SetLightState.Context());
+        setLightState.setLightState(lightOnOff);
+        lightOnOff.setOnOff(onOff);
+        lightOnOff.setTransition(transition);
+        return gsonWithExpose.toJson(setLightState);
+    }
+
+    /**
+     * 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 SetLightState setLightStripStateResponse(final String response) {
+        return gsonWithExpose.fromJson(response, SetLightState.class);
+    }
 }
index c7a6d54a63c79552c8fed6420813a1d8be39f16e..1421e34c73d16d86a7f547060b5740209adb4716 100644 (file)
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
 public class Connection {
 
     public static final int TP_LINK_SMART_HOME_PORT = 9999;
-    private static final int SOCKET_TIMEOUT_MILLISECONDS = 3_000;
+    private static final int SOCKET_TIMEOUT_MILLISECONDS = 2_000;
 
     private final Logger logger = LoggerFactory.getLogger(Connection.class);
 
index 9402e0f10da1dc2924b2a1a5bb61a7b43563bf27..d4366b2f328e969e7f51014960c2dc786ef6e0c4 100644 (file)
@@ -19,6 +19,7 @@ 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.LightStripDevice;
 import org.openhab.binding.tplinksmarthome.internal.device.PowerStripDevice;
 import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice;
 import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice;
@@ -67,6 +68,9 @@ public class TPLinkSmartHomeHandlerFactory extends BaseThingHandlerFactory {
             case DIMMER:
                 device = new DimmerDevice();
                 break;
+            case LIGHT_STRIP:
+                device = new LightStripDevice(type);
+                break;
             case PLUG:
                 device = new SwitchDevice();
                 break;
index 7550dee49e8ea83f23012ad387d944fdb1fc0d76..c4b35b71144eecaa31b2bb2e79e9e696642b30b3 100644 (file)
@@ -49,6 +49,10 @@ public enum TPLinkSmartHomeThingType {
     KL130("kl130", DeviceType.BULB, ColorScales.K_2500_9000),
     KL135("kl135", DeviceType.BULB, ColorScales.K_2500_6500),
 
+    // Light String thing Type UIDs.
+    KL400("kl400", DeviceType.LIGHT_STRIP, ColorScales.K_2500_9000),
+    KL430("kl430", DeviceType.LIGHT_STRIP, ColorScales.K_2500_9000),
+
     // Plug Thing Type UIDs
     EP10("ep10", DeviceType.PLUG),
     HS100("hs100", DeviceType.PLUG),
@@ -175,6 +179,10 @@ public enum TPLinkSmartHomeThingType {
          * Dimmer device.
          */
         DIMMER,
+        /**
+         * Light Strip device.
+         */
+        LIGHT_STRIP,
         /**
          * Plug device.
          */
index ff4160f4f1e9e05700c11643d3a21a838a74bcca..9381114f632b3fb28071c0ee2c3620b45bd741bc 100644 (file)
@@ -28,11 +28,12 @@ import org.openhab.binding.tplinksmarthome.internal.Commands;
 import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType;
 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.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.State;
@@ -69,9 +70,9 @@ public class BulbDevice extends SmartHomeDevice {
         final int transitionPeriod = configuration.transitionPeriod;
         final HasErrorResponse response;
 
-        if (command instanceof OnOffType) {
+        if (command instanceof OnOffType && CHANNELS_BULB_SWITCH.contains(channelId)) {
             response = handleOnOffType(channelId, (OnOffType) command, transitionPeriod);
-        } else if (command instanceof HSBType) {
+        } else if (command instanceof HSBType && CHANNEL_COLOR.equals(channelId)) {
             response = handleHSBType(channelId, (HSBType) command, transitionPeriod);
         } else if (command instanceof DecimalType) {
             response = handleDecimalType(channelId, (DecimalType) command, transitionPeriod);
@@ -82,42 +83,42 @@ public class BulbDevice extends SmartHomeDevice {
         return response != null;
     }
 
-    private @Nullable HasErrorResponse handleOnOffType(final String channelID, final OnOffType onOff,
+    protected @Nullable HasErrorResponse handleOnOffType(final String channelID, final OnOffType onOff,
             final int transitionPeriod) throws IOException {
-        if (CHANNELS_BULB_SWITCH.contains(channelID)) {
-            return commands.setTransitionLightStateResponse(
-                    connection.sendCommand(commands.setLightState(onOff, transitionPeriod)));
-        }
-        return null;
+        return commands.setTransitionLightStateResponse(
+                connection.sendCommand(commands.setTransitionLightState(onOff, transitionPeriod)));
     }
 
     private @Nullable HasErrorResponse handleDecimalType(final String channelID, final DecimalType command,
             final int transitionPeriod) throws IOException {
+        final int intValue = command.intValue();
+
         if (CHANNEL_COLOR.equals(channelID) || CHANNEL_BRIGHTNESS.equals(channelID)) {
-            return commands.setTransitionLightStateResponse(
-                    connection.sendCommand(commands.setBrightness(command.intValue(), transitionPeriod)));
+            return handleBrightness(intValue, transitionPeriod);
         } else if (CHANNEL_COLOR_TEMPERATURE.equals(channelID)) {
-            return handleColorTemperature(convertPercentageToKelvin(command.intValue()), transitionPeriod);
+            return handleColorTemperature(convertPercentageToKelvin(intValue), transitionPeriod);
         } else if (CHANNEL_COLOR_TEMPERATURE_ABS.equals(channelID)) {
-            return handleColorTemperature(guardColorTemperature(command.intValue()), transitionPeriod);
+            return handleColorTemperature(guardColorTemperature(intValue), transitionPeriod);
         }
         return null;
     }
 
-    private @Nullable TransitionLightStateResponse handleColorTemperature(final int colorTemperature,
-            final int transitionPeriod) throws IOException {
+    protected @Nullable HasErrorResponse handleBrightness(final int brightness, final int transitionPeriod)
+            throws IOException {
         return commands.setTransitionLightStateResponse(
-                connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod)));
+                connection.sendCommand(commands.setTransitionLightStateBrightness(brightness, transitionPeriod)));
     }
 
-    @Nullable
-    private HasErrorResponse handleHSBType(final String channelID, final HSBType command, final int transitionPeriod)
+    protected @Nullable HasErrorResponse handleColorTemperature(final int colorTemperature, final int transitionPeriod)
             throws IOException {
-        if (CHANNEL_COLOR.equals(channelID)) {
-            return commands.setTransitionLightStateResponse(
-                    connection.sendCommand(commands.setColor(command, transitionPeriod)));
-        }
-        return null;
+        return commands.setTransitionLightStateResponse(
+                connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod)));
+    }
+
+    protected @Nullable HasErrorResponse handleHSBType(final String channelID, final HSBType command,
+            final int transitionPeriod) throws IOException {
+        return commands.setTransitionLightStateResponse(
+                connection.sendCommand(commands.setTransitionLightStateColor(command, transitionPeriod)));
     }
 
     @Override
@@ -142,7 +143,7 @@ public class BulbDevice extends SmartHomeDevice {
                 state = lightState.getOnOff();
                 break;
             case CHANNEL_ENERGY_POWER:
-                state = new DecimalType(deviceState.getRealtime().getPower());
+                state = new QuantityType<>(deviceState.getRealtime().getPower(), Units.WATT);
                 break;
             default:
                 state = UnDefType.UNDEF;
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/LightStripDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/LightStripDevice.java
new file mode 100644 (file)
index 0000000..796b8fd
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.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.TPLinkSmartHomeThingType;
+import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
+import org.openhab.core.library.types.HSBType;
+import org.openhab.core.library.types.OnOffType;
+
+/**
+ * TP-Link Smart Home Light Strip.
+ *
+ * @author Hilbrand Bouwkamp - Initial contribution
+ */
+@NonNullByDefault
+public class LightStripDevice extends BulbDevice {
+
+    public LightStripDevice(final TPLinkSmartHomeThingType type) {
+        super(type);
+    }
+
+    @Override
+    protected @Nullable HasErrorResponse handleOnOffType(final String channelID, final OnOffType onOff,
+            final int transitionPeriod) throws IOException {
+        return commands.setLightStripStateResponse(
+                connection.sendCommand(commands.setLightStripState(onOff, transitionPeriod)));
+    }
+
+    @Override
+    protected @Nullable HasErrorResponse handleBrightness(final int brightness, final int transitionPeriod)
+            throws IOException {
+        return commands.setLightStripStateResponse(
+                connection.sendCommand(commands.setLightStripBrightness(brightness, transitionPeriod)));
+    }
+
+    @Override
+    protected @Nullable HasErrorResponse handleColorTemperature(final int colorTemperature, final int transitionPeriod)
+            throws IOException {
+        return commands.setLightStripStateResponse(
+                connection.sendCommand(commands.setLightStripColorTemperature(colorTemperature, transitionPeriod)));
+    }
+
+    @Override
+    protected @Nullable HasErrorResponse handleHSBType(final String channelID, final HSBType command,
+            final int transitionPeriod) throws IOException {
+        return commands.setLightStripStateResponse(
+                connection.sendCommand(commands.setLightStripColor(command, transitionPeriod)));
+    }
+}
index e3a3d4c25be0548abd8fb554cdb3da1acf366856..cb1a20db520fc7ad2dad954890f8a7590a9a7db2 100644 (file)
  */
 package org.openhab.binding.tplinksmarthome.internal.handler;
 
-import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*;
+import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CHANNEL_RSSI;
+import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CONFIG_DEVICE_ID;
+import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CONFIG_IP;
+import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.FORCED_REFRESH_BOUNDERY_SECONDS;
+import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.FORCED_REFRESH_BOUNDERY_SWITCHED_SECONDS;
 
 import java.io.IOException;
 import java.time.Duration;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
@@ -38,6 +44,7 @@ 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.thing.binding.ThingHandlerService;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.RefreshType;
 import org.openhab.core.types.State;
@@ -55,6 +62,7 @@ import org.slf4j.LoggerFactory;
 public class SmartHomeHandler extends BaseThingHandler {
 
     private static final Duration ONE_SECOND = Duration.ofSeconds(1);
+    private static final int CONNECTION_IO_RETRIES = 5;
 
     private final Logger logger = LoggerFactory.getLogger(SmartHomeHandler.class);
 
@@ -79,8 +87,8 @@ public class SmartHomeHandler extends BaseThingHandler {
      * @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) {
+    public SmartHomeHandler(final Thing thing, final SmartHomeDevice smartHomeDevice,
+            final TPLinkSmartHomeThingType type, final TPLinkIpAddressService ipAddressService) {
         super(thing);
         this.smartHomeDevice = smartHomeDevice;
         this.ipAddressService = ipAddressService;
@@ -90,7 +98,16 @@ public class SmartHomeHandler extends BaseThingHandler {
     }
 
     @Override
-    public void handleCommand(ChannelUID channelUid, Command command) {
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return List.of(TPLinkSmartHomeActions.class);
+    }
+
+    Connection getConnection() {
+        return connection;
+    }
+
+    @Override
+    public void handleCommand(final ChannelUID channelUid, final Command command) {
         try {
             if (command instanceof RefreshType) {
                 updateChannelState(channelUid, fastCache.getValue());
@@ -100,7 +117,7 @@ public class SmartHomeHandler extends BaseThingHandler {
             } else {
                 logger.debug("Command {} is not supported for channel: {}", command, channelUid.getId());
             }
-        } catch (IOException e) {
+        } catch (final IOException e) {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
         }
     }
@@ -143,7 +160,7 @@ public class SmartHomeHandler extends BaseThingHandler {
      * @param config configuration to be used by the connection
      * @return new Connection object
      */
-    Connection createConnection(TPLinkSmartHomeConfiguration config) {
+    Connection createConnection(final TPLinkSmartHomeConfiguration config) {
         return new Connection(config.ipAddress);
     }
 
@@ -158,22 +175,34 @@ public class SmartHomeHandler extends BaseThingHandler {
     }
 
     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);
+        int retry = 1;
+
+        while (true) {
+            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 (final IOException e) {
+                // If there is a connection problem retry before throwing an exception
+                if (retry < CONNECTION_IO_RETRIES) {
+                    logger.trace("Communication error, retry {}", retry, e);
+                    retry++;
+                } else {
+                    updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+                    return null;
+                }
+            } catch (final 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;
             }
-            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;
         }
     }
 
@@ -187,10 +216,10 @@ public class SmartHomeHandler extends BaseThingHandler {
             // 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);
+        final String lastKnownIpAddress = ipAddressService.getLastKnownIpAddress(configuration.deviceId);
 
         if (lastKnownIpAddress != null && !lastKnownIpAddress.equals(configuration.ipAddress)) {
-            Configuration editConfig = editConfiguration();
+            final Configuration editConfig = editConfiguration();
             editConfig.put(CONFIG_IP, lastKnownIpAddress);
             updateConfiguration(editConfig);
             configuration.ipAddress = lastKnownIpAddress;
@@ -206,9 +235,9 @@ public class SmartHomeHandler extends BaseThingHandler {
      * @throws IllegalArgumentException if the configured device id doesn't match with the id reported by the device
      *             itself.
      */
-    private void updateDeviceId(String actualDeviceId) {
+    private void updateDeviceId(final String actualDeviceId) {
         if (StringUtil.isBlank(configuration.deviceId)) {
-            Configuration editConfig = editConfiguration();
+            final Configuration editConfig = editConfiguration();
             editConfig.put(CONFIG_DEVICE_ID, actualDeviceId);
             updateConfiguration(editConfig);
             configuration.deviceId = actualDeviceId;
@@ -222,7 +251,7 @@ public class SmartHomeHandler extends BaseThingHandler {
     /**
      * Starts the background refresh thread.
      */
-    private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) {
+    private void startAutomaticRefresh(final TPLinkSmartHomeConfiguration config) {
         if (refreshJob == null || refreshJob.isCancelled()) {
             refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, config.refresh, config.refresh,
                     TimeUnit.SECONDS);
@@ -241,11 +270,11 @@ public class SmartHomeHandler extends BaseThingHandler {
      * @param deviceState the state object containing the value to set of the channel
      *
      */
-    private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState deviceState) {
+    private void updateChannelState(final ChannelUID channelUID, @Nullable final DeviceState deviceState) {
         if (!isLinked(channelUID)) {
             return;
         }
-        String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId();
+        final String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId();
         final State state;
 
         if (deviceState == null) {
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/TPLinkSmartHomeActions.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/TPLinkSmartHomeActions.java
new file mode 100644 (file)
index 0000000..551edae
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tplinksmarthome.internal.handler;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TP-Link Smart Home Rule Actions.
+ *
+ * @author Hilbrand Bouwkamp - Initial contribution
+ */
+@ThingActionsScope(name = "tplinksmarthome")
+@NonNullByDefault
+public class TPLinkSmartHomeActions implements ThingActions, ThingHandlerService {
+
+    private final Logger logger = LoggerFactory.getLogger(TPLinkSmartHomeActions.class);
+
+    private @Nullable SmartHomeHandler handler;
+
+    @RuleAction(label = "@text/actions.tplinksmarthome.send.label", description = "@text/actions.tplinksmarthome.send.description")
+    @ActionOutput(name = "response", label = "@text/actions.tplinksmarthome.send.response.label", description = "@text/actions.tplinksmarthome.send.response.description", type = "java.lang.String")
+    public String send(
+            @ActionInput(name = "command", label = "@text/actions.tplinksmarthome.send.command.label", description = "@text/actions.tplinksmarthome.send.command.description", type = "java.lang.String", required = true) final String command)
+            throws IOException {
+        if (handler instanceof SmartHomeHandler) {
+            return handler.getConnection().sendCommand(command);
+        } else {
+            logger.warn("Could not send command to tplink device because handler not set.");
+            return "";
+        }
+    }
+
+    public static String send(final ThingActions actions, final String command) throws IOException {
+        return ((TPLinkSmartHomeActions) actions).send(command);
+    }
+
+    @Override
+    public void setThingHandler(final ThingHandler handler) {
+        if (handler instanceof SmartHomeHandler) {
+            this.handler = (SmartHomeHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLightState.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLightState.java
new file mode 100644 (file)
index 0000000..235222a
--- /dev/null
@@ -0,0 +1,175 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tplinksmarthome.internal.model;
+
+import java.util.Arrays;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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 Light Strip state and retrieving the result.
+ *
+ * @author Hilbrand Bouwkamp - Initial contribution
+ */
+public class SetLightState implements HasErrorResponse {
+
+    private static final int GROUPS_INDEX_HUE = 2;
+    private static final int GROUPS_INDEX_SATURATION = 3;
+    private static final int GROUPS_INDEX_BRIGHTNESS = 4;
+    private static final int GROUPS_INDEX_COLOR_TEMPERATURE = 5;
+
+    public static class ColorTemperature extends LightOnOff {
+        @Expose(deserialize = false)
+        private int colorTemp;
+        @Expose(deserialize = false)
+        private int hue = 0;
+        @Expose(deserialize = false)
+        private int saturation = 0;
+
+        public void setColorTemp(final int colorTemperature) {
+            this.colorTemp = colorTemperature;
+        }
+    }
+
+    public static class Color extends Brightness {
+        @Expose(deserialize = false)
+        private int colorTemp;
+        @Expose(deserialize = false)
+        private int hue;
+        @Expose(deserialize = false)
+        private int saturation;
+
+        public void setHue(final int hue) {
+            this.hue = hue;
+        }
+
+        public void setSaturation(final int saturation) {
+            this.saturation = saturation;
+        }
+    }
+
+    public static class Brightness extends LightOnOff {
+        @Expose(deserialize = false)
+        private int brightness;
+
+        public void setBrightness(final int brightness) {
+            this.brightness = brightness;
+        }
+    }
+
+    public static class LightOnOff extends ErrorResponse {
+        @Expose
+        private int onOff;
+        @Expose(serialize = false)
+        private String mode;
+        @Expose(deserialize = false)
+        private int transition;
+        /**
+         * groups contain status: [[0,31,0,0,73,5275]]
+         * [?,?,hue,saturation,brightness,color_temp]
+         */
+        @Expose(serialize = false)
+        protected int[][] groups;
+
+        public OnOffType getOnOff() {
+            return OnOffType.from(onOff == 1);
+        }
+
+        public void setOnOff(final OnOffType onOff) {
+            this.onOff = onOff == OnOffType.ON ? 1 : 0;
+        }
+
+        public void setTransition(final int transition) {
+            this.transition = transition;
+        }
+
+        public int getHue() {
+            return groups[0][GROUPS_INDEX_HUE];
+        }
+
+        public int getSaturation() {
+            return groups[0][GROUPS_INDEX_SATURATION];
+        }
+
+        public int getBrightness() {
+            return groups[0][GROUPS_INDEX_BRIGHTNESS];
+        }
+
+        public int getColorTemperature() {
+            return groups[0][GROUPS_INDEX_COLOR_TEMPERATURE];
+        }
+
+        public int[][] getGroups() {
+            return groups;
+        }
+
+        @Override
+        public String toString() {
+            return "onOff:" + onOff + ", mode:" + mode + ", transition:" + transition + ", groups:"
+                    + Arrays.toString(groups);
+        }
+    }
+
+    public static class Context {
+        @Expose
+        private String source = "12345668-1234-1234-1234-123456789012";
+
+        public String getSource() {
+            return source;
+        }
+
+        public void setSource(final String source) {
+            this.source = source;
+        }
+    }
+
+    public static class LightingStrip {
+        @Expose
+        private LightOnOff setLightState;
+
+        @Override
+        public String toString() {
+            return "setLightState:{" + setLightState + "}";
+        }
+    }
+
+    @NonNullByDefault
+    @SerializedName("smartlife.iot.lightStrip")
+    @Expose
+    private final LightingStrip strip = new LightingStrip();
+
+    @Expose(deserialize = false)
+    private Context context;
+
+    public void setLightState(final LightOnOff lightState) {
+        strip.setLightState = lightState;
+    }
+
+    public void setContext(final Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public ErrorResponse getErrorResponse() {
+        return strip.setLightState;
+    }
+
+    @Override
+    public String toString() {
+        return "SetLightState {strip:{" + strip + "}";
+    }
+}
index 6447c4425cfa2885241bad287762573c2d0a58c6..9fde9b44ea09afd8d634da1966e0728318100346 100644 (file)
@@ -53,6 +53,10 @@ thing-type.tplinksmarthome.kl130.label = KL130
 thing-type.tplinksmarthome.kl130.description = TP-Link KL130 Smart Wi-Fi LED Bulb with Color Changing Hue
 thing-type.tplinksmarthome.kl135.label = KL135
 thing-type.tplinksmarthome.kl135.description = TP-Link KL135 Kasa Smart Wi-Fi Bulb Multicolor
+thing-type.tplinksmarthome.kl400.label = KL400
+thing-type.tplinksmarthome.kl400.description = TP-Link KL400 Kasa Smart Light Strip, Multicolour
+thing-type.tplinksmarthome.kl430.label = KL430
+thing-type.tplinksmarthome.kl430.description = TP-Link KL430 Kasa Smart Light Strip, Multicolour
 thing-type.tplinksmarthome.kl50.label = KL50
 thing-type.tplinksmarthome.kl50.description = Kasa Filament Smart Bulb, Soft White
 thing-type.tplinksmarthome.kl60.label = KL60
@@ -150,3 +154,12 @@ channel-type.tplinksmarthome.switch-readonly.label = Switch
 channel-type.tplinksmarthome.switch-readonly.description = Shows the switch state of the Smart Home device.
 channel-type.tplinksmarthome.voltage.label = Voltage
 channel-type.tplinksmarthome.voltage.description = Actual voltage usage.
+
+# actions
+
+actions.tplinksmarthome.send.label = Send Command
+actions.tplinksmarthome.send.description = Sends the command, a json string, encrypted to a TP-Link device and decrypts the json response.
+actions.tplinksmarthome.send.command.label = Command
+actions.tplinksmarthome.send.command.description = The json string command to send to the TP-Link device.
+actions.tplinksmarthome.send.response.label = Response
+actions.tplinksmarthome.send.response.description = The decrypted json response returned by the TP-Link device.
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/thing/KL400.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/thing/KL400.xml
new file mode 100644 (file)
index 0000000..023b004
--- /dev/null
@@ -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="kl400">
+               <label>KL400</label>
+               <description>TP-Link KL400 Kasa Smart Light Strip, Multicolour</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:tplinksmarthome:device-bulb"/>
+       </thing-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/thing/KL430.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/OH-INF/thing/KL430.xml
new file mode 100644 (file)
index 0000000..b96857a
--- /dev/null
@@ -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="kl430">
+               <label>KL430</label>
+               <description>TP-Link KL430 Kasa Smart Light Strip, Multicolour</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:tplinksmarthome:device-bulb"/>
+       </thing-type>
+</thing:thing-descriptions>
index bc7c3d97798a5be9efb0334bb07d3a5f18b59928..f5fdba6d023964421a7941a4c8a34d537f744597 100644 (file)
@@ -34,6 +34,8 @@ 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.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
 import org.openhab.core.types.UnDefType;
 
 /**
@@ -173,7 +175,7 @@ public class BulbDeviceTest extends DeviceTestBase<BulbDevice> {
 
     @Test
     public void testUpdateChannelPower() {
-        assertEquals(new DecimalType(10.8), device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState),
+        assertEquals(new QuantityType<>(10.8, Units.WATT), device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState),
                 "Power values should be set");
     }
 }
index 951fdedbc991c38fe4405754cd2f9bbdade04965..e1a96460a01b1120b03a9c99647df1d4f8a815c6 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.tplinksmarthome.internal.device;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.lenient;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -63,7 +63,7 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
      *
      * @throws IOException exception in case device not reachable
      */
-    protected DeviceTestBase(T device, String deviceStateFilename) throws IOException {
+    protected DeviceTestBase(final T device, final String deviceStateFilename) throws IOException {
         this.device = device;
         this.deviceStateFilename = deviceStateFilename;
         configuration.ipAddress = "localhost";
@@ -80,7 +80,7 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
 
     @BeforeEach
     public void setUp() throws IOException {
-        when(socket.getOutputStream()).thenReturn(outputStream);
+        lenient().when(socket.getOutputStream()).thenReturn(outputStream);
         deviceState = new DeviceState(ModelTestUtil.readJson(deviceStateFilename));
     }
 
@@ -91,11 +91,11 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
      * @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();
+    protected void setSocketReturnAssert(final String... responseFilenames) throws IOException {
+        final AtomicInteger index = new AtomicInteger();
 
-        doAnswer(i -> {
-            String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
+        lenient().doAnswer(i -> {
+            final String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
 
             return new ByteArrayInputStream(CryptUtil.encryptWithLength(stateResponse));
         }).when(socket).getInputStream();
@@ -109,20 +109,20 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
      * @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 {
+    protected void assertInput(final 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();
+    protected void assertInput(final Function<String, String> jsonProcessor,
+            final Function<String, String> expectedProcessor, final String... filenames) throws IOException {
+        final AtomicInteger index = new AtomicInteger();
 
-        doAnswer(arg -> {
-            String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()]));
+        lenient().doAnswer(arg -> {
+            final String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()]));
 
-            byte[] input = (byte[]) arg.getArguments()[0];
+            final byte[] input = (byte[]) arg.getArguments()[0];
             try (ByteArrayInputStream inputStream = new ByteArrayInputStream(input)) {
-                String expectedString = expectedProcessor.apply(CryptUtil.decryptWithLength(inputStream));
+                final String expectedString = expectedProcessor.apply(CryptUtil.decryptWithLength(inputStream));
                 assertEquals(json, expectedString, filenames[index.get()]);
             }
             index.incrementAndGet();
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/LightStripDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/LightStripDeviceTest.java
new file mode 100644 (file)
index 0000000..a157bb9
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tplinksmarthome.internal.device;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_BRIGHTNESS;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_COLOR;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_COLOR_TEMPERATURE;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_COLOR_TEMPERATURE_ABS;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_ENERGY_POWER;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_OTHER;
+import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_SWITCH;
+import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.KL430;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.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.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * Test class for {@link BulbDevice} class.
+ *
+ * @author Hilbrand Bouwkamp - Initial contribution
+ */
+@NonNullByDefault
+public class LightStripDeviceTest extends DeviceTestBase<LightStripDevice> {
+
+    private static final String DEVICE_OFF = "bulb_get_sysinfo_response_off";
+
+    public LightStripDeviceTest() throws IOException {
+        super(new LightStripDevice(KL430), "bulb_get_sysinfo_response_on");
+    }
+
+    @BeforeEach
+    @Override
+    public void setUp() throws IOException {
+        super.setUp();
+    }
+
+    @Test
+    public void testHandleCommandBrightness() throws IOException {
+        assertInput("kl430_set_brightness");
+        setSocketReturnAssert("kl430_set_brightness_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(73)),
+                "Brightness channel should be handled");
+    }
+
+    @Test
+    public void testHandleCommandBrightnessOnOff() throws IOException {
+        assertInput("kl430_set_on");
+        setSocketReturnAssert("kl430_set_brightness_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON),
+                "Brightness channel with OnOff state should be handled");
+    }
+
+    @Test
+    public void testHandleCommandColor() throws IOException {
+        assertInput("kl430_set_color");
+        setSocketReturnAssert("kl430_set_color_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_COLOR, new HSBType("115,75,73")),
+                "Color channel should be handled");
+    }
+
+    public void testHandleCommandColorBrightness() throws IOException {
+        assertInput("kl430_set_brightness");
+        setSocketReturnAssert("kl430_set_brightness_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_COLOR, new PercentType(33)),
+                "Color channel with Percentage state (=brightness) should be handled");
+    }
+
+    public void testHandleCommandColorOnOff() throws IOException {
+        assertInput("bulb_transition_light_state_on");
+        assertTrue(device.handleCommand(CHANNEL_UID_COLOR, OnOffType.ON),
+                "Color channel with OnOff state should be handled");
+    }
+
+    @Test
+    public void testHandleCommandColorTemperature() throws IOException {
+        assertInput("kl430_set_colortemperature");
+        setSocketReturnAssert("kl430_set_colortemperature_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, new PercentType(40)),
+                "Color temperature channel should be handled");
+    }
+
+    @Test
+    public void testHandleCommandColorTemperatureAbs() throws IOException {
+        assertInput("kl430_set_colortemperature");
+        setSocketReturnAssert("kl430_set_colortemperature_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE_ABS, new DecimalType(5100)),
+                "Color temperature channel should be handled");
+    }
+
+    @Test
+    public void testHandleCommandColorTemperatureOnOff() throws IOException {
+        assertInput("kl430_set_on");
+        setSocketReturnAssert("kl430_set_colortemperature_response");
+        assertTrue(device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, OnOffType.ON),
+                "Color temperature channel with OnOff state should be handled");
+    }
+
+    // ---- Update ----
+
+    @Test
+    public void testUpdateChannelBrightnessOn() {
+        assertEquals(new PercentType(92), device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState),
+                "Brightness should be on");
+    }
+
+    @Test
+    public void testUpdateChannelBrightnessOff() throws IOException {
+        deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF));
+        assertEquals(PercentType.ZERO, device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState),
+                "Brightness should be off");
+    }
+
+    @Test
+    public void testUpdateChannelColorOn() {
+        assertEquals(new HSBType("7,44,92"), device.updateChannel(CHANNEL_UID_COLOR, deviceState),
+                "Color should be on");
+    }
+
+    @Test
+    public void testUpdateChannelColorOff() throws IOException {
+        deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF));
+        assertEquals(new HSBType("7,44,0"), device.updateChannel(CHANNEL_UID_COLOR, deviceState),
+                "Color should be off");
+    }
+
+    @Test
+    public void testUpdateChannelSwitchOn() {
+        assertSame(OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState), "Switch should be on");
+    }
+
+    @Test
+    public void testUpdateChannelSwitchOff() throws IOException {
+        deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF));
+        assertSame(OnOffType.OFF, device.updateChannel(CHANNEL_UID_SWITCH, deviceState), "Switch should be off");
+    }
+
+    @Test
+    public void testUpdateChannelColorTemperature() throws IOException {
+        assertInput("kl430_set_colortemperature");
+        assertEquals(new PercentType(2), device.updateChannel(CHANNEL_UID_COLOR_TEMPERATURE, deviceState),
+                "Color temperature should be set");
+    }
+
+    @Test
+    public void testUpdateChannelColorTemperatureAbs() throws IOException {
+        assertInput("kl430_set_colortemperature");
+        assertEquals(new DecimalType(2630), device.updateChannel(CHANNEL_UID_COLOR_TEMPERATURE_ABS, deviceState),
+                "Color temperature should be set");
+    }
+
+    @Test
+    public void testUpdateChannelOther() {
+        assertSame(UnDefType.UNDEF, device.updateChannel(CHANNEL_UID_OTHER, deviceState),
+                "Unknown channel should return UNDEF");
+    }
+
+    @Test
+    public void testUpdateChannelPower() {
+        assertEquals(new QuantityType<>(10.8, Units.WATT), device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState),
+                "Power values should be set");
+    }
+}
index 0b8ca68b8b12944c2f889fafc6e5377b89f33daf..5f8715947ffffb59c7b5713fd46147b6a9210074 100644 (file)
@@ -75,8 +75,8 @@ public class SmartHomeHandlerTest {
         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()))
+        lenient().when(smartHomeDevice.getUpdateCommand()).thenReturn(Commands.getSysinfo());
+        lenient().when(connection.sendCommand(Commands.getSysinfo()))
                 .thenReturn(ModelTestUtil.readJson("plug_get_sysinfo_response"));
         handler = new SmartHomeHandler(thing, smartHomeDevice, TPLinkSmartHomeThingType.HS100, discoveryService) {
             @Override
@@ -84,8 +84,8 @@ public class SmartHomeHandlerTest {
                 return connection;
             }
         };
-        when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true);
-        when(callback.isChannelLinked(any())).thenReturn(true);
+        lenient().when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true);
+        lenient().when(callback.isChannelLinked(any())).thenReturn(true);
         handler.setCallback(callback);
     }
 
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_brightness.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_brightness.json
new file mode 100644 (file)
index 0000000..4ba58c7
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "brightness": 73,
+                       "on_off": 1,
+                       "transition": 10
+               }
+       },
+       "context": {
+               "source": "12345668-1234-1234-1234-123456789012"
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_brightness_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_brightness_response.json
new file mode 100644 (file)
index 0000000..63a7ad5
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "on_off": 1,
+                       "mode": "normal",
+                       "groups": [
+                               [
+                                       0,
+                                       31,
+                                       0,
+                                       0,
+                                       73,
+                                       4970
+                               ]
+                       ],
+                       "err_code": 0
+               }
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_color.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_color.json
new file mode 100644 (file)
index 0000000..624a184
--- /dev/null
@@ -0,0 +1,15 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "color_temp": 0,
+                       "hue": 115,
+                       "saturation": 75,
+                       "brightness": 73,
+                       "on_off": 1,
+                       "transition": 10
+               }
+       },
+       "context": {
+    "source": "12345668-1234-1234-1234-123456789012"
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_color_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_color_response.json
new file mode 100644 (file)
index 0000000..52b2edb
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "on_off": 1,
+                       "mode": "normal",
+                       "groups": [
+                               [
+                                       0,
+                                       31,
+                                       115,
+                                       75,
+                                       73,
+                                       0
+                               ]
+                       ],
+                       "err_code": 0
+               }
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_colortemperature.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_colortemperature.json
new file mode 100644 (file)
index 0000000..f39d90f
--- /dev/null
@@ -0,0 +1,14 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "color_temp": 5100,
+                       "hue": 0,
+                       "saturation": 0,
+                       "on_off": 1,
+                       "transition": 10
+               }
+       },
+       "context": {
+               "source": "12345668-1234-1234-1234-123456789012"
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_colortemperature_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_colortemperature_response.json
new file mode 100644 (file)
index 0000000..a199464
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "on_off": 1,
+                       "mode": "normal",
+                       "groups": [
+                               [
+                                       0,
+                                       31,
+                                       0,
+                                       0,
+                                       73,
+                                       5100
+                               ]
+                       ],
+                       "err_code": 0
+               }
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_on.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_on.json
new file mode 100644 (file)
index 0000000..6f14775
--- /dev/null
@@ -0,0 +1,11 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "on_off": 1,
+                       "transition": 10
+               }
+       },
+       "context": {
+               "source": "12345668-1234-1234-1234-123456789012"
+       }
+}
diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_on_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/kl430_set_on_response.json
new file mode 100644 (file)
index 0000000..fa551a2
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       "smartlife.iot.lightStrip": {
+               "set_light_state": {
+                       "on_off": 1,
+                       "mode": "normal",
+                       "groups": [
+                               [
+                                       0,
+                                       31,
+                                       0,
+                                       0,
+                                       100,
+                                       2630
+                               ]
+                       ],
+                       "err_code": 0
+               }
+       }
+}