[iCloud] Fix things reappearing in inbox (#14661)
* Use deviceDiscoveryId instead of id * Use device display name if discovery id is empty Signed-off-by: Simon Spielmann <simon.spielmann@gmx.de>
This commit is contained in:
parent
dc5d76d762
commit
67f1a80afd
|
@ -63,19 +63,23 @@ public class ICloudDeviceDiscovery extends AbstractDiscoveryService implements I
|
|||
String deviceOwnerName = deviceInformationRecord.getName();
|
||||
|
||||
String thingLabel = deviceOwnerName + " (" + deviceTypeName + ")";
|
||||
String deviceId = deviceInformationRecord.getId();
|
||||
String deviceIdHash = Integer.toHexString(deviceId.hashCode());
|
||||
String deviceDiscoveryId = deviceInformationRecord.getDeviceDiscoveryId();
|
||||
if (deviceDiscoveryId == null || deviceDiscoveryId.isBlank()) {
|
||||
logger.debug("deviceDiscoveryId is empty, using device name for identification.");
|
||||
deviceDiscoveryId = deviceOwnerName;
|
||||
}
|
||||
String deviceIdHash = Integer.toHexString(deviceDiscoveryId.hashCode());
|
||||
|
||||
logger.debug("iCloud device discovery for [{}]", deviceInformationRecord.getDeviceDisplayName());
|
||||
|
||||
ThingUID uid = new ThingUID(THING_TYPE_ICLOUDDEVICE, bridgeUID, deviceIdHash);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
|
||||
.withProperty(DEVICE_PROPERTY_ID, deviceId)
|
||||
.withProperty(translatorService.getText(DEVICE_PROPERTY_ID_LABEL), deviceId)
|
||||
.withProperty(DEVICE_PROPERTY_ID, deviceDiscoveryId)
|
||||
.withProperty(translatorService.getText(DEVICE_PROPERTY_ID_LABEL), deviceDiscoveryId)
|
||||
.withProperty(translatorService.getText(DEVICE_PROPERTY_OWNER_LABEL), deviceOwnerName)
|
||||
.withRepresentationProperty(DEVICE_PROPERTY_ID).withLabel(thingLabel).build();
|
||||
|
||||
logger.debug("Device [{}, {}] found.", deviceIdHash, deviceId);
|
||||
logger.debug("Device [{}, {}] found.", deviceIdHash, deviceDiscoveryId);
|
||||
|
||||
thingDiscovered(result);
|
||||
|
||||
|
|
|
@ -185,8 +185,11 @@ public class ICloudDeviceHandler extends BaseThingHandler implements ICloudDevic
|
|||
this.logger.debug("Device: [{}]", this.deviceId);
|
||||
|
||||
for (ICloudDeviceInformation deviceInformationRecord : deviceInformationList) {
|
||||
String currentId = deviceInformationRecord.getId();
|
||||
|
||||
String currentId = deviceInformationRecord.getDeviceDiscoveryId();
|
||||
if (currentId == null || currentId.isBlank()) {
|
||||
logger.debug("deviceDiscoveryId is empty, using device name for identification.");
|
||||
currentId = deviceInformationRecord.getName();
|
||||
}
|
||||
logger.debug("Current data element: [id = {}]", currentId);
|
||||
|
||||
if (currentId != null && currentId.equals(deviceId)) {
|
||||
|
|
|
@ -50,6 +50,8 @@ public class ICloudDeviceInformation {
|
|||
|
||||
private String id;
|
||||
|
||||
private String deviceDiscoveryId;
|
||||
|
||||
private boolean isLocating;
|
||||
|
||||
private boolean isMac;
|
||||
|
@ -160,6 +162,10 @@ public class ICloudDeviceInformation {
|
|||
return this.id;
|
||||
}
|
||||
|
||||
public String getDeviceDiscoveryId() {
|
||||
return this.deviceDiscoveryId;
|
||||
}
|
||||
|
||||
public boolean getIsLocating() {
|
||||
return this.isLocating;
|
||||
}
|
||||
|
|
|
@ -13,22 +13,46 @@
|
|||
package org.openhab.binding.icloud;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.openhab.binding.icloud.internal.ICloudBindingConstants.THING_TYPE_ICLOUDDEVICE;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||||
import org.openhab.binding.icloud.internal.ICloudApiResponseException;
|
||||
import org.openhab.binding.icloud.internal.ICloudBindingConstants;
|
||||
import org.openhab.binding.icloud.internal.ICloudService;
|
||||
import org.openhab.binding.icloud.internal.handler.ICloudDeviceHandler;
|
||||
import org.openhab.binding.icloud.internal.handler.dto.json.response.ICloudAccountDataResponse;
|
||||
import org.openhab.binding.icloud.internal.utilities.JsonUtils;
|
||||
import org.openhab.core.config.core.ConfigDescription;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.storage.json.internal.JsonStorage;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelGroupUID;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.internal.ThingImpl;
|
||||
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -94,4 +118,403 @@ public class TestICloud {
|
|||
assertNotNull(deviceInfo);
|
||||
stateStorage.flush();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIfSystemProperty(named = "icloud.test.email", matches = ".*", disabledReason = "Only for manual execution.")
|
||||
public void testDiscovery() {
|
||||
|
||||
String icloudDeviceRespond = """
|
||||
{
|
||||
"userInfo": {
|
||||
"accountFormatter": 0,
|
||||
"firstName": "Firstname",
|
||||
"lastName": "Lastname",
|
||||
"membersInfo": {
|
||||
"XXX~": {
|
||||
"accountFormatter": 0,
|
||||
"firstName": "Firstname",
|
||||
"lastName": "Lastname",
|
||||
"deviceFetchStatus": "LOADING",
|
||||
"useAuthWidget": true,
|
||||
"isHSA": true,
|
||||
"appleId": "dummy@dummy.local"
|
||||
}
|
||||
},
|
||||
"hasMembers": true
|
||||
},
|
||||
"alert": null,
|
||||
"serverContext": {
|
||||
"minCallbackIntervalInMS": 5000,
|
||||
"preferredLanguage": "de-de",
|
||||
"enable2FAFamilyActions": false,
|
||||
"lastSessionExtensionTime": null,
|
||||
"validRegion": true,
|
||||
"callbackIntervalInMS": 2000,
|
||||
"enableMapStats": true,
|
||||
"timezone": {
|
||||
"currentOffset": -25200000,
|
||||
"previousTransition": 1678615199999,
|
||||
"previousOffset": -28800000,
|
||||
"tzCurrentName": "Pacific Daylight Time",
|
||||
"tzName": "America/Los_Angeles"
|
||||
},
|
||||
"authToken": null,
|
||||
"maxCallbackIntervalInMS": 60000,
|
||||
"classicUser": false,
|
||||
"isHSA": true,
|
||||
"trackInfoCacheDurationInSecs": 86400,
|
||||
"imageBaseUrl": "https://statici.icloud.com",
|
||||
"minTrackLocThresholdInMts": 100,
|
||||
"itemLearnMoreURL": "https://support.apple.com/kb/HT211331?viewlocale=de_DE",
|
||||
"maxLocatingTime": 90000,
|
||||
"itemsTabEnabled": true,
|
||||
"sessionLifespan": 900000,
|
||||
"info": "",
|
||||
"prefsUpdateTime": 1679378160178,
|
||||
"useAuthWidget": true,
|
||||
"inaccuracyRadiusThreshold": 200,
|
||||
"clientId": "",
|
||||
"serverTimestamp": 1679640300550,
|
||||
"enable2FAFamilyRemove": false,
|
||||
"deviceImageVersion": "22",
|
||||
"macCount": 0,
|
||||
"deviceLoadStatus": "200",
|
||||
"maxDeviceLoadTime": 60000,
|
||||
"prsId": 146786264,
|
||||
"showSllNow": false,
|
||||
"cloudUser": true,
|
||||
"enable2FAErase": false
|
||||
},
|
||||
"userPreferences": {
|
||||
"webPrefs": {
|
||||
"id": "web_prefs",
|
||||
"selectedDeviceId": "XXX"
|
||||
}
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"msg": null,
|
||||
"activationLocked": true,
|
||||
"passcodeLength": 6,
|
||||
"features": {
|
||||
"BTR": false,
|
||||
"LLC": false,
|
||||
"CLK": false,
|
||||
"TEU": true,
|
||||
"SND": true,
|
||||
"ALS": false,
|
||||
"CLT": false,
|
||||
"SVP": false,
|
||||
"SPN": false,
|
||||
"XRM": false,
|
||||
"NWF": true,
|
||||
"CWP": false,
|
||||
"MSG": true,
|
||||
"LOC": true,
|
||||
"LME": false,
|
||||
"LMG": false,
|
||||
"LYU": false,
|
||||
"LKL": false,
|
||||
"LST": true,
|
||||
"LKM": false,
|
||||
"WMG": true,
|
||||
"SCA": false,
|
||||
"PSS": false,
|
||||
"EAL": false,
|
||||
"LAE": false,
|
||||
"PIN": false,
|
||||
"LCK": true,
|
||||
"REM": true,
|
||||
"MCS": false,
|
||||
"KEY": false,
|
||||
"KPD": false,
|
||||
"WIP": true
|
||||
},
|
||||
"scd": false,
|
||||
"id": "device.id",
|
||||
"remoteLock": null,
|
||||
"rm2State": 0,
|
||||
"modelDisplayName": "iPad",
|
||||
"fmlyShare": false,
|
||||
"lostModeCapable": true,
|
||||
"wipedTimestamp": null,
|
||||
"encodedDeviceId": null,
|
||||
"scdPh": "",
|
||||
"locationCapable": true,
|
||||
"trackingInfo": null,
|
||||
"name": "My iPad",
|
||||
"isMac": false,
|
||||
"thisDevice": false,
|
||||
"deviceClass": "iPad",
|
||||
"nwd": false,
|
||||
"remoteWipe": null,
|
||||
"canWipeAfterLock": true,
|
||||
"baUUID": "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF",
|
||||
"lostModeEnabled": false,
|
||||
"wipeInProgress": false,
|
||||
"deviceStatus": "200",
|
||||
"deviceColor": "1-1-0",
|
||||
"isConsideredAccessory": false,
|
||||
"deviceWithYou": false,
|
||||
"lowPowerMode": false,
|
||||
"rawDeviceModel": "iPad13,1",
|
||||
"deviceDiscoveryId": "AAAAAAAA-FFFF-FFFF-FFFF-FFFFFFFFFFFF",
|
||||
"isLocating": true,
|
||||
"lostTimestamp": "",
|
||||
"mesg": null,
|
||||
"batteryLevel": 0.1599999964237213,
|
||||
"locationEnabled": true,
|
||||
"lockedTimestamp": null,
|
||||
"locFoundEnabled": false,
|
||||
"snd": null,
|
||||
"lostDevice": null,
|
||||
"deviceDisplayName": "iPad Air (4th Generation)",
|
||||
"prsId": null,
|
||||
"audioChannels": [
|
||||
|
||||
],
|
||||
"batteryStatus": "NotCharging",
|
||||
"location": {
|
||||
"isOld": false,
|
||||
"isInaccurate": false,
|
||||
"altitude": 0.0,
|
||||
"positionType": "Unknown",
|
||||
"secureLocation": null,
|
||||
"secureLocationTs": 0,
|
||||
"latitude": 20.00000000000000,
|
||||
"floorLevel": 0,
|
||||
"horizontalAccuracy": 1.0,
|
||||
"locationType": "",
|
||||
"timeStamp": 1679640074735,
|
||||
"locationFinished": true,
|
||||
"verticalAccuracy": 0.0,
|
||||
"locationMode": null,
|
||||
"longitude": 10.00000000000000
|
||||
},
|
||||
"deviceModel": "FourthGen-1-1-0",
|
||||
"maxMsgChar": 160,
|
||||
"darkWake": false
|
||||
},
|
||||
{
|
||||
"msg": null,
|
||||
"activationLocked": true,
|
||||
"passcodeLength": 6,
|
||||
"features": {
|
||||
"BTR": false,
|
||||
"LLC": false,
|
||||
"CLK": false,
|
||||
"TEU": true,
|
||||
"SND": true,
|
||||
"ALS": false,
|
||||
"CLT": false,
|
||||
"SVP": false,
|
||||
"SPN": false,
|
||||
"XRM": false,
|
||||
"NWF": true,
|
||||
"CWP": false,
|
||||
"MSG": true,
|
||||
"LOC": true,
|
||||
"LME": false,
|
||||
"LMG": false,
|
||||
"LYU": false,
|
||||
"LKL": false,
|
||||
"LST": true,
|
||||
"LKM": false,
|
||||
"WMG": true,
|
||||
"SCA": false,
|
||||
"PSS": false,
|
||||
"EAL": false,
|
||||
"LAE": false,
|
||||
"PIN": false,
|
||||
"LCK": true,
|
||||
"REM": true,
|
||||
"MCS": false,
|
||||
"KEY": false,
|
||||
"KPD": false,
|
||||
"WIP": true
|
||||
},
|
||||
"scd": false,
|
||||
"id": "device.id",
|
||||
"remoteLock": null,
|
||||
"rm2State": 0,
|
||||
"modelDisplayName": "iPad",
|
||||
"fmlyShare": false,
|
||||
"lostModeCapable": true,
|
||||
"wipedTimestamp": null,
|
||||
"encodedDeviceId": null,
|
||||
"scdPh": "",
|
||||
"locationCapable": true,
|
||||
"trackingInfo": null,
|
||||
"name": "iPad Air without ID",
|
||||
"isMac": false,
|
||||
"thisDevice": false,
|
||||
"deviceClass": "iPad",
|
||||
"nwd": false,
|
||||
"remoteWipe": null,
|
||||
"canWipeAfterLock": true,
|
||||
"baUUID": "",
|
||||
"lostModeEnabled": false,
|
||||
"wipeInProgress": false,
|
||||
"deviceStatus": "200",
|
||||
"deviceColor": "1-1-0",
|
||||
"isConsideredAccessory": false,
|
||||
"deviceWithYou": false,
|
||||
"lowPowerMode": false,
|
||||
"rawDeviceModel": "iPad13,1",
|
||||
"deviceDiscoveryId": "",
|
||||
"isLocating": true,
|
||||
"lostTimestamp": "",
|
||||
"mesg": null,
|
||||
"batteryLevel": 0.1599999964237213,
|
||||
"locationEnabled": true,
|
||||
"lockedTimestamp": null,
|
||||
"locFoundEnabled": false,
|
||||
"snd": null,
|
||||
"lostDevice": null,
|
||||
"deviceDisplayName": "iPad",
|
||||
"prsId": null,
|
||||
"audioChannels": [
|
||||
|
||||
],
|
||||
"batteryStatus": "NotCharging",
|
||||
"location": {
|
||||
"isOld": false,
|
||||
"isInaccurate": false,
|
||||
"altitude": 0.0,
|
||||
"positionType": "Unknown",
|
||||
"secureLocation": null,
|
||||
"secureLocationTs": 0,
|
||||
"latitude": 20.00000000000000,
|
||||
"floorLevel": 0,
|
||||
"horizontalAccuracy": 1.0,
|
||||
"locationType": "",
|
||||
"timeStamp": 1679640074735,
|
||||
"locationFinished": true,
|
||||
"verticalAccuracy": 0.0,
|
||||
"locationMode": null,
|
||||
"longitude": 10.00000000000000
|
||||
},
|
||||
"deviceModel": "FourthGen-1-1-0",
|
||||
"maxMsgChar": 160,
|
||||
"darkWake": false
|
||||
}
|
||||
],
|
||||
"statusCode": "200"
|
||||
}
|
||||
""";
|
||||
ICloudAccountDataResponse deviceInfo = JsonUtils.fromJson(icloudDeviceRespond, ICloudAccountDataResponse.class);
|
||||
|
||||
// Check device with discoveryId
|
||||
ThingImpl thing = createThing("AAAAAAAA-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
|
||||
ICloudDeviceHandler handler = createDeviceHandler(thing);
|
||||
|
||||
handler.deviceInformationUpdate(deviceInfo.getICloudDeviceInformationList());
|
||||
|
||||
assertEquals(ThingStatus.ONLINE, handler.getThing().getStatus());
|
||||
|
||||
// Check device without discoveryId
|
||||
thing = createThing("iPad Air without ID");
|
||||
handler = createDeviceHandler(thing);
|
||||
|
||||
handler.deviceInformationUpdate(deviceInfo.getICloudDeviceInformationList());
|
||||
|
||||
assertEquals(ThingStatus.ONLINE, handler.getThing().getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private ThingImpl createThing(String deviceId) {
|
||||
String deviceIdHash = Integer.toHexString(deviceId.hashCode());
|
||||
|
||||
ThingUID uid = new ThingUID(THING_TYPE_ICLOUDDEVICE, "dummyBridgeId", deviceIdHash);
|
||||
ThingImpl thing = new ThingImpl(THING_TYPE_ICLOUDDEVICE, uid);
|
||||
thing.getConfiguration().put(ICloudBindingConstants.DEVICE_PROPERTY_ID, deviceId);
|
||||
return thing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param thing
|
||||
* @return
|
||||
*/
|
||||
private ICloudDeviceHandler createDeviceHandler(ThingImpl thing) {
|
||||
ICloudDeviceHandler handler = new ICloudDeviceHandler(thing);
|
||||
handler.setCallback(new ThingHandlerCallback() {
|
||||
|
||||
@Override
|
||||
public void validateConfigurationParameters(Channel channel, Map<String, Object> configurationParameters) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateConfigurationParameters(Thing thing, Map<String, Object> configurationParameters) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thingUpdated(Thing thing) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusUpdated(Thing thing, ThingStatusInfo thingStatus) {
|
||||
thing.setStatusInfo(thingStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateUpdated(ChannelUID channelUID, State state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrateThingType(Thing thing, ThingTypeUID thingTypeUID, Configuration configuration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChannelLinked(ChannelUID channelUID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigDescription getConfigDescription(ThingTypeUID thingTypeUID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigDescription getConfigDescription(ChannelTypeUID channelTypeUID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Bridge getBridge(ThingUID bridgeUID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelBuilder editChannel(Thing thing, ChannelUID channelUID) {
|
||||
return ChannelBuilder.create(channelUID); // dummy implementation, probably won't work.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChannelBuilder> createChannelBuilders(ChannelGroupUID channelGroupUID,
|
||||
ChannelGroupTypeUID channelGroupTypeUID) {
|
||||
return new ArrayList<ChannelBuilder>(); // dummy implementation, probably won't work.
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelBuilder createChannelBuilder(ChannelUID channelUID, ChannelTypeUID channelTypeUID) {
|
||||
return ChannelBuilder.create(channelUID); // dummy implementation, probably won't work.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurationUpdated(Thing thing) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelTriggered(Thing thing, ChannelUID channelUID, String event) {
|
||||
}
|
||||
});
|
||||
handler.initialize();
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue