]> git.basschouten.com Git - openhab-addons.git/commitdiff
[tapocontrol] add L610, L630 Spot and L930 LightStrip (#13814)
authorChristian Wild <40909464+wildcs@users.noreply.github.com>
Sat, 10 Dec 2022 15:53:30 +0000 (16:53 +0100)
committerGitHub <noreply@github.com>
Sat, 10 Dec 2022 15:53:30 +0000 (16:53 +0100)
* fix color change bug*

Signed-off-by: Christian Wild <christian@wildclan.de>
15 files changed:
bundles/org.openhab.binding.tapocontrol/README.md
bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/TapoDiscoveryService.java
bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoThingConstants.java
bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/device/TapoBridgeHandler.java
bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/device/TapoSmartBulb.java
bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/i18n/tapocontrol.properties
bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L610.xml [new file with mode: 0644]
bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L630.xml [new file with mode: 0644]
bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L930.xml [new file with mode: 0644]
bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/channels.xml
bundles/org.openhab.binding.tapocontrol/src/test/TapoDiscoveryService.java [deleted file]
bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/TapoDiscoveryService.java [new file with mode: 0644]
bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/api/TapoUDP.java [deleted file]
bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/discovery/TapoMDNS.java [new file with mode: 0644]
bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/discovery/TapoUDP.java [new file with mode: 0644]

index 562c0f66a1c658c59d6bbaa783449bd4fb473610..4df37cd0989d1439388f00b217bfff7a66d8f81c 100644 (file)
@@ -4,49 +4,21 @@ This binding adds support to control Tapo (Copyright © TP-Link Corporation Limi
 
 ## Supported Things
 
-The following Tapo-Devices are supported
-
-### P100/P105 SmartPlug (Wi-Fi)
-
-* Power On/Off
-* Wi-Fi signal (SignalStrength)
-* On-Time (Time in seconds device is switched on)
-
-### P110/P115 EnergyMonitoring SmartPlug (Wi-Fi)
-
-* Power On/Off
-* Wi-Fi signal (SignalStrength)
-* On-Time (Time in seconds device is switched on)
-* actual PowerUsage (Watt)
-* today EnergyUsage (Wh)
-* today Runtime (Time in seconds device was on today)
-
-### L510(Series) dimmable SmartBulb (Wi-Fi)
-
-* Light On/Off
-* Brightnes (Dimmer)  0-100 %
-* ColorTemperature (Number) 2500-6500 K
-* Wi-Fi signal (SignalStrength)
-* On-Time (Time in seconds device is switched on)
-
-### L530(Series) MultiColor SmartBulb (Wi-Fi)
-
-* Light On/Off
-* Brightnes (Dimmer)  0-100 %
-* ColorTemperature (Number) 2500-6500 K
-* Color (Color)
-* Wi-Fi signal (SignalStrength)
-* On-Time (Time in seconds device is switched on)
-
-### L900/L920 LED-LightStrip (Wi-Fi)
-
-* Light On/Off
-* Brightnes (Dimmer)  0-100 %
-* ColorTemperature (Number) 2500-6500 K
-* Color (Color)
-* Wi-Fi signal (SignalStrength)
-* On-Time (Time in seconds device is switched on)
-
+The following Tapo-Devices are supported. For precise channel-description look at `channels-table` below
+
+| DeviceType                         | ThingType   | Description                                 |
+|------------------------------------|-------------|---------------------------------------------|
+| SmartPlug (Wi-Fi)                  | P100        | Smart Socket                                |
+|                                    | P105        | Smart Mini Socket                           |
+| EnergyMonitoring SmartPlug (Wi-Fi) | P110        | Energy Monitoring Smart Socket              |
+|                                    | P115        | Energy Monitoring Mini Smart Socket         |
+| Dimmable SmartBulb (Wi-Fi)         | L510        | Dimmable White-Light Smart-Bulb (E27)       |
+|                                    | L610        | Dimmable White-Light Smart-Spot (GU10)      |
+| MultiColor SmartBulb (Wi-Fi)       | L530        | Multicolor Smart-Bulb (E27)                 |
+|                                    | L630        | Multicolor Smart-Spot (GU10)                |
+| MultiColor LightStrip (Wi-Fi)      | L900        | Multicolor RGB Dimmable LightStrip (5m)     |
+|                                    | L920        | Multicolor RGB-IC ColorZone LightStrip (5m) |
+|                                    | L930        | Multicolor RGBW-IC 50-Zone LightStrip (5m)  |
 
 ## Prerequisites
 
@@ -91,17 +63,17 @@ The thing has the following configuration parameters:
 
 All devices support some of the following channels:
 
-| group     | channel          |type                    | description                  | things supporting this channel                 |
-|-----------|----------------- |------------------------|------------------------------|------------------------------------------------|
-| actuator  | output           | Switch                 | Power device on or off       | P100, P105, P110, P115, L510, L530, L900, L920 |
-|           | brightness       | Dimmer                 | Brightness 0-100%            | L510, L530, L900                               |
-|           | colorTemperature | Number                 | White-Color-Temp 2500-6500K  | L510, L530, L900                               |
-|           | color            | Color                  | Color                        | L530, L900                                     |
-| device    | wifiSignal       | system.signal-strength | WiFi-quality-level           | P100, P105, P110, P115, L510, L530, L900, L920 |
-|           | onTime           | Number:Time            | seconds output is on         | P100, P105, P110, P115, L510, L530, L900, L920 |
-| energy    | actualPower      | Number:Power           | actual Power (Watt)          | P110, P115                                     |
-|           | todayEnergyUsage | Number:Energy          | used energy today (Wh)       | P110, P115                                     |
-|           | todayRuntime     | Number:Time            | seconds output was on today  | P110, P115                                     |
+| group     | channel          | type                   | description                  | things supporting this channel                                   |
+|-----------|----------------- |------------------------|------------------------------|------------------------------------------------------------------|
+| actuator  | output           | Switch                 | Power device on or off       | P100, P105, P110, P115, L510, L530, L610, L630, L900, L920, L930 |
+|           | brightness       | Dimmer                 | Brightness 0-100%            | L510, L530, L610, L630, L900                                     |
+|           | colorTemperature | Number                 | White-Color-Temp 2500-6500K  | L510, L530, L610, L630, L900                                     |
+|           | color            | Color                  | Color                        | L530, L630, L900                                                 |
+| device    | wifiSignal       | Number                 | WiFi-quality-level           | P100, P105, P110, P115, L510, L530, L610, L630, L900, L920, L930 |
+|           | onTime           | Number:Time            | seconds output is on         | P100, P105, P110, P115, L510, L530, L900, L920, L930             |
+| energy    | actualPower      | Number:Power           | actual Power (Watt)          | P110, P115                                                       |
+|           | todayEnergyUsage | Number:Energy          | used energy today (Wh)       | P110, P115                                                       |
+|           | todayRuntime     | Number:Time            | seconds output was on today  | P110, P115                                                       |
 
 
 ## Channel Refresh
index f13bfc7f705c1f48861aa89098b2bdf8b3c1b5fd..0e465af8c0d2f86560909d0da41cb972e9b54221 100644 (file)
@@ -64,7 +64,7 @@ public class TapoDiscoveryService extends AbstractDiscoveryService implements Th
     }
 
     /**
-     * deactivate
+     * activate
      */
     @Override
     public void activate() {
@@ -178,7 +178,7 @@ public class TapoDiscoveryService extends AbstractDiscoveryService implements Th
                 }
             }
         } catch (Exception e) {
-            logger.debug("error handlling CloudDevices", e);
+            logger.debug("error handling CloudDevices", e);
         }
     }
 
