]> git.basschouten.com Git - openhab-addons.git/commitdiff
[hueemulation] Changes to fix pairing and device discovery with Alexa (#9164)
authorMike Major <mike_j_major@hotmail.com>
Sun, 29 Nov 2020 03:46:35 +0000 (03:46 +0000)
committerGitHub <noreply@github.com>
Sun, 29 Nov 2020 03:46:35 +0000 (19:46 -0800)
Signed-off-by: Mike Major <mike_j_major@hotmail.com>
bundles/org.openhab.io.hueemulation/README.md
bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java
bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java
bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java
bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java
bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java
bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java

index b82aa26396a8c43e027572155c7df9f6113d42f7..5961bf284b41655f0e04dace7b6b5d0ab2d5aec7 100644 (file)
@@ -167,6 +167,15 @@ You must either
   ```
 * or let openHAB run on port 80 (the entire java process requires elevated privileges).
 
+* For Amazon Echo the pairing process may fail due to a 302 response from openHAB, this can be resolved by using a reverse proxy to change the request url from `/api` to `/api/`, for example nginx with the following configuration:
+```
+  server {
+    listen 80;
+    location /api {
+      proxy_pass http://localhost:8080/api/;
+    }
+  }
+  ```
 Please open port 80 tcp and port 1900 udp in your firewall installation.
 
 You can test if the hue emulation does its job by enabling pairing mode including the option "Amazon Echo device discovery fix".
index c084eb9d07003446e2c68d990978075b41eb4f97..4f411eae7fd16d40ad7f127cffc21254846998a6 100644 (file)
@@ -17,8 +17,8 @@ import java.net.UnknownHostException;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
-import java.util.UUID;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -147,7 +147,7 @@ public class ConfigStore {
         determineHighestAssignedHueID();
 
         if (config.uuid.isEmpty()) {
-            config.uuid = UUID.randomUUID().toString();
+            config.uuid = getHueUUID();
             writeUUIDFuture = scheduler.schedule(() -> {
                 logger.info("No unique ID assigned yet. Assigning {} and restarting...", config.uuid);
                 WriteConfig.setUUID(configAdmin, config.uuid);
@@ -158,6 +158,19 @@ public class ConfigStore {
         }
     }
 
+    private static String getHueUUID() {
+        // Hue API example is AA:BB:CC:DD:EE:FF:00:11-XX
+        // XX is generated from the item.
+        final Random r = new Random();
+        int n = r.nextInt(255);
+        final StringBuilder uuid = new StringBuilder(String.format("%02X", n));
+        for (int i = 0; i < 7; i++) {
+            n = r.nextInt(255);
+            uuid.append(String.format(":%02X", n));
+        }
+        return uuid.toString();
+    }
+
     private @Nullable InetAddress byName(@Nullable String address) {
         if (address == null) {
             return null;
@@ -311,7 +324,7 @@ public class ConfigStore {
             metadataRegistry.add(new Metadata(key, String.valueOf(hueId), null));
         }
 
-        return String.valueOf(hueId);
+        return String.format("%02X", hueId);
     }
 
     public boolean isReady() {
index 17773a2520f4552d4a95273558cbc8ec609bc2dc..ce1abff7bd1d50f19f9d9e0a8faeeaa5069661d0 100644 (file)
@@ -126,7 +126,7 @@ public class HueEmulationService implements EventHandler {
         @Override
         public Set<Object> getSingletons() {
             return Set.of(userManagement, configurationAccess, lightItems, sensors, scenes, schedules, rules,
-                    statusResource, accessInterceptor);
+                    statusResource, accessInterceptor, requestCleaner);
         }
 
         Dictionary<String, String> serviceProperties() {
index 97d03ce22d053a1c8e74bcb9e60714402e8c7461..ff64840e42afcd393e85864076fc19eb3a8b720f 100644 (file)
@@ -95,8 +95,7 @@ class HueEmulationConfigWithRuntime extends Thread implements Runnable {
         }
     }
 
-    public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow(
-            @Nullable HueEmulationConfigWithRuntime ignored) {
+    public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow() {
         if (hasAlreadyBeenStarted) {
             logger.debug("Cannot restart thread");
             return future;
index 6ad3e5fc1823e4058b2c40882b207dd7e9fed212..8189554f3a3aad97af520c1fb06396931af67609 100644 (file)
@@ -349,8 +349,9 @@ public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConf
         }
         configChangeFuture = root.thenApply(this::createConfiguration)
                 .thenApplyAsync(this::performAddressTest, executor).thenApply(this::applyConfiguration)
-                .thenCompose(config::startNow)
-                .whenComplete((HueEmulationConfigWithRuntime config, @Nullable Throwable e) -> {
+                .thenCompose(c -> {
+                    return c.startNow();
+                }).whenComplete((HueEmulationConfigWithRuntime config, @Nullable Throwable e) -> {
                     if (e != null) {
                         logger.warn("Upnp server: Address test failed", e);
                     }
index bfa5da42b7e7586f771eaddcc386ae2ee4f16115..9b017e6be83ca024f60dd5f0982d6eee8cb230d3 100644 (file)
@@ -88,7 +88,7 @@ public class ItemUIDtoHueIDMappingTests {
         itemRegistry.add(item);
 
         String hueID = cs.mapItemUIDtoHueID(item);
-        assertThat(hueID, CoreMatchers.is("2"));
+        assertThat(hueID, CoreMatchers.is("02"));
 
         HueLightEntry device = cs.ds.lights.get(hueID);
         assertThat(device.item, is(item));
@@ -108,7 +108,7 @@ public class ItemUIDtoHueIDMappingTests {
         itemRegistry.add(item);
 
         String hueID = cs.mapItemUIDtoHueID(item);
-        assertThat(hueID, CoreMatchers.is("10"));
+        assertThat(hueID, CoreMatchers.is("0A"));
 
         HueLightEntry device = cs.ds.lights.get(hueID);
         assertThat(device.item, is(item));
index 63da3f003fad0b303d9925adff5ca66d4d86ac21..65f0797962808fc0f4d57ed8624f546fa4127bb0 100644 (file)
@@ -123,7 +123,7 @@ public class UpnpTests {
         }
 
         // UDP thread started?
-        r.startNow(r).get(5, TimeUnit.SECONDS);
+        r.startNow().get(5, TimeUnit.SECONDS);
         assertThat(subject.upnpAnnouncementThreadRunning(), is(true));
 
         // Send M-SEARCH UPNP "packet" and check if the result contains our bridge ID