[deconz] improve initialization (#9321)
Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>
This commit is contained in:
parent
941189ad55
commit
843ec092dc
|
@ -25,8 +25,6 @@ import java.util.stream.Stream;
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.core.library.types.DateTimeType;
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
import org.openhab.core.library.types.PercentType;
|
import org.openhab.core.library.types.PercentType;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link Util} class defines common utility methods
|
* The {@link Util} class defines common utility methods
|
||||||
|
@ -35,7 +33,6 @@ import org.slf4j.LoggerFactory;
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class Util {
|
public class Util {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);
|
|
||||||
|
|
||||||
public static String buildUrl(String host, int port, String... urlParts) {
|
public static String buildUrl(String host, int port, String... urlParts) {
|
||||||
StringBuilder url = new StringBuilder();
|
StringBuilder url = new StringBuilder();
|
||||||
|
|
|
@ -25,7 +25,6 @@ import java.util.stream.Stream;
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.deconz.internal.Util;
|
import org.openhab.binding.deconz.internal.Util;
|
||||||
import org.openhab.binding.deconz.internal.dto.BridgeFullState;
|
|
||||||
import org.openhab.binding.deconz.internal.dto.GroupMessage;
|
import org.openhab.binding.deconz.internal.dto.GroupMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.LightMessage;
|
import org.openhab.binding.deconz.internal.dto.LightMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.SensorMessage;
|
import org.openhab.binding.deconz.internal.dto.SensorMessage;
|
||||||
|
@ -70,10 +69,19 @@ public class ThingDiscoveryService extends AbstractDiscoveryService implements D
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startScan() {
|
public void startScan() {
|
||||||
final DeconzBridgeHandler handler = this.handler;
|
final DeconzBridgeHandler handler = this.handler;
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
handler.requestFullState(false);
|
handler.getBridgeFullState().thenAccept(fullState -> {
|
||||||
|
stopScan();
|
||||||
|
removeOlderResults(getTimestampOfLastScan());
|
||||||
|
fullState.ifPresent(state -> {
|
||||||
|
state.sensors.forEach(this::addSensor);
|
||||||
|
state.lights.forEach(this::addLight);
|
||||||
|
state.groups.forEach(this::addGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +290,6 @@ public class ThingDiscoveryService extends AbstractDiscoveryService implements D
|
||||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||||
if (handler instanceof DeconzBridgeHandler) {
|
if (handler instanceof DeconzBridgeHandler) {
|
||||||
this.handler = (DeconzBridgeHandler) handler;
|
this.handler = (DeconzBridgeHandler) handler;
|
||||||
((DeconzBridgeHandler) handler).setDiscoveryService(this);
|
|
||||||
this.bridgeUID = handler.getThing().getUID();
|
this.bridgeUID = handler.getThing().getUID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,20 +308,4 @@ public class ThingDiscoveryService extends AbstractDiscoveryService implements D
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
super.deactivate();
|
super.deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this method when a full bridge state request has been performed and either the fullState
|
|
||||||
* are known or a failure happened.
|
|
||||||
*
|
|
||||||
* @param fullState The fullState or null.
|
|
||||||
*/
|
|
||||||
public void stateRequestFinished(final @Nullable BridgeFullState fullState) {
|
|
||||||
stopScan();
|
|
||||||
removeOlderResults(getTimestampOfLastScan());
|
|
||||||
if (fullState != null) {
|
|
||||||
fullState.sensors.forEach(this::addSensor);
|
|
||||||
fullState.lights.forEach(this::addLight);
|
|
||||||
fullState.groups.forEach(this::addGroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.deconz.internal.types.ResourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http://dresden-elektronik.github.io/deconz-rest-doc/configuration/
|
* http://dresden-elektronik.github.io/deconz-rest-doc/configuration/
|
||||||
|
@ -42,4 +44,17 @@ public class BridgeFullState {
|
||||||
public Map<String, SensorMessage> sensors = Collections.emptyMap();
|
public Map<String, SensorMessage> sensors = Collections.emptyMap();
|
||||||
public Map<String, LightMessage> lights = Collections.emptyMap();
|
public Map<String, LightMessage> lights = Collections.emptyMap();
|
||||||
public Map<String, GroupMessage> groups = Collections.emptyMap();
|
public Map<String, GroupMessage> groups = Collections.emptyMap();
|
||||||
|
|
||||||
|
public @Nullable DeconzBaseMessage getMessage(ResourceType resourceType, String id) {
|
||||||
|
switch (resourceType) {
|
||||||
|
case LIGHTS:
|
||||||
|
return lights.get(id);
|
||||||
|
case SENSORS:
|
||||||
|
return sensors.get(id);
|
||||||
|
case GROUPS:
|
||||||
|
return groups.get(id);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,8 @@ package org.openhab.binding.deconz.internal.handler;
|
||||||
|
|
||||||
import static org.openhab.binding.deconz.internal.Util.buildUrl;
|
import static org.openhab.binding.deconz.internal.Util.buildUrl;
|
||||||
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
@ -46,8 +43,7 @@ import com.google.gson.Gson;
|
||||||
* @author Jan N. Klug - Refactored to abstract class
|
* @author Jan N. Klug - Refactored to abstract class
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extends BaseThingHandler
|
public abstract class DeconzBaseThingHandler extends BaseThingHandler implements WebSocketMessageListener {
|
||||||
implements WebSocketMessageListener {
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(DeconzBaseThingHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(DeconzBaseThingHandler.class);
|
||||||
protected final ResourceType resourceType;
|
protected final ResourceType resourceType;
|
||||||
protected ThingConfig config = new ThingConfig();
|
protected ThingConfig config = new ThingConfig();
|
||||||
|
@ -88,6 +84,15 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @Nullable DeconzBridgeHandler getBridgeHandler() {
|
||||||
|
Bridge bridge = getBridge();
|
||||||
|
if (bridge == null) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (DeconzBridgeHandler) bridge.getHandler();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||||
if (config.id.isEmpty()) {
|
if (config.id.isEmpty()) {
|
||||||
|
@ -98,12 +103,7 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
|
||||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||||
// the bridge is ONLINE, we can communicate with the gateway, so we update the connection parameters and
|
// the bridge is ONLINE, we can communicate with the gateway, so we update the connection parameters and
|
||||||
// register the listener
|
// register the listener
|
||||||
Bridge bridge = getBridge();
|
DeconzBridgeHandler bridgeHandler = getBridgeHandler();
|
||||||
if (bridge == null) {
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DeconzBridgeHandler bridgeHandler = (DeconzBridgeHandler) bridge.getHandler();
|
|
||||||
if (bridgeHandler == null) {
|
if (bridgeHandler == null) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||||
return;
|
return;
|
||||||
|
@ -129,14 +129,6 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* parse the initial state response message
|
|
||||||
*
|
|
||||||
* @param r AsyncHttpClient.Result with the state response result
|
|
||||||
* @return a message of the correct type
|
|
||||||
*/
|
|
||||||
protected abstract @Nullable T parseStateResponse(AsyncHttpClient.Result r);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* processes a newly received (initial) state response
|
* processes a newly received (initial) state response
|
||||||
*
|
*
|
||||||
|
@ -144,36 +136,26 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
|
||||||
*
|
*
|
||||||
* @param stateResponse
|
* @param stateResponse
|
||||||
*/
|
*/
|
||||||
protected abstract void processStateResponse(@Nullable T stateResponse);
|
protected abstract void processStateResponse(DeconzBaseMessage stateResponse);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a request to the REST API for retrieving the full light state with all data and configuration.
|
* Perform a request to the REST API for retrieving the full light state with all data and configuration.
|
||||||
*/
|
*/
|
||||||
protected void requestState(Consumer<@Nullable T> processor) {
|
protected void requestState(Consumer<DeconzBaseMessage> processor) {
|
||||||
AsyncHttpClient asyncHttpClient = http;
|
DeconzBridgeHandler bridgeHandler = getBridgeHandler();
|
||||||
if (asyncHttpClient == null) {
|
if (bridgeHandler != null) {
|
||||||
return;
|
bridgeHandler.getBridgeFullState()
|
||||||
}
|
.thenAccept(f -> f.map(s -> s.getMessage(resourceType, config.id)).ifPresentOrElse(message -> {
|
||||||
|
logger.trace("{} processing {}", thing.getUID(), message);
|
||||||
String url = buildUrl(bridgeConfig.host, bridgeConfig.httpPort, bridgeConfig.apikey,
|
processor.accept(message);
|
||||||
resourceType.getIdentifier(), config.id);
|
}, () -> {
|
||||||
logger.trace("Requesting URL for initial data: {}", url);
|
if (initializationJob != null) {
|
||||||
|
|
||||||
// Get initial data
|
|
||||||
asyncHttpClient.get(url, bridgeConfig.timeout).thenApply(this::parseStateResponse).exceptionally(e -> {
|
|
||||||
if (e instanceof SocketTimeoutException || e instanceof TimeoutException
|
|
||||||
|| e instanceof CompletionException) {
|
|
||||||
logger.debug("Get new state failed: ", e);
|
|
||||||
} else {
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
stopInitializationJob();
|
stopInitializationJob();
|
||||||
initializationJob = scheduler.schedule(() -> requestState(this::processStateResponse), 10,
|
initializationJob = scheduler.schedule(() -> requestState(this::processStateResponse), 10,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
return null;
|
}));
|
||||||
}).thenAccept(processor);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,10 +16,7 @@ import static org.openhab.binding.deconz.internal.BindingConstants.*;
|
||||||
import static org.openhab.binding.deconz.internal.Util.buildUrl;
|
import static org.openhab.binding.deconz.internal.Util.buildUrl;
|
||||||
|
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
@ -34,6 +31,7 @@ import org.openhab.binding.deconz.internal.dto.BridgeFullState;
|
||||||
import org.openhab.binding.deconz.internal.netutils.AsyncHttpClient;
|
import org.openhab.binding.deconz.internal.netutils.AsyncHttpClient;
|
||||||
import org.openhab.binding.deconz.internal.netutils.WebSocketConnection;
|
import org.openhab.binding.deconz.internal.netutils.WebSocketConnection;
|
||||||
import org.openhab.binding.deconz.internal.netutils.WebSocketConnectionListener;
|
import org.openhab.binding.deconz.internal.netutils.WebSocketConnectionListener;
|
||||||
|
import org.openhab.core.cache.ExpiringCacheAsync;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
import org.openhab.core.io.net.http.WebSocketFactory;
|
import org.openhab.core.io.net.http.WebSocketFactory;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
|
@ -64,7 +62,6 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(BRIDGE_TYPE);
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(BRIDGE_TYPE);
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(DeconzBridgeHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(DeconzBridgeHandler.class);
|
||||||
private @Nullable ThingDiscoveryService thingDiscoveryService;
|
|
||||||
private final WebSocketConnection websocket;
|
private final WebSocketConnection websocket;
|
||||||
private final AsyncHttpClient http;
|
private final AsyncHttpClient http;
|
||||||
private DeconzBridgeConfig config = new DeconzBridgeConfig();
|
private DeconzBridgeConfig config = new DeconzBridgeConfig();
|
||||||
|
@ -75,6 +72,8 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
private boolean ignoreConfigurationUpdate;
|
private boolean ignoreConfigurationUpdate;
|
||||||
private boolean websocketReconnect = false;
|
private boolean websocketReconnect = false;
|
||||||
|
|
||||||
|
private final ExpiringCacheAsync<Optional<BridgeFullState>> fullStateCache = new ExpiringCacheAsync<>(1000);
|
||||||
|
|
||||||
/** The poll frequency for the API Key verification */
|
/** The poll frequency for the API Key verification */
|
||||||
private static final int POLL_FREQUENCY_SEC = 10;
|
private static final int POLL_FREQUENCY_SEC = 10;
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
stopTimer();
|
stopTimer();
|
||||||
scheduledFuture = scheduler.schedule(() -> requestApiKey(), POLL_FREQUENCY_SEC, TimeUnit.SECONDS);
|
scheduledFuture = scheduler.schedule(() -> requestApiKey(), POLL_FREQUENCY_SEC, TimeUnit.SECONDS);
|
||||||
} else if (r.getResponseCode() == 200) {
|
} else if (r.getResponseCode() == 200) {
|
||||||
ApiKeyMessage[] response = gson.fromJson(r.getBody(), ApiKeyMessage[].class);
|
ApiKeyMessage[] response = Objects.requireNonNull(gson.fromJson(r.getBody(), ApiKeyMessage[].class));
|
||||||
if (response.length == 0) {
|
if (response.length == 0) {
|
||||||
throw new IllegalStateException("Authorisation request response is empty");
|
throw new IllegalStateException("Authorisation request response is empty");
|
||||||
}
|
}
|
||||||
|
@ -139,65 +138,65 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
configuration.put(CONFIG_APIKEY, config.apikey);
|
configuration.put(CONFIG_APIKEY, config.apikey);
|
||||||
updateConfiguration(configuration);
|
updateConfiguration(configuration);
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Waiting for configuration");
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Waiting for configuration");
|
||||||
requestFullState(true);
|
initializeBridgeState();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Unknown status code for authorisation request");
|
throw new IllegalStateException("Unknown status code for authorisation request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the response message to the REST API for retrieving the full bridge state with all sensors and switches
|
* get the full state of the bridge from the cache
|
||||||
* and configuration.
|
|
||||||
*
|
*
|
||||||
* @param r The response
|
* @return a CompletableFuture that returns an Optional of the bridge full state
|
||||||
*/
|
*/
|
||||||
private @Nullable BridgeFullState parseBridgeFullStateResponse(AsyncHttpClient.Result r) {
|
public CompletableFuture<Optional<BridgeFullState>> getBridgeFullState() {
|
||||||
|
return fullStateCache.getValue(this::refreshFullStateCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refresh the full bridge state (used for initial processing and state-lookup)
|
||||||
|
*
|
||||||
|
* @return Completable future with an Optional of the BridgeFullState
|
||||||
|
*/
|
||||||
|
private CompletableFuture<Optional<BridgeFullState>> refreshFullStateCache() {
|
||||||
|
logger.trace("{} starts refreshing the fullStateCache", thing.getUID());
|
||||||
|
if (config.apikey == null) {
|
||||||
|
return CompletableFuture.completedFuture(Optional.empty());
|
||||||
|
}
|
||||||
|
String url = buildUrl(config.getHostWithoutPort(), config.httpPort, config.apikey);
|
||||||
|
return http.get(url, config.timeout).thenApply(r -> {
|
||||||
if (r.getResponseCode() == 403) {
|
if (r.getResponseCode() == 403) {
|
||||||
return null;
|
return Optional.ofNullable((BridgeFullState) null);
|
||||||
} else if (r.getResponseCode() == 200) {
|
} else if (r.getResponseCode() == 200) {
|
||||||
return gson.fromJson(r.getBody(), BridgeFullState.class);
|
return Optional.ofNullable(gson.fromJson(r.getBody(), BridgeFullState.class));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Unknown status code for full state request");
|
throw new IllegalStateException("Unknown status code for full state request");
|
||||||
}
|
}
|
||||||
|
}).handle((v, t) -> {
|
||||||
|
if (t == null) {
|
||||||
|
return v;
|
||||||
|
} else if (t instanceof SocketTimeoutException || t instanceof TimeoutException
|
||||||
|
|| t instanceof CompletionException) {
|
||||||
|
logger.debug("Get full state failed", t);
|
||||||
|
} else if (t != null) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, t.getMessage());
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a request to the REST API for retrieving the full bridge state with all sensors and switches
|
* Perform a request to the REST API for retrieving the full bridge state with all sensors and switches
|
||||||
* and configuration.
|
* and configuration.
|
||||||
*/
|
*/
|
||||||
public void requestFullState(boolean isInitialRequest) {
|
public void initializeBridgeState() {
|
||||||
if (config.apikey == null) {
|
getBridgeFullState().thenAccept(fullState -> fullState.ifPresentOrElse(state -> {
|
||||||
return;
|
if (state.config.name.isEmpty()) {
|
||||||
}
|
|
||||||
String url = buildUrl(config.getHostWithoutPort(), config.httpPort, config.apikey);
|
|
||||||
http.get(url, config.timeout).thenApply(this::parseBridgeFullStateResponse).exceptionally(e -> {
|
|
||||||
if (e instanceof SocketTimeoutException || e instanceof TimeoutException
|
|
||||||
|| e instanceof CompletionException) {
|
|
||||||
logger.debug("Get full state failed", e);
|
|
||||||
} else {
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).whenComplete((value, error) -> {
|
|
||||||
final ThingDiscoveryService thingDiscoveryService = this.thingDiscoveryService;
|
|
||||||
if (thingDiscoveryService != null) {
|
|
||||||
// Hand over sensors to discovery service
|
|
||||||
thingDiscoveryService.stateRequestFinished(value);
|
|
||||||
}
|
|
||||||
}).thenAccept(fullState -> {
|
|
||||||
if (fullState == null) {
|
|
||||||
if (isInitialRequest) {
|
|
||||||
scheduledFuture = scheduler.schedule(() -> requestFullState(true), POLL_FREQUENCY_SEC,
|
|
||||||
TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fullState.config.name.isEmpty()) {
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
||||||
"You are connected to a HUE bridge, not a deCONZ software!");
|
"You are connected to a HUE bridge, not a deCONZ software!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fullState.config.websocketport == 0) {
|
if (state.config.websocketport == 0) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
||||||
"deCONZ software too old. No websocket support!");
|
"deCONZ software too old. No websocket support!");
|
||||||
return;
|
return;
|
||||||
|
@ -205,35 +204,37 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
|
|
||||||
// Add some information about the bridge
|
// Add some information about the bridge
|
||||||
Map<String, String> editProperties = editProperties();
|
Map<String, String> editProperties = editProperties();
|
||||||
editProperties.put("apiversion", fullState.config.apiversion);
|
editProperties.put("apiversion", state.config.apiversion);
|
||||||
editProperties.put("swversion", fullState.config.swversion);
|
editProperties.put("swversion", state.config.swversion);
|
||||||
editProperties.put("fwversion", fullState.config.fwversion);
|
editProperties.put("fwversion", state.config.fwversion);
|
||||||
editProperties.put("uuid", fullState.config.uuid);
|
editProperties.put("uuid", state.config.uuid);
|
||||||
editProperties.put("zigbeechannel", String.valueOf(fullState.config.zigbeechannel));
|
editProperties.put("zigbeechannel", String.valueOf(state.config.zigbeechannel));
|
||||||
editProperties.put("ipaddress", fullState.config.ipaddress);
|
editProperties.put("ipaddress", state.config.ipaddress);
|
||||||
ignoreConfigurationUpdate = true;
|
ignoreConfigurationUpdate = true;
|
||||||
updateProperties(editProperties);
|
updateProperties(editProperties);
|
||||||
ignoreConfigurationUpdate = false;
|
ignoreConfigurationUpdate = false;
|
||||||
|
|
||||||
// Use requested websocket port if no specific port is given
|
// Use requested websocket port if no specific port is given
|
||||||
websocketPort = config.port == 0 ? fullState.config.websocketport : config.port;
|
websocketPort = config.port == 0 ? state.config.websocketport : config.port;
|
||||||
websocketReconnect = true;
|
websocketReconnect = true;
|
||||||
startWebsocket();
|
startWebsocket();
|
||||||
}).exceptionally(e -> {
|
}, () -> {
|
||||||
|
// initial response was empty, re-trying in POLL_FREQUENCY_SEC seconds
|
||||||
|
scheduledFuture = scheduler.schedule(this::initializeBridgeState, POLL_FREQUENCY_SEC, TimeUnit.SECONDS);
|
||||||
|
})).exceptionally(e -> {
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
|
||||||
} else {
|
} else {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE);
|
||||||
}
|
}
|
||||||
logger.warn("Full state parsing failed", e);
|
logger.warn("Initial full state parsing failed", e);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the websocket connection.
|
* Starts the websocket connection.
|
||||||
*
|
* {@link #initializeBridgeState} need to be called first to obtain the websocket port.
|
||||||
* {@link #requestFullState} need to be called first to obtain the websocket port.
|
|
||||||
*/
|
*/
|
||||||
private void startWebsocket() {
|
private void startWebsocket() {
|
||||||
if (websocket.isConnected() || websocketPort == 0 || websocketReconnect == false) {
|
if (websocket.isConnected() || websocketPort == 0 || websocketReconnect == false) {
|
||||||
|
@ -269,7 +270,7 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
if (config.apikey == null) {
|
if (config.apikey == null) {
|
||||||
requestApiKey();
|
requestApiKey();
|
||||||
} else {
|
} else {
|
||||||
requestFullState(true);
|
initializeBridgeState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,13 +325,4 @@ public class DeconzBridgeHandler extends BaseBridgeHandler implements WebSocketC
|
||||||
public DeconzBridgeConfig getBridgeConfig() {
|
public DeconzBridgeConfig getBridgeConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the {@link ThingDiscoveryService}. Informs the bridge handler about the service.
|
|
||||||
*
|
|
||||||
* @param thingDiscoveryService The service
|
|
||||||
*/
|
|
||||||
public void setDiscoveryService(ThingDiscoveryService thingDiscoveryService) {
|
|
||||||
this.thingDiscoveryService = thingDiscoveryService;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,11 @@ import static org.openhab.binding.deconz.internal.BindingConstants.*;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
|
||||||
import org.openhab.binding.deconz.internal.Util;
|
import org.openhab.binding.deconz.internal.Util;
|
||||||
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.GroupAction;
|
import org.openhab.binding.deconz.internal.dto.GroupAction;
|
||||||
import org.openhab.binding.deconz.internal.dto.GroupMessage;
|
import org.openhab.binding.deconz.internal.dto.GroupMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.GroupState;
|
import org.openhab.binding.deconz.internal.dto.GroupState;
|
||||||
import org.openhab.binding.deconz.internal.netutils.AsyncHttpClient;
|
|
||||||
import org.openhab.binding.deconz.internal.types.ResourceType;
|
import org.openhab.binding.deconz.internal.types.ResourceType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
|
@ -55,7 +53,7 @@ import com.google.gson.Gson;
|
||||||
* @author Jan N. Klug - Initial contribution
|
* @author Jan N. Klug - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class GroupThingHandler extends DeconzBaseThingHandler<GroupMessage> {
|
public class GroupThingHandler extends DeconzBaseThingHandler {
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_LIGHTGROUP);
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_LIGHTGROUP);
|
||||||
private final Logger logger = LoggerFactory.getLogger(GroupThingHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(GroupThingHandler.class);
|
||||||
|
|
||||||
|
@ -128,21 +126,7 @@ public class GroupThingHandler extends DeconzBaseThingHandler<GroupMessage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable GroupMessage parseStateResponse(AsyncHttpClient.Result r) {
|
protected void processStateResponse(DeconzBaseMessage stateResponse) {
|
||||||
if (r.getResponseCode() == 403) {
|
|
||||||
return null;
|
|
||||||
} else if (r.getResponseCode() == 200) {
|
|
||||||
return gson.fromJson(r.getBody(), GroupMessage.class);
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unknown status code " + r.getResponseCode() + " for full state request");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processStateResponse(@Nullable GroupMessage stateResponse) {
|
|
||||||
if (stateResponse == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
messageReceived(config.id, stateResponse);
|
messageReceived(config.id, stateResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.openhab.binding.deconz.internal.Util;
|
||||||
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.LightMessage;
|
import org.openhab.binding.deconz.internal.dto.LightMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.LightState;
|
import org.openhab.binding.deconz.internal.dto.LightState;
|
||||||
import org.openhab.binding.deconz.internal.netutils.AsyncHttpClient;
|
|
||||||
import org.openhab.binding.deconz.internal.types.ResourceType;
|
import org.openhab.binding.deconz.internal.types.ResourceType;
|
||||||
import org.openhab.core.library.types.*;
|
import org.openhab.core.library.types.*;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
@ -58,7 +57,7 @@ import com.google.gson.Gson;
|
||||||
* @author Jan N. Klug - Initial contribution
|
* @author Jan N. Klug - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
|
public class LightThingHandler extends DeconzBaseThingHandler {
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_COLOR_TEMPERATURE_LIGHT,
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_COLOR_TEMPERATURE_LIGHT,
|
||||||
THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_COLOR_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT, THING_TYPE_ONOFF_LIGHT,
|
THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_COLOR_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT, THING_TYPE_ONOFF_LIGHT,
|
||||||
THING_TYPE_WINDOW_COVERING, THING_TYPE_WARNING_DEVICE, THING_TYPE_DOORLOCK);
|
THING_TYPE_WINDOW_COVERING, THING_TYPE_WARNING_DEVICE, THING_TYPE_DOORLOCK);
|
||||||
|
@ -263,11 +262,12 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable LightMessage parseStateResponse(AsyncHttpClient.Result r) {
|
protected void processStateResponse(DeconzBaseMessage stateResponse) {
|
||||||
if (r.getResponseCode() == 403) {
|
if (!(stateResponse instanceof LightMessage)) {
|
||||||
return null;
|
return;
|
||||||
} else if (r.getResponseCode() == 200) {
|
}
|
||||||
LightMessage lightMessage = Objects.requireNonNull(gson.fromJson(r.getBody(), LightMessage.class));
|
|
||||||
|
LightMessage lightMessage = (LightMessage) stateResponse;
|
||||||
if (needsPropertyUpdate) {
|
if (needsPropertyUpdate) {
|
||||||
// if we did not receive an ctmin/ctmax, then we probably don't need it
|
// if we did not receive an ctmin/ctmax, then we probably don't need it
|
||||||
needsPropertyUpdate = false;
|
needsPropertyUpdate = false;
|
||||||
|
@ -276,28 +276,17 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
|
||||||
Integer ctmin = lightMessage.ctmin;
|
Integer ctmin = lightMessage.ctmin;
|
||||||
if (ctmin != null && ctmax != null) {
|
if (ctmin != null && ctmax != null) {
|
||||||
Map<String, String> properties = new HashMap<>(thing.getProperties());
|
Map<String, String> properties = new HashMap<>(thing.getProperties());
|
||||||
properties.put(PROPERTY_CT_MAX,
|
properties.put(PROPERTY_CT_MAX, Integer.toString(Util.constrainToRange(ctmax, ZCL_CT_MIN, ZCL_CT_MAX)));
|
||||||
Integer.toString(Util.constrainToRange(ctmax, ZCL_CT_MIN, ZCL_CT_MAX)));
|
properties.put(PROPERTY_CT_MIN, Integer.toString(Util.constrainToRange(ctmin, ZCL_CT_MIN, ZCL_CT_MAX)));
|
||||||
properties.put(PROPERTY_CT_MIN,
|
|
||||||
Integer.toString(Util.constrainToRange(ctmin, ZCL_CT_MIN, ZCL_CT_MAX)));
|
|
||||||
updateProperties(properties);
|
updateProperties(properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lightMessage;
|
|
||||||
} else {
|
if (lightMessage.state.effect != null) {
|
||||||
throw new IllegalStateException("Unknown status code " + r.getResponseCode() + " for full state request");
|
checkAndUpdateEffectChannels(lightMessage);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
messageReceived(config.id, lightMessage);
|
||||||
protected void processStateResponse(@Nullable LightMessage stateResponse) {
|
|
||||||
if (stateResponse == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stateResponse.state.effect != null) {
|
|
||||||
checkAndUpdateEffectChannels(stateResponse);
|
|
||||||
}
|
|
||||||
messageReceived(config.id, stateResponse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum EffectLightModel {
|
private enum EffectLightModel {
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.SensorConfig;
|
import org.openhab.binding.deconz.internal.dto.SensorConfig;
|
||||||
import org.openhab.binding.deconz.internal.dto.SensorMessage;
|
import org.openhab.binding.deconz.internal.dto.SensorMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.SensorState;
|
import org.openhab.binding.deconz.internal.dto.SensorState;
|
||||||
import org.openhab.binding.deconz.internal.netutils.AsyncHttpClient;
|
|
||||||
import org.openhab.binding.deconz.internal.types.ResourceType;
|
import org.openhab.binding.deconz.internal.types.ResourceType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
@ -63,7 +62,7 @@ import com.google.gson.Gson;
|
||||||
* @author Lukas Agethen - Refactored to provide better extensibility
|
* @author Lukas Agethen - Refactored to provide better extensibility
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler<SensorMessage> {
|
public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler {
|
||||||
private final Logger logger = LoggerFactory.getLogger(SensorBaseThingHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(SensorBaseThingHandler.class);
|
||||||
/**
|
/**
|
||||||
* The sensor state. Contains all possible fields for all supported sensors and switches
|
* The sensor state. Contains all possible fields for all supported sensors and switches
|
||||||
|
@ -106,25 +105,15 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler<Sens
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable SensorMessage parseStateResponse(AsyncHttpClient.Result r) {
|
protected void processStateResponse(DeconzBaseMessage stateResponse) {
|
||||||
if (r.getResponseCode() == 403) {
|
if (!(stateResponse instanceof SensorMessage)) {
|
||||||
return null;
|
|
||||||
} else if (r.getResponseCode() == 200) {
|
|
||||||
return gson.fromJson(r.getBody(), SensorMessage.class);
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unknown status code " + r.getResponseCode() + " for full state request");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processStateResponse(@Nullable SensorMessage stateResponse) {
|
|
||||||
logger.trace("{} received {}", thing.getUID(), stateResponse);
|
|
||||||
if (stateResponse == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SensorConfig newSensorConfig = stateResponse.config;
|
|
||||||
|
SensorMessage sensorMessage = (SensorMessage) stateResponse;
|
||||||
|
SensorConfig newSensorConfig = sensorMessage.config;
|
||||||
sensorConfig = newSensorConfig != null ? newSensorConfig : new SensorConfig();
|
sensorConfig = newSensorConfig != null ? newSensorConfig : new SensorConfig();
|
||||||
SensorState newSensorState = stateResponse.state;
|
SensorState newSensorState = sensorMessage.state;
|
||||||
sensorState = newSensorState != null ? newSensorState : new SensorState();
|
sensorState = newSensorState != null ? newSensorState : new SensorState();
|
||||||
|
|
||||||
// Add some information about the sensor
|
// Add some information about the sensor
|
||||||
|
@ -139,9 +128,9 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler<Sens
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> editProperties = editProperties();
|
Map<String, String> editProperties = editProperties();
|
||||||
editProperties.put(Thing.PROPERTY_FIRMWARE_VERSION, stateResponse.swversion);
|
editProperties.put(Thing.PROPERTY_FIRMWARE_VERSION, sensorMessage.swversion);
|
||||||
editProperties.put(Thing.PROPERTY_MODEL_ID, stateResponse.modelid);
|
editProperties.put(Thing.PROPERTY_MODEL_ID, sensorMessage.modelid);
|
||||||
editProperties.put(UNIQUE_ID, stateResponse.uniqueid);
|
editProperties.put(UNIQUE_ID, sensorMessage.uniqueid);
|
||||||
ignoreConfigurationUpdate = true;
|
ignoreConfigurationUpdate = true;
|
||||||
updateProperties(editProperties);
|
updateProperties(editProperties);
|
||||||
|
|
||||||
|
@ -166,7 +155,7 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler<Sens
|
||||||
// So to monitor a sensor is still alive, the "last seen" is necessary.
|
// So to monitor a sensor is still alive, the "last seen" is necessary.
|
||||||
// Because "last seen" is never updated by the WebSocket API - if this is supported, then we have to
|
// Because "last seen" is never updated by the WebSocket API - if this is supported, then we have to
|
||||||
// manually poll it after the defined time
|
// manually poll it after the defined time
|
||||||
String lastSeen = stateResponse.lastseen;
|
String lastSeen = sensorMessage.lastseen;
|
||||||
if (lastSeen != null && config.lastSeenPolling > 0) {
|
if (lastSeen != null && config.lastSeenPolling > 0) {
|
||||||
createChannel(CHANNEL_LAST_SEEN, ChannelKind.STATE);
|
createChannel(CHANNEL_LAST_SEEN, ChannelKind.STATE);
|
||||||
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen));
|
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen));
|
||||||
|
@ -179,10 +168,7 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler<Sens
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processLastSeen(@Nullable SensorMessage stateResponse) {
|
private void processLastSeen(DeconzBaseMessage stateResponse) {
|
||||||
if (stateResponse == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String lastSeen = stateResponse.lastseen;
|
String lastSeen = stateResponse.lastseen;
|
||||||
if (lastSeen != null) {
|
if (lastSeen != null) {
|
||||||
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen));
|
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen));
|
||||||
|
|
|
@ -26,9 +26,6 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
||||||
import org.openhab.binding.deconz.internal.dto.GroupMessage;
|
|
||||||
import org.openhab.binding.deconz.internal.dto.LightMessage;
|
|
||||||
import org.openhab.binding.deconz.internal.dto.SensorMessage;
|
|
||||||
import org.openhab.binding.deconz.internal.types.ResourceType;
|
import org.openhab.binding.deconz.internal.types.ResourceType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -45,10 +42,6 @@ import com.google.gson.Gson;
|
||||||
@WebSocket
|
@WebSocket
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class WebSocketConnection {
|
public class WebSocketConnection {
|
||||||
private static final Map<ResourceType, Class<? extends DeconzBaseMessage>> EXPECTED_MESSAGE_TYPES = Map.of(
|
|
||||||
ResourceType.GROUPS, GroupMessage.class, ResourceType.LIGHTS, LightMessage.class, ResourceType.SENSORS,
|
|
||||||
SensorMessage.class);
|
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(WebSocketConnection.class);
|
private final Logger logger = LoggerFactory.getLogger(WebSocketConnection.class);
|
||||||
|
|
||||||
private final WebSocketClient client;
|
private final WebSocketClient client;
|
||||||
|
@ -130,7 +123,7 @@ public class WebSocketConnection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<? extends DeconzBaseMessage> expectedMessageType = EXPECTED_MESSAGE_TYPES.get(changedMessage.r);
|
Class<? extends DeconzBaseMessage> expectedMessageType = changedMessage.r.getExpectedMessageType();
|
||||||
if (expectedMessageType == null) {
|
if (expectedMessageType == null) {
|
||||||
logger.warn("BUG! Could not get expected message type for resource type {}. Please report this incident.",
|
logger.warn("BUG! Could not get expected message type for resource type {}. Please report this incident.",
|
||||||
changedMessage.r);
|
changedMessage.r);
|
||||||
|
|
|
@ -17,6 +17,11 @@ import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
|
||||||
|
import org.openhab.binding.deconz.internal.dto.GroupMessage;
|
||||||
|
import org.openhab.binding.deconz.internal.dto.LightMessage;
|
||||||
|
import org.openhab.binding.deconz.internal.dto.SensorMessage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -27,10 +32,10 @@ import org.slf4j.LoggerFactory;
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public enum ResourceType {
|
public enum ResourceType {
|
||||||
GROUPS("groups", "action"),
|
GROUPS("groups", "action", GroupMessage.class),
|
||||||
LIGHTS("lights", "state"),
|
LIGHTS("lights", "state", LightMessage.class),
|
||||||
SENSORS("sensors", "config"),
|
SENSORS("sensors", "config", SensorMessage.class),
|
||||||
UNKNOWN("", "");
|
UNKNOWN("", "", null);
|
||||||
|
|
||||||
private static final Map<String, ResourceType> MAPPING = Arrays.stream(ResourceType.values())
|
private static final Map<String, ResourceType> MAPPING = Arrays.stream(ResourceType.values())
|
||||||
.collect(Collectors.toMap(v -> v.identifier, v -> v));
|
.collect(Collectors.toMap(v -> v.identifier, v -> v));
|
||||||
|
@ -38,10 +43,13 @@ public enum ResourceType {
|
||||||
|
|
||||||
private String identifier;
|
private String identifier;
|
||||||
private String commandUrl;
|
private String commandUrl;
|
||||||
|
private @Nullable Class<? extends DeconzBaseMessage> expectedMessageType;
|
||||||
|
|
||||||
ResourceType(String identifier, String commandUrl) {
|
ResourceType(String identifier, String commandUrl,
|
||||||
|
@Nullable Class<? extends DeconzBaseMessage> expectedMessageType) {
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
this.commandUrl = commandUrl;
|
this.commandUrl = commandUrl;
|
||||||
|
this.expectedMessageType = expectedMessageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +70,15 @@ public enum ResourceType {
|
||||||
return commandUrl;
|
return commandUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the expected message type for this resource type
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public @Nullable Class<? extends DeconzBaseMessage> getExpectedMessageType() {
|
||||||
|
return expectedMessageType;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the resource type from a string
|
* get the resource type from a string
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,6 +20,8 @@ import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -78,11 +80,12 @@ public class DeconzTest {
|
||||||
assertEquals(6, bridgeFullState.lights.size());
|
assertEquals(6, bridgeFullState.lights.size());
|
||||||
assertEquals(9, bridgeFullState.sensors.size());
|
assertEquals(9, bridgeFullState.sensors.size());
|
||||||
|
|
||||||
|
Mockito.doAnswer(answer -> CompletableFuture.completedFuture(Optional.of(bridgeFullState))).when(bridgeHandler)
|
||||||
|
.getBridgeFullState();
|
||||||
ThingDiscoveryService discoveryService = new ThingDiscoveryService();
|
ThingDiscoveryService discoveryService = new ThingDiscoveryService();
|
||||||
discoveryService.setThingHandler(bridgeHandler);
|
discoveryService.setThingHandler(bridgeHandler);
|
||||||
discoveryService.addDiscoveryListener(discoveryListener);
|
discoveryService.addDiscoveryListener(discoveryListener);
|
||||||
|
discoveryService.startScan();
|
||||||
discoveryService.stateRequestFinished(bridgeFullState);
|
|
||||||
Mockito.verify(discoveryListener, times(20)).thingDiscovered(any(), any());
|
Mockito.verify(discoveryListener, times(20)).thingDiscovered(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue