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
|---------------------|--------------------------|------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 |
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;
* @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 + "}";
}
* @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();
}
* @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();
}
* @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);
* @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);
}
* @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);
}
* @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);
}
* @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);
}
* @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.
*
* @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);
* @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.
*
* @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);
}
/**
* @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);
}
/**
* @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);
}
* @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);
+ }
}
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);
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;
case DIMMER:
device = new DimmerDevice();
break;
+ case LIGHT_STRIP:
+ device = new LightStripDevice(type);
+ break;
case PLUG:
device = new SwitchDevice();
break;
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),
* Dimmer device.
*/
DIMMER,
+ /**
+ * Light Strip device.
+ */
+ LIGHT_STRIP,
/**
* Plug device.
*/
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;
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);
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
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;
--- /dev/null
+/**
+ * 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)));
+ }
+}
*/
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;
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;
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);
* @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;
}
@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());
} 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());
}
}
* @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);
}
}
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;
}
}
// 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;
* @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;
/**
* 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);
* @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) {
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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 + "}";
+ }
+}
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
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.
--- /dev/null
+<?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>
--- /dev/null
+<?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>
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
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");
}
}
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;
*
* @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";
@BeforeEach
public void setUp() throws IOException {
- when(socket.getOutputStream()).thenReturn(outputStream);
+ lenient().when(socket.getOutputStream()).thenReturn(outputStream);
deviceState = new DeviceState(ModelTestUtil.readJson(deviceStateFilename));
}
* @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();
* @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();
--- /dev/null
+/**
+ * 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");
+ }
+}
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
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);
}
--- /dev/null
+{
+ "smartlife.iot.lightStrip": {
+ "set_light_state": {
+ "brightness": 73,
+ "on_off": 1,
+ "transition": 10
+ }
+ },
+ "context": {
+ "source": "12345668-1234-1234-1234-123456789012"
+ }
+}
--- /dev/null
+{
+ "smartlife.iot.lightStrip": {
+ "set_light_state": {
+ "on_off": 1,
+ "mode": "normal",
+ "groups": [
+ [
+ 0,
+ 31,
+ 0,
+ 0,
+ 73,
+ 4970
+ ]
+ ],
+ "err_code": 0
+ }
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+{
+ "smartlife.iot.lightStrip": {
+ "set_light_state": {
+ "on_off": 1,
+ "mode": "normal",
+ "groups": [
+ [
+ 0,
+ 31,
+ 115,
+ 75,
+ 73,
+ 0
+ ]
+ ],
+ "err_code": 0
+ }
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+{
+ "smartlife.iot.lightStrip": {
+ "set_light_state": {
+ "on_off": 1,
+ "mode": "normal",
+ "groups": [
+ [
+ 0,
+ 31,
+ 0,
+ 0,
+ 73,
+ 5100
+ ]
+ ],
+ "err_code": 0
+ }
+ }
+}
--- /dev/null
+{
+ "smartlife.iot.lightStrip": {
+ "set_light_state": {
+ "on_off": 1,
+ "transition": 10
+ }
+ },
+ "context": {
+ "source": "12345668-1234-1234-1234-123456789012"
+ }
+}
--- /dev/null
+{
+ "smartlife.iot.lightStrip": {
+ "set_light_state": {
+ "on_off": 1,
+ "mode": "normal",
+ "groups": [
+ [
+ 0,
+ 31,
+ 0,
+ 0,
+ 100,
+ 2630
+ ]
+ ],
+ "err_code": 0
+ }
+ }
+}