Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
various lifecycle improvements and fixes on Tradfri (#4530)
Browse files Browse the repository at this point in the history
Signed-off-by: Kai Kreuzer <kai@openhab.org>
  • Loading branch information
kaikreuzer authored and sjsf committed Nov 10, 2017
1 parent 271f567 commit a06703e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 63 deletions.
Expand Up @@ -26,6 +26,7 @@
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.pskstore.StaticPskStore;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.binding.tradfri.TradfriBindingConstants;
import org.eclipse.smarthome.binding.tradfri.internal.CoapCallback;
import org.eclipse.smarthome.binding.tradfri.internal.DeviceUpdateListener;
Expand Down Expand Up @@ -73,7 +74,7 @@ public class TradfriGatewayHandler extends BaseBridgeHandler implements CoapCall

private ScheduledFuture<?> scanJob;

public TradfriGatewayHandler(Bridge bridge) {
public TradfriGatewayHandler(@NonNull Bridge bridge) {
super(bridge);
}

Expand All @@ -97,7 +98,7 @@ public void initialize() {
"Either security code or identity and pre-shared key must be provided in the configuration!");
return;
} else {
setupCoapClient();
establishConnection();
}
} else {
if (isOldFirmware()) {
Expand All @@ -112,21 +113,26 @@ public void initialize() {
editedConfig.put(TradfriBindingConstants.GATEWAY_CONFIG_PRE_SHARED_KEY, configuration.code);
updateConfiguration(editedConfig);

setupCoapClient();
establishConnection();
} else {
/*
* Running async operation to retrieve new <'identity','key'> pair
*/
scheduler.execute(this::obtainIdentityAndPreSharedKey);
updateStatus(ThingStatus.UNKNOWN);
// Running async operation to retrieve new <'identity','key'> pair
scheduler.execute(() -> {
boolean success = obtainIdentityAndPreSharedKey();
if (success) {
establishConnection();
}
});
}
}
}

