```
* 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".
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;
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);
}
}
+ 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;
metadataRegistry.add(new Metadata(key, String.valueOf(hueId), null));
}
- return String.valueOf(hueId);
+ return String.format("%02X", hueId);
}
public boolean isReady() {
@Override
public Set<Object> getSingletons() {
return Set.of(userManagement, configurationAccess, lightItems, sensors, scenes, schedules, rules,
- statusResource, accessInterceptor);
+ statusResource, accessInterceptor, requestCleaner);
}
Dictionary<String, String> serviceProperties() {
}
}
- public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow(
- @Nullable HueEmulationConfigWithRuntime ignored) {
+ public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow() {
if (hasAlreadyBeenStarted) {
logger.debug("Cannot restart thread");
return future;
}
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);
}
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));
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));
}
// 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