From 8810dae7e7784c47a01cde91db245aef7f98e6a5 Mon Sep 17 00:00:00 2001 From: Hilbrand Bouwkamp Date: Thu, 27 Jun 2019 13:00:33 +0200 Subject: [PATCH] [tplinksmarthome] Added support for power outlets HS107, HS300, KP200, KP400 (#5716) Closes #5051 Signed-off-by: Hilbrand Bouwkamp --- .../.classpath | 3 +- .../README.md | 64 ++++++--- .../tplinksmarthome/internal/Commands.java | 38 ++++-- .../tplinksmarthome/internal/Connection.java | 2 +- .../TPLinkSmartHomeBindingConstants.java | 6 +- .../TPLinkSmartHomeDiscoveryService.java | 32 +++-- .../TPLinkSmartHomeHandlerFactory.java | 7 +- .../internal/TPLinkSmartHomeThingType.java | 40 ++++-- .../internal/device/BulbDevice.java | 24 ++-- .../internal/device/DimmerDevice.java | 26 ++-- .../internal/device/EnergySwitchDevice.java | 14 +- .../internal/device/PowerStripDevice.java | 129 ++++++++++++++++++ .../internal/device/RangeExtenderDevice.java | 4 +- .../internal/device/SmartHomeDevice.java | 35 +++-- .../internal/device/SwitchDevice.java | 54 ++++++-- .../internal/handler/SmartHomeHandler.java | 30 ++-- .../internal/model/ContextState.java | 50 +++++++ .../internal/model/GetRealtime.java | 2 +- .../internal/model/HasErrorResponse.java | 1 + .../internal/model/SetLedOff.java | 2 +- .../internal/model/SetRelayState.java | 8 +- .../internal/model/Sysinfo.java | 41 ++++++ .../main/resources/ESH-INF/thing/HS107.xml | 26 ++++ .../main/resources/ESH-INF/thing/HS110.xml | 2 +- .../main/resources/ESH-INF/thing/HS300.xml | 39 ++++++ .../main/resources/ESH-INF/thing/KP200.xml | 26 ++++ .../main/resources/ESH-INF/thing/KP400.xml | 26 ++++ .../main/resources/ESH-INF/thing/RE270K.xml | 2 +- .../main/resources/ESH-INF/thing/RE370K.xml | 2 +- .../main/resources/ESH-INF/thing/channels.xml | 32 ++++- .../internal/ChannelUIDConstants.java | 54 ++++++++ .../internal/PropertiesCollectorTest.java | 7 +- .../internal/device/BulbDeviceTest.java | 52 +++---- .../internal/device/DeviceTestBase.java | 32 +++-- .../internal/device/DimmerDeviceTest.java | 25 ++-- .../device/EnergySwitchDeviceTest.java | 13 +- .../internal/device/PowerStripDeviceTest.java | 94 +++++++++++++ .../device/RangeExtenderDeviceTest.java | 28 ++-- .../internal/device/SwitchDeviceTest.java | 23 ++-- .../handler/SmartHomeHandlerTest.java | 13 +- .../internal/model/ModelTestUtil.java | 8 +- .../internal/model/hs300_get_realtime.json | 1 + .../model/hs300_get_realtime_response.json | 1 + .../model/hs300_get_sysinfo_response.json | 78 +++++++++++ .../internal/model/hs300_set_relay_state.json | 1 + .../model/hs300_set_relay_state_response.json | 1 + 46 files changed, 965 insertions(+), 233 deletions(-) create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.java create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.java create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.java create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.java create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json create mode 100644 bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json diff --git a/bundles/org.openhab.binding.tplinksmarthome/.classpath b/bundles/org.openhab.binding.tplinksmarthome/.classpath index a5d95095ccaaf..7737f39c03922 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/.classpath +++ b/bundles/org.openhab.binding.tplinksmarthome/.classpath @@ -13,11 +13,12 @@ + - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/README.md b/bundles/org.openhab.binding.tplinksmarthome/README.md index 7050ee6fb8a03..51d2a96c441c7 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/README.md +++ b/bundles/org.openhab.binding.tplinksmarthome/README.md @@ -26,7 +26,10 @@ The following TP-Link Smart Devices are supported: ### HS107 Smart Wi-Fi Plug, 2-Outlets -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### HS110 Smart Wi-Fi Plug @@ -64,7 +67,11 @@ Switching via openHAB activates the switch directly. ### HS300 Smart Wi-Fi Power Strip -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Energy readings Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### KB100 Kasa Smart Light Bulb @@ -94,11 +101,17 @@ Switching, Brightness and Color is done using the `color` channel. ### KP200 Smart Wi-Fi Power Outlet, 2-Sockets -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### KP400 Smart Outdoor Plug -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### LB100 Smart Wi-Fi LED Bulb with Dimmable Light @@ -241,33 +254,44 @@ Either `deviceId` or `ipAddress` must be set. All devices support some of the following channels: -| Channel Type ID | Item Type | Description | Thing types supporting this channel | -|------------------|-----------|----------------------------------------------------|-----------------------------------------------------------------| -| switch | Switch | Switch the Smart Home device on or off. | HS100, HS103, HS105, HS110, HS200, HS210, KP100, RE270K, RE370K | -| brightness | Dimmer | Set the brightness of Smart Home device or dimmer. | HS220, KB100, KL110, KL120, LB100, LB110, LB120, LB200 | -| colorTemperature | Dimmer | Set the color temperature of Smart Home light. | KB130, KL120, KL130, LB120, LB130, LB230 | -| color | Color | Set the color of the Smart Home light. | KB130, KL130, LB130, LB230 | -| power | Number | Actual energy usage in Watt. | HS110, KLxxx, LBxxx | -| eneryUsage | Number | Energy Usage in kWh. | HS110 | -| current | Number | Actual current usage in Ampere. | HS110 | -| voltage | Number | Actual voltage usage in Volt. | HS110 | -| led | Switch | Switch the status led on the device on or off. | HS100, HS103, HS105, HS110, HS200, HS210, HS220, KP100 | -| rssi | Number | Wi-Fi signal strength indicator in dBm. | All | +| Channel Type ID | Item Type | Description | Thing types supporting this channel | +|------------------|-----------|----------------------------------------------------|---------------------------------------------------------------------------------------------| +| switch | Switch | Switch the Smart Home device on or off. | HS100, HS103, HS105, HS107, HS110, HS200, HS210, HS300, KP100, KP200, KP400, RE270K, RE370K | +| brightness | Dimmer | Set the brightness of Smart Home device or dimmer. | HS220, KB100, KL110, KL120, LB100, LB110, LB120, LB200 | +| colorTemperature | Dimmer | Set the color temperature of Smart Home light. | KB130, KL120, KL130, LB120, LB130, LB230 | +| color | Color | Set the color of the Smart Home light. | KB130, KL130, LB130, LB230 | +| power | Number | Actual energy usage in Watt. | HS110, HS300, KLxxx, LBxxx | +| eneryUsage | Number | Energy Usage in kWh. | HS110, HS300 | +| current | Number | Actual current usage in Ampere. | HS110, HS300 | +| voltage | Number | Actual voltage usage in Volt. | HS110, HS300 | +| led | Switch | Switch the status led on the device on or off. | HS100, HS103, HS105, HS107, HS110, HS200, HS210, HS220, HS300, KP100, KP200, KP400 | +| rssi | Number | Wi-Fi signal strength indicator in dBm. | All | + +The outlet devices (HS107, HS300, KP200, KP400) have group channels. +This means the channel is prefixed with the group id. +The following group ids are available: + +| Group ID | Description | +|-------------------|-------------------------------------------------------------------------------------------------------| +| groupSwitch | General channels. e.g. `groupSwitch#switch` | +| outlet<number> | The outlet to control. <number> is the number of the outlet (starts with 1). e.g. `outlet1#switch` | ## Full Example ### tplinksmarthome.things: ``` -tplinksmarthome:hs100:tv "Living Room" [ deviceId="00000000000000000000000000000001", refresh=60 ] -tplinksmarthome:lb110:bulb1 "Living Room Bulb 1" [ deviceId="00000000000000000000000000000002", refresh=60, transitionPeriod=2500 ] -tplinksmarthome:lb130:bulb2 "Living Room Bulb 2" [ deviceId="00000000000000000000000000000003", refresh=60, transitionPeriod=2500 ] +tplinksmarthome:hs100:tv "TV" [ deviceId="00000000000000000000000000000001", refresh=60 ] +tplinksmarthome:hs300:laptop "Laptop" [ deviceId="00000000000000000000000000000004", refresh=60 ] +tplinksmarthome:lb110:bulb1 "Living Room Bulb 1" [ deviceId="00000000000000000000000000000002", refresh=60, transitionPeriod=2500 ] +tplinksmarthome:lb130:bulb2 "Living Room Bulb 2" [ deviceId="00000000000000000000000000000003", refresh=60, transitionPeriod=2500 ] ``` ### tplinksmarthome.items: ``` -Switch TP_L_Switch "Switch" { channel="tplinksmarthome:hs100:tv:switch" } +Switch TP_L_TV "TV" { channel="tplinksmarthome:hs100:tv:switch" } +Switch TP_L_Laptop "Laptop" { channel="tplinksmarthome:hs300:laptop:outlet1#switch" } Number TP_L_RSSI "Signal [%d] dB" { channel="tplinksmarthome:hs100:tv:rssi" } Dimmer TP_LB_Bulb "Dimmer [%d %%]" { channel="tplinksmarthome:lb110:bulb1:brightness" } Dimmer TP_LB_ColorT "Color Temperature [%d] %%" { channel="tplinksmarthome:lb130:bulb2:colorTemperature" } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java index 16037e3e2b250..59594c3b437f1 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java @@ -13,6 +13,7 @@ package org.openhab.binding.tplinksmarthome.internal; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.HSBType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.openhab.binding.tplinksmarthome.internal.model.GetRealtime; @@ -43,10 +44,11 @@ @NonNullByDefault public class Commands { + private static final String CONTEXT = "{\"context\":{\"child_ids\":[\"%s\"]},"; private static final String SYSTEM_GET_SYSINFO = "\"system\":{\"get_sysinfo\":{}}"; private static final String GET_SYSINFO = "{" + SYSTEM_GET_SYSINFO + "}"; - private static final String GET_REALTIME_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO - + ", \"emeter\":{\"get_realtime\":{}}}"; + private static final String REALTIME = "\"emeter\":{\"get_realtime\":{}}"; + private static final String GET_REALTIME_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO + ", " + REALTIME + "}"; private static final String GET_REALTIME_BULB_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO + ", \"smartlife.iot.common.emeter\":{\"get_realtime\":{}}}"; @@ -71,6 +73,16 @@ public static String getRealtimeBulbAndSysinfo() { return GET_REALTIME_BULB_AND_SYSINFO; } + /** + * Returns the json to get the energy and sys info data from an outlet device. + * + * @param id optional id of the device + * @return The json string of the command to send to the device + */ + public static String getRealtimeWithContext(String id) { + return String.format(CONTEXT, id) + REALTIME + "}"; + } + /** * Returns the json response of the get_realtime command to the data object. * @@ -108,11 +120,15 @@ public Sysinfo getSysinfoReponse(String getSysinfoReponse) { * Returns the json for the set_relay_state command to switch on or off. * * @param onOff the switch state to set + * @param childId optional child id if multiple children are supported by a single device * @return The json string of the command to send to the device */ - public String setRelayState(OnOffType onOff) { + public String setRelayState(OnOffType onOff, @Nullable String childId) { SetRelayState relayState = new SetRelayState(); relayState.setRelayState(onOff); + if (childId != null) { + relayState.setChildId(childId); + } return gsonWithExpose.toJson(relayState); } @@ -122,7 +138,7 @@ public String setRelayState(OnOffType onOff) { * @param relayStateResponse the json string * @return The data object containing the state data from the json string */ - public SetRelayState setRelayStateResponse(String relayStateResponse) { + public @Nullable SetRelayState setRelayStateResponse(String relayStateResponse) { return gsonWithExpose.fromJson(relayStateResponse, SetRelayState.class); } @@ -144,7 +160,7 @@ public String setSwitchState(OnOffType onOff) { * @param switchStateResponse the json string * @return The data object containing the state data from the json string */ - public SetSwitchState setSwitchStateResponse(String switchStateResponse) { + public @Nullable SetSwitchState setSwitchStateResponse(String switchStateResponse) { return gsonWithExpose.fromJson(switchStateResponse, SetSwitchState.class); } @@ -166,7 +182,7 @@ public String setDimmerBrightness(int brightness) { * @param dimmerBrightnessResponse the json string * @return The data object containing the state data from the json string */ - public HasErrorResponse setDimmerBrightnessResponse(String dimmerBrightnessResponse) { + public @Nullable HasErrorResponse setDimmerBrightnessResponse(String dimmerBrightnessResponse) { return gsonWithExpose.fromJson(dimmerBrightnessResponse, SetBrightness.class); } @@ -190,11 +206,15 @@ public String setLightState(OnOffType onOff, int transitionPeriod) { * Returns the json for the set_led_off command to switch the led of the device on or off. * * @param onOff the led state to set + * @param childId optional child id if multiple children are supported by a single device * @return The json string of the command to send to the device */ - public String setLedOn(OnOffType onOff) { + public String setLedOn(OnOffType onOff, @Nullable String childId) { SetLedOff sLOff = new SetLedOff(); sLOff.setLed(onOff); + if (childId != null) { + sLOff.setChildId(childId); + } return gsonWithExpose.toJson(sLOff); } @@ -204,7 +224,7 @@ public String setLedOn(OnOffType onOff) { * @param setLedOnResponse the json string * @return The data object containing the data from the json string */ - public SetLedOff setLedOnResponse(String setLedOnResponse) { + public @Nullable SetLedOff setLedOnResponse(String setLedOnResponse) { return gsonWithExpose.fromJson(setLedOnResponse, SetLedOff.class); } @@ -268,7 +288,7 @@ public String setColorTemperature(int colorTemperature, int transitionPeriod) { * @param response the json string * @return The data object containing the state data from the json string */ - public TransitionLightStateResponse setTransitionLightStateResponse(String response) { + public @Nullable TransitionLightStateResponse setTransitionLightStateResponse(String response) { return gson.fromJson(response, TransitionLightStateResponse.class); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java index a734c4e3afb1d..76076d84758c8 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java @@ -67,7 +67,7 @@ public String sendCommand(String command) throws IOException { logger.trace("Executing command: {}", command); try (Socket socket = createSocket(); final OutputStream outputStream = socket.getOutputStream()) { outputStream.write(CryptUtil.encryptWithLength(command)); - String response = readReturnValue(socket); + final String response = readReturnValue(socket); logger.trace("Command response: {}", response); return response; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java index 728d3524ebe6f..7354234415488 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java @@ -29,7 +29,7 @@ public final class TPLinkSmartHomeBindingConstants { public static final String BINDING_ID = "tplinksmarthome"; - // List of all channel ids + // List of all switch channel ids public static final String CHANNEL_SWITCH = "switch"; // List of all plug channel ids @@ -60,6 +60,10 @@ public final class TPLinkSmartHomeBindingConstants { // List of all misc channel ids public static final String CHANNEL_RSSI = "rssi"; + // List of all group channel ids + public static final String CHANNEL_SWITCH_GROUP = "group"; + public static final String CHANNEL_OUTLET_GROUP_PREFIX = "outlet"; + // List of configuration keys public static final String CONFIG_IP = "ipAddress"; public static final String CONFIG_DEVICE_ID = "deviceId"; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java index a7235a0a6efcb..e8656a7df83b6 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java @@ -48,7 +48,7 @@ * @author Hilbrand Bouwkamp - Complete make-over, reorganized code and code cleanup. */ @Component(service = { DiscoveryService.class, - TPLinkIpAddressService.class }, immediate = true, configurationPid = "discovery.tplinksmarthome") +TPLinkIpAddressService.class }, immediate = true, configurationPid = "discovery.tplinksmarthome") @NonNullByDefault public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService implements TPLinkIpAddressService { @@ -69,7 +69,7 @@ public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService im public TPLinkSmartHomeDiscoveryService() throws UnknownHostException { super(SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS); InetAddress broadcast = InetAddress.getByName(BROADCAST_IP); - byte[] discoverbuffer = CryptUtil.encrypt(Commands.getSysinfo()); + final byte[] discoverbuffer = CryptUtil.encrypt(Commands.getSysinfo()); discoverPacket = new DatagramPacket(discoverbuffer, discoverbuffer.length, broadcast, Connection.TP_LINK_SMART_HOME_PORT); } @@ -106,7 +106,7 @@ protected void startScan() { if (discoverSocket == null) { break; } - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + final DatagramPacket packet = new DatagramPacket(buffer, buffer.length); discoverSocket.receive(packet); logger.debug("TP-Link Smart device discovery returned package with length {}", packet.getLength()); @@ -139,7 +139,8 @@ protected void stopScan() { * @throws IOException exception in case sending the packet failed */ protected DatagramSocket sendDiscoveryPacket() throws IOException { - DatagramSocket ds = new DatagramSocket(null); + final DatagramSocket ds = new DatagramSocket(null); + ds.setBroadcast(true); ds.setSoTimeout(UDP_PACKET_TIMEOUT_MS); ds.send(discoverPacket); @@ -165,24 +166,25 @@ private void closeDiscoverSocket() { * @throws IOException in case decrypting of the data failed */ private void detectThing(DatagramPacket packet) throws IOException { - String ipAddress = packet.getAddress().getHostAddress(); - String rawData = CryptUtil.decrypt(packet.getData(), packet.getLength()); - Sysinfo sysinfoRaw = commands.getSysinfoReponse(rawData); - Sysinfo sysinfo = sysinfoRaw.getActualSysinfo(); + final String ipAddress = packet.getAddress().getHostAddress(); + final String rawData = CryptUtil.decrypt(packet.getData(), packet.getLength()); + final Sysinfo sysinfoRaw = commands.getSysinfoReponse(rawData); + final Sysinfo sysinfo = sysinfoRaw.getActualSysinfo(); logger.trace("Detected TP-Link Smart Home device: {}", rawData); - String deviceId = sysinfo.getDeviceId(); + final String deviceId = sysinfo.getDeviceId(); logger.debug("TP-Link Smart Home device '{}' with id {} found on {} ", sysinfo.getAlias(), deviceId, ipAddress); idInetAddressCache.put(deviceId, ipAddress); - Optional thingTypeUID = getThingTypeUID(sysinfo.getModel()); + final Optional thingTypeUID = getThingTypeUID(sysinfo.getModel()); if (thingTypeUID.isPresent()) { - ThingUID thingUID = new ThingUID(thingTypeUID.get(), + final ThingUID thingUID = new ThingUID(thingTypeUID.get(), deviceId.substring(deviceId.length() - 6, deviceId.length())); - Map properties = PropertiesCollector.collectProperties(thingTypeUID.get(), ipAddress, + final Map properties = PropertiesCollector.collectProperties(thingTypeUID.get(), ipAddress, sysinfoRaw); - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(sysinfo.getAlias()) - .withRepresentationProperty(deviceId).withProperties(properties).build(); + final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withLabel(sysinfo.getAlias()).withRepresentationProperty(deviceId).withProperties(properties) + .build(); thingDiscovered(discoveryResult); } else { logger.debug("Detected, but ignoring unsupported TP-Link Smart Home device model '{}'", sysinfo.getModel()); @@ -196,7 +198,7 @@ private void detectThing(DatagramPacket packet) throws IOException { * @return {@link ThingTypeUID} or null if device not recognized */ private Optional getThingTypeUID(String model) { - String modelLC = model.toLowerCase(Locale.ENGLISH); + final String modelLC = model.toLowerCase(Locale.ENGLISH); return SUPPORTED_THING_TYPES.stream().filter(suid -> modelLC.startsWith(suid.getId())).findFirst(); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java index e25ebe580e3fb..5bb9eecfebea4 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java @@ -25,6 +25,7 @@ import org.openhab.binding.tplinksmarthome.internal.device.BulbDevice; import org.openhab.binding.tplinksmarthome.internal.device.DimmerDevice; import org.openhab.binding.tplinksmarthome.internal.device.EnergySwitchDevice; +import org.openhab.binding.tplinksmarthome.internal.device.PowerStripDevice; import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice; import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice; import org.openhab.binding.tplinksmarthome.internal.device.SwitchDevice; @@ -52,8 +53,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Nullable @Override protected ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - SmartHomeDevice device; + final ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + final SmartHomeDevice device; if (HS110.is(thingTypeUID)) { device = new EnergySwitchDevice(); @@ -63,6 +64,8 @@ protected ThingHandler createHandler(Thing thing) { device = new BulbDevice(thingTypeUID, COLOR_TEMPERATURE_LB130_MIN, COLOR_TEMPERATURE_LB130_MAX); } else if (LB120.is(thingTypeUID) || KL120.is(thingTypeUID)) { device = new BulbDevice(thingTypeUID, COLOR_TEMPERATURE_LB120_MIN, COLOR_TEMPERATURE_LB120_MAX); + } else if (TPLinkSmartHomeThingType.isStripDevice(thingTypeUID)) { + device = new PowerStripDevice(thingTypeUID); } else if (TPLinkSmartHomeThingType.isSwitchingDevice(thingTypeUID)) { device = new SwitchDevice(); } else if (TPLinkSmartHomeThingType.isBulbDevice(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java index 354e94bd27c2d..5e9a807c131cb 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java @@ -27,7 +27,7 @@ * */ @NonNullByDefault -enum TPLinkSmartHomeThingType { +public enum TPLinkSmartHomeThingType { // Bulb Thing Type UIDs KB100("kb100", DeviceType.BULB), @@ -56,6 +56,12 @@ enum TPLinkSmartHomeThingType { // Dimmer Thing Type UIDs HS220("hs220", DeviceType.DIMMER), + // Power Strip Thing Type UIDs. + HS107("hs107", DeviceType.STRIP), + HS300("hs300", DeviceType.STRIP), + KP200("kp200", DeviceType.STRIP), + KP400("kp400", DeviceType.STRIP), + // Range Extender Thing Type UIDs RE270K("re270", DeviceType.RANGE_EXTENDER), RE370K("re370", DeviceType.RANGE_EXTENDER); @@ -99,26 +105,38 @@ public static boolean isBulbDevice(ThingTypeUID thingTypeUID) { } /** - * Returns true if the given {@link ThingTypeUID} matches a device that supports the switching communication + * Returns true if the given {@link ThingTypeUID} matches a device that is a range extender. + * + * @param thingTypeUID if the check + * @return true if it's a range extender + */ + public static boolean isRangeExtenderDevice(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_LIST.stream().filter(t -> t.is(thingTypeUID)) + .anyMatch(t -> t.type == DeviceType.RANGE_EXTENDER); + } + + /** + * Returns true if the given {@link ThingTypeUID} matches a device that supports the power strip communication * protocol. * * @param thingTypeUID if the check - * @return true if it's a switching supporting device + * @return true if it's a power strip supporting device */ - public static boolean isSwitchingDevice(ThingTypeUID thingTypeUID) { + public static boolean isStripDevice(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_LIST.stream().filter(t -> t.is(thingTypeUID)) - .anyMatch(t -> t.type == DeviceType.PLUG || t.type == DeviceType.SWITCH); + .anyMatch(t -> t.type == DeviceType.STRIP); } /** - * Returns true if the given {@link ThingTypeUID} matches a device that is a range extender. + * Returns true if the given {@link ThingTypeUID} matches a device that supports the switching communication + * protocol. * * @param thingTypeUID if the check - * @return true if it's a range extender + * @return true if it's a switching supporting device */ - public static boolean isRangeExtenderDevice(ThingTypeUID thingTypeUID) { + public static boolean isSwitchingDevice(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_LIST.stream().filter(t -> t.is(thingTypeUID)) - .anyMatch(t -> t.type == DeviceType.RANGE_EXTENDER); + .anyMatch(t -> t.type == DeviceType.PLUG || t.type == DeviceType.SWITCH); } /** @@ -151,6 +169,10 @@ private enum DeviceType { * Wi-Fi range extender device with plug. */ RANGE_EXTENDER, + /** + * Power strip device. + */ + STRIP, /** * Switch device. */ diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java index 20b05acf52c4c..dea463b1a3e1f 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java @@ -22,13 +22,13 @@ import org.eclipse.smarthome.core.library.types.HSBType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.Connection; -import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse; import org.openhab.binding.tplinksmarthome.internal.model.LightState; import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightStateResponse; @@ -63,17 +63,17 @@ public String getUpdateCommand() { } @Override - public boolean handleCommand(String channelID, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { - int transitionPeriod = configuration.transitionPeriod; - HasErrorResponse response; + public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException { + final String channelId = channelUid.getId(); + final int transitionPeriod = configuration.transitionPeriod; + final HasErrorResponse response; if (command instanceof OnOffType) { - response = handleOnOffType(channelID, connection, (OnOffType) command, transitionPeriod); + response = handleOnOffType(channelId, connection, (OnOffType) command, transitionPeriod); } else if (command instanceof HSBType) { - response = handleHSBType(channelID, connection, (HSBType) command, transitionPeriod); + response = handleHSBType(channelId, connection, (HSBType) command, transitionPeriod); } else if (command instanceof DecimalType) { - response = handleDecimalType(channelID, connection, (DecimalType) command, transitionPeriod); + response = handleDecimalType(channelId, connection, (DecimalType) command, transitionPeriod); } else { return false; } @@ -103,7 +103,7 @@ private HasErrorResponse handleDecimalType(String channelID, Connection connecti return null; } - private TransitionLightStateResponse handleColorTemperature(Connection connection, int colorTemperature, + private @Nullable TransitionLightStateResponse handleColorTemperature(Connection connection, int colorTemperature, int transitionPeriod) throws IOException { return commands.setTransitionLightStateResponse( connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod))); @@ -120,11 +120,11 @@ private HasErrorResponse handleHSBType(String channelID, Connection connection, } @Override - public State updateChannel(String channelId, DeviceState deviceState) { - LightState lightState = deviceState.getSysinfo().getLightState(); + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + final LightState lightState = deviceState.getSysinfo().getLightState(); final State state; - switch (channelId) { + switch (channelUid.getId()) { case CHANNEL_BRIGHTNESS: state = lightState.getBrightness(); break; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java index 3541366ad74f1..9ba3383f378e0 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java @@ -20,6 +20,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.tplinksmarthome.internal.Connection; @@ -35,33 +36,34 @@ public class DimmerDevice extends SwitchDevice { @Override - protected @Nullable HasErrorResponse setOnOffState(Connection connection, OnOffType onOff) throws IOException { + protected @Nullable HasErrorResponse setOnOffState(Connection connection, ChannelUID channelUid, OnOffType onOff) + throws IOException { return commands.setSwitchStateResponse(connection.sendCommand(commands.setSwitchState(onOff))); } @Override - public boolean handleCommand(String channelId, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { - return CHANNEL_BRIGHTNESS.equals(channelId) - ? handleBrightnessChannel(channelId, connection, command, configuration) - : super.handleCommand(channelId, connection, command, configuration); + public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException { + return CHANNEL_BRIGHTNESS.equals(channelUid.getId()) + ? handleBrightnessChannel(channelUid, connection, command, configuration) + : super.handleCommand(channelUid, command); } /** * Handle the brightness channel. Because the device has different commands for setting the device on/off and * setting the brightness the on/off command must be send to the device as well when the brightness. * + * @param channelUid uid of the channel to handle * @param connection Connection to use * @param command command to the send * @return returns true if the command was handled * @throws IOException throws an {@link IOException} if the command handling failed */ - private boolean handleBrightnessChannel(String channelId, Connection connection, Command command, + private boolean handleBrightnessChannel(ChannelUID channelUid, Connection connection, Command command, TPLinkSmartHomeConfiguration configuration) throws IOException { HasErrorResponse response = null; if (command instanceof OnOffType) { - response = setOnOffState(connection, (OnOffType) command); + response = setOnOffState(connection, channelUid, (OnOffType) command); } else if (command instanceof PercentType) { PercentType percentCommand = (PercentType) command; @@ -70,7 +72,7 @@ private boolean handleBrightnessChannel(String channelId, Connection connection, response = commands.setDimmerBrightnessResponse( connection.sendCommand(commands.setDimmerBrightness(percentCommand.intValue()))); } else { - response = setOnOffState(connection, OnOffType.OFF); + response = setOnOffState(connection, channelUid, OnOffType.OFF); } } checkErrors(response); @@ -78,12 +80,12 @@ private boolean handleBrightnessChannel(String channelId, Connection connection, } @Override - public State updateChannel(String channelId, DeviceState deviceState) { - if (CHANNEL_BRIGHTNESS.equals(channelId)) { + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + if (CHANNEL_BRIGHTNESS.equals(channelUid.getId())) { return deviceState.getSysinfo().getRelayState() == OnOffType.OFF ? PercentType.ZERO : new PercentType(deviceState.getSysinfo().getBrightness()); } else { - return super.updateChannel(channelId, deviceState); + return super.updateChannel(channelUid, deviceState); } } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java index 33701d3cec2cd..18231c7ba8274 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.model.Realtime; @@ -34,21 +35,22 @@ public String getUpdateCommand() { } @Override - public State updateChannel(String channelId, DeviceState deviceState) { + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { final State state; + final String matchChannelId = channelUid.isInGroup() ? channelUid.getIdWithoutGroup() : channelUid.getId(); - if (CHANNELS_ENERGY.contains(channelId)) { - state = updateEnergyChannel(channelId, deviceState.getRealtime()); + if (CHANNELS_ENERGY.contains(matchChannelId)) { + state = updateEnergyChannel(matchChannelId, deviceState.getRealtime()); } else { - state = super.updateChannel(channelId, deviceState); + state = super.updateChannel(channelUid, deviceState); } return state; } - private State updateEnergyChannel(String channelId, Realtime realtime) { + protected State updateEnergyChannel(String matchChannelId, Realtime realtime) { final double value; - switch (channelId) { + switch (matchChannelId) { case CHANNEL_ENERGY_CURRENT: value = realtime.getCurrent(); break; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.java new file mode 100644 index 0000000000000..640991ebd639c --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tplinksmarthome.internal.device; + +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.HS300; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.tplinksmarthome.internal.Commands; +import org.openhab.binding.tplinksmarthome.internal.model.Realtime; +import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo.Outlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * TP-Link Smart Home device with multiple sockets, like the HS107 and HS300. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class PowerStripDevice extends EnergySwitchDevice { + + private final Logger logger = LoggerFactory.getLogger(PowerStripDevice.class); + + private final List<@Nullable Realtime> realTimeCacheList; + private final List<@Nullable String> childIds; + + public PowerStripDevice(ThingTypeUID thingTypeUID) { + final int nrOfSockets; + + if (HS300.is(thingTypeUID)) { + nrOfSockets = 6; + } else { + nrOfSockets = 2; + } + realTimeCacheList = new ArrayList<>(nrOfSockets); + childIds = new ArrayList<>(Collections.nCopies(nrOfSockets, "")); + } + + @Override + public String getUpdateCommand() { + return Commands.getSysinfo(); + } + + @Override + public void refreshedDeviceState(@Nullable DeviceState deviceState) { + if (deviceState != null) { + for (int i = 0; i < childIds.size(); i++) { + childIds.set(i, deviceState.getSysinfo().getChildren().get(i).getId()); + realTimeCacheList.add(i, refreshCache(i)); + } + } + } + + @Override + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + final int idx = channelToIndex(channelUid); + + if (idx >= 0 && idx < childIds.size()) { + final Outlet outlet = deviceState.getSysinfo().getChildren().get(idx); + final String baseChannel = channelUid.getIdWithoutGroup(); + + if (CHANNEL_SWITCH.equals(baseChannel)) { + return outlet.getState(); + } else if (CHANNELS_ENERGY.contains(baseChannel)) { + final Realtime realTime = realTimeCacheList.get(idx); + + return realTime == null ? UnDefType.UNDEF : updateEnergyChannel(baseChannel, realTime); + } + } else { + if (idx >= 0) { + logger.debug("For channel update the index '{}' could be mapped to a channel. passed channel: {}", idx, + channelUid); + } + } + return super.updateChannel(channelUid, deviceState); + } + + @Override + protected @Nullable String getChildId(ChannelUID channelUid) { + final int idx = channelToIndex(channelUid); + + return idx >= 0 && idx < childIds.size() ? childIds.get(idx) : null; + } + + private int channelToIndex(final ChannelUID channelUid) { + final String groupId = channelUid.getGroupId(); + + return (groupId != null && groupId.startsWith(CHANNEL_OUTLET_GROUP_PREFIX) + ? Integer.parseInt(groupId.substring(CHANNEL_OUTLET_GROUP_PREFIX.length())) + : 0) - 1; + } + + private @Nullable Realtime refreshCache(int idx) { + try { + final String childId = childIds.get(idx); + + return childId == null ? null + : commands.getRealtimeResponse(connection.sendCommand(Commands.getRealtimeWithContext(childId))); + } catch (IOException e) { + return null; + } catch (RuntimeException e) { + logger.debug( + "Obtaining realtime data for channel-'{}' unexpectedly crashed. If this keeps happening please report: ", + idx, e); + return null; + } + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java index be684e6335e77..e96561bfe3882 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java @@ -14,10 +14,10 @@ import java.io.IOException; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.tplinksmarthome.internal.Connection; import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState; @@ -36,7 +36,7 @@ protected State getOnOffState(DeviceState deviceState) { } @Override - protected @Nullable SetRelayState setOnOffState(@NonNull Connection connection, @NonNull OnOffType onOff) + protected @Nullable SetRelayState setOnOffState(Connection connection, ChannelUID channelUid, OnOffType onOff) throws IOException { // It's unknown what the command is to send to the device so it's not supported. return null; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java index 9e663fb220675..1ba75b526099d 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.tplinksmarthome.internal.Commands; @@ -32,7 +33,9 @@ @NonNullByDefault public abstract class SmartHomeDevice { - protected Commands commands = new Commands(); + protected final Commands commands = new Commands(); + protected @NonNullByDefault({}) Connection connection; + protected @NonNullByDefault({}) TPLinkSmartHomeConfiguration configuration; /** * Checks if the response object contains errors and if so throws an {@link IOException} when an error code was set. @@ -41,13 +44,23 @@ public abstract class SmartHomeDevice { * @throws IOException if an error code was set in the response object */ protected void checkErrors(@Nullable HasErrorResponse response) throws IOException { - ErrorResponse errorResponse = response == null ? null : response.getErrorResponse(); + final ErrorResponse errorResponse = response == null ? null : response.getErrorResponse(); if (errorResponse != null && errorResponse.getErrorCode() != 0) { throw new IOException("Error (" + errorResponse.getErrorCode() + "): " + errorResponse.getErrorMessage()); } } + /** + * + * @param connection The connection to the device + * @param configuration The global configuration + */ + public void initialize(Connection connection, TPLinkSmartHomeConfiguration configuration) { + this.connection = connection; + this.configuration = configuration; + } + /** * @return the json string to send to the device to get the state of the device. */ @@ -56,23 +69,27 @@ protected void checkErrors(@Nullable HasErrorResponse response) throws IOExcepti /** * Handle the command for the given channel * - * @param channelID The channel the command is for - * @param connection The connection to the device + * @param channelUID The channel the command is for * @param command The command to be send to the device - * @param configuration The global configuration * @return Returns true if the commands successfully was send to the device * @throws IOException In case of communications error or the device returned an error */ - public abstract boolean handleCommand(String channelID, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException; + public abstract boolean handleCommand(ChannelUID channelUID, Command command) throws IOException; /** * Returns the {@link State} value for the given value extracted from the deviceState data. * - * @param channelId channel to get state for + * @param channelUid channel to get state for * @param deviceState state object containing the state * @return {@link State} value for the given channel */ - public abstract State updateChannel(String channelId, DeviceState deviceState); + public abstract State updateChannel(ChannelUID channelUid, DeviceState deviceState); + /** + * Called with the new device state after the new device state is retrieved from the device. + * + * @param deviceState new device state + */ + public void refreshedDeviceState(@Nullable DeviceState deviceState) { + } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java index ae5fe31d42264..22f38a31b3313 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java @@ -19,12 +19,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.Connection; -import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse; /** @@ -41,9 +41,8 @@ public String getUpdateCommand() { } @Override - public boolean handleCommand(String channelID, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { - return command instanceof OnOffType && handleOnOffType(channelID, connection, (OnOffType) command); + public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException { + return command instanceof OnOffType && handleOnOffType(channelUid, connection, (OnOffType) command); } /** @@ -56,13 +55,17 @@ protected State getOnOffState(DeviceState deviceState) { return deviceState.getSysinfo().getRelayState(); } - private boolean handleOnOffType(String channelID, Connection connection, OnOffType onOff) throws IOException { - HasErrorResponse response = null; + private boolean handleOnOffType(ChannelUID channelUid, Connection connection, OnOffType onOff) throws IOException { + final HasErrorResponse response; + final String baseChannelId = getBaseChannel(channelUid); - if (CHANNEL_SWITCH.equals(channelID)) { - response = setOnOffState(connection, onOff); - } else if (CHANNEL_LED.equals(channelID)) { - response = commands.setLedOnResponse(connection.sendCommand(commands.setLedOn(onOff))); + if (CHANNEL_SWITCH.contentEquals(baseChannelId)) { + response = setOnOffState(connection, channelUid, onOff); + } else if (CHANNEL_LED.contentEquals(baseChannelId)) { + response = commands + .setLedOnResponse(connection.sendCommand(commands.setLedOn(onOff, getChildId(channelUid)))); + } else { + response = null; } checkErrors(response); return response != null; @@ -72,20 +75,41 @@ private boolean handleOnOffType(String channelID, Connection connection, OnOffTy * Sends the {@link OnOffType} command to the device and returns the returned answer. * * @param connection Connection to use + * @param channelUid channel Id to use to determine child id * @param onOff command to the send * @return state returned by the device + * @throws IOException exception in case device not reachable */ - protected @Nullable HasErrorResponse setOnOffState(Connection connection, OnOffType onOff) throws IOException { - return commands.setRelayStateResponse(connection.sendCommand(commands.setRelayState(onOff))); + protected @Nullable HasErrorResponse setOnOffState(Connection connection, ChannelUID channelUid, OnOffType onOff) + throws IOException { + return commands + .setRelayStateResponse(connection.sendCommand(commands.setRelayState(onOff, getChildId(channelUid)))); } @Override - public State updateChannel(String channelId, DeviceState deviceState) { - if (CHANNEL_SWITCH.equals(channelId)) { + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + final String baseChannelId = getBaseChannel(channelUid); + + if (CHANNEL_SWITCH.equals(baseChannelId)) { return getOnOffState(deviceState); - } else if (CHANNEL_LED.equals(channelId)) { + } else if (CHANNEL_LED.equals(baseChannelId)) { return deviceState.getSysinfo().getLedOff(); } return UnDefType.UNDEF; } + + /** + * Returns the child Id for the given channel if the device supports children and it's a channel for a specific + * child. + * + * @param channelUid channel Id to get the child id for + * @return null or child id + */ + protected @Nullable String getChildId(ChannelUID channelUid) { + return null; + } + + private String getBaseChannel(ChannelUID channelUid) { + return channelUid.isInGroup() ? channelUid.getIdWithoutGroup() : channelUid.getId(); + } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java index 67da055cd810f..70b51efd2d590 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java @@ -61,7 +61,7 @@ public class SmartHomeHandler extends BaseThingHandler { private @NonNullByDefault({}) ExpiringCache<@Nullable DeviceState> cache; /** - * Constructor + * Constructor. * * @param thing The thing to handle * @param smartHomeDevice Specific Smart Home device handler @@ -74,12 +74,12 @@ public SmartHomeHandler(Thing thing, SmartHomeDevice smartHomeDevice, TPLinkIpAd } @Override - public void handleCommand(ChannelUID channelUID, Command command) { + public void handleCommand(ChannelUID channelUid, Command command) { try { if (command instanceof RefreshType) { - updateChannelState(channelUID, cache.getValue()); - } else if (!smartHomeDevice.handleCommand(channelUID.getId(), connection, command, configuration)) { - logger.debug("Command {} is not supported for channel: {}", command, channelUID.getId()); + updateChannelState(channelUid, cache.getValue()); + } else if (!smartHomeDevice.handleCommand(channelUid, command)) { + logger.debug("Command {} is not supported for channel: {}", command, channelUid.getId()); } } catch (IOException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); @@ -102,8 +102,10 @@ public void initialize() { "No ip address or the device id configured."); return; } - logger.debug("Initializing TP-Link Smart device on ip {}", configuration.ipAddress); + logger.debug("Initializing TP-Link Smart device on ip '{}' or deviceId '{}' ", configuration.ipAddress, + configuration.deviceId); connection = createConnection(configuration); + smartHomeDevice.initialize(connection, configuration); cache = new ExpiringCache<@Nullable DeviceState>(TimeUnit.SECONDS.toMillis(configuration.refresh), this::refreshCache); updateStatus(ThingStatus.UNKNOWN); @@ -123,12 +125,12 @@ Connection createConnection(TPLinkSmartHomeConfiguration config) { return new Connection(config.ipAddress); } - @Nullable - private DeviceState refreshCache() { + private @Nullable DeviceState refreshCache() { try { updateIpAddress(); - DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand())); + final DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand())); updateDeviceId(deviceState.getSysinfo().getDeviceId()); + smartHomeDevice.refreshedDeviceState(deviceState); if (getThing().getStatus() != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); } @@ -197,8 +199,7 @@ private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) { void refreshChannels() { logger.trace("Update Channels for:{}", thing.getUID()); - DeviceState value = cache.getValue(); - getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), value)); + getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), cache.getValue())); } /** @@ -209,7 +210,10 @@ void refreshChannels() { * */ private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState deviceState) { - String channelId = channelUID.getId(); + if (!isLinked(channelUID)) { + return; + } + String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId(); final State state; if (deviceState == null) { @@ -217,7 +221,7 @@ private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState dev } else if (CHANNEL_RSSI.equals(channelId)) { state = new DecimalType(deviceState.getSysinfo().getRssi()); } else { - state = smartHomeDevice.updateChannel(channelId, deviceState); + state = smartHomeDevice.updateChannel(channelUID, deviceState); } updateState(channelUID, state); } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.java new file mode 100644 index 0000000000000..2505abf96a537 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tplinksmarthome.internal.model; + +import com.google.gson.annotations.Expose; + +/** + * Class to be extended by state classes that support context. i.e. has multiple children that can be controlled. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +public class ContextState { + + public static class Context { + @Expose + private String[] childIds = new String[1]; + + public void setChildId(String childId) { + this.childIds[0] = childId; + } + + @Override + public String toString() { + return " child_ids:[" + childIds[0] + "]"; + } + } + + @Expose + private Context context; + + public void setChildId(String childId) { + context = new Context(); + context.setChildId(childId); + } + + @Override + public String toString() { + return context == null ? "" : context.toString(); + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java index 4343c1f743a90..e83640941fe55 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java @@ -23,7 +23,7 @@ * @author Hilbrand Bouwkamp - Initial contribution */ @NonNullByDefault -public class GetRealtime { +public class GetRealtime extends ContextState { public static class EMeter { private Realtime getRealtime = new Realtime(); diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java index e219d804e72df..bb7724c86313b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java @@ -17,6 +17,7 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ +@FunctionalInterface public interface HasErrorResponse { /** * @return returns the object containing the error response diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java index 49e57b6754316..6093859d71a25 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java @@ -22,7 +22,7 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class SetLedOff implements HasErrorResponse { +public class SetLedOff extends ContextState implements HasErrorResponse { public static class LedOff extends ErrorResponse { @Expose diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java index 498ea8cd0c374..5033ccffe50d5 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java @@ -22,7 +22,7 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class SetRelayState implements HasErrorResponse { +public class SetRelayState extends ContextState implements HasErrorResponse { public static class RelayState extends ErrorResponse { @Expose(deserialize = false) @@ -38,6 +38,10 @@ public static class System { @Expose private RelayState setRelayState = new RelayState(); + public void setRelayState(OnOffType onOff) { + setRelayState.state = onOff == OnOffType.ON ? 1 : 0; + } + @Override public String toString() { return "set_relay_state:{" + setRelayState + "}"; @@ -53,7 +57,7 @@ public ErrorResponse getErrorResponse() { } public void setRelayState(OnOffType onOff) { - system.setRelayState.state = onOff == OnOffType.ON ? 1 : 0; + system.setRelayState(onOff); } @Override diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java index 03acec812b1f4..5e28ce3189d02 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java @@ -12,6 +12,9 @@ */ package org.openhab.binding.tplinksmarthome.internal.model; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.smarthome.core.library.types.OnOffType; import com.google.gson.annotations.SerializedName; @@ -80,6 +83,32 @@ public OnOffType getRelayStatus() { } } + /** + * Status of a single outlet on power strip. + */ + public static class Outlet { + private String alias; + private String id; + private long onTime; + private int state; + + public String getAlias() { + return alias; + } + + public String getId() { + return id; + } + + public long getOnTime() { + return onTime; + } + + public OnOffType getState() { + return state == 1 ? OnOffType.ON : OnOffType.OFF; + } + } + /** * Status of the range extended Wi-Fi. */ @@ -122,6 +151,10 @@ public int getW2gRssi() { private double latitude; private double longitude; + // powerstrip/multiple plugs support. + private int childNum; + private List children = new ArrayList<>(); + // dimmer specific system info private int brightness; @@ -251,6 +284,14 @@ public Plug getPlug() { return plug; } + public int getChildNum() { + return childNum; + } + + public List getChildren() { + return children; + } + public Sysinfo getSystem() { return system; } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml new file mode 100644 index 0000000000000..452caec2571de --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml @@ -0,0 +1,26 @@ + + + + + + TP-Link HS107 Smart Wi-Fi Plug, 2-Outlets + PowerOutlet + + + + + + + + + + + + deviceId + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml index c7542b31de0cc..79a36c47f0f5a 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml @@ -14,7 +14,7 @@ - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml new file mode 100644 index 0000000000000..57779f37a8925 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml @@ -0,0 +1,39 @@ + + + + + + TP-Link HS300 Smart Wi-Fi Power Strip + PowerOutlet + + + + + + + + + + + + + + + + + + + + + + + + deviceId + + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml new file mode 100644 index 0000000000000..0a96fb11f526b --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml @@ -0,0 +1,26 @@ + + + + + + TP-Link KP200 Smart Wi-Fi Power Outlet, 2-Sockets + PowerOutlet + + + + + + + + + + + + deviceId + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml new file mode 100644 index 0000000000000..d2fd8122dc7ba --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml @@ -0,0 +1,26 @@ + + + + + + TP-Link KP400 Smart Outdoor Plug + PowerOutlet + + + + + + + + + + + + deviceId + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml index 008810abd6852..9f10a59173d0b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml @@ -10,7 +10,7 @@ PowerOutlet - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml index efb63a052763e..19838ad31c8eb 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml @@ -10,7 +10,7 @@ PowerOutlet - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml index 871d48d660413..c8931f35f1d54 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - + Switch Shows the switch state of the Smart Home device. @@ -27,7 +27,7 @@ Energy - + Number Actual energy usage. @@ -58,4 +58,32 @@ + + + + PowerOutlet + + + + + + + + + PowerOutlet + + + + + + + PowerOutlet + + + + + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.java new file mode 100644 index 0000000000000..f36c1be9e0329 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tplinksmarthome.internal; + +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingUID; + +/** + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public final class ChannelUIDConstants { + + public static final ChannelUID CHANNEL_UID_BRIGHTNESS = createChannel(LB130, CHANNEL_BRIGHTNESS); + public static final ChannelUID CHANNEL_UID_COLOR = createChannel(LB130, CHANNEL_COLOR); + public static final ChannelUID CHANNEL_UID_COLOR_TEMPERATURE = createChannel(LB130, CHANNEL_COLOR_TEMPERATURE); + public static final ChannelUID CHANNEL_UID_ENERGY_CURRENT = createChannel(HS110, CHANNEL_ENERGY_CURRENT); + public static final ChannelUID CHANNEL_UID_ENERGY_POWER = createChannel(HS110, CHANNEL_ENERGY_POWER); + public static final ChannelUID CHANNEL_UID_ENERGY_TOTAL = createChannel(HS110, CHANNEL_ENERGY_TOTAL); + public static final ChannelUID CHANNEL_UID_ENERGY_VOLTAGE = createChannel(HS110, CHANNEL_ENERGY_VOLTAGE); + public static final ChannelUID CHANNEL_UID_LED = createChannel(HS100, CHANNEL_LED); + public static final ChannelUID CHANNEL_UID_OTHER = createChannel(HS100, "OTHER"); + public static final ChannelUID CHANNEL_UID_RSSI = createChannel(HS100, CHANNEL_RSSI); + public static final ChannelUID CHANNEL_UID_SWITCH = createChannel(HS100, CHANNEL_SWITCH); + + private static final String ID = "1234"; + + private ChannelUIDConstants() { + // Util class + } + + private static ChannelUID createChannel(TPLinkSmartHomeThingType thingType, String channelId) { + return new ChannelUID(new ThingUID(thingType.thingTypeUID(), ID), channelId); + } + + public static ChannelUID createChannel(TPLinkSmartHomeThingType thingType, String groupId, String channelId) { + return new ChannelUID(new ThingUID(thingType.thingTypeUID(), ID), groupId, channelId); + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java index 19652da9b94db..16fdd2b884699 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java @@ -21,11 +21,8 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.junit.Test; import org.openhab.binding.tplinksmarthome.internal.model.GetSysinfo; -import org.openhab.binding.tplinksmarthome.internal.model.GsonUtil; import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; -import com.google.gson.Gson; - /** * Test class for {@link PropertiesCollector} class. * @@ -33,8 +30,6 @@ */ public class PropertiesCollectorTest { - private final Gson gson = GsonUtil.createGson(); - /** * Tests if properties for a bulb device are correctly parsed. * @@ -69,7 +64,7 @@ private void assertProperties(@NonNull String responseFile, @NonNull TPLinkSmart int expectedSize) throws IOException { ThingTypeUID thingTypeUID = thingType.thingTypeUID(); Map props = PropertiesCollector.collectProperties(thingTypeUID, "localhost", - ModelTestUtil.toJson(gson, responseFile, GetSysinfo.class).getSysinfo()); + ModelTestUtil.jsonFromFile(responseFile, GetSysinfo.class).getSysinfo()); assertEquals("Number of properties not as expected for properties: " + props, expectedSize, props.size()); props.entrySet().stream().forEach( diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java index 39e344702a615..6283714d14992 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java @@ -13,15 +13,17 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.LB130; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.HSBType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; -import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.types.UnDefType; import org.junit.Test; import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; @@ -31,15 +33,14 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class BulbDeviceTest extends DeviceTestBase { +@NonNullByDefault +public class BulbDeviceTest extends DeviceTestBase { private static final String DEVICE_OFF = "bulb_get_sysinfo_response_off"; - private final BulbDevice device = new BulbDevice(new ThingTypeUID(BINDING_ID, "lb130"), COLOR_TEMPERATURE_LB130_MIN, - COLOR_TEMPERATURE_LB130_MAX); - public BulbDeviceTest() throws IOException { - super("bulb_get_sysinfo_response_on"); + super(new BulbDevice(LB130.thingTypeUID(), COLOR_TEMPERATURE_LB130_MIN, COLOR_TEMPERATURE_LB130_MAX), + "bulb_get_sysinfo_response_on"); } @Override @@ -52,106 +53,107 @@ public void setUp() throws IOException { public void testHandleCommandBrightness() throws IOException { assertInput("bulb_transition_light_state_brightness"); assertTrue("Brightness channel should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, new PercentType(33), configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(33))); } @Test public void testHandleCommandBrightnessOnOff() throws IOException { assertInput("bulb_transition_light_state_on"); assertTrue("Brightness channel with OnOff state should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON)); } @Test public void testHandleCommandColor() throws IOException { assertInput("bulb_transition_light_state_color"); - assertTrue("Color channel should be handled", - device.handleCommand(CHANNEL_COLOR, connection, new HSBType("55,44,33"), configuration)); + assertTrue("Color channel should be handled", device.handleCommand(CHANNEL_UID_COLOR, new HSBType("55,44,33"))); } public void testHandleCommandColorBrightness() throws IOException { assertInput("bulb_transition_light_state_brightness"); assertTrue("Color channel with Percentage state (=brightness) should be handled", - device.handleCommand(CHANNEL_COLOR, connection, new PercentType(33), configuration)); + device.handleCommand(CHANNEL_UID_COLOR, new PercentType(33))); } public void testHandleCommandColorOnOff() throws IOException { assertInput("bulb_transition_light_state_on"); assertTrue("Color channel with OnOff state should be handled", - device.handleCommand(CHANNEL_COLOR, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_COLOR, OnOffType.ON)); } @Test public void testHandleCommandColorTemperature() throws IOException { assertInput("bulb_transition_light_state_color_temp"); assertTrue("Color temperature channel should be handled", - device.handleCommand(CHANNEL_COLOR_TEMPERATURE, connection, new PercentType(40), configuration)); + device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, new PercentType(40))); } @Test public void testHandleCommandColorTemperatureOnOff() throws IOException { assertInput("bulb_transition_light_state_on"); assertTrue("Color temperature channel with OnOff state should be handled", - device.handleCommand(CHANNEL_COLOR_TEMPERATURE, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, OnOffType.ON)); } @Test public void testHandleCommandSwitch() throws IOException { assertInput("bulb_transition_light_state_on"); - assertTrue("Switch channel should be handled", - device.handleCommand(CHANNEL_SWITCH, connection, OnOffType.ON, configuration)); + assertTrue("Switch channel should be handled", device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON)); } @Test public void testUpdateChannelBrightnessOn() { assertEquals("Brightness should be on", new PercentType(92), - device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)); + device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)); } @Test public void testUpdateChannelBrightnessOff() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF)); assertEquals("Brightness should be off", PercentType.ZERO, - device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)); + device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)); } @Test public void testUpdateChannelColorOn() { - assertEquals("Color should be on", new HSBType("7,44,92"), device.updateChannel(CHANNEL_COLOR, deviceState)); + assertEquals("Color should be on", new HSBType("7,44,92"), + device.updateChannel(CHANNEL_UID_COLOR, deviceState)); } @Test public void testUpdateChannelColorOff() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF)); - assertEquals("Color should be off", new HSBType("7,44,0"), device.updateChannel(CHANNEL_COLOR, deviceState)); + assertEquals("Color should be off", new HSBType("7,44,0"), + device.updateChannel(CHANNEL_UID_COLOR, deviceState)); } @Test public void testUpdateChannelSwitchOn() { - assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelSwitchOff() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF)); - assertSame("Switch should be off", OnOffType.OFF, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be off", OnOffType.OFF, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelColorTemperature() { assertEquals("Color temperature should be set", new PercentType(3), - device.updateChannel(CHANNEL_COLOR_TEMPERATURE, deviceState)); + device.updateChannel(CHANNEL_UID_COLOR_TEMPERATURE, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } @Test public void testUpdateChannelPower() { assertEquals("Power values should be set", new DecimalType(10.8), - device.updateChannel(CHANNEL_ENERGY_POWER, deviceState)); + device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java index cb5ec2ff99ef3..b93e60783d1b6 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java @@ -22,8 +22,10 @@ import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.Before; import org.mockito.Mock; import org.openhab.binding.tplinksmarthome.internal.Connection; @@ -36,28 +38,31 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class DeviceTestBase { +@NonNullByDefault +public class DeviceTestBase { - @NonNull + protected final T device; protected final Connection connection; - @NonNull protected final TPLinkSmartHomeConfiguration configuration = new TPLinkSmartHomeConfiguration(); - protected DeviceState deviceState; + protected @NonNullByDefault({}) DeviceState deviceState; private final String deviceStateFilename; @Mock - private Socket socket; + private @NonNullByDefault({}) Socket socket; @Mock - private OutputStream outputStream; + private @NonNullByDefault({}) OutputStream outputStream; /** * Constructor. * + * @param device Device under test * @param deviceStateFilename name of the file to read the device state json from to use in tests + * * @throws IOException exception in case device not reachable */ - public DeviceTestBase(@NonNull String deviceStateFilename) throws IOException { + protected DeviceTestBase(T device, String deviceStateFilename) throws IOException { + this.device = device; this.deviceStateFilename = deviceStateFilename; configuration.ipAddress = "localhost"; configuration.refresh = 30; @@ -68,6 +73,7 @@ protected Socket createSocket() throws IOException { return socket; }; }; + device.initialize(connection, configuration); } @Before @@ -103,15 +109,21 @@ protected void setSocketReturnAssert(@NonNull String... responseFilenames) throw * @param filenames names of the files containing the reference json * @throws IOException exception in case device not reachable */ - protected void assertInput(@NonNull String... filename) throws IOException { + protected void assertInput(@NonNull String... filenames) throws IOException { + assertInput(Function.identity(), Function.identity(), filenames); + } + + protected void assertInput(Function jsonProcessor, Function expectedProcessor, + @NonNull String... filenames) throws IOException { AtomicInteger index = new AtomicInteger(); doAnswer(arg -> { - String json = ModelTestUtil.readJson(filename[index.get()]); + String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()])); byte[] input = (byte[]) arg.getArguments()[0]; try (ByteArrayInputStream inputStream = new ByteArrayInputStream(input)) { - assertEquals(filename[index.get()], json, CryptUtil.decryptWithLength(inputStream)); + String expectedString = expectedProcessor.apply(CryptUtil.decryptWithLength(inputStream)); + assertEquals(filenames[index.get()], json, expectedString); } index.incrementAndGet(); return null; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java index 97cd710cffd42..7c3ef0ea0aaf5 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java @@ -13,10 +13,11 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CHANNEL_BRIGHTNESS; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.types.UnDefType; @@ -28,22 +29,21 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class DimmerDeviceTest extends DeviceTestBase { +@NonNullByDefault +public class DimmerDeviceTest extends DeviceTestBase { private static final PercentType BRIGHTNESS_VALUE = new PercentType(50); - private final DimmerDevice device = new DimmerDevice(); - public DimmerDeviceTest() throws IOException { - super("hs220_get_sysinfo_response_on"); + super(new DimmerDevice(), "hs220_get_sysinfo_response_on"); } @Test public void testHandleCommandBrightnessOnOff() throws IOException { assertInput("dimmer_set_switch_state_on"); - setSocketReturnAssert("dimmer_set_switch_state_response"); + setSocketReturnAssert("dimmer_set_switch_state_on"); assertTrue("Brightness channel as OnOffType type should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON)); } @Test @@ -51,7 +51,7 @@ public void testHandleCommandBrightnessZero() throws IOException { assertInput("dimmer_set_switch_state_off"); setSocketReturnAssert("dimmer_set_switch_state_response"); assertTrue("Brightness channel with percentage 0 should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, PercentType.ZERO, configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, PercentType.ZERO)); } @Test @@ -59,7 +59,7 @@ public void testHandleCommandBrightness() throws IOException { assertInput("dimmer_set_brightness"); setSocketReturnAssert("dimmer_set_brightness_response"); assertTrue("Brightness channel should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, new PercentType(17), configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(17))); } @Test @@ -67,17 +67,18 @@ public void testUpdateChannelSwitch() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson("hs220_get_sysinfo_response_off")); assertSame("Dimmer device should be off", OnOffType.OFF, - ((PercentType) device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)).as(OnOffType.class)); + ((PercentType) device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)).as(OnOffType.class)); } @Test public void testUpdateChannelBrightness() { assertEquals("Dimmer brightness should be set", BRIGHTNESS_VALUE, - device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)); + device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java index a3bf32795abfc..7f3517718223b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java @@ -13,7 +13,7 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; import java.util.Arrays; @@ -56,18 +56,18 @@ public static List data() { @Test public void testUpdateChannelEnergyCurrent() { assertEquals("Energy current should have valid state value", 1, - ((DecimalType) device.updateChannel(CHANNEL_ENERGY_CURRENT, deviceState)).intValue()); + ((DecimalType) device.updateChannel(CHANNEL_UID_ENERGY_CURRENT, deviceState)).intValue()); } @Test public void testUpdateChannelEnergyTotal() { assertEquals("Energy total should have valid state value", 10, - ((DecimalType) device.updateChannel(CHANNEL_ENERGY_TOTAL, deviceState)).intValue()); + ((DecimalType) device.updateChannel(CHANNEL_UID_ENERGY_TOTAL, deviceState)).intValue()); } @Test public void testUpdateChannelEnergyVoltage() { - State state = device.updateChannel(CHANNEL_ENERGY_VOLTAGE, deviceState); + State state = device.updateChannel(CHANNEL_UID_ENERGY_VOLTAGE, deviceState); assertEquals("Energy voltage should have valid state value", 230, ((DecimalType) state).intValue()); assertEquals("Channel patten to display as int", "230 V", state.format("%.0f V")); } @@ -75,12 +75,13 @@ public void testUpdateChannelEnergyVoltage() { @Test public void testUpdateChanneEnergyPowerl() { assertEquals("Energy power should have valid state value", 20, - ((DecimalType) device.updateChannel(CHANNEL_ENERGY_POWER, deviceState)).intValue()); + ((DecimalType) device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState)).intValue()); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.java new file mode 100644 index 0000000000000..884d711fc4809 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tplinksmarthome.internal.device; + +import static org.junit.Assert.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.HS300; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants; +import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; +import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState; + +/** + * Test class for {@link PowerStripDevice} class. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class PowerStripDeviceTest extends DeviceTestBase { + + private static final ChannelUID CHANNEL_OUTLET_1 = ChannelUIDConstants.createChannel(HS300, + CHANNEL_OUTLET_GROUP_PREFIX + '1', CHANNEL_SWITCH); + private static final ChannelUID CHANNEL_OUTLET_2 = ChannelUIDConstants.createChannel(HS300, + CHANNEL_OUTLET_GROUP_PREFIX + '2', CHANNEL_SWITCH); + private static final ChannelUID CHANNEL_ENERGY_CURRENT_OUTLET_2 = ChannelUIDConstants.createChannel(HS300, + CHANNEL_OUTLET_GROUP_PREFIX + '2', CHANNEL_ENERGY_CURRENT); + private static final String[] REALTIME_INPUTS = IntStream.range(0, 6).mapToObj(i -> "hs300_get_realtime") + .collect(Collectors.toList()).toArray(new String[0]); + private static final String[] REALTIME_RESPONSES = IntStream.range(0, 6).mapToObj(i -> "plug_get_realtime_response") + .collect(Collectors.toList()).toArray(new String[0]); + + public PowerStripDeviceTest() throws IOException { + super(new PowerStripDevice(HS300.thingTypeUID()), "hs300_get_sysinfo_response"); + } + + @Override + @Before + public void setUp() throws IOException { + super.setUp(); + final AtomicInteger inputCounter = new AtomicInteger(0); + final Function inputWrapper = s -> s.replace("001", "00" + inputCounter.incrementAndGet()); + + assertInput(inputWrapper, Function.identity(), REALTIME_INPUTS); + setSocketReturnAssert(REALTIME_RESPONSES); + device.refreshedDeviceState(deviceState); + } + + @Test + public void testHandleCommandSwitchChannel2() throws IOException { + Function normalize = s -> ModelTestUtil.GSON + .toJson(ModelTestUtil.GSON.fromJson(s, SetRelayState.class)); + assertInput(normalize, normalize, "hs300_set_relay_state"); + setSocketReturnAssert("hs300_set_relay_state_response"); + assertTrue("Outlet channel 2 should be handled", device.handleCommand(CHANNEL_OUTLET_2, OnOffType.ON)); + } + + @Test + public void testUpdateChannelOutlet1() { + assertSame("Outlet 1 should be on", OnOffType.ON, device.updateChannel(CHANNEL_OUTLET_1, deviceState)); + } + + @Test + public void testUpdateChannelOutlet2() { + assertSame("Outlet 2 should be off", OnOffType.OFF, device.updateChannel(CHANNEL_OUTLET_2, deviceState)); + } + + @Test + public void testUpdateChannelEnergyCurrent() { + assertEquals("Energy current should have valid state value", 1, + ((DecimalType) device.updateChannel(CHANNEL_ENERGY_CURRENT_OUTLET_2, deviceState)).intValue()); + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java index 177df0a4f890e..01c72ae239a9b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java @@ -13,13 +13,13 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.UnDefType; -import org.junit.Ignore; import org.junit.Test; /** @@ -27,41 +27,33 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class RangeExtenderDeviceTest extends DeviceTestBase { - private final RangeExtenderDevice device = new RangeExtenderDevice(); +@NonNullByDefault +public class RangeExtenderDeviceTest extends DeviceTestBase { public RangeExtenderDeviceTest() throws IOException { - super("rangeextender_get_sysinfo_response"); + super(new RangeExtenderDevice(), "rangeextender_get_sysinfo_response"); } @Test public void testHandleCommandSwitch() throws IOException { assertFalse("Switch channel not yet supported so should not be handled", - device.handleCommand(CHANNEL_SWITCH, connection, OnOffType.ON, configuration)); - } - - @Ignore("Ignore handle led command because led support in binding not (yet) implemented.") - @Test - public void testHandleCommandLed() throws IOException { - // assertInput("rangeextender_set_led_on"); - // setSocketReturnAssert("rangeextender_set_led_on"); - assertFalse("Led channel should be handled", - device.handleCommand(CHANNEL_LED, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON)); } @Test public void testUpdateChannelSwitch() { - assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelLed() { - assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_LED, deviceState)); + assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_LED, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java index bcdfb04dc23a9..c045a4c319e27 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java @@ -13,10 +13,11 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.UnDefType; import org.junit.Test; @@ -26,42 +27,40 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class SwitchDeviceTest extends DeviceTestBase { - - private final SwitchDevice device = new SwitchDevice(); +@NonNullByDefault +public class SwitchDeviceTest extends DeviceTestBase { public SwitchDeviceTest() throws IOException { - super("plug_get_sysinfo_response"); + super(new SwitchDevice(), "plug_get_sysinfo_response"); } @Test public void testHandleCommandSwitch() throws IOException { assertInput("plug_set_relay_state_on"); setSocketReturnAssert("plug_set_relay_state_on"); - assertTrue("Switch channel should be handled", - device.handleCommand(CHANNEL_SWITCH, connection, OnOffType.ON, configuration)); + assertTrue("Switch channel should be handled", device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON)); } @Test public void testHandleCommandLed() throws IOException { assertInput("plug_set_led_on"); setSocketReturnAssert("plug_set_led_on"); - assertTrue("Led channel should be handled", - device.handleCommand(CHANNEL_LED, connection, OnOffType.ON, configuration)); + assertTrue("Led channel should be handled", device.handleCommand(CHANNEL_UID_LED, OnOffType.ON)); } @Test public void testUpdateChannelSwitch() { - assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelLed() { - assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_LED, deviceState)); + assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_LED, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java index 54da03d5f550d..fa95ff0027e6c 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java @@ -16,6 +16,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_SWITCH; import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; import java.io.IOException; @@ -37,6 +38,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; +import org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants; import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.Connection; import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; @@ -51,8 +53,6 @@ */ public class SmartHomeHandlerTest { - private static final String CHANNEL_PREFIX = "binding:tplinksmarthome:1234:"; - private SmartHomeHandler handler; @Mock @@ -83,7 +83,8 @@ Connection createConnection(TPLinkSmartHomeConfiguration config) { return connection; } }; - when(smartHomeDevice.handleCommand(eq(CHANNEL_SWITCH), eq(connection), any(), any())).thenReturn(true); + when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true); + when(callback.isChannelLinked(any())).thenReturn(true); handler.setCallback(callback); } @@ -118,7 +119,7 @@ public void testHandleCommandRefreshTypeRangeExtender() throws IOException { private void assertHandleCommandRefreshType(int expectedRssi) { handler.initialize(); - ChannelUID channelUID = new ChannelUID(CHANNEL_PREFIX + CHANNEL_RSSI); + ChannelUID channelUID = ChannelUIDConstants.CHANNEL_UID_RSSI; handler.handleCommand(channelUID, RefreshType.REFRESH); ArgumentCaptor stateCaptor = ArgumentCaptor.forClass(State.class); verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture()); @@ -128,8 +129,8 @@ private void assertHandleCommandRefreshType(int expectedRssi) { @Test public void testHandleCommandOther() throws InterruptedException { handler.initialize(); - ChannelUID channelUID = new ChannelUID(CHANNEL_PREFIX + CHANNEL_SWITCH); - Mockito.doReturn(OnOffType.ON).when(smartHomeDevice).updateChannel(eq(channelUID.getId()), any()); + ChannelUID channelUID = ChannelUIDConstants.CHANNEL_UID_SWITCH; + Mockito.doReturn(OnOffType.ON).when(smartHomeDevice).updateChannel(eq(channelUID), any()); handler.handleCommand(channelUID, RefreshType.REFRESH); ArgumentCaptor stateCaptor = ArgumentCaptor.forClass(State.class); verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture()); diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java index 906e017377d95..cbf44bfc425c1 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java @@ -28,6 +28,8 @@ @NonNullByDefault public final class ModelTestUtil { + public static final Gson GSON = GsonUtil.createGson(); + private ModelTestUtil() { // Util class } @@ -42,8 +44,9 @@ private ModelTestUtil() { * @return instance of clazz with read data from json file * @throws IOException when file could not be read. */ - public static T toJson(Gson gson, String filename, Class clazz) throws IOException { - return gson.fromJson(readJson(filename), clazz); + public static T jsonFromFile(String filename, Class clazz) throws IOException { + return GSON.fromJson(readJson(filename), clazz); + } /** @@ -60,4 +63,5 @@ public static String readJson(String filename) throws IOException { .toString(ModelTestUtil.class.getResourceAsStream(filename + ".json"), StandardCharsets.UTF_8.name()) .replaceAll("[\n\r\t ]", ""); } + } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json new file mode 100644 index 0000000000000..fe1c17fba8307 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json @@ -0,0 +1 @@ +{"context": {"child_ids": ["00000000000000000000000000000001"]}, "emeter": {"get_realtime": {}}} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json new file mode 100644 index 0000000000000..fe1c17fba8307 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json @@ -0,0 +1 @@ +{"context": {"child_ids": ["00000000000000000000000000000001"]}, "emeter": {"get_realtime": {}}} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json new file mode 100644 index 0000000000000..e6228e293cd36 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json @@ -0,0 +1,78 @@ +{ + "system": { + "get_sysinfo": { + "alias": "powerstip", + "child_num": 6, + "children": [ + { + "alias": "plug 1", + "id": "00000000000000000000000000000001", + "next_action": { + "type": -1 + }, + "on_time": 12345, + "state": 1 + }, + { + "alias": "plug 2", + "id": "00000000000000000000000000000002", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 3", + "id": "00000000000000000000000000000003", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 4", + "id": "00000000000000000000000000000004", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 5", + "id": "00000000000000000000000000000005", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 6", + "id": "00000000000000000000000000000006", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + } + ], + "deviceId": "00000000000000000000000000000000", + "feature": "TIM:ENE", + "hwId": "00000000000000000000000000000000", + "hw_ver": "1.0", + "latitude_i": 0, + "led_off": 0, + "longitude_i": -0, + "mac": "00:00:00:00:00:00", + "mic_type": "IOT.SMARTPLUGSWITCH", + "model": "HS300(US)", + "oemId": "00000000000000000000000000000000", + "rssi": -10, + "sw_ver": "1.0.6 Build 180627 Rel.081000", + "updating": 0 + } + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json new file mode 100644 index 0000000000000..3253ea2003bd8 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json @@ -0,0 +1 @@ +{"context":{"child_ids":["00000000000000000000000000000002"]},"system":{"set_relay_state":{"state":1}}} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json new file mode 100644 index 0000000000000..3253ea2003bd8 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json @@ -0,0 +1 @@ +{"context":{"child_ids":["00000000000000000000000000000002"]},"system":{"set_relay_state":{"state":1}}}