diff --git a/bundles/org.openhab.io.hueemulation/README.md b/bundles/org.openhab.io.hueemulation/README.md index b82aa2639..5961bf284 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 c084eb9d0..4f411eae7 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 17773a252..ce1abff7b 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 97d03ce22..ff64840e4 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 6ad3e5fc1..8189554f3 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 bfa5da42b..9b017e6be 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 63da3f003..65f079796 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