diff --git a/addons/binding/org.openhab.binding.tplinksmarthome.test/org.openhab.binding.tplinksmarthome.test.launch b/addons/binding/org.openhab.binding.tplinksmarthome.test/org.openhab.binding.tplinksmarthome.test.launch index 6b2e945fa756d..5b2e3a41cc75c 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome.test/org.openhab.binding.tplinksmarthome.test.launch +++ b/addons/binding/org.openhab.binding.tplinksmarthome.test/org.openhab.binding.tplinksmarthome.test.launch @@ -34,7 +34,7 @@ - + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome.test/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java b/addons/binding/org.openhab.binding.tplinksmarthome.test/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java index 7c3e7e0322222..6721ca54cb894 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome.test/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome.test/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java @@ -24,7 +24,6 @@ import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusInfo; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; @@ -37,6 +36,7 @@ import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.Connection; import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; +import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeDiscoveryService; import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice; import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; @@ -49,7 +49,7 @@ public class SmartHomeHandlerTest { private static final String CHANNEL_PREFIX = "binding:tplinksmarthome:1234:"; - private ThingHandler handler; + private SmartHomeHandler handler; @Mock private Connection connection; @@ -59,9 +59,10 @@ public class SmartHomeHandlerTest { private Thing thing; @Mock private SmartHomeDevice smartHomeDevice; + @Mock + private TPLinkSmartHomeDiscoveryService discoveryService; - @NonNull - private final Configuration configuration = new Configuration(); + private @NonNull final Configuration configuration = new Configuration(); @Before public void setUp() throws IOException { @@ -72,7 +73,7 @@ public void setUp() throws IOException { when(smartHomeDevice.getUpdateCommand()).thenReturn(Commands.getSysinfo()); when(connection.sendCommand(Commands.getSysinfo())) .thenReturn(ModelTestUtil.readJson("plug_get_sysinfo_response")); - handler = new SmartHomeHandler(thing, smartHomeDevice) { + handler = new SmartHomeHandler(thing, smartHomeDevice, discoveryService) { @Override Connection createConnection(TPLinkSmartHomeConfiguration config) { return connection; @@ -128,4 +129,10 @@ public void testHandleCommandOther() throws InterruptedException { verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture()); assertSame("State of channel switch should be set", OnOffType.ON, stateCaptor.getValue()); } + + @Test + public void testRefreshChannels() { + handler.initialize(); + handler.refreshChannels(); + } } diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/config/config.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/config/config.xml index 37d53786cdeb5..f9b269618d581 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/config/config.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/config/config.xml @@ -1,15 +1,20 @@ - - + network-address IP Address of the device. + + + The id of the device. + Time the transition to the new state takes in milliseconds. @@ -25,11 +30,15 @@ - + network-address IP Address of the device. + + + The id of the device. + Refresh of device state in seconds. @@ -39,11 +48,15 @@ - + network-address IP Address of the device. + + + The id of the device. + Refresh of device state in seconds. diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS100.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS100.xml index 27c974afa3eaa..04230d432f6ca 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS100.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS100.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS105.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS105.xml index 6f5ae375876f3..59cfaee710cd6 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS105.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS105.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS110.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS110.xml index f486aad5f84db..d2c16eaab58a6 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS110.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS110.xml @@ -19,6 +19,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS200.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS200.xml index 2570a7a0cbe92..0e997da95eb6f 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS200.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS200.xml @@ -14,6 +14,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS210.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS210.xml index 8b1aef6d9cd6d..a547f2e234d3c 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS210.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS210.xml @@ -14,6 +14,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS220.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS220.xml index bb0a465a4f77b..6ebabd0e8c911 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS220.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/HS220.xml @@ -14,6 +14,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB100.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB100.xml index 7d3f8e59ac19a..d465dd57b4e66 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB100.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB100.xml @@ -14,6 +14,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB130.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB130.xml index c89a79d675bc4..b21c662ff5450 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB130.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KB130.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KP100.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KP100.xml index f1aa4914354ab..cf390516acb24 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KP100.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/KP100.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB100.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB100.xml index 9a949bfe99184..db1499a868f8c 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB100.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB100.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB110.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB110.xml index 85fe44e49da2c..73f00c208b349 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB110.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB110.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB120.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB120.xml index 625a226812cbc..336eaea64d184 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB120.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB120.xml @@ -16,6 +16,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB130.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB130.xml index d13d1a1fe372c..b6390f453163c 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB130.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB130.xml @@ -16,6 +16,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB200.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB200.xml index d77943b372ea4..f3e9b389e6102 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB200.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB200.xml @@ -15,6 +15,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB230.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB230.xml index 7733d21045fa1..195506a1721ea 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB230.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/LB230.xml @@ -16,6 +16,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE270K.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE270K.xml index 6d39cfc523783..d087170a4b8b0 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE270K.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE270K.xml @@ -14,6 +14,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE370K.xml b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE370K.xml index cd08a36298acf..af8f8f0a341cb 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE370K.xml +++ b/addons/binding/org.openhab.binding.tplinksmarthome/ESH-INF/thing/RE370K.xml @@ -14,6 +14,8 @@ + deviceId + diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.tplinksmarthome/META-INF/MANIFEST.MF index 33db88c9352b0..7909c8d41099d 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/META-INF/MANIFEST.MF +++ b/addons/binding/org.openhab.binding.tplinksmarthome/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Import-Package: com.google.gson, com.google.gson.annotations, com.google.gson.reflect, com.google.gson.stream, + org.eclipse.jetty.util, org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.core.status, diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/README.md b/addons/binding/org.openhab.binding.tplinksmarthome/README.md index 09c1e81de8842..7ae715fac874c 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/README.md +++ b/addons/binding/org.openhab.binding.tplinksmarthome/README.md @@ -91,7 +91,6 @@ Switching and Brightness is done using the `brightness` channel. Switching, Brightness and Color is done using the `color` channel. - ### HS100 Smart Wi-Fi Plug * Switch On/Off @@ -113,9 +112,21 @@ Switching, Brightness and Color is done using the `color` channel. * Switch On/Off * Wi-Fi signal strength (rssi) -The default refresh is set to 1 second. So it polls the switch for status changes. If you don't use the switch manually -often, you can set it to a higher refresh. The refresh is only relevant to detect manual using the switch. Switching -via openHAB activates the switch directly. +### HS210 Smart Wi-Fi Light Switch 3-Way Kit + +* Switch On/Off +* Wi-Fi signal strength (rssi) + +### HS220 Smart Wi-Fi Light Switch, Dimmer + +* Switch On/Off +* Brightness +* Wi-Fi signal strength (rssi) + +The default refresh is set to 1 second. So it polls the switch for status changes. +If you don't use the switch manually often, you can set it to a higher refresh. +The refresh is only relevant to detect manual using the switch. +Switching via openHAB activates the switch directly. ### RE270K AC750 Wi-Fi Range Extender with Smart Plug @@ -129,59 +140,72 @@ via openHAB activates the switch directly. ## Prerequisites -Before using Smart Plugs with openHAB the devices must be connected to the Wi-Fi network. This can be done using the -TP-Link provided mobile app Kasa. +Before using Smart Plugs with openHAB the devices must be connected to the Wi-Fi network. +This can be done using the TP-Link provided mobile app Kasa. ## Discovery -Devices can be auto discovered in the same local network as the openHAB application. It's possible to connect to -devices in a different network, but these must be added manually. +Devices can be auto discovered in the same local network as the openHAB application. +It's possible to connect to devices on a different network, but these must be added manually by ipAddress. +It's not possible to connect to devices on a different network using `deviceId` as configuration. ## Thing Configuration -The thing id is the product type in lower case. For example `HS100` has id `hs100`. +The thingId is the product type in lower case. For example `HS100` has thingId `hs100`. -The thing has a few configuration parameters: +The thing can be configured by `ipAddress` or by `deviceId`. +If the one of them is used the other is automatically set by the binding. +When manually configured it's preferred to set the `deviceId` because if the ip address of the device would change this will be automatically updated. +Using a configuration with `deviceId` depends on the discovery service of the binding. +The binding supports background discovery and this will update the ip address in case it changes within a minute. +With background discovery disabled the ip address, which is needed to communicate with the device, needs to be set by starting a manual discovery. +It won't update the ip address if background discovery is disabled and the ip address of the device changes. +Manually starting a discovery can also be used to set the ip address directly instead of waiting for the 1 minute background discovery refresh period. + +The thing has the following configuration parameters: | Parameter | Description | |--------------------|-----------------------------------------------------------------------------| -| ipAddress | IP Address of the device. Mandatory. | +| deviceId | The id of the device. | +| ipAddress | IP Address of the device. | | refresh | Refresh interval in seconds. Optional, the default value is 30 seconds. | | transitionPeriod | Duration of state changes in milliseconds, only for light bulbs, default 0. | +Either `deviceId` or `ipAddress` must be set. + ## Channels 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. | KP100, HS100, HS105, HS110, HS200, RE270K, RE370K | -| brightness | Dimmer | Set the brightness of Smart Home light. | KB100, LB100, LB110, LB120, LB200 | -| colorTemperature | Dimmer | Set the color temperature of Smart Home light. | KB130, LB120, LB130, LB230 | -| color | Color | Set the color of the Smart Home light. | KB130, LB130, LB230 | -| power | Number | Actual energy usage in Watt. | HS110, LBxxx | -| eneryUsage | Number | Energy Usage in kWh. | HS110 | -| current | Number | Actual current usage in Ampere. | HS110 | -| voltage | Number | Actual voltage usage in Volt. | HS110 | -| 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. | KP100, HS100, HS105, HS110, HS200, HS210, HS220, RE270K, RE370K | +| brightness | Dimmer | Set the brightness of Smart Home device. | KB100, LB100, LB110, LB120, LB200, HS220 | +| colorTemperature | Dimmer | Set the color temperature of Smart Home light. | KB130, LB120, LB130, LB230 | +| color | Color | Set the color of the Smart Home light. | KB130, LB130, LB230 | +| power | Number | Actual energy usage in Watt. | HS110, LBxxx | +| eneryUsage | Number | Energy Usage in kWh. | HS110 | +| current | Number | Actual current usage in Ampere. | HS110 | +| voltage | Number | Actual voltage usage in Volt. | HS110 | +| rssi | Number | Wi-Fi signal strength indicator in dBm. | All | ## Full Example ### tplinksmarthome.things: ``` -tplinksmarthome:hs100:home "Living Room" [ ipAddress="192.168.0.13", refresh=60 ] -tplinksmarthome:lb110:home "Living Room Bulb 1" [ ipAddress="192.168.0.14", refresh=60, transitionPeriod=2500 ] -tplinksmarthome:lb130:home "Living Room Bulb 2" [ ipAddress="192.168.0.15", refresh=60, transitionPeriod=2500 ] +tplinksmarthome:hs100:home "Living Room" [ deviceId="00000000000000000000000000000001", refresh=60 ] +tplinksmarthome:lb110:home "Living Room Bulb 1" [ deviceId="00000000000000000000000000000002", refresh=60, transitionPeriod=2500 ] +tplinksmarthome:lb130:home "Living Room Bulb 2" [ deviceId="00000000000000000000000000000003", refresh=60, transitionPeriod=2500 ] ``` ### tplinksmarthome.items: ``` -Switch TP_L_Switch "Switch" { channel="tplinksmarthome:hs100:home:switch" } -Number TP_L_RSSI "Signal [%d] dB" { channel="tplinksmarthome:hs100:home:rssi" } -Dimmer TP_LB_Bulb "Dimmer [%d %%]" { channel="tplinksmarthome:lb110:home:brightness" } +Switch TP_L_Switch "Switch" { channel="tplinksmarthome:hs100:home:switch" } +Number TP_L_RSSI "Signal [%d] dB" { channel="tplinksmarthome:hs100:home:rssi" } +Dimmer TP_LB_Bulb "Dimmer [%d %%]" { channel="tplinksmarthome:lb110:home:brightness" } Dimmer TP_LB_ColorT "Color Temperature [%d] %%" { channel="tplinksmarthome:lb130:home:colorTemperature" } -Color TP_LB_Color "Color" { channel="tplinksmarthome:lb130:home:color" } -Switch TP_LB_ColorS "Switch" { channel="tplinksmarthome:lb130:home:color" } +Color TP_LB_Color "Color" { channel="tplinksmarthome:lb130:home:color" } +Switch TP_LB_ColorS "Switch" { channel="tplinksmarthome:lb130:home:color" } ``` diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java index 2bc2ccf6c2dd3..6eab19638b53b 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java @@ -15,6 +15,7 @@ import java.net.UnknownHostException; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,18 +28,27 @@ @NonNullByDefault public class Connection { - private Logger logger = LoggerFactory.getLogger(Connection.class); + public static final int TP_LINK_SMART_HOME_PORT = 9999; - public static final int SMART_PLUG_PORT = 9999; + private Logger logger = LoggerFactory.getLogger(Connection.class); - private final String ipAddress; + private @Nullable String ipAddress; /** * Initializes a connection to the given ip address. * * @param ipAddress ip address of the connection */ - public Connection(String ipAddress) { + public Connection(@Nullable String ipAddress) { + this.ipAddress = ipAddress; + } + + /** + * Set the ip address to connect to. + * + * @param ipAddress The ip address to connect to + */ + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } @@ -78,9 +88,12 @@ private String readReturnValue(Socket socket) throws IOException { * * @return new Socket instance * @throws UnknownHostException exception in case the host could not be determined - * @throws IOException exception in case device not reachable + * @throws IOException exception in case device not reachable */ protected Socket createSocket() throws UnknownHostException, IOException { - return new Socket(ipAddress, SMART_PLUG_PORT); + if (ipAddress == null) { + throw new IOException("Ip address not set. Wait for discovery or manually trigger discovery process."); + } + return new Socket(ipAddress, TP_LINK_SMART_HOME_PORT); } } diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollector.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollector.java index bf9463ca97aa4..67ba63d013290 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollector.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollector.java @@ -33,8 +33,8 @@ private PropertiesCollector() { * Collect all properties of the thing from the {@link Sysinfo} object. * * @param thingTypeUID thing to get the properties for - * @param ipAddress ip address of the device - * @param sysinfo system info data returned from the device + * @param ipAddress ip address of the device + * @param sysinfo system info data returned from the device * @return map of properties */ public static Map collectProperties(ThingTypeUID thingTypeUID, String ipAddress, Sysinfo sysinfo) { @@ -58,11 +58,11 @@ public static Map collectProperties(ThingTypeUID thingTypeUID, S * Collect generic properties. * * @param properties properties object to store properties in - * @param sysinfo system info data returned from the device + * @param sysinfo system info data returned from the device */ private static void collectProperties(Map properties, Sysinfo sysinfo) { + putNonNull(properties, CONFIG_DEVICE_ID, sysinfo.getDeviceId()); putNonNull(properties, PROPERTY_MODEL, sysinfo.getModel()); - putNonNull(properties, PROPERTY_DEVICE_ID, sysinfo.getDeviceId()); putNonNull(properties, PROPERTY_HARDWARE_VERSION, sysinfo.getHwVer()); putNonNull(properties, PROPERTY_SOFWARE_VERSION, sysinfo.getSwVer()); putNonNull(properties, PROPERTY_HARDWARE_ID, sysinfo.getHwId()); @@ -73,7 +73,7 @@ private static void collectProperties(Map properties, Sysinfo sy * Collect Smart Bulb specific properties. * * @param properties properties object to store properties in - * @param sysinfo system info data returned from the device + * @param sysinfo system info data returned from the device */ private static void collectPropertiesBulb(Map properties, Sysinfo sysinfo) { putNonNull(properties, PROPERTY_TYPE, sysinfo.getType()); @@ -86,7 +86,7 @@ private static void collectPropertiesBulb(Map properties, Sysinf * Collect Smart Range Extender specific properties. * * @param properties properties object to store properties in - * @param sysinfo system info data returned from the device + * @param sysinfo system info data returned from the device */ private static void collectPropertiesRangeExtender(Map properties, Sysinfo sysinfo) { Sysinfo system = sysinfo.getSystem(); @@ -101,7 +101,7 @@ private static void collectPropertiesRangeExtender(Map propertie * Collect Smart Switch specific properties. * * @param properties properties object to store properties in - * @param sysinfo system info data returned from the device + * @param sysinfo system info data returned from the device */ private static void collectPropertiesOther(Map properties, Sysinfo sysinfo) { putNonNull(properties, PROPERTY_TYPE, sysinfo.getType()); diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkIpAddressService.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkIpAddressService.java new file mode 100644 index 0000000000000..5f1493014591a --- /dev/null +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkIpAddressService.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.tplinksmarthome.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Service to get the actual ip of a TP-Link Smart Home device as registered on the network. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public interface TPLinkIpAddressService { + + /** + * Returns the last known ip address of the given device id. If no ip address known null is returned. + * + * @param deviceId id of the device to get the ip address of + * @return ip address or null + */ + @Nullable + String getLastKnownIpAddress(String deviceId); +} diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java index 8deed6238ef6c..e70deb62e6fd1 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java @@ -58,6 +58,7 @@ public final class TPLinkSmartHomeBindingConstants { // List of configuration keys public static final String CONFIG_IP = "ipAddress"; + public static final String CONFIG_DEVICE_ID = "deviceId"; public static final String CONFIG_REFRESH = "refresh"; // Only for bulbs public static final String CONFIG_TRANSITION_PERIOD = "transitionPeriod"; @@ -67,7 +68,6 @@ public final class TPLinkSmartHomeBindingConstants { public static final String PROPERTY_MODEL = "model"; public static final String PROPERTY_DEVICE_NAME = "device name"; public static final String PROPERTY_MAC = "mac"; - public static final String PROPERTY_DEVICE_ID = "device id"; public static final String PROPERTY_HARDWARE_VERSION = "hardware version"; public static final String PROPERTY_SOFWARE_VERSION = "sofware version"; public static final String PROPERTY_HARDWARE_ID = "hardware id"; diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeConfiguration.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeConfiguration.java index 4835ba7e5bd23..208a9ac62e88d 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeConfiguration.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeConfiguration.java @@ -20,10 +20,15 @@ public class TPLinkSmartHomeConfiguration { */ public String ipAddress; + /** + * The id of the device; + */ + public String deviceId; + /** * Refresh rate for the device in seconds. */ - public Integer refresh; + public int refresh; /** * Transition period of light bulb state changes in seconds. diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java index 2b23e89f433a9..9c2126b868a23 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java @@ -16,12 +16,15 @@ import java.net.InetAddress; import java.net.SocketTimeoutException; import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; import java.util.Locale; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; @@ -40,28 +43,36 @@ * @author Christian Fischer - Initial contribution * @author Hilbrand Bouwkamp - Complete make-over, reorganized code and code cleanup. */ -@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.tplinksmarthome") -public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService { +@Component(service = { DiscoveryService.class, + TPLinkIpAddressService.class }, immediate = true, configurationPid = "discovery.tplinksmarthome") +@NonNullByDefault +public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService implements TPLinkIpAddressService { private static final String BROADCAST_IP = "255.255.255.255"; - private static final int DISCOVERY_TIMEOUT_SECONDS = 20; - private static final int UDP_PACKET_TIMEOUT = 1000; - private static final long REFRESH_INTERVAL_MINUTES = 10; + private static final int DISCOVERY_TIMEOUT_SECONDS = 30; + private static final int UDP_PACKET_TIMEOUT_MS = 3000; + private static final long REFRESH_INTERVAL_MINUTES = 1; private final Logger logger = LoggerFactory.getLogger(TPLinkSmartHomeDiscoveryService.class); private final Commands commands = new Commands(); + private final Map idInetAddressCache = new ConcurrentHashMap<>(); private final DatagramPacket discoverPacket; private final byte[] buffer = new byte[2048]; - private DatagramSocket discoverSocket; - private ScheduledFuture discoveryJob; + private @NonNullByDefault({}) DatagramSocket discoverSocket; + private @NonNullByDefault({}) ScheduledFuture discoveryJob; public TPLinkSmartHomeDiscoveryService() throws UnknownHostException { - super(SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS, false); + super(SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS); InetAddress broadcast = InetAddress.getByName(BROADCAST_IP); byte[] discoverbuffer = CryptUtil.encrypt(Commands.getSysinfo()); discoverPacket = new DatagramPacket(discoverbuffer, discoverbuffer.length, broadcast, - Connection.SMART_PLUG_PORT); + Connection.TP_LINK_SMART_HOME_PORT); + } + + @Override + public @Nullable String getLastKnownIpAddress(String deviceId) { + return idInetAddressCache.get(deviceId); } @Override @@ -81,20 +92,6 @@ protected void stopBackgroundDiscovery() { @Override protected void startScan() { logger.debug("Start scan for TP-Link Smart devices."); - discoverThings(); - } - - @Override - protected void stopScan() { - logger.debug("Stop scan for TP-Link Smart devices."); - closeDiscoverSocket(); - super.stopScan(); - } - - /** - * Performs the discovery of TP-Link Smart Home devices. - */ - private void discoverThings() { synchronized (this) { try { discoverSocket = sendDiscoveryPacket(); @@ -123,6 +120,13 @@ private void discoverThings() { } } + @Override + protected void stopScan() { + logger.debug("Stop scan for TP-Link Smart devices."); + closeDiscoverSocket(); + super.stopScan(); + } + /** * Opens a {@link DatagramSocket} and sends a packet for discovery of TP-Link Smart Home devices. * @@ -132,11 +136,9 @@ private void discoverThings() { protected DatagramSocket sendDiscoveryPacket() throws IOException { DatagramSocket ds = new DatagramSocket(null); ds.setBroadcast(true); - ds.setSoTimeout(UDP_PACKET_TIMEOUT); + ds.setSoTimeout(UDP_PACKET_TIMEOUT_MS); ds.send(discoverPacket); - if (logger.isTraceEnabled()) { - logger.trace("Discovery package sent: {}", new String(discoverPacket.getData(), StandardCharsets.UTF_8)); - } + logger.trace("Discovery package sent."); return ds; } @@ -166,14 +168,16 @@ private void detectThing(DatagramPacket packet) throws IOException { logger.trace("Detected TP-Link Smart Home device: {}", rawData); 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()); if (thingTypeUID.isPresent()) { ThingUID thingUID = new ThingUID(thingTypeUID.get(), deviceId.substring(deviceId.length() - 6, deviceId.length())); + Map properties = PropertiesCollector.collectProperties(thingTypeUID.get(), ipAddress, + sysinfoRaw); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(sysinfo.getAlias()) - .withProperties(PropertiesCollector.collectProperties(thingTypeUID.get(), ipAddress, sysinfoRaw)) - .build(); + .withRepresentationProperty(deviceId).withProperties(properties).build(); thingDiscovered(discoveryResult); } else { logger.debug("Detected, but ignoring unsupported TP-Link Smart Home device model '{}'", sysinfo.getModel()); diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java index 4ef83cb48a368..13df79738b4be 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java @@ -26,6 +26,7 @@ import org.openhab.binding.tplinksmarthome.internal.device.SwitchDevice; import org.openhab.binding.tplinksmarthome.internal.handler.SmartHomeHandler; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * The {@link TPLinkSmartHomeHandlerFactory} is responsible for creating things and thing handlers. @@ -37,6 +38,8 @@ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.tplinksmarthome") public class TPLinkSmartHomeHandlerFactory extends BaseThingHandlerFactory { + private @NonNullByDefault({}) TPLinkIpAddressService ipAddressService; + @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES.contains(thingTypeUID); @@ -65,6 +68,15 @@ protected ThingHandler createHandler(Thing thing) { } else { return null; } - return new SmartHomeHandler(thing, device); + return new SmartHomeHandler(thing, device, ipAddressService); + } + + @Reference + protected void setTPLinkIpAddressCache(TPLinkIpAddressService ipAddressCache) { + this.ipAddressService = ipAddressCache; + } + + protected void unsetTPLinkIpAddressCache(TPLinkIpAddressService ipAddressCache) { + this.ipAddressService = null; } } diff --git a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java index 5f762c77f3a70..f0cbf49e180d9 100644 --- a/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java +++ b/addons/binding/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java @@ -8,13 +8,16 @@ */ package org.openhab.binding.tplinksmarthome.internal.handler; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CHANNEL_RSSI; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; import java.io.IOException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.cache.ExpiringCache; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -27,6 +30,7 @@ import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.tplinksmarthome.internal.Connection; +import org.openhab.binding.tplinksmarthome.internal.TPLinkIpAddressService; import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; import org.openhab.binding.tplinksmarthome.internal.device.DeviceState; import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice; @@ -39,26 +43,30 @@ * @author Christian Fischer - Initial contribution * @author Hilbrand Bouwkamp - Rewrite to generic TP-Link Smart Home Handler */ +@NonNullByDefault public class SmartHomeHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(SmartHomeHandler.class); private final SmartHomeDevice smartHomeDevice; + private final TPLinkIpAddressService ipAddressService; - private TPLinkSmartHomeConfiguration configuration; - private Connection connection; - private ScheduledFuture refreshJob; - private ExpiringCache cache; + private @NonNullByDefault({}) TPLinkSmartHomeConfiguration configuration; + private @NonNullByDefault({}) Connection connection; + private @NonNullByDefault({}) ScheduledFuture refreshJob; + private @NonNullByDefault({}) ExpiringCache<@Nullable DeviceState> cache; /** * Constructor * - * @param thing the thing to handle + * @param thing The thing to handle * @param smartHomeDevice Specific Smart Home device handler + * @param ipAddressService Cache keeping track of ip addresses of tp link devices */ - public SmartHomeHandler(@NonNull Thing thing, @NonNull SmartHomeDevice smartHomeDevice) { + public SmartHomeHandler(Thing thing, SmartHomeDevice smartHomeDevice, TPLinkIpAddressService ipAddressService) { super(thing); this.smartHomeDevice = smartHomeDevice; + this.ipAddressService = ipAddressService; } @Override @@ -70,7 +78,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Command {} is not supported for channel: {}", command, channelUID.getId()); } } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } @@ -85,9 +93,15 @@ public void dispose() { @Override public void initialize() { configuration = getConfigAs(TPLinkSmartHomeConfiguration.class); + if (StringUtil.isBlank(configuration.ipAddress) && StringUtil.isBlank(configuration.deviceId)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "No ip address or the device id configured."); + return; + } logger.debug("Initializing TP-Link Smart device on ip {}", configuration.ipAddress); connection = createConnection(configuration); - cache = new ExpiringCache(TimeUnit.SECONDS.toMillis(configuration.refresh), this::refreshCache); + cache = new ExpiringCache<@Nullable DeviceState>(TimeUnit.SECONDS.toMillis(configuration.refresh), + this::refreshCache); updateStatus(ThingStatus.UNKNOWN); // While config.xml defines refresh as min 1, this check is used to run a test that doesn't start refresh. if (configuration.refresh > 0) { @@ -105,15 +119,65 @@ Connection createConnection(TPLinkSmartHomeConfiguration config) { return new Connection(config.ipAddress); } + @Nullable private DeviceState refreshCache() { try { + updateIpAddress(); DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand())); - updateStatus(ThingStatus.ONLINE); - + updateDeviceId(deviceState.getSysinfo().getDeviceId()); + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } return deviceState; } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); return null; + } catch (RuntimeException e) { + logger.debug("Obtaining new device data unexpectedly crashed. If this keeps happening please report: ", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DISABLED, e.getMessage()); + return null; + } + } + + /** + * Checks if the current configured ip addres is still the same as by which the device is registered on the network. + * If there is a different ip address for this device it will update the configuration with this ip and start using + * this ip address. + */ + private void updateIpAddress() { + if (configuration.deviceId == null) { + // The device id is needed to get the ip address so if not known no need to continue. + return; + } + String lastKnownIpAddress = ipAddressService.getLastKnownIpAddress(configuration.deviceId); + + if (lastKnownIpAddress != null && !lastKnownIpAddress.equals(configuration.ipAddress)) { + Configuration editConfig = editConfiguration(); + editConfig.put(CONFIG_IP, lastKnownIpAddress); + updateConfiguration(editConfig); + configuration.ipAddress = lastKnownIpAddress; + connection.setIpAddress(lastKnownIpAddress); + } + } + + /** + * Updates the device id configuration if it's not set or throws an {@link IllegalArgumentException} if the + * configured device id doesn't match with the id reported by the device. + * + * @param actualDeviceId The id of the device as actual returned by the device. + * @throws IllegalArgumentException if the configured device id doesn't match with the id reported by the device + * itself. + */ + private void updateDeviceId(String actualDeviceId) { + if (StringUtil.isBlank(configuration.deviceId)) { + Configuration editConfig = editConfiguration(); + editConfig.put(CONFIG_DEVICE_ID, actualDeviceId); + updateConfiguration(editConfig); + configuration.deviceId = actualDeviceId; + } else if (!StringUtil.isBlank(actualDeviceId) && !actualDeviceId.equals(configuration.deviceId)) { + throw new IllegalArgumentException( + String.format("The configured device '%s' doesn't match with the id the device reports: '%s'.", + configuration.deviceId, actualDeviceId)); } } @@ -122,16 +186,17 @@ private DeviceState refreshCache() { */ private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) { if (refreshJob == null || refreshJob.isCancelled()) { - Runnable runnable = () -> { - logger.trace("Update Channels for:{}", thing.getUID()); - DeviceState value = cache.getValue(); - getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), value)); - }; - - refreshJob = scheduler.scheduleWithFixedDelay(runnable, 0, config.refresh.intValue(), TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, config.refresh, config.refresh, + TimeUnit.SECONDS); } } + void refreshChannels() { + logger.trace("Update Channels for:{}", thing.getUID()); + DeviceState value = cache.getValue(); + getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), value)); + } + /** * Updates the state from the device data for the channel given the data.. * @@ -139,7 +204,7 @@ private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) { * @param deviceState the state object containing the value to set of the channel * */ - private void updateChannelState(ChannelUID channelUID, DeviceState deviceState) { + private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState deviceState) { String channelId = channelUID.getId(); final State state;