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

various lifecycle improvements and fixes on Tradfri #4530

Merged
merged 1 commit into from Nov 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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