index b2422668ea60acaf3d825a88147a5b924ab4956b..60f2ec9009369295c2ac8bc209a16ac477941b76 100644 (file)
@@ -40,8 +40,11 @@ public class TapoThingConstants {
     public static final String DEVICE_P115 = "P115";
     public static final String DEVICE_L510 = "L510";
     public static final String DEVICE_L530 = "L530";
+    public static final String DEVICE_L610 = "L610";
+    public static final String DEVICE_L630 = "L630";
     public static final String DEVICE_L900 = "L900";
     public static final String DEVICE_L920 = "L920";
+    public static final String DEVICE_L930 = "L930";
     public static final String DEVICE_UNIVERSAL = "Test_Device";
 
     /*** LIST OF SUPPORTED DEVICE DESCRIPTIONS ***/
@@ -59,17 +62,21 @@ public class TapoThingConstants {
     public static final ThingTypeUID P115_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_P115);
     public static final ThingTypeUID L510_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L510);
     public static final ThingTypeUID L530_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L530);
+    public static final ThingTypeUID L610_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L610);
+    public static final ThingTypeUID L630_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L630);
     public static final ThingTypeUID L900_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L900);
     public static final ThingTypeUID L920_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L920);
+    public static final ThingTypeUID L930_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_L930);
     public static final ThingTypeUID UNIVERSAL_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_UNIVERSAL);
 
     /*** SET OF SUPPORTED UIDS ***/
     public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_UIDS = Set.of(BRIDGE_THING_TYPE);
     public static final Set<ThingTypeUID> SUPPORTED_SMART_PLUG_UIDS = Set.of(P100_THING_TYPE, P105_THING_TYPE,
             P110_THING_TYPE, P115_THING_TYPE);
