[tplinksmarthome] Added KL400, KL430 lightstrip support (#12000)
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>
This commit is contained in:
parent
a0236bf993
commit
888cf00245
|
@ -242,6 +242,24 @@ Switching, Brightness and Color is done using the `color` channel.
|
||||||
|
|
||||||
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
|
### KP100 Kasa Wi-Fi Smart Plug - Slim Edition
|
||||||
|
|
||||||
* Power On/Off
|
* 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 |
|
| 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 |
|
| 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 |
|
| 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, 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, 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, |
|
| 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 |
|
| eneryUsage | Number:Energy | Energy Usage in kWh. | HS110, HS300, KP115, KP125 |
|
||||||
| current | Number:ElectricCurrent | Actual current usage in Ampere. | HS110, HS300, KP115, KP125 |
|
| current | Number:ElectricCurrent | Actual current usage in Ampere. | HS110, HS300, KP115, KP125 |
|
||||||
|
|
|
@ -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.Realtime;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.model.SetBrightness;
|
import org.openhab.binding.tplinksmarthome.internal.model.SetBrightness;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.model.SetLedOff;
|
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.SetRelayState;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.model.SetSwitchState;
|
import org.openhab.binding.tplinksmarthome.internal.model.SetSwitchState;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
|
import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo;
|
||||||
|
@ -79,7 +80,7 @@ public class Commands {
|
||||||
* @param id optional id of the device
|
* @param id optional id of the device
|
||||||
* @return The json string of the command to send to 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 String.format(CONTEXT, id) + REALTIME + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +91,8 @@ public class Commands {
|
||||||
* @return The data object containing the energy data from the json string
|
* @return The data object containing the energy data from the json string
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("null")
|
@SuppressWarnings("null")
|
||||||
public Realtime getRealtimeResponse(String realtimeResponse) {
|
public Realtime getRealtimeResponse(final String realtimeResponse) {
|
||||||
GetRealtime getRealtime = gson.fromJson(realtimeResponse, GetRealtime.class);
|
final GetRealtime getRealtime = gson.fromJson(realtimeResponse, GetRealtime.class);
|
||||||
return getRealtime == null ? new Realtime() : getRealtime.getRealtime();
|
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
|
* @return The data object containing the state data from the json string
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("null")
|
@SuppressWarnings("null")
|
||||||
public Sysinfo getSysinfoReponse(String getSysinfoReponse) {
|
public Sysinfo getSysinfoReponse(final String getSysinfoReponse) {
|
||||||
GetSysinfo getSysinfo = gson.fromJson(getSysinfoReponse, GetSysinfo.class);
|
final GetSysinfo getSysinfo = gson.fromJson(getSysinfoReponse, GetSysinfo.class);
|
||||||
return getSysinfo == null ? new Sysinfo() : getSysinfo.getSysinfo();
|
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
|
* @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
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setRelayState(OnOffType onOff, @Nullable String childId) {
|
public String setRelayState(final OnOffType onOff, @Nullable final String childId) {
|
||||||
SetRelayState relayState = new SetRelayState();
|
final SetRelayState relayState = new SetRelayState();
|
||||||
relayState.setRelayState(onOff);
|
relayState.setRelayState(onOff);
|
||||||
if (childId != null) {
|
if (childId != null) {
|
||||||
relayState.setChildId(childId);
|
relayState.setChildId(childId);
|
||||||
|
@ -138,7 +139,7 @@ public class Commands {
|
||||||
* @param relayStateResponse the json string
|
* @param relayStateResponse the json string
|
||||||
* @return The data object containing the state data from 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);
|
return gsonWithExpose.fromJson(relayStateResponse, SetRelayState.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +149,8 @@ public class Commands {
|
||||||
* @param onOff the switch state to set
|
* @param onOff the switch state to set
|
||||||
* @return The json string of the command to send to the device
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setSwitchState(OnOffType onOff) {
|
public String setSwitchState(final OnOffType onOff) {
|
||||||
SetSwitchState switchState = new SetSwitchState();
|
final SetSwitchState switchState = new SetSwitchState();
|
||||||
switchState.setSwitchState(onOff);
|
switchState.setSwitchState(onOff);
|
||||||
return gsonWithExpose.toJson(switchState);
|
return gsonWithExpose.toJson(switchState);
|
||||||
}
|
}
|
||||||
|
@ -160,7 +161,7 @@ public class Commands {
|
||||||
* @param switchStateResponse the json string
|
* @param switchStateResponse the json string
|
||||||
* @return The data object containing the state data from 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);
|
return gsonWithExpose.fromJson(switchStateResponse, SetSwitchState.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +171,8 @@ public class Commands {
|
||||||
* @param brightness the brightness value to set
|
* @param brightness the brightness value to set
|
||||||
* @return The json string of the command to send to the device
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setDimmerBrightness(int brightness) {
|
public String setDimmerBrightness(final int brightness) {
|
||||||
SetBrightness setBrightness = new SetBrightness();
|
final SetBrightness setBrightness = new SetBrightness();
|
||||||
setBrightness.setBrightness(brightness);
|
setBrightness.setBrightness(brightness);
|
||||||
return gsonWithExpose.toJson(setBrightness);
|
return gsonWithExpose.toJson(setBrightness);
|
||||||
}
|
}
|
||||||
|
@ -182,26 +183,10 @@ public class Commands {
|
||||||
* @param dimmerBrightnessResponse the json string
|
* @param dimmerBrightnessResponse the json string
|
||||||
* @return The data object containing the state data from 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);
|
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.
|
* 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
|
* @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
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setLedOn(OnOffType onOff, @Nullable String childId) {
|
public String setLedOn(final OnOffType onOff, @Nullable final String childId) {
|
||||||
SetLedOff sLOff = new SetLedOff();
|
final SetLedOff sLOff = new SetLedOff();
|
||||||
sLOff.setLed(onOff);
|
sLOff.setLed(onOff);
|
||||||
if (childId != null) {
|
if (childId != null) {
|
||||||
sLOff.setChildId(childId);
|
sLOff.setChildId(childId);
|
||||||
|
@ -224,10 +209,21 @@ public class Commands {
|
||||||
* @param setLedOnResponse the json string
|
* @param setLedOnResponse the json string
|
||||||
* @return The data object containing the data from 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);
|
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.
|
* 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
|
* @param transitionPeriod the transition period for the action to take place
|
||||||
* @return The json string of the command to send to the device
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setBrightness(int brightness, int transitionPeriod) {
|
public String setTransitionLightStateBrightness(final int brightness, final int transitionPeriod) {
|
||||||
TransitionLightState transitionLightState = new TransitionLightState();
|
final LightStateBrightness lightState = new LightStateBrightness();
|
||||||
LightStateBrightness lightState = new LightStateBrightness();
|
|
||||||
lightState.setOnOff(brightness == 0 ? OnOffType.OFF : OnOffType.ON);
|
|
||||||
lightState.setBrightness(brightness);
|
lightState.setBrightness(brightness);
|
||||||
lightState.setTransitionPeriod(transitionPeriod);
|
return setTransitionLightState(lightState, OnOffType.from(brightness != 0), transitionPeriod);
|
||||||
transitionLightState.setLightState(lightState);
|
|
||||||
return gson.toJson(transitionLightState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -252,17 +244,13 @@ public class Commands {
|
||||||
* @param transitionPeriod the transition period for the action to take place
|
* @param transitionPeriod the transition period for the action to take place
|
||||||
* @return The json string of the command to send to the device
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setColor(HSBType hsb, int transitionPeriod) {
|
public String setTransitionLightStateColor(final HSBType hsb, final int transitionPeriod) {
|
||||||
TransitionLightState transitionLightState = new TransitionLightState();
|
final LightStateColor lightState = new LightStateColor();
|
||||||
LightStateColor lightState = new LightStateColor();
|
final int brightness = hsb.getBrightness().intValue();
|
||||||
int brightness = hsb.getBrightness().intValue();
|
|
||||||
lightState.setOnOff(brightness == 0 ? OnOffType.OFF : OnOffType.ON);
|
|
||||||
lightState.setBrightness(brightness);
|
lightState.setBrightness(brightness);
|
||||||
lightState.setHue(hsb.getHue().intValue());
|
lightState.setHue(hsb.getHue().intValue());
|
||||||
lightState.setSaturation(hsb.getSaturation().intValue());
|
lightState.setSaturation(hsb.getSaturation().intValue());
|
||||||
lightState.setTransitionPeriod(transitionPeriod);
|
return setTransitionLightState(lightState, OnOffType.from(brightness != 0), transitionPeriod);
|
||||||
transitionLightState.setLightState(lightState);
|
|
||||||
return gson.toJson(transitionLightState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -272,13 +260,18 @@ public class Commands {
|
||||||
* @param transitionPeriod the transition period for the action to take place
|
* @param transitionPeriod the transition period for the action to take place
|
||||||
* @return The json string of the command to send to the device
|
* @return The json string of the command to send to the device
|
||||||
*/
|
*/
|
||||||
public String setColorTemperature(int colorTemperature, int transitionPeriod) {
|
public String setColorTemperature(final int colorTemperature, final int transitionPeriod) {
|
||||||
TransitionLightState transitionLightState = new TransitionLightState();
|
final LightStateColorTemperature lightState = new LightStateColorTemperature();
|
||||||
LightStateColorTemperature lightState = new LightStateColorTemperature();
|
|
||||||
lightState.setOnOff(OnOffType.ON);
|
|
||||||
lightState.setColorTemperature(colorTemperature);
|
lightState.setColorTemperature(colorTemperature);
|
||||||
lightState.setTransitionPeriod(transitionPeriod);
|
return setTransitionLightState(lightState, OnOffType.ON, transitionPeriod);
|
||||||
transitionLightState.setLightState(lightState);
|
}
|
||||||
|
|
||||||
|
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);
|
return gson.toJson(transitionLightState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +281,82 @@ public class Commands {
|
||||||
* @param response the json string
|
* @param response the json string
|
||||||
* @return The data object containing the state data from 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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
|
||||||
public class Connection {
|
public class Connection {
|
||||||
|
|
||||||
public static final int TP_LINK_SMART_HOME_PORT = 9999;
|
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);
|
private final Logger logger = LoggerFactory.getLogger(Connection.class);
|
||||||
|
|
||||||
|
|
|
@ -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.BulbDevice;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.device.DimmerDevice;
|
import org.openhab.binding.tplinksmarthome.internal.device.DimmerDevice;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.device.EnergySwitchDevice;
|
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.PowerStripDevice;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice;
|
import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice;
|
import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice;
|
||||||
|
@ -67,6 +68,9 @@ public class TPLinkSmartHomeHandlerFactory extends BaseThingHandlerFactory {
|
||||||
case DIMMER:
|
case DIMMER:
|
||||||
device = new DimmerDevice();
|
device = new DimmerDevice();
|
||||||
break;
|
break;
|
||||||
|
case LIGHT_STRIP:
|
||||||
|
device = new LightStripDevice(type);
|
||||||
|
break;
|
||||||
case PLUG:
|
case PLUG:
|
||||||
device = new SwitchDevice();
|
device = new SwitchDevice();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -49,6 +49,10 @@ public enum TPLinkSmartHomeThingType {
|
||||||
KL130("kl130", DeviceType.BULB, ColorScales.K_2500_9000),
|
KL130("kl130", DeviceType.BULB, ColorScales.K_2500_9000),
|
||||||
KL135("kl135", DeviceType.BULB, ColorScales.K_2500_6500),
|
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
|
// Plug Thing Type UIDs
|
||||||
EP10("ep10", DeviceType.PLUG),
|
EP10("ep10", DeviceType.PLUG),
|
||||||
HS100("hs100", DeviceType.PLUG),
|
HS100("hs100", DeviceType.PLUG),
|
||||||
|
@ -175,6 +179,10 @@ public enum TPLinkSmartHomeThingType {
|
||||||
* Dimmer device.
|
* Dimmer device.
|
||||||
*/
|
*/
|
||||||
DIMMER,
|
DIMMER,
|
||||||
|
/**
|
||||||
|
* Light Strip device.
|
||||||
|
*/
|
||||||
|
LIGHT_STRIP,
|
||||||
/**
|
/**
|
||||||
* Plug device.
|
* Plug device.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -28,11 +28,12 @@ import org.openhab.binding.tplinksmarthome.internal.Commands;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType;
|
import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
|
import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse;
|
||||||
import org.openhab.binding.tplinksmarthome.internal.model.LightState;
|
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.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
import org.openhab.core.library.types.PercentType;
|
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.thing.ChannelUID;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
|
@ -69,9 +70,9 @@ public class BulbDevice extends SmartHomeDevice {
|
||||||
final int transitionPeriod = configuration.transitionPeriod;
|
final int transitionPeriod = configuration.transitionPeriod;
|
||||||
final HasErrorResponse response;
|
final HasErrorResponse response;
|
||||||
|
|
||||||
if (command instanceof OnOffType) {
|
if (command instanceof OnOffType && CHANNELS_BULB_SWITCH.contains(channelId)) {
|
||||||
response = handleOnOffType(channelId, (OnOffType) command, transitionPeriod);
|
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);
|
response = handleHSBType(channelId, (HSBType) command, transitionPeriod);
|
||||||
} else if (command instanceof DecimalType) {
|
} else if (command instanceof DecimalType) {
|
||||||
response = handleDecimalType(channelId, (DecimalType) command, transitionPeriod);
|
response = handleDecimalType(channelId, (DecimalType) command, transitionPeriod);
|
||||||
|
@ -82,42 +83,42 @@ public class BulbDevice extends SmartHomeDevice {
|
||||||
return response != null;
|
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 {
|
final int transitionPeriod) throws IOException {
|
||||||
if (CHANNELS_BULB_SWITCH.contains(channelID)) {
|
return commands.setTransitionLightStateResponse(
|
||||||
return commands.setTransitionLightStateResponse(
|
connection.sendCommand(commands.setTransitionLightState(onOff, transitionPeriod)));
|
||||||
connection.sendCommand(commands.setLightState(onOff, transitionPeriod)));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable HasErrorResponse handleDecimalType(final String channelID, final DecimalType command,
|
private @Nullable HasErrorResponse handleDecimalType(final String channelID, final DecimalType command,
|
||||||
final int transitionPeriod) throws IOException {
|
final int transitionPeriod) throws IOException {
|
||||||
|
final int intValue = command.intValue();
|
||||||
|
|
||||||
if (CHANNEL_COLOR.equals(channelID) || CHANNEL_BRIGHTNESS.equals(channelID)) {
|
if (CHANNEL_COLOR.equals(channelID) || CHANNEL_BRIGHTNESS.equals(channelID)) {
|
||||||
return commands.setTransitionLightStateResponse(
|
return handleBrightness(intValue, transitionPeriod);
|
||||||
connection.sendCommand(commands.setBrightness(command.intValue(), transitionPeriod)));
|
|
||||||
} else if (CHANNEL_COLOR_TEMPERATURE.equals(channelID)) {
|
} 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)) {
|
} else if (CHANNEL_COLOR_TEMPERATURE_ABS.equals(channelID)) {
|
||||||
return handleColorTemperature(guardColorTemperature(command.intValue()), transitionPeriod);
|
return handleColorTemperature(guardColorTemperature(intValue), transitionPeriod);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable TransitionLightStateResponse handleColorTemperature(final int colorTemperature,
|
protected @Nullable HasErrorResponse handleBrightness(final int brightness, final int transitionPeriod)
|
||||||
final int transitionPeriod) throws IOException {
|
throws IOException {
|
||||||
|
return commands.setTransitionLightStateResponse(
|
||||||
|
connection.sendCommand(commands.setTransitionLightStateBrightness(brightness, transitionPeriod)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @Nullable HasErrorResponse handleColorTemperature(final int colorTemperature, final int transitionPeriod)
|
||||||
|
throws IOException {
|
||||||
return commands.setTransitionLightStateResponse(
|
return commands.setTransitionLightStateResponse(
|
||||||
connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod)));
|
connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
protected @Nullable HasErrorResponse handleHSBType(final String channelID, final HSBType command,
|
||||||
private HasErrorResponse handleHSBType(final String channelID, final HSBType command, final int transitionPeriod)
|
final int transitionPeriod) throws IOException {
|
||||||
throws IOException {
|
return commands.setTransitionLightStateResponse(
|
||||||
if (CHANNEL_COLOR.equals(channelID)) {
|
connection.sendCommand(commands.setTransitionLightStateColor(command, transitionPeriod)));
|
||||||
return commands.setTransitionLightStateResponse(
|
|
||||||
connection.sendCommand(commands.setColor(command, transitionPeriod)));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -142,7 +143,7 @@ public class BulbDevice extends SmartHomeDevice {
|
||||||
state = lightState.getOnOff();
|
state = lightState.getOnOff();
|
||||||
break;
|
break;
|
||||||
case CHANNEL_ENERGY_POWER:
|
case CHANNEL_ENERGY_POWER:
|
||||||
state = new DecimalType(deviceState.getRealtime().getPower());
|
state = new QuantityType<>(deviceState.getRealtime().getPower(), Units.WATT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state = UnDefType.UNDEF;
|
state = UnDefType.UNDEF;
|
||||||
|
|
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,10 +12,16 @@
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.tplinksmarthome.internal.handler;
|
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.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
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.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
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.Command;
|
||||||
import org.openhab.core.types.RefreshType;
|
import org.openhab.core.types.RefreshType;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
|
@ -55,6 +62,7 @@ import org.slf4j.LoggerFactory;
|
||||||
public class SmartHomeHandler extends BaseThingHandler {
|
public class SmartHomeHandler extends BaseThingHandler {
|
||||||
|
|
||||||
private static final Duration ONE_SECOND = Duration.ofSeconds(1);
|
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);
|
private final Logger logger = LoggerFactory.getLogger(SmartHomeHandler.class);
|
||||||
|
|
||||||
|
@ -79,8 +87,8 @@ public class SmartHomeHandler extends BaseThingHandler {
|
||||||
* @param type The device type
|
* @param type The device type
|
||||||
* @param ipAddressService Cache keeping track of ip addresses of tp link devices
|
* @param ipAddressService Cache keeping track of ip addresses of tp link devices
|
||||||
*/
|
*/
|
||||||
public SmartHomeHandler(Thing thing, SmartHomeDevice smartHomeDevice, TPLinkSmartHomeThingType type,
|
public SmartHomeHandler(final Thing thing, final SmartHomeDevice smartHomeDevice,
|
||||||
TPLinkIpAddressService ipAddressService) {
|
final TPLinkSmartHomeThingType type, final TPLinkIpAddressService ipAddressService) {
|
||||||
super(thing);
|
super(thing);
|
||||||
this.smartHomeDevice = smartHomeDevice;
|
this.smartHomeDevice = smartHomeDevice;
|
||||||
this.ipAddressService = ipAddressService;
|
this.ipAddressService = ipAddressService;
|
||||||
|
@ -90,7 +98,16 @@ public class SmartHomeHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 {
|
try {
|
||||||
if (command instanceof RefreshType) {
|
if (command instanceof RefreshType) {
|
||||||
updateChannelState(channelUid, fastCache.getValue());
|
updateChannelState(channelUid, fastCache.getValue());
|
||||||
|
@ -100,7 +117,7 @@ public class SmartHomeHandler extends BaseThingHandler {
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Command {} is not supported for channel: {}", command, channelUid.getId());
|
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());
|
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
|
* @param config configuration to be used by the connection
|
||||||
* @return new Connection object
|
* @return new Connection object
|
||||||
*/
|
*/
|
||||||
Connection createConnection(TPLinkSmartHomeConfiguration config) {
|
Connection createConnection(final TPLinkSmartHomeConfiguration config) {
|
||||||
return new Connection(config.ipAddress);
|
return new Connection(config.ipAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,22 +175,34 @@ public class SmartHomeHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable DeviceState refreshCache() {
|
private @Nullable DeviceState refreshCache() {
|
||||||
try {
|
int retry = 1;
|
||||||
updateIpAddress();
|
|
||||||
final DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand()));
|
while (true) {
|
||||||
updateDeviceId(deviceState.getSysinfo().getDeviceId());
|
try {
|
||||||
smartHomeDevice.refreshedDeviceState(deviceState);
|
updateIpAddress();
|
||||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
final DeviceState deviceState = new DeviceState(
|
||||||
updateStatus(ThingStatus.ONLINE);
|
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.
|
// The device id is needed to get the ip address so if not known no need to continue.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String lastKnownIpAddress = ipAddressService.getLastKnownIpAddress(configuration.deviceId);
|
final String lastKnownIpAddress = ipAddressService.getLastKnownIpAddress(configuration.deviceId);
|
||||||
|
|
||||||
if (lastKnownIpAddress != null && !lastKnownIpAddress.equals(configuration.ipAddress)) {
|
if (lastKnownIpAddress != null && !lastKnownIpAddress.equals(configuration.ipAddress)) {
|
||||||
Configuration editConfig = editConfiguration();
|
final Configuration editConfig = editConfiguration();
|
||||||
editConfig.put(CONFIG_IP, lastKnownIpAddress);
|
editConfig.put(CONFIG_IP, lastKnownIpAddress);
|
||||||
updateConfiguration(editConfig);
|
updateConfiguration(editConfig);
|
||||||
configuration.ipAddress = lastKnownIpAddress;
|
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
|
* @throws IllegalArgumentException if the configured device id doesn't match with the id reported by the device
|
||||||
* itself.
|
* itself.
|
||||||
*/
|
*/
|
||||||
private void updateDeviceId(String actualDeviceId) {
|
private void updateDeviceId(final String actualDeviceId) {
|
||||||
if (StringUtil.isBlank(configuration.deviceId)) {
|
if (StringUtil.isBlank(configuration.deviceId)) {
|
||||||
Configuration editConfig = editConfiguration();
|
final Configuration editConfig = editConfiguration();
|
||||||
editConfig.put(CONFIG_DEVICE_ID, actualDeviceId);
|
editConfig.put(CONFIG_DEVICE_ID, actualDeviceId);
|
||||||
updateConfiguration(editConfig);
|
updateConfiguration(editConfig);
|
||||||
configuration.deviceId = actualDeviceId;
|
configuration.deviceId = actualDeviceId;
|
||||||
|
@ -222,7 +251,7 @@ public class SmartHomeHandler extends BaseThingHandler {
|
||||||
/**
|
/**
|
||||||
* Starts the background refresh thread.
|
* Starts the background refresh thread.
|
||||||
*/
|
*/
|
||||||
private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) {
|
private void startAutomaticRefresh(final TPLinkSmartHomeConfiguration config) {
|
||||||
if (refreshJob == null || refreshJob.isCancelled()) {
|
if (refreshJob == null || refreshJob.isCancelled()) {
|
||||||
refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, config.refresh, config.refresh,
|
refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, config.refresh, config.refresh,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
|
@ -241,11 +270,11 @@ public class SmartHomeHandler extends BaseThingHandler {
|
||||||
* @param deviceState the state object containing the value to set of the channel
|
* @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)) {
|
if (!isLinked(channelUID)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId();
|
final String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId();
|
||||||
final State state;
|
final State state;
|
||||||
|
|
||||||
if (deviceState == null) {
|
if (deviceState == 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + "}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.kl130.description = TP-Link KL130 Smart Wi-Fi LED Bulb with Color Changing Hue
|
||||||
thing-type.tplinksmarthome.kl135.label = KL135
|
thing-type.tplinksmarthome.kl135.label = KL135
|
||||||
thing-type.tplinksmarthome.kl135.description = TP-Link KL135 Kasa Smart Wi-Fi Bulb Multicolor
|
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.label = KL50
|
||||||
thing-type.tplinksmarthome.kl50.description = Kasa Filament Smart Bulb, Soft White
|
thing-type.tplinksmarthome.kl50.description = Kasa Filament Smart Bulb, Soft White
|
||||||
thing-type.tplinksmarthome.kl60.label = KL60
|
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.switch-readonly.description = Shows the switch state of the Smart Home device.
|
||||||
channel-type.tplinksmarthome.voltage.label = Voltage
|
channel-type.tplinksmarthome.voltage.label = Voltage
|
||||||
channel-type.tplinksmarthome.voltage.description = Actual voltage usage.
|
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.
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -34,6 +34,8 @@ import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
import org.openhab.core.library.types.PercentType;
|
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;
|
import org.openhab.core.types.UnDefType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -173,7 +175,7 @@ public class BulbDeviceTest extends DeviceTestBase<BulbDevice> {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateChannelPower() {
|
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");
|
"Power values should be set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ package org.openhab.binding.tplinksmarthome.internal.device;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.lenient;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -63,7 +63,7 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
|
||||||
*
|
*
|
||||||
* @throws IOException exception in case device not reachable
|
* @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.device = device;
|
||||||
this.deviceStateFilename = deviceStateFilename;
|
this.deviceStateFilename = deviceStateFilename;
|
||||||
configuration.ipAddress = "localhost";
|
configuration.ipAddress = "localhost";
|
||||||
|
@ -80,7 +80,7 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() throws IOException {
|
public void setUp() throws IOException {
|
||||||
when(socket.getOutputStream()).thenReturn(outputStream);
|
lenient().when(socket.getOutputStream()).thenReturn(outputStream);
|
||||||
deviceState = new DeviceState(ModelTestUtil.readJson(deviceStateFilename));
|
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
|
* @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
|
* @throws IOException exception in case device not reachable
|
||||||
*/
|
*/
|
||||||
protected void setSocketReturnAssert(String... responseFilenames) throws IOException {
|
protected void setSocketReturnAssert(final String... responseFilenames) throws IOException {
|
||||||
AtomicInteger index = new AtomicInteger();
|
final AtomicInteger index = new AtomicInteger();
|
||||||
|
|
||||||
doAnswer(i -> {
|
lenient().doAnswer(i -> {
|
||||||
String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
|
final String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
|
||||||
|
|
||||||
return new ByteArrayInputStream(CryptUtil.encryptWithLength(stateResponse));
|
return new ByteArrayInputStream(CryptUtil.encryptWithLength(stateResponse));
|
||||||
}).when(socket).getInputStream();
|
}).when(socket).getInputStream();
|
||||||
|
@ -109,20 +109,20 @@ public class DeviceTestBase<T extends SmartHomeDevice> {
|
||||||
* @param filenames names of the files containing the reference json
|
* @param filenames names of the files containing the reference json
|
||||||
* @throws IOException exception in case device not reachable
|
* @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);
|
assertInput(Function.identity(), Function.identity(), filenames);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertInput(Function<String, String> jsonProcessor, Function<String, String> expectedProcessor,
|
protected void assertInput(final Function<String, String> jsonProcessor,
|
||||||
String... filenames) throws IOException {
|
final Function<String, String> expectedProcessor, final String... filenames) throws IOException {
|
||||||
AtomicInteger index = new AtomicInteger();
|
final AtomicInteger index = new AtomicInteger();
|
||||||
|
|
||||||
doAnswer(arg -> {
|
lenient().doAnswer(arg -> {
|
||||||
String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()]));
|
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)) {
|
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()]);
|
assertEquals(json, expectedString, filenames[index.get()]);
|
||||||
}
|
}
|
||||||
index.incrementAndGet();
|
index.incrementAndGet();
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,8 +75,8 @@ public class SmartHomeHandlerTest {
|
||||||
configuration.put(CONFIG_IP, "localhost");
|
configuration.put(CONFIG_IP, "localhost");
|
||||||
configuration.put(CONFIG_REFRESH, 1);
|
configuration.put(CONFIG_REFRESH, 1);
|
||||||
when(thing.getConfiguration()).thenReturn(configuration);
|
when(thing.getConfiguration()).thenReturn(configuration);
|
||||||
when(smartHomeDevice.getUpdateCommand()).thenReturn(Commands.getSysinfo());
|
lenient().when(smartHomeDevice.getUpdateCommand()).thenReturn(Commands.getSysinfo());
|
||||||
when(connection.sendCommand(Commands.getSysinfo()))
|
lenient().when(connection.sendCommand(Commands.getSysinfo()))
|
||||||
.thenReturn(ModelTestUtil.readJson("plug_get_sysinfo_response"));
|
.thenReturn(ModelTestUtil.readJson("plug_get_sysinfo_response"));
|
||||||
handler = new SmartHomeHandler(thing, smartHomeDevice, TPLinkSmartHomeThingType.HS100, discoveryService) {
|
handler = new SmartHomeHandler(thing, smartHomeDevice, TPLinkSmartHomeThingType.HS100, discoveryService) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -84,8 +84,8 @@ public class SmartHomeHandlerTest {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true);
|
lenient().when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true);
|
||||||
when(callback.isChannelLinked(any())).thenReturn(true);
|
lenient().when(callback.isChannelLinked(any())).thenReturn(true);
|
||||||
handler.setCallback(callback);
|
handler.setCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"smartlife.iot.lightStrip": {
|
||||||
|
"set_light_state": {
|
||||||
|
"brightness": 73,
|
||||||
|
"on_off": 1,
|
||||||
|
"transition": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"source": "12345668-1234-1234-1234-123456789012"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"smartlife.iot.lightStrip": {
|
||||||
|
"set_light_state": {
|
||||||
|
"on_off": 1,
|
||||||
|
"transition": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"source": "12345668-1234-1234-1234-123456789012"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue