]> git.basschouten.com Git - openhab-addons.git/blob
8615ef193db9baaa11e0c1ebc2f42bf202ba993e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.amazonechocontrol.internal.discovery;
14
15 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*;
16
17 import java.util.*;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20 import java.util.stream.Collectors;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.amazonechocontrol.internal.Connection;
25 import org.openhab.binding.amazonechocontrol.internal.handler.AccountHandler;
26 import org.openhab.binding.amazonechocontrol.internal.handler.SmartHomeDeviceHandler;
27 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDeviceAlias;
28 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDevices.DriverIdentity;
29 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDevices.SmartHomeDevice;
30 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeGroups.SmartHomeGroup;
31 import org.openhab.binding.amazonechocontrol.internal.jsons.SmartHomeBaseDevice;
32 import org.openhab.binding.amazonechocontrol.internal.smarthome.Constants;
33 import org.openhab.core.config.discovery.AbstractDiscoveryService;
34 import org.openhab.core.config.discovery.DiscoveryResult;
35 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
36 import org.openhab.core.thing.ThingUID;
37 import org.osgi.service.component.annotations.Activate;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * @author Lukas Knoeller - Initial contribution
43  */
44 @NonNullByDefault
45 public class SmartHomeDevicesDiscovery extends AbstractDiscoveryService {
46     private AccountHandler accountHandler;
47     private final Logger logger = LoggerFactory.getLogger(SmartHomeDevicesDiscovery.class);
48
49     private @Nullable ScheduledFuture<?> startScanStateJob;
50     private @Nullable Long activateTimeStamp;
51
52     public SmartHomeDevicesDiscovery(AccountHandler accountHandler) {
53         super(SUPPORTED_SMART_HOME_THING_TYPES_UIDS, 10);
54         this.accountHandler = accountHandler;
55     }
56
57     public void activate() {
58         activate(new Hashtable<String, Object>());
59     }
60
61     @Override
62     public void deactivate() {
63         super.deactivate();
64     }
65
66     @Override
67     protected void startScan() {
68         stopScanJob();
69         Long activateTimeStamp = this.activateTimeStamp;
70         if (activateTimeStamp != null) {
71             removeOlderResults(activateTimeStamp);
72         }
73         setSmartHomeDevices(accountHandler.updateSmartHomeDeviceList(false));
74     }
75
76     protected void startAutomaticScan() {
77         if (!this.accountHandler.getThing().getThings().isEmpty()) {
78             stopScanJob();
79             return;
80         }
81         Connection connection = this.accountHandler.findConnection();
82         if (connection == null) {
83             return;
84         }
85         Date verifyTime = connection.tryGetVerifyTime();
86         if (verifyTime == null) {
87             return;
88         }
89         if (new Date().getTime() - verifyTime.getTime() < 10000) {
90             return;
91         }
92         startScan();
93     }
94
95     @Override
96     protected void startBackgroundDiscovery() {
97         stopScanJob();
98         startScanStateJob = scheduler.scheduleWithFixedDelay(this::startAutomaticScan, 3000, 1000,
99                 TimeUnit.MILLISECONDS);
100     }
101
102     @Override
103     protected void stopBackgroundDiscovery() {
104         stopScanJob();
105     }
106
107     void stopScanJob() {
108         ScheduledFuture<?> currentStartScanStateJob = startScanStateJob;
109         if (currentStartScanStateJob != null) {
110             currentStartScanStateJob.cancel(false);
111             startScanStateJob = null;
112         }
113         super.stopScan();
114     }
115
116     @Override
117     @Activate
118     public void activate(@Nullable Map<String, Object> config) {
119         super.activate(config);
120         if (config != null) {
121             modified(config);
122         }
123         Long activateTimeStamp = this.activateTimeStamp;
124         if (activateTimeStamp == null) {
125             this.activateTimeStamp = new Date().getTime();
126         }
127     };
128
129     synchronized void setSmartHomeDevices(List<SmartHomeBaseDevice> deviceList) {
130         int smartHomeDeviceDiscoveryMode = accountHandler.getSmartHomeDevicesDiscoveryMode();
131         if (smartHomeDeviceDiscoveryMode == 0) {
132             return;
133         }
134
135         for (Object smartHomeDevice : deviceList) {
136             ThingUID bridgeThingUID = this.accountHandler.getThing().getUID();
137             ThingUID thingUID = null;
138             String deviceName = null;
139             Map<String, Object> props = new HashMap<>();
140
141             if (smartHomeDevice instanceof SmartHomeDevice) {
142                 SmartHomeDevice shd = (SmartHomeDevice) smartHomeDevice;
143                 logger.trace("Found SmartHome device: {}", shd);
144
145                 String entityId = shd.entityId;
146                 if (entityId == null) {
147                     // No entity id
148                     continue;
149                 }
150                 String id = shd.findId();
151                 if (id == null) {
152                     // No id
153                     continue;
154                 }
155                 boolean isSkillDevice = false;
156                 DriverIdentity driverIdentity = shd.driverIdentity;
157                 isSkillDevice = driverIdentity != null && "SKILL".equals(driverIdentity.namespace);
158
159                 if (smartHomeDeviceDiscoveryMode == 1 && isSkillDevice) {
160                     // Connected through skill
161                     continue;
162                 }
163                 if (!(smartHomeDeviceDiscoveryMode == 2) && "openHAB".equalsIgnoreCase(shd.manufacturerName)) {
164                     // OpenHAB device
165                     continue;
166                 }
167
168                 if (shd.getCapabilities().stream()
169                         .noneMatch(capability -> Constants.SUPPORTED_INTERFACES.contains(capability.interfaceName))) {
170                     // No supported interface found
171                     continue;
172                 }
173
174                 thingUID = new ThingUID(THING_TYPE_SMART_HOME_DEVICE, bridgeThingUID, entityId.replace(".", "-"));
175
176                 List<JsonSmartHomeDeviceAlias> aliases = shd.aliases;
177                 if ("Amazon".equals(shd.manufacturerName) && driverIdentity != null
178                         && "SonarCloudService".equals(driverIdentity.identifier)) {
179                     List<@Nullable String> interfaces = shd.getCapabilities().stream().map(c -> c.interfaceName)
180                             .collect(Collectors.toList());
181                     if (interfaces.contains("Alexa.AcousticEventSensor")) {
182                         deviceName = "Alexa Guard on " + shd.friendlyName;
183                     } else if (interfaces.contains("Alexa.ColorController")) {
184                         deviceName = "Alexa Color Controller on " + shd.friendlyName;
185                     } else if (interfaces.contains("Alexa.PowerController")) {
186                         deviceName = "Alexa Plug on " + shd.friendlyName;
187                     } else {
188                         deviceName = "Unknown Device on " + shd.friendlyName;
189                     }
190                 } else if ("Amazon".equals(shd.manufacturerName) && driverIdentity != null
191                         && "OnGuardSmartHomeBridgeService".equals(driverIdentity.identifier)) {
192                     deviceName = "Alexa Guard";
193                 } else if (aliases != null && !aliases.isEmpty() && aliases.get(0).friendlyName != null) {
194                     deviceName = aliases.get(0).friendlyName;
195                 } else {
196                     deviceName = shd.friendlyName;
197                 }
198                 props.put(DEVICE_PROPERTY_ID, id);
199             } else if (smartHomeDevice instanceof SmartHomeGroup) {
200                 SmartHomeGroup shg = (SmartHomeGroup) smartHomeDevice;
201                 logger.trace("Found SmartHome device: {}", shg);
202
203                 String id = shg.findId();
204                 if (id == null) {
205                     // No id
206                     continue;
207                 }
208                 Set<SmartHomeDevice> supportedChildren = SmartHomeDeviceHandler.getSupportedSmartHomeDevices(shg,
209                         deviceList);
210                 if (supportedChildren.size() == 0) {
211                     // No children with an supported interface
212                     continue;
213                 }
214                 thingUID = new ThingUID(THING_TYPE_SMART_HOME_DEVICE_GROUP, bridgeThingUID, id.replace(".", "-"));
215                 deviceName = shg.applianceGroupName;
216                 props.put(DEVICE_PROPERTY_ID, id);
217             }
218
219             if (thingUID != null) {
220                 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel(deviceName)
221                         .withProperties(props).withBridge(bridgeThingUID).build();
222
223                 logger.debug("Device [{}] found.", deviceName);
224
225                 thingDiscovered(result);
226             }
227         }
228     }
229 }