Skip to content

Commit

Permalink
[icloud] Rework authentication to reflect changes in iCloud API (open…
Browse files Browse the repository at this point in the history
…hab#13691)

* Implement Authentication (WIP)
* Validation Code accepted
* Refactor session state
* RefreshClient working
* Implement session persistence in openhab store
* Integration in binding
* Remove persistent cookies, which break authentication
* Bugfixing
* Add code configuration to UI
* Improve documentation, error-handling and cleanup
* Rework auth order
* Rework auth process
* Add 2-FA-auth to documentation
* Set bridge to online if data refresh works
* Case-sensitive rename ICloudAPIResponseException
* Include authentication in refresh flow
* Fix regression for data not being updated
* Fix typo in i18n props
* Fix review and checkstyle.
* More javadoc, new RetryException
* Introduce @NonNullByDefault
* Introduce server for RetryException, add NonNullbyDefault, fix warnings
* Rework for contribution, e.g. null checks, ...
* Fix checkstyle
* Move JsonUtils to utilities package
* Async initialize bridge handler.
* Report Device OFFLINE if Bridge is OFFLINE
* Set bridge thing status to UNKOWN in init
* Move refresh init into async init
* Cancel init task in dispose

Also-by: Leo Siepel <leosiepel@gmail.com>
Signed-off-by: Simon Spielmann <simon.spielmann@gmx.de>
  • Loading branch information
maihacke committed Dec 15, 2022
1 parent 6e8b35c commit 04f059c
Show file tree
Hide file tree
Showing 34 changed files with 1,733 additions and 532 deletions.
22 changes: 14 additions & 8 deletions bundles/org.openhab.binding.icloud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ The account Thing, more precisely the account Bridge, represents one Apple iClou
The account can be connected to multiple Apple devices which are represented as Things below the Bridge, see the example below.
You may create multiple account Things for multiple accounts.

If your Apple account has 2-factor-authentication enabled configuration requires two steps.
First start by adding the Apple ID and password to your account thing configuration.
You will receive a notification with a code on one of your Apple devices then.
Add this code to the code parameter of the thing then and wait.
The binding should be reinitialized and perform the authentication.

### Device Thing

A device is identified by the device ID provided by Apple.
Expand Down Expand Up @@ -62,7 +68,7 @@ The following channels are available (if supported by the device):
### icloud.things

```php
Bridge icloud:account:myaccount [appleId="mail@example.com", password="secure", refreshTimeInMinutes=5]
Bridge icloud:account:myaccount [appleId="mail@example.com", password="secure", code="123456", refreshTimeInMinutes=5]
{
Thing device myiPhone8 "iPhone 8" @ "World" [deviceId="VIRG9FsrvXfE90ewVBA1H5swtwEQePdXVjHq3Si6pdJY2Cjro8QlreHYVGSUzuWV"]
}
Expand All @@ -76,14 +82,14 @@ The information _@ "World"_ is optional.
```php
Group iCloud_Group "iPhone"

String iPhone_BatteryStatus "Battery Status [%s]" <battery> (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:batteryStatus"}
Number iPhone_BatteryLevel "Battery Level [%d %%]" <battery> (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:batteryLevel"}
Switch iPhone_FindMyPhone "Trigger Find My iPhone" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:findMyPhone", autoupdate="false"}
Switch iPhone_Refresh "Force iPhone Refresh" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:location", autoupdate="false"}
Location iPhone_Location "Coordinates" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:location"}
Number iPhone_LocationAccuracy "Coordinates Accuracy [%.0f m]" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:locationAccuracy"}
String iPhone_BatteryStatus "Battery Status [%s]" <battery> (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:batteryStatus"}
Number iPhone_BatteryLevel "Battery Level [%d %%]" <battery> (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:batteryLevel"}
Switch iPhone_FindMyPhone "Trigger Find My iPhone" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:findMyPhone", autoupdate="false"}
Switch iPhone_Refresh "Force iPhone Refresh" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:location", autoupdate="false"}
Location iPhone_Location "Coordinates" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:location"}
Number iPhone_LocationAccuracy "Coordinates Accuracy [%.0f m]" (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:locationAccuracy"}
DateTime iPhone_LocationLastUpdate "Last Update [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]" <time> (iCloud_Group) {channel="icloud:device:myaccount:myiPhone8:locationLastUpdate"}
Switch iPhone_Home "Phone Home" <presence> (iCloud_Group)
Switch iPhone_Home "Phone Home" <presence> (iCloud_Group)
```

### icloud.sitemap
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.icloud.internal;

import java.io.IOException;
import java.net.URI;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.icloud.internal.utilities.JsonUtils;

/**
* This class gives access to the find my iPhone (FMIP) service.
*
* @author Simon Spielmann - Initial Contribution.
*/
@NonNullByDefault
public class FindMyIPhoneServiceManager {

private ICloudSession session;

private URI fmipRefreshUrl;

private URI fmipSoundUrl;

private static final String FMIP_ENDPOINT = "/fmipservice/client/web";

/**
* The constructor.
*
* @param session {@link ICloudSession} to use for API calls.
* @param serviceRoot Root URL for FMIP service.
*/
public FindMyIPhoneServiceManager(ICloudSession session, String serviceRoot) {
this.session = session;
this.fmipRefreshUrl = URI.create(serviceRoot + FMIP_ENDPOINT + "/refreshClient");
this.fmipSoundUrl = URI.create(serviceRoot + FMIP_ENDPOINT + "/playSound");
}

/**
* Receive client information as JSON.
*
* @return Information about all clients as JSON
* {@link org.openhab.binding.icloud.internal.handler.dto.json.response.ICloudDeviceInformation}.
*
* @throws IOException if I/O error occurred
* @throws InterruptedException if this blocking request was interrupted
* @throws ICloudApiResponseException if the request failed (e.g. not OK HTTP return code)
*
*/
public String refreshClient() throws IOException, InterruptedException, ICloudApiResponseException {
Map<String, Object> request = Map.of("clientContext",
Map.of("fmly", true, "shouldLocate", true, "selectedDevice", "All", "deviceListVersion", 1));
return session.post(this.fmipRefreshUrl.toString(), JsonUtils.toJson(request), null);
}

/**
* Play sound (find my iPhone) on given device.
*
* @param deviceId ID of the device to play sound on
* @throws IOException if I/O error occurred
* @throws InterruptedException if this blocking request was interrupted
* @throws ICloudApiResponseException if the request failed (e.g. not OK HTTP return code)
*/
public void playSound(String deviceId) throws IOException, InterruptedException, ICloudApiResponseException {
Map<String, Object> request = Map.of("device", deviceId, "fmyl", true, "subject", "Message from openHAB.");
session.post(this.fmipSoundUrl.toString(), JsonUtils.toJson(request), null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.icloud.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
*
* Exception for errors during calls of the iCloud API.
*
* @author Simon Spielmann - Initial contribution
*/
@NonNullByDefault
public class ICloudApiResponseException extends Exception {

private static final long serialVersionUID = 1L;
private int statusCode;

/**
* The constructor.
*
* @param url URL for which the exception occurred
* @param statusCode HTTP status code which was reported
*/
public ICloudApiResponseException(String url, int statusCode) {
super(String.format("Request %s failed with %s.", url, statusCode));
this.statusCode = statusCode;
}

/**
* @return statusCode HTTP status code of failed request.
*/
public int getStatusCode() {
return this.statusCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
* used across the whole binding.
*
* @author Patrik Gfeller - Initial contribution
* @author Patrik Gfeller
* - Class renamed to be more consistent
* - Constant FIND_MY_DEVICE_REQUEST_SUBJECT introduced
* @author Patrik Gfeller - Class renamed to be more consistent
* @author Patrik Gfeller - Constant FIND_MY_DEVICE_REQUEST_SUBJECT introduced
* @author Gaël L'hopital - Added low battery
*/
@NonNullByDefault
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.util.List;

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

/**
* Classes that implement this interface are interested in device information updates.
Expand Down

This file was deleted.

0 comments on commit 04f059c

Please sign in to comment.