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

Commit

Permalink
[icloud] Improved the lifecycle (#4329)
Browse files Browse the repository at this point in the history
Add nullable annotations and added additional information to the status updates.

Signed-off-by: Martin van Wingerden <martin@martinvw.nl>
  • Loading branch information
martinvw authored and kaikreuzer committed Dec 7, 2018
1 parent f3f6fad commit 03142bc
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
*/
package org.openhab.binding.icloud.internal;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
Expand All @@ -20,7 +21,7 @@
import org.openhab.binding.icloud.internal.json.request.ICloudFindMyDeviceRequest;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.GsonBuilder;;

/**
* Handles communication with the Apple server. Provides methods to
Expand All @@ -45,8 +46,8 @@ public class ICloudConnection {
private final String iCloudDataRequestURL;
private final String iCloudFindMyDeviceURL;

public ICloudConnection(String appleId, String password) throws UnsupportedEncodingException, URISyntaxException {
authorization = new String(Base64.getEncoder().encode((appleId + ":" + password).getBytes()), "UTF-8");
public ICloudConnection(String appleId, String password) throws URISyntaxException {
authorization = new String(Base64.getEncoder().encode((appleId + ":" + password).getBytes()), UTF_8);
iCloudDataRequestURL = new URI(ICLOUD_API_URL + appleId + ICLOUD_API_COMMAND_REQUEST_DATA).toASCIIString();
iCloudFindMyDeviceURL = new URI(ICLOUD_API_URL + appleId + ICLOUD_API_COMMAND_PING_DEVICE).toASCIIString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.icloud.internal.json.response.ICloudDeviceInformation;

/**
* Classes that implement this interface are interested in device information updates.
*
* @author Patrik Gfeller
*
* @author Patrik Gfeller - Initial Contribution
*/
@NonNullByDefault
public interface ICloudDeviceInformationListener {
void deviceInformationUpdate(List<ICloudDeviceInformation> deviceInformationList);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
*/
package org.openhab.binding.icloud.internal.handler;

import static java.util.concurrent.TimeUnit.*;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.core.cache.ExpiringCache;
Expand All @@ -39,14 +40,14 @@
* Retrieves the data for a given account from iCloud and passes the
* information to {@link DeviceDiscover} and to the {@link ICloudDeviceHandler}s.
*
* @author Patrik Gfeller - Initial Contribution
* @author Patrik Gfeller - Initial contribution
* @author Hans-Jörg Merk - Extended support with initial Contribution
*/
public class ICloudAccountBridgeHandler extends BaseBridgeHandler {

private final Logger logger = LoggerFactory.getLogger(ICloudAccountBridgeHandler.class);

private static final int CACHE_EXPIRY = (int) TimeUnit.SECONDS.toMillis(5);
private static final int CACHE_EXPIRY = (int) SECONDS.toMillis(5);

private final ICloudDeviceInformationParser deviceInformationParser = new ICloudDeviceInformationParser();
private ICloudConnection connection;
Expand Down Expand Up @@ -80,9 +81,8 @@ public void initialize() {
logger.debug("iCloud bridge handler initializing ...");
iCloudDeviceInformationCache = new ExpiringCache<String>(CACHE_EXPIRY, () -> {
try {
connection = new ICloudConnection(config.appleId, config.password);
return connection.requestDeviceStatusJSON();
} catch (IOException | URISyntaxException e) {
} catch (IOException e) {
logger.warn("Unable to refresh device data", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
return null;
Expand Down Expand Up @@ -119,13 +119,19 @@ public void unregisterListener(ICloudDeviceInformationListener listener) {
}

private void startHandler() {
logger.debug("iCloud bridge starting handler ...");
config = getConfigAs(ICloudAccountThingConfiguration.class);
try {
logger.debug("iCloud bridge starting handler ...");
config = getConfigAs(ICloudAccountThingConfiguration.class);

refreshJob = scheduler.scheduleWithFixedDelay(() -> {
refreshData();
}, 0, config.refreshTimeInMinutes, TimeUnit.MINUTES);
logger.debug("iCloud bridge handler started.");
connection = new ICloudConnection(config.appleId, config.password);

refreshJob = scheduler.scheduleWithFixedDelay(this::refreshData, 0, config.refreshTimeInMinutes, MINUTES);

logger.debug("iCloud bridge handler started.");
} catch (URISyntaxException e) {
logger.debug("Something went wrong while constructing the connection object", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
}
}

public void refreshData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
*/
package org.openhab.binding.icloud.internal.handler;

import static org.eclipse.smarthome.core.thing.ThingStatus.OFFLINE;
import static org.eclipse.smarthome.core.thing.ThingStatus.ONLINE;
import static org.eclipse.smarthome.core.thing.ThingStatusDetail.*;
import static org.openhab.binding.icloud.internal.ICloudBindingConstants.*;

import java.io.IOException;
Expand All @@ -16,17 +19,18 @@
import java.util.Date;
import java.util.List;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.PointType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.thing.Bridge;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.RefreshType;
import org.eclipse.smarthome.core.types.State;
Expand All @@ -38,29 +42,32 @@
import org.slf4j.LoggerFactory;

/**
* Handles updates of an icloud device Thing.
* Handles updates of an iCloud device Thing.
*
* @author Patrik Gfeller - Initial Contribution
* @author Hans-Jörg Merk
* @author Patrik Gfeller - Initial contribution
* @author Hans-Jörg Merk - Helped with testing and feedback
* @author Gaël L'hopital - Added low battery
*
*/
@NonNullByDefault
public class ICloudDeviceHandler extends BaseThingHandler implements ICloudDeviceInformationListener {
private final Logger logger = LoggerFactory.getLogger(ICloudDeviceHandler.class);
private String deviceId;
private ICloudAccountBridgeHandler bridge;
private @Nullable String deviceId;
private @Nullable ICloudAccountBridgeHandler icloudAccount;

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

@Override
public void deviceInformationUpdate(List<ICloudDeviceInformation> deviceInformationList) {
ICloudDeviceInformation deviceInformationRecord = getDeviceInformationRecord(deviceInformationList);
if (deviceInformationRecord != null) {
deviceId = deviceInformationRecord.getId();

updateStatus(deviceInformationRecord.getDeviceStatus() == 200 ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
if (deviceInformationRecord.getDeviceStatus() == 200) {
updateStatus(ONLINE);
} else {
updateStatus(OFFLINE, COMMUNICATION_ERROR, "Reported offline by iCloud webservice");
}

updateState(BATTERY_STATUS, new StringType(deviceInformationRecord.getBatteryStatus()));

Expand All @@ -74,21 +81,44 @@ public void deviceInformationUpdate(List<ICloudDeviceInformation> deviceInformat
updateLocationRelatedStates(deviceInformationRecord);
}
} else {
updateStatus(ThingStatus.OFFLINE);
updateStatus(OFFLINE, CONFIGURATION_ERROR, "The device is not included in the current account");
}
}

@SuppressWarnings("null")
@Override
public void initialize() {
logger.debug("Initializing iCloud device handler.");
initializeThing((getBridge() == null) ? null : getBridge().getStatus());
Bridge bridge = getBridge();
Object bridgeStatus = (bridge == null) ? null : bridge.getStatus();
logger.debug("initializeThing thing [{}]; bridge status: [{}]", getThing().getUID(), bridgeStatus);

ICloudDeviceThingConfiguration configuration = getConfigAs(ICloudDeviceThingConfiguration.class);
this.deviceId = configuration.deviceId;

ICloudAccountBridgeHandler handler = getIcloudAccount();
if (handler != null) {
refreshData();
} else {
updateStatus(OFFLINE, BRIDGE_UNINITIALIZED, "Bridge not found");
}
}

private void refreshData() {
ICloudAccountBridgeHandler bridge = getIcloudAccount();
if (bridge != null) {
bridge.refreshData();
}
}

@Override
public void handleCommand(@NonNull ChannelUID channelUID, Command command) {
public void handleCommand(ChannelUID channelUID, Command command) {
logger.trace("Command '{}' received for channel '{}'", command, channelUID);

ICloudAccountBridgeHandler bridge = getIcloudAccount();
if (bridge == null) {
logger.debug("No bridge found, ignoring command");
return;
}

String channelId = channelUID.getId();
if (channelId.equals(FIND_MY_PHONE)) {
if (command == OnOffType.ON) {
Expand All @@ -108,7 +138,10 @@ public void handleCommand(@NonNull ChannelUID channelUID, Command command) {

@Override
public void dispose() {
bridge.unregisterListener(this);
ICloudAccountBridgeHandler bridge = getIcloudAccount();
if (bridge != null) {
bridge.unregisterListener(this);
}
super.dispose();
}

Expand All @@ -125,37 +158,16 @@ private void updateLocationRelatedStates(ICloudDeviceInformation deviceInformati
updateState(LOCATION_LASTUPDATE, getLastLocationUpdateDateTimeState(deviceInformationRecord));
}

@SuppressWarnings("null")
private void initializeThing(ThingStatus bridgeStatus) {
logger.debug("initializeThing thing [{}]; bridge status: [{}]", getThing().getUID(), bridgeStatus);

ICloudDeviceThingConfiguration configuration = getConfigAs(ICloudDeviceThingConfiguration.class);
this.deviceId = configuration.deviceId;

bridge = (ICloudAccountBridgeHandler) getBridge().getHandler();

if (bridge != null) {
if (bridgeStatus == ThingStatus.ONLINE) {
bridge.registerListener(this);
bridge.refreshData();
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE);
}
}

private ICloudDeviceInformation getDeviceInformationRecord(List<ICloudDeviceInformation> deviceInformationList) {
private @Nullable ICloudDeviceInformation getDeviceInformationRecord(
List<ICloudDeviceInformation> deviceInformationList) {
logger.debug("Device: [{}]", deviceId);

for (ICloudDeviceInformation deviceInformationRecord : deviceInformationList) {
String currentId = deviceInformationRecord.getId();

logger.debug("Current data element: [{}]", currentId);
logger.debug("Current data element: [id = {}]", currentId);

if (deviceId.equalsIgnoreCase(currentId)) {
if (currentId != null && currentId.equals(deviceId)) {
return deviceInformationRecord;
}
}
Expand All @@ -176,4 +188,21 @@ private State getLastLocationUpdateDateTimeState(ICloudDeviceInformation deviceI
return dateTime;
}

}
private @Nullable ICloudAccountBridgeHandler getIcloudAccount() {
if (icloudAccount == null) {
Bridge bridge = getBridge();
if (bridge == null) {
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof ICloudAccountBridgeHandler) {
icloudAccount = (ICloudAccountBridgeHandler) handler;
icloudAccount.registerListener(this);
} else {
return null;
}
}
return icloudAccount;
}

}

0 comments on commit 03142bc

Please sign in to comment.