From ab7ac79fab9415b1e4a15cbfcaa4ca8d7f9cf16c Mon Sep 17 00:00:00 2001 From: Mike Major Date: Sun, 29 Nov 2020 03:46:35 +0000 Subject: [PATCH] [hueemulation] Changes to fix pairing and device discovery with Alexa (#9164) Signed-off-by: Mike Major --- bundles/org.openhab.io.hueemulation/README.md | 9 +++++++++ .../io/hueemulation/internal/ConfigStore.java | 19 ++++++++++++++++--- .../internal/HueEmulationService.java | 2 +- .../upnp/HueEmulationConfigWithRuntime.java | 3 +-- .../internal/upnp/UpnpServer.java | 5 +++-- .../rest/ItemUIDtoHueIDMappingTests.java | 4 ++-- .../hueemulation/internal/upnp/UpnpTests.java | 2 +- 7 files changed, 33 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.io.hueemulation/README.md b/bundles/org.openhab.io.hueemulation/README.md index b82aa26396..5961bf284b 100644 --- a/bundles/org.openhab.io.hueemulation/README.md +++ b/bundles/org.openhab.io.hueemulation/README.md @@ -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". diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java index c084eb9d07..4f411eae7f 100644 --- a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java @@ -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() { diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java index 17773a2520..ce1abff7bd 100644 --- a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java @@ -126,7 +126,7 @@ public class HueEmulationService implements EventHandler { @Override public Set getSingletons() { return Set.of(userManagement, configurationAccess, lightItems, sensors, scenes, schedules, rules, - statusResource, accessInterceptor); + statusResource, accessInterceptor, requestCleaner); } Dictionary serviceProperties() { diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java index 97d03ce22d..ff64840e42 100644 --- a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java @@ -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; diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java index 6ad3e5fc18..8189554f3a 100644 --- a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java @@ -349,8 +349,9 @@ public class UpnpServer extends HttpServlet implements Consumer { + .thenCompose(c -> { + return c.startNow(); + }).whenComplete((HueEmulationConfigWithRuntime config, @Nullable Throwable e) -> { if (e != null) { logger.warn("Upnp server: Address test failed", e); } diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java index bfa5da42b7..9b017e6be8 100644 --- a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java @@ -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)); diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java index 63da3f003f..65f0797962 100644 --- a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java @@ -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 -- 2.47.3