-    public static final Set<ThingTypeUID> SUPPORTED_WHITE_BULB_UIDS = Set.of(L510_THING_TYPE);
-    public static final Set<ThingTypeUID> SUPPORTED_COLOR_BULB_UIDS = Set.of(L530_THING_TYPE);
-    public static final Set<ThingTypeUID> SUPPORTED_LIGHT_STRIP_UIDS = Set.of(L900_THING_TYPE, L920_THING_TYPE);
+    public static final Set<ThingTypeUID> SUPPORTED_WHITE_BULB_UIDS = Set.of(L510_THING_TYPE, L610_THING_TYPE);
+    public static final Set<ThingTypeUID> SUPPORTED_COLOR_BULB_UIDS = Set.of(L530_THING_TYPE, L630_THING_TYPE);
+    public static final Set<ThingTypeUID> SUPPORTED_LIGHT_STRIP_UIDS = Set.of(L900_THING_TYPE, L920_THING_TYPE,
+            L930_THING_TYPE);
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
             .unmodifiableSet(Stream
                     .of(SUPPORTED_BRIDGE_UIDS, SUPPORTED_SMART_PLUG_UIDS, SUPPORTED_WHITE_BULB_UIDS,
index 832f1dfdf1d049cc23f3cac59ffd3cb11db53c25..2f7296b07467d5f8557b47f41efffcb082bfc1c1 100644 (file)
@@ -259,6 +259,8 @@ public class TapoBridgeHandler extends BaseBridgeHandler {
         if (config.cloudDiscovery) {
             logger.trace("{} discover devicelist from cloud", this.uid);
             deviceList = getDeviceListCloud();
+        } else {
+            logger.info("{} Discovery disabled in bridge settings ", this.uid);
         }
         return deviceList;
     }
index 985c005144b5176614d870fb6b4263e318b201a2..dd73822472253e4d23d95c1b5a40b6c68439c5c6 100644 (file)
@@ -127,9 +127,9 @@ public class TapoSmartBulb extends TapoDevice {
     protected void setColor(HSBType command) {
         HashMap<String, Object> newState = new HashMap<>();
         newState.put(DEVICE_PROPERTY_ON, true);
-        newState.put(DEVICE_PROPERTY_HUE, command.getHue());
-        newState.put(DEVICE_PROPERTY_SATURATION, command.getSaturation());
-        newState.put(DEVICE_PROPERTY_BRIGHTNES, command.getBrightness());
+        newState.put(DEVICE_PROPERTY_HUE, command.getHue().intValue());
+        newState.put(DEVICE_PROPERTY_SATURATION, command.getSaturation().intValue());
+        newState.put(DEVICE_PROPERTY_BRIGHTNES, command.getBrightness().intValue());
         connector.sendDeviceCommands(newState);
     }
 
index 2dc9379b14ef4514280245dfa929e7fdfa56be3c..1fc39a16f332191c3b0c9aaf9501139f89d4df75 100644 (file)
@@ -9,10 +9,16 @@ thing-type.tapocontrol.L510.label = L510 Series White-Bulb
 thing-type.tapocontrol.L510.description = Tapo Smart dimmable White-Light-Bulb
 thing-type.tapocontrol.L530.label = L530 Series Color-Bulb
 thing-type.tapocontrol.L530.description = Tapo Smart Multicolor Light-Bulb
+thing-type.tapocontrol.L610.label = L610 Series White-Spot
+thing-type.tapocontrol.L610.description = Tapo Smart dimmable White-Light-Spot
+thing-type.tapocontrol.L630.label = L630 Series Color-Spot
+thing-type.tapocontrol.L630.description = Tapo Smart Multicolor Light-Spot
 thing-type.tapocontrol.L900.label = L900 LightStrip
 thing-type.tapocontrol.L900.description = Tapo Smart LED-Lightstrip
 thing-type.tapocontrol.L920.label = L920 LightStrip
 thing-type.tapocontrol.L920.description = Tapo Smart Multicolor LED-Lightstrip
+thing-type.tapocontrol.L930.label = L930 LightStrip
+thing-type.tapocontrol.L930.description = Tapo Smart Multicolor LED-Lightstrip with ZoneControl
 thing-type.tapocontrol.P100.label = P100 SmartPlug
 thing-type.tapocontrol.P100.description = Tapo Smart Wifi Plug
 thing-type.tapocontrol.P105.label = P105 SmartPlug
@@ -44,6 +50,8 @@ channel-group-type.tapocontrol.colorBulb.label = Color Light Bulb
 channel-group-type.tapocontrol.colorBulb.description = Tapo Multicolor Smart Light Bulb
 channel-group-type.tapocontrol.deviceState.label = Device State
 channel-group-type.tapocontrol.deviceState.description = Information about the device
+channel-group-type.tapocontrol.deviceStateS.label = Device State
+channel-group-type.tapocontrol.deviceStateS.description = Information about the device
 channel-group-type.tapocontrol.energyMonitor.label = Energy Usage
 channel-group-type.tapocontrol.energyMonitor.description = Energy and Power usage
 channel-group-type.tapocontrol.lightBulb.label = Light Bulb
diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L610.xml b/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L610.xml
new file mode 100644 (file)
index 0000000..cc57394
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="tapocontrol"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <!-- L610 THING-TYPE (WHITE-LIGHT-BULB) -->
+       <thing-type id="L610">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>L610 Series White-Spot</label>
+               <description>Tapo Smart dimmable White-Light-Spot</description>
+               <channel-groups>
+                       <channel-group id="actuator" typeId="lightBulb"/>
+                       <channel-group id="device" typeId="deviceStateS"/>
+               </channel-groups>
+               <representation-property>macAddress</representation-property>
+
+               <config-description-ref uri="thing-type:tapo:device"/>
+       </thing-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L630.xml b/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L630.xml
new file mode 100644 (file)
index 0000000..1108af0
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="tapocontrol"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <!-- L630 THING-TYPE (COLOR-LIGHT-BULB) -->
+       <thing-type id="L630">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>L630 Series Color-Spot</label>
+               <description>Tapo Smart Multicolor Light-Spot</description>
+               <channel-groups>
+                       <channel-group id="actuator" typeId="colorBulb"/>
+                       <channel-group id="device" typeId="deviceStateS"/>
+               </channel-groups>
+               <representation-property>macAddress</representation-property>
+
+               <config-description-ref uri="thing-type:tapo:device"/>
+       </thing-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L930.xml b/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/thing/L930.xml
new file mode 100644 (file)
index 0000000..2fe6704
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="tapocontrol"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <!-- L930 THING-TYPE (Multicolor LED-Lightstrip) -->
+       <thing-type id="L930">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>L930 LightStrip</label>
+               <description>Tapo Smart Multicolor LED-Lightstrip with ZoneControl</description>
+               <channel-groups>
+                       <channel-group id="actuator" typeId="lightStrip"/>
+                       <channel-group id="device" typeId="deviceState"/>
+               </channel-groups>
+               <representation-property>macAddress</representation-property>
+
+               <config-description-ref uri="thing-type:tapo:device"/>
+       </thing-type>
+</thing:thing-descriptions>
index 04e6226ddadb72efe8779516b963d6586ef4ac13..009179a5f4810ecce0ee43f3d715cfca3b4ed9bc 100644 (file)
@@ -7,7 +7,7 @@
        <!-- ############################### CHANNEL-GROUPS ############################### -->
 
        <!-- CHANNEL GROUP TYPES -->
-       <!--Device-Statuss Channel Type -->
+       <!--Device-Status Channel Type -->
        <channel-group-type id="deviceState">
                <label>Device State</label>
                <description>Information about the device</description>
                </channels>
        </channel-group-type>
 
+       <!--Device-Status Channel Type (Small) -->
+       <channel-group-type id="deviceStateS">
+               <label>Device State</label>
+               <description>Information about the device</description>
+               <channels>
+                       <channel id="wifiSignal" typeId="system.signal-strength"/>
+                       <channel id="overheated" typeId="overheated"/>
+               </channels>
+       </channel-group-type>
+
        <!--Actor Channel Type -->
        <channel-group-type id="smartPlug">
                <label>SmartPlug</label>
diff --git a/bundles/org.openhab.binding.tapocontrol/src/test/TapoDiscoveryService.java b/bundles/org.openhab.binding.tapocontrol/src/test/TapoDiscoveryService.java
deleted file mode 100644 (file)
index 3c02a7c..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.tapocontrol.internal;
-
-import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
-import static org.openhab.binding.tapocontrol.internal.constants.TapoThingConstants.*;
-import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.tapocontrol.internal.device.TapoBridgeHandler;
-import org.openhab.binding.tapocontrol.internal.structures.TapoBridgeConfiguration;
-import org.openhab.core.config.discovery.AbstractDiscoveryService;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryResultBuilder;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-
-/**
- * Handler class for TAPO Smart Home thing discovery
- *
- * @author Christian Wild - Initial contribution
- */
-@NonNullByDefault
-public class TapoDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
-    private final Logger logger = LoggerFactory.getLogger(TapoDiscoveryService.class);
-    protected @NonNullByDefault({}) TapoBridgeHandler bridge;
-
-    /***********************************
-     *
-     * INITIALIZATION
-     *
-     ************************************/
-
-    /**
-     * INIT CLASS
-     * 
-     * @param bridgeHandler
-     */
-    public TapoDiscoveryService() {
-        super(SUPPORTED_THING_TYPES_UIDS, TAPO_DISCOVERY_TIMEOUT_S, false);
-    }
-
-    /**
-     * deactivate
-     */
-    @Override
-    public void activate() {
-        TapoBridgeConfiguration config = bridge.getBridgeConfig();
-        if (config.cloudDiscovery || config.udpDiscovery) {
-            startBackgroundDiscovery();
-        }
-    }
-
-    /**
-     * deactivate
-     */
-    @Override
-    public void deactivate() {
-        super.deactivate();
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof TapoBridgeHandler) {
-            TapoBridgeHandler tapoBridge = (TapoBridgeHandler) handler;
-            tapoBridge.setDiscoveryService(this);
-            this.bridge = tapoBridge;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.bridge;
-    }
-
-    /***********************************
-     *
-     * SCAN HANDLING
-     *
-     ************************************/
-
-    /**
-     * Start scan manually
-     */
-    @Override
-    public void startScan() {
-        removeOlderResults(getTimestampOfLastScan());
-        if (bridge != null) {
-            JsonArray jsonArray = bridge.getDeviceList();
-            handleCloudDevices(jsonArray);
-        }
-    }
-
-    /***********************************
-     *
-     * handle Results
-     *
-     ************************************/
-
-    /**
-     * CREATE DISCOVERY RESULT
-     * creates discoveryResult (Thing) from JsonObject got from Cloud
-     * 
-     * @param device JsonObject with device information
-     * @return DiscoveryResult-Object
-     */
-    public DiscoveryResult createResult(JsonObject device) {
-        TapoBridgeHandler tapoBridge = this.bridge;
-        String deviceModel = getDeviceModel(device);
-        String label = getDeviceLabel(device);
-        String deviceMAC = device.get(CLOUD_PROPERTY_MAC).getAsString();
-        ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, deviceModel);
-
-        /* create properties */
-        Map<String, Object> properties = new HashMap<>();
-        properties.put(Thing.PROPERTY_VENDOR, DEVICE_VENDOR);
-        properties.put(Thing.PROPERTY_MAC_ADDRESS, formatMac(deviceMAC, MAC_DIVISION_CHAR));
-        properties.put(Thing.PROPERTY_FIRMWARE_VERSION, device.get(CLOUD_PROPERTY_FW).getAsString());
-        properties.put(Thing.PROPERTY_HARDWARE_VERSION, device.get(CLOUD_PROPERTY_HW).getAsString());
-        properties.put(Thing.PROPERTY_MODEL_ID, deviceModel);
-        properties.put(Thing.PROPERTY_SERIAL_NUMBER, device.get(CLOUD_PROPERTY_ID).getAsString());
-
-        logger.debug("device {} discovered", deviceModel);
-        if (tapoBridge != null) {
-            ThingUID bridgeUID = tapoBridge.getUID();
-            ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, deviceMAC);
-            return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
-                    .withRepresentationProperty(DEVICE_REPRESENTATION_PROPERTY).withBridge(bridgeUID).withLabel(label)
-                    .build();
-        } else {
-            ThingUID thingUID = new ThingUID(BINDING_ID, deviceMAC);
-            return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
-                    .withRepresentationProperty(DEVICE_REPRESENTATION_PROPERTY).withLabel(label).build();
-        }
-    }
-
-    /**
-     * work with result from get devices from cloud devices
-     * 
-     * @param deviceList
-     */
-    protected void handleCloudDevices(JsonArray deviceList) {
-        try {
-            for (JsonElement deviceElement : deviceList) {
-                if (deviceElement.isJsonObject()) {
-                    JsonObject device = deviceElement.getAsJsonObject();
-                    String deviceModel = getDeviceModel(device);
-                    ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, deviceModel);
-
-                    /* create thing */
-                    if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
-                        DiscoveryResult discoveryResult = createResult(device);
-                        thingDiscovered(discoveryResult);
-                    }
-                }
-            }
-        } catch (Exception e) {
-            logger.debug("error handlling CloudDevices", e);
-        }
-    }
-
-    /**
-     * GET DEVICEMODEL
-     * 
-     * @param device JsonObject with deviceData
-     * @return String with DeviceModel
-     */
-    protected String getDeviceModel(JsonObject device) {
-        try {
-            String deviceModel = device.get(CLOUD_PROPERTY_MODEL).getAsString();
-            deviceModel = deviceModel.replaceAll("\\(.*\\)", ""); // replace (DE)
-            deviceModel = deviceModel.replace("Tapo", "");
-            deviceModel = deviceModel.replace("Series", "");
-            deviceModel = deviceModel.trim();
-            deviceModel = deviceModel.replace(" ", "_");
-            return deviceModel;
-        } catch (Exception e) {
-            logger.debug("error getDeviceModel", e);
-            return "";
-        }
-    }
-
-    /**
-     * GET DEVICE LABEL
-     * 
-     * @param device JsonObject with deviceData
-     * @return String with DeviceLabel
-     */
-    protected String getDeviceLabel(JsonObject device) {
-        try {
-            String deviceLabel = "";
-            String deviceModel = getDeviceModel(device);
-            ThingTypeUID deviceUID = new ThingTypeUID(BINDING_ID, deviceModel);
-
-            if (SUPPORTED_SMART_PLUG_UIDS.contains(deviceUID)) {
-                deviceLabel = DEVICE_DESCRIPTION_SMART_PLUG;
-            } else if (SUPPORTED_WHITE_BULB_UIDS.contains(deviceUID)) {
-                deviceLabel = DEVICE_DESCRIPTION_WHITE_BULB;
-            } else if (SUPPORTED_COLOR_BULB_UIDS.contains(deviceUID)) {
-                deviceLabel = DEVICE_DESCRIPTION_COLOR_BULB;
-            }
-            return DEVICE_VENDOR + " " + deviceModel + " " + deviceLabel;
-        } catch (Exception e) {
-            logger.debug("error getDeviceLabel", e);
-            return "";
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/TapoDiscoveryService.java b/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/TapoDiscoveryService.java
new file mode 100644 (file)
index 0000000..c2e9994
--- /dev/null
@@ -0,0 +1,231 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tapocontrol.internal;
+
+import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
+import static org.openhab.binding.tapocontrol.internal.constants.TapoThingConstants.*;
+import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.tapocontrol.internal.device.TapoBridgeHandler;
+import org.openhab.binding.tapocontrol.internal.structures.TapoBridgeConfiguration;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+/**
+ * Handler class for TAPO Smart Home thing discovery
+ *
+ * @author Christian Wild - Initial contribution
+ */
+@NonNullByDefault
+public class TapoDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
+    private final Logger logger = LoggerFactory.getLogger(TapoDiscoveryService.class);
+    protected @NonNullByDefault({}) TapoBridgeHandler bridge;
+
+    /***********************************
+     *
+     * INITIALIZATION
+     *
+     ************************************/
+
+    /**
+     * INIT CLASS
+     * 
+     * @param bridgeHandler
+     */
+    public TapoDiscoveryService() {
+        super(SUPPORTED_THING_TYPES_UIDS, TAPO_DISCOVERY_TIMEOUT_S, false);
+    }
+
+    /**
+     * activate
+     */
+    @Override
+    public void activate() {
+        TapoBridgeConfiguration config = bridge.getBridgeConfig();
+        if (config.cloudDiscovery || config.udpDiscovery) {
+            startBackgroundDiscovery();
+        }
+    }
+
+    /**
+     * deactivate
+     */
+    @Override
+    public void deactivate() {
+        super.deactivate();
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof TapoBridgeHandler) {
+            TapoBridgeHandler tapoBridge = (TapoBridgeHandler) handler;
+            tapoBridge.setDiscoveryService(this);
+            this.bridge = tapoBridge;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return this.bridge;
+    }
+
+    /***********************************
+     *
+     * SCAN HANDLING
+     *
+     ************************************/
+
+    /**
+     * Start scan manually
+     */
+    @Override
+    public void startScan() {
+        removeOlderResults(getTimestampOfLastScan());
+        if (bridge != null) {
+            JsonArray jsonArray = bridge.getDeviceList();
+            handleCloudDevices(jsonArray);
+        }
+    }
+
+    /***********************************
+     *
+     * handle Results
+     *
+     ************************************/
+
+    /**
+     * CREATE DISCOVERY RESULT
+     * creates discoveryResult (Thing) from JsonObject got from Cloud
+     * 
+     * @param device JsonObject with device information
+     * @return DiscoveryResult-Object
+     */
+    public DiscoveryResult createResult(JsonObject device) {
+        TapoBridgeHandler tapoBridge = this.bridge;
+        String deviceModel = getDeviceModel(device);
+        String label = getDeviceLabel(device);
+        String deviceMAC = device.get(CLOUD_PROPERTY_MAC).getAsString();
+        ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, deviceModel);
+
+        /* create properties */
+        Map<String, Object> properties = new HashMap<>();
+        properties.put(Thing.PROPERTY_VENDOR, DEVICE_VENDOR);
+        properties.put(Thing.PROPERTY_MAC_ADDRESS, formatMac(deviceMAC, MAC_DIVISION_CHAR));
+        properties.put(Thing.PROPERTY_FIRMWARE_VERSION, device.get(CLOUD_PROPERTY_FW).getAsString());
+        properties.put(Thing.PROPERTY_HARDWARE_VERSION, device.get(CLOUD_PROPERTY_HW).getAsString());
+        properties.put(Thing.PROPERTY_MODEL_ID, deviceModel);
+        properties.put(Thing.PROPERTY_SERIAL_NUMBER, device.get(CLOUD_PROPERTY_ID).getAsString());
+
+        logger.debug("device {} discovered", deviceModel);
+        if (tapoBridge != null) {
+            ThingUID bridgeUID = tapoBridge.getUID();
+            ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, deviceMAC);
+            return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
+                    .withRepresentationProperty(DEVICE_REPRESENTATION_PROPERTY).withBridge(bridgeUID).withLabel(label)
+                    .build();
+        } else {
+            ThingUID thingUID = new ThingUID(BINDING_ID, deviceMAC);
+            return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
+                    .withRepresentationProperty(DEVICE_REPRESENTATION_PROPERTY).withLabel(label).build();
+        }
+    }
+
+    /**
+     * work with result from get devices from cloud devices
+     * 
+     * @param deviceList
+     */
+    protected void handleCloudDevices(JsonArray deviceList) {
+        try {
+            for (JsonElement deviceElement : deviceList) {
+                if (deviceElement.isJsonObject()) {
+                    JsonObject device = deviceElement.getAsJsonObject();
+                    String deviceModel = getDeviceModel(device);
+                    ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, deviceModel);
+
+                    /* create thing */
+                    if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
+                        DiscoveryResult discoveryResult = createResult(device);
+                        thingDiscovered(discoveryResult);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.debug("error handlling CloudDevices", e);
+        }
+    }
+
+    /**
+     * GET DEVICEMODEL
+     * 
+     * @param device JsonObject with deviceData
+     * @return String with DeviceModel
+     */
+    protected String getDeviceModel(JsonObject device) {
+        try {
+            String deviceModel = device.get(CLOUD_PROPERTY_MODEL).getAsString();
+            deviceModel = deviceModel.replaceAll("\\(.*\\)", ""); // replace (DE)
+            deviceModel = deviceModel.replace("Tapo", "");
+            deviceModel = deviceModel.replace("Series", "");
+            deviceModel = deviceModel.trim();
+            deviceModel = deviceModel.replace(" ", "_");
+            return deviceModel;
+        } catch (Exception e) {
+            logger.debug("error getDeviceModel", e);
+            return "";
+        }
+    }
+
+    /**
+     * GET DEVICE LABEL
+     * 
+     * @param device JsonObject with deviceData
+     * @return String with DeviceLabel
+     */
+    protected String getDeviceLabel(JsonObject device) {
+        try {
+            String deviceLabel = "";
+            String deviceModel = getDeviceModel(device);
+            ThingTypeUID deviceUID = new ThingTypeUID(BINDING_ID, deviceModel);
+
+            if (SUPPORTED_SMART_PLUG_UIDS.contains(deviceUID)) {
+                deviceLabel = DEVICE_DESCRIPTION_SMART_PLUG;
+            } else if (SUPPORTED_WHITE_BULB_UIDS.contains(deviceUID)) {
+                deviceLabel = DEVICE_DESCRIPTION_WHITE_BULB;
+            } else if (SUPPORTED_COLOR_BULB_UIDS.contains(deviceUID)) {
+                deviceLabel = DEVICE_DESCRIPTION_COLOR_BULB;
+            }
+            return DEVICE_VENDOR + " " + deviceModel + " " + deviceLabel;
+        } catch (Exception e) {
+            logger.debug("error getDeviceLabel", e);
+            return "";
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/api/TapoUDP.java b/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/api/TapoUDP.java
deleted file mode 100644 (file)
index f0abdad..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.tapocontrol.internal.api;
-
-import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*;
-
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.SocketTimeoutException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.SecureRandom;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.tapocontrol.internal.helpers.TapoCredentials;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-
-/**
- * Handler class for TAPO Smart Home device UDP-connections.
- * THIS IS FOR TESTING
- *
- * @author Christian Wild - Initial contribution
- */
-@NonNullByDefault
-public class TapoUDP {
-    private final Logger logger = LoggerFactory.getLogger(TapoUDP.class);
-    private static final Integer BROADCAST_TIMEOUT_MS = 5000;
-    private static final Integer BROADCAST_DISCOVERY_PORT = 20002; // int
-    private static final String BROADCAST_IP = "255.255.255.255";
-    private static final String DISCOVERY_MESSAGE_KEY = "rsa_key";
-    private static final String DISCOVERY_MESSAGE_START_BYTES = "0200000101e5110001cb8c577dd7deb8";
-    private static final Integer BUFFER_SIZE = 501;
-    private TapoCredentials credentials;
-
-    public TapoUDP(TapoCredentials credentials) {
-        this.credentials = credentials; // new TapoCredentials();
-    }
-
-    public JsonArray udpScan() {
-        try {
-            DatagramSocket udpSocket = new DatagramSocket();
-            udpSocket.setSoTimeout(BROADCAST_TIMEOUT_MS);
-            udpSocket.setBroadcast(true);
-
-            /* create payload for handshake */
-            String publicKey = credentials.getPublicKey();
-            publicKey = generateOwnRSAKey(); // credentials.getPublicKey();
-            JsonObject parameters = new JsonObject();
-            JsonObject messageObject = new JsonObject();
-            parameters.addProperty(DISCOVERY_MESSAGE_KEY, publicKey);
-            messageObject.add("params", parameters);
-
-            String discoveryMessage = messageObject.toString();
-
-            byte[] startByte = hexStringToByteArray(DISCOVERY_MESSAGE_START_BYTES);
-            byte[] message = discoveryMessage.getBytes("UTF-8");
-            byte[] sendData = new byte[startByte.length + message.length];
-            System.arraycopy(startByte, 0, sendData, 0, startByte.length);
-            System.arraycopy(message, 0, sendData, startByte.length, message.length);
-
-            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length,
-                    InetAddress.getByName(BROADCAST_IP), BROADCAST_DISCOVERY_PORT);
-
-            udpSocket.send(sendPacket);
-
-            while (true) {
-                // Wait for a response
-                byte[] recvBuf = new byte[BUFFER_SIZE];
-                DatagramPacket receivePacket;
-                try {
-                    receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
-                    udpSocket.receive(receivePacket);
-                } catch (SocketTimeoutException e) {
-                    udpSocket.close();
-                    return new JsonArray();
-                } catch (Exception e) {
-                    udpSocket.close();
-                    return new JsonArray();
-                }
-
-                // Check if the message is correct
-                String responseMessage = new String(receivePacket.getData(), "UTF-8").trim();
-
-                if (responseMessage.length() == 0) {
-                    udpSocket.close();
-                }
-                String addressBC = receivePacket.getAddress().getHostAddress();
-                gotDeviceAdress(addressBC);
-            }
-        } catch (Exception e) {
-            // handle exception
-        }
-        return new JsonArray();
-    }
-
-    private void gotDeviceAdress(String ipAddress) {
-        // handle exception
-    }
-
-    private String generateOwnRSAKey() {
-        try {
-            logger.trace("generating new keypair");
-            KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA");
-            instance.initialize(1536, new SecureRandom());
-            KeyPair generateKeyPair = instance.generateKeyPair();
-
-            String publicKey = new String(java.util.Base64.getMimeEncoder()
-                    .encode(((RSAPublicKey) generateKeyPair.getPublic()).getEncoded()));
-            String privateKey = new String(java.util.Base64.getMimeEncoder()
-                    .encode(((RSAPrivateKey) generateKeyPair.getPrivate()).getEncoded()));
-            logger.trace("new privateKey: '{}'", privateKey);
-            logger.trace("new ublicKey: '{}'", publicKey);
-
-            return String.format("-----BEGIN PUBLIC KEY-----%n%s%n-----END PUBLIC KEY-----%n", publicKey);
-
-        } catch (Exception e) {
-            // couldn't generate own rsa key
-            return "";
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/discovery/TapoMDNS.java b/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/discovery/TapoMDNS.java
new file mode 100644 (file)
index 0000000..83fabce
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tapocontrol.internal.api;
+
+import static org.openhab.binding.tapocontrol.internal.constants.TapoThingConstants.*;
+
+import java.util.Dictionary;
+import java.util.Set;
+
+import javax.jmdns.ServiceInfo;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handler class for TAPO Smart Home thing discovery over mDNS
+ *
+ * @author Christian Wild - Initial contribution
+ */
+@Component(configurationPid = "discovery.tapocontrol")
+@NonNullByDefault
+public class TapoMDNS implements MDNSDiscoveryParticipant {
+    private final Logger logger = LoggerFactory.getLogger(TapoMDNS.class);
+    private boolean isAutoDiscoveryEnabled = true;
+
+    @Activate
+    protected void activate(ComponentContext componentContext) {
+        activateOrModifyService(componentContext);
+    }
+
+    @Modified
+    protected void modified(ComponentContext componentContext) {
+        activateOrModifyService(componentContext);
+    }
+
+    private void activateOrModifyService(ComponentContext componentContext) {
+        Dictionary<String, @Nullable Object> properties = componentContext.getProperties();
+        String autoDiscoveryPropertyValue = (String) properties
+                .get(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY);
+        if (autoDiscoveryPropertyValue != null && !autoDiscoveryPropertyValue.isBlank()) {
+            isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue);
+        }
+    }
+
+    @Override
+    public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
+        return SUPPORTED_THING_TYPES_UIDS;
+    }
+
+    @Override
+    public String getServiceType() {
+        return "NULL";
+    }
+
+    @Override
+    public @Nullable ThingUID getThingUID(ServiceInfo service) {
+        ThingTypeUID thingTypeUID = getThingType(service);
+        if (thingTypeUID != null) {
+            String id = service.getPropertyString(PROPERTY_FAMILY); // device id
+            return new ThingUID(thingTypeUID, id);
+        }
+        return null;
+    }
+
+    private @Nullable ThingTypeUID getThingType(final ServiceInfo service) {
+        String model = service.getPropertyString(PROPERTY_FAMILY); // model
+        logger.debug("found Type: {}", model);
+        if (model == null) {
+            return null;
+        }
+        return L510_THING_TYPE;
+    }
+
+    @Override
+    public @Nullable DiscoveryResult createResult(ServiceInfo service) {
+        if (isAutoDiscoveryEnabled) {
+            ThingUID uid = getThingUID(service);
+            if (uid != null) {
+                String host = service.getHostAddresses()[0];
+                int port = service.getPort();
+                logger.debug("device Found: {} {}", host, port);
+            }
+        }
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/discovery/TapoUDP.java b/bundles/org.openhab.binding.tapocontrol/src/test/java/org/openhab/binding/tapocontrol/internal/discovery/TapoUDP.java
new file mode 100644 (file)
index 0000000..f0abdad
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tapocontrol.internal.api;
+
+import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketTimeoutException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.tapocontrol.internal.helpers.TapoCredentials;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+/**
+ * Handler class for TAPO Smart Home device UDP-connections.
+ * THIS IS FOR TESTING
+ *
+ * @author Christian Wild - Initial contribution
+ */
+@NonNullByDefault
+public class TapoUDP {
+    private final Logger logger = LoggerFactory.getLogger(TapoUDP.class);
+    private static final Integer BROADCAST_TIMEOUT_MS = 5000;
+    private static final Integer BROADCAST_DISCOVERY_PORT = 20002; // int
+    private static final String BROADCAST_IP = "255.255.255.255";
+    private static final String DISCOVERY_MESSAGE_KEY = "rsa_key";
+    private static final String DISCOVERY_MESSAGE_START_BYTES = "0200000101e5110001cb8c577dd7deb8";
+    private static final Integer BUFFER_SIZE = 501;
+    private TapoCredentials credentials;
+
+    public TapoUDP(TapoCredentials credentials) {
+        this.credentials = credentials; // new TapoCredentials();
+    }
+
+    public JsonArray udpScan() {
+        try {
+            DatagramSocket udpSocket = new DatagramSocket();
+            udpSocket.setSoTimeout(BROADCAST_TIMEOUT_MS);
+            udpSocket.setBroadcast(true);
+
+            /* create payload for handshake */
+            String publicKey = credentials.getPublicKey();
+            publicKey = generateOwnRSAKey(); // credentials.getPublicKey();
+            JsonObject parameters = new JsonObject();
+            JsonObject messageObject = new JsonObject();
+            parameters.addProperty(DISCOVERY_MESSAGE_KEY, publicKey);
+            messageObject.add("params", parameters);
+
+            String discoveryMessage = messageObject.toString();
+
+            byte[] startByte = hexStringToByteArray(DISCOVERY_MESSAGE_START_BYTES);
+            byte[] message = discoveryMessage.getBytes("UTF-8");
+            byte[] sendData = new byte[startByte.length + message.length];
+            System.arraycopy(startByte, 0, sendData, 0, startByte.length);
+            System.arraycopy(message, 0, sendData, startByte.length, message.length);
+
+            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length,
+                    InetAddress.getByName(BROADCAST_IP), BROADCAST_DISCOVERY_PORT);
+
+            udpSocket.send(sendPacket);
+
+            while (true) {
+                // Wait for a response
+                byte[] recvBuf = new byte[BUFFER_SIZE];
+                DatagramPacket receivePacket;
+                try {
+                    receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
+                    udpSocket.receive(receivePacket);
+                } catch (SocketTimeoutException e) {
+                    udpSocket.close();
+                    return new JsonArray();
+                } catch (Exception e) {
+                    udpSocket.close();
+                    return new JsonArray();
+                }
+
+                // Check if the message is correct
+                String responseMessage = new String(receivePacket.getData(), "UTF-8").trim();
+
+                if (responseMessage.length() == 0) {
+                    udpSocket.close();
+                }
+                String addressBC = receivePacket.getAddress().getHostAddress();
+                gotDeviceAdress(addressBC);
+            }
+        } catch (Exception e) {
+            // handle exception
+        }
+        return new JsonArray();
+    }
+
+    private void gotDeviceAdress(String ipAddress) {
+        // handle exception
+    }
+
+    private String generateOwnRSAKey() {
+        try {
+            logger.trace("generating new keypair");
+            KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA");
+            instance.initialize(1536, new SecureRandom());
+            KeyPair generateKeyPair = instance.generateKeyPair();
+
+            String publicKey = new String(java.util.Base64.getMimeEncoder()
+                    .encode(((RSAPublicKey) generateKeyPair.getPublic()).getEncoded()));
+            String privateKey = new String(java.util.Base64.getMimeEncoder()
+                    .encode(((RSAPrivateKey) generateKeyPair.getPrivate()).getEncoded()));
+            logger.trace("new privateKey: '{}'", privateKey);
+            logger.trace("new ublicKey: '{}'", publicKey);
+
+            return String.format("-----BEGIN PUBLIC KEY-----%n%s%n-----END PUBLIC KEY-----%n", publicKey);
+
+        } catch (Exception e) {
+            // couldn't generate own rsa key
+            return "";
+        }
+    }
+}