]> git.basschouten.com Git - openhab-addons.git/commitdiff
[iCloud] Fix things reappearing in inbox (#14661)
authorSimon Spielmann <simon.spielmann@gmx.de>
Wed, 3 May 2023 10:27:48 +0000 (12:27 +0200)
committerGitHub <noreply@github.com>
Wed, 3 May 2023 10:27:48 +0000 (12:27 +0200)
* Use deviceDiscoveryId instead of id
* Use device display name if discovery id is empty

Signed-off-by: Simon Spielmann <simon.spielmann@gmx.de>
bundles/org.openhab.binding.icloud/src/main/java/org/openhab/binding/icloud/internal/discovery/ICloudDeviceDiscovery.java
bundles/org.openhab.binding.icloud/src/main/java/org/openhab/binding/icloud/internal/handler/ICloudDeviceHandler.java
bundles/org.openhab.binding.icloud/src/main/java/org/openhab/binding/icloud/internal/handler/dto/json/response/ICloudDeviceInformation.java
bundles/org.openhab.binding.icloud/src/test/java/org/openhab/binding/icloud/TestICloud.java

index 429795bf584d92f980d955650be78551b739a579..82e6f12a7aeaf12933ab84aa4cd8fb6416b82739 100644 (file)
@@ -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);
 
index aa51c597ca5e8f6b5164018388666e4beb6445ab..30e9aa8f9344f2940fc9ddff44e63f9dbc671905 100644 (file)
@@ -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)) {
index b3a6b8a472ae0d1ef63fafbd0f4778f1e029fc25..cdbd565342e9e1d69566bc1bcd1d2627a5e66407 100644 (file)
@@ -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;
     }
index 126fd8eac46fcc0eb0c70c94279f01101b329cc8..da9af630ca26079e099f1c6b83f4a6dc629db8e8 100644 (file)
 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;
+    }
 }