private void setupCoapClient() {
private void establishConnection() {
TradfriGatewayConfig configuration = getConfigAs(TradfriGatewayConfig.class);

this.gatewayURI = "coaps://" + configuration.host + ":" + configuration.port + "/" + DEVICES;
this.gatewayInfoURI = "coaps://" + configuration.host + ":" + configuration.port + "/" + GATEWAY + "/" + GATEWAY_DETAILS;
this.gatewayInfoURI = "coaps://" + configuration.host + ":" + configuration.port + "/" + GATEWAY + "/"
+ GATEWAY_DETAILS;
try {
URI uri = new URI(gatewayURI);
deviceClient = new TradfriCoapClient(uri);
Expand All @@ -147,7 +153,14 @@ private void setupCoapClient() {
scanJob = scheduler.scheduleWithFixedDelay(this::startScan, 0, 1, TimeUnit.MINUTES);
}

protected void obtainIdentityAndPreSharedKey() {
/**
* Authenticates against the gateway with the security code in order to receive a pre-shared key for a newly
* generated identity.
* As this requires a remote request, this method might be long-running.
*
* @return true, if credentials were successfully obtained, false otherwise
*/
protected boolean obtainIdentityAndPreSharedKey() {
TradfriGatewayConfig configuration = getConfigAs(TradfriGatewayConfig.class);

String identity = UUID.randomUUID().toString().replace("-", "");
Expand All @@ -165,6 +178,7 @@ protected void obtainIdentityAndPreSharedKey() {
authUrl = "coaps://" + configuration.host + ":" + configuration.port + "/15011/9063";

CoapClient deviceClient = new CoapClient(new URI(authUrl));
deviceClient.setTimeout(TimeUnit.SECONDS.toMillis(10));
deviceClient.setEndpoint(authEndpoint);

JsonObject json = new JsonObject();
Expand All @@ -175,32 +189,44 @@ protected void obtainIdentityAndPreSharedKey() {
authEndpoint.destroy();
deviceClient.shutdown();

if (gatewayResponse == null) {
// seems we ran in a timeout, which potentially also happens
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"No response from gateway. Might be due to an invalid security code.");
return false;
}

if (gatewayResponse.isSuccess()) {
responseText = gatewayResponse.getResponseText();
json = new JsonParser().parse(responseText).getAsJsonObject();
preSharedKey = json.get(NEW_PSK_BY_GW).getAsString();

if (isNullOrEmpty(preSharedKey)) {
logger.error("Pre-shared key was not obtain successfully");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Pre-shared key was not obtain successfully");
logger.error("Received pre-shared key is empty for thing {} on gateway at {}", getThing().getUID(),
configuration.host);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Pre-shared key was not obtain successfully");
return false;
} else {
logger.info("Recieved pre-shared key for gateway '{}'", configuration.host);
logger.debug("Using identity '{}' with pre-shared key '{}'. Code can be discarded now", identity, preSharedKey);
logger.info("Received pre-shared key for gateway '{}'", configuration.host);
logger.debug("Using identity '{}' with pre-shared key '{}'.", identity, preSharedKey);

Configuration editedConfig = editConfiguration();
editedConfig.put(TradfriBindingConstants.GATEWAY_CONFIG_CODE, null);
editedConfig.put(TradfriBindingConstants.GATEWAY_CONFIG_IDENTITY, identity);
editedConfig.put(TradfriBindingConstants.GATEWAY_CONFIG_PRE_SHARED_KEY, preSharedKey);
updateConfiguration(editedConfig);

setupCoapClient();
return true;
}
} else {
logger.warn("Failed obtaining pre-shared key for identity '{}' (response code '{}', response text '{}')",
logger.warn(
"Failed obtaining pre-shared key for identity '{}' (response code '{}', response text '{}')",
identity, gatewayResponse.getCode(),
isNullOrEmpty(gatewayResponse.getResponseText()) ? "<empty>" : gatewayResponse.getResponseText());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
String.format("Failed obtaining pre-shared key with status code '%s'", gatewayResponse.getCode()));
isNullOrEmpty(gatewayResponse.getResponseText()) ? "<empty>"
: gatewayResponse.getResponseText());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String
.format("Failed obtaining pre-shared key with status code '%s'", gatewayResponse.getCode()));
}
} catch (URISyntaxException e) {
logger.error("Illegal gateway URI '{}'", authUrl, e);
Expand All @@ -210,6 +236,7 @@ protected void obtainIdentityAndPreSharedKey() {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Invalid response recieved from gateway '%s'", responseText));
}
return false;
}

@Override
Expand Down Expand Up @@ -339,7 +366,7 @@ private boolean isNullOrEmpty(String string) {

/**
* Checks current firmware in the thing properties and compares it with the value of {@link #MIN_SUPPORTED_VERSION}
*
*
* @return true if current firmware is older than {@value #MIN_SUPPORTED_VERSION}
*/
private boolean isOldFirmware() {
Expand Down
Expand Up @@ -114,24 +114,26 @@ private void setColor(HSBType hsb) {

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
logger.debug("Refreshing channel {}", channelUID);
coapClient.asyncGet(this);
return;
}
if (active) {
if (command instanceof RefreshType) {
logger.debug("Refreshing channel {}", channelUID);
coapClient.asyncGet(this);
return;
}

switch (channelUID.getId()) {
case CHANNEL_BRIGHTNESS:
handleBrightnessCommand(command);
break;
case CHANNEL_COLOR_TEMPERATURE:
handleColorTemperatureCommand(command);
break;
case CHANNEL_COLOR:
handleColorCommand(command);
break;
default:
logger.error("Unknown channel UID {}", channelUID);
switch (channelUID.getId()) {
case CHANNEL_BRIGHTNESS:
handleBrightnessCommand(command);
break;
case CHANNEL_COLOR_TEMPERATURE:
handleColorTemperatureCommand(command);
break;
case CHANNEL_COLOR:
handleColorCommand(command);
break;
default:
logger.error("Unknown channel UID {}", channelUID);
}
}
}

Expand Down
Expand Up @@ -7,14 +7,13 @@
*/
package org.eclipse.smarthome.binding.tradfri.handler;

import static org.eclipse.smarthome.core.thing.Thing.PROPERTY_FIRMWARE_VERSION;
import static org.eclipse.smarthome.core.thing.Thing.PROPERTY_MODEL_ID;
import static org.eclipse.smarthome.core.thing.Thing.PROPERTY_VENDOR;
import static org.eclipse.smarthome.core.thing.Thing.*;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.binding.tradfri.internal.CoapCallback;
import org.eclipse.smarthome.binding.tradfri.internal.TradfriCoapClient;
import org.eclipse.smarthome.binding.tradfri.internal.config.TradfriDeviceConfig;
Expand Down Expand Up @@ -46,45 +45,40 @@ public abstract class TradfriThingHandler extends BaseThingHandler implements Co

protected TradfriCoapClient coapClient;

public TradfriThingHandler(Thing thing) {
public TradfriThingHandler(@NonNull Thing thing) {
super(thing);
}

@Override
public synchronized void initialize() {
Bridge tradfriGateway = getBridge();
this.id = getConfigAs(TradfriDeviceConfig.class).id;
TradfriGatewayHandler handler = (TradfriGatewayHandler) tradfriGateway.getHandler();

String uriString = handler.getGatewayURI() + "/" + id;
try {
URI uri = new URI(uriString);
coapClient = new TradfriCoapClient(uri);
coapClient.setEndpoint(handler.getEndpoint());
} catch (URISyntaxException e) {
logger.debug("Illegal device URI `{}`: {}", uriString, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
return;
}
active = true;
updateStatus(ThingStatus.UNKNOWN);
switch (tradfriGateway.getStatus()) {
case ONLINE:
this.id = getConfigAs(TradfriDeviceConfig.class).id;
TradfriGatewayHandler handler = (TradfriGatewayHandler) tradfriGateway.getHandler();

String uriString = handler.getGatewayURI() + "/" + id;
try {
URI uri = new URI(uriString);
coapClient = new TradfriCoapClient(uri);
coapClient.setEndpoint(handler.getEndpoint());
} catch (URISyntaxException e) {
logger.debug("Illegal device URI `{}`: {}", uriString, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
return;
}
updateStatus(ThingStatus.UNKNOWN);
active = true;

scheduler.schedule(() -> {
coapClient.startObserve(this);
}, 3, TimeUnit.SECONDS);
break;
case INITIALIZING:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, "Tradfri gateway uninitialized");
break;
case OFFLINE:
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
String.format("Tradfri gateway offline '%s'", tradfriGateway.getStatusInfo()));
String.format("Gateway offline '%s'", tradfriGateway.getStatusInfo()));
break;
}

}

@Override
Expand All @@ -110,7 +104,10 @@ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
super.bridgeStatusChanged(bridgeStatusInfo);

if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
scheduler.submit(() -> coapClient.startObserve(this));
// the status might have changed because the bridge is completely reconfigured - so we need to re-establish
// our CoAP connection as well
dispose();
initialize();
}
}

Expand Down

0 comments on commit a06703e

Please sign in to comment.