2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.amazonechocontrol.internal.discovery;
15 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20 import java.util.stream.Collectors;
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;
42 * @author Lukas Knoeller - Initial contribution
45 public class SmartHomeDevicesDiscovery extends AbstractDiscoveryService {
46 private AccountHandler accountHandler;
47 private final Logger logger = LoggerFactory.getLogger(SmartHomeDevicesDiscovery.class);
49 private @Nullable ScheduledFuture<?> startScanStateJob;
50 private @Nullable Long activateTimeStamp;
52 public SmartHomeDevicesDiscovery(AccountHandler accountHandler) {
53 super(SUPPORTED_SMART_HOME_THING_TYPES_UIDS, 10);
54 this.accountHandler = accountHandler;
57 public void activate() {
58 activate(new Hashtable<String, Object>());
62 public void deactivate() {
67 protected void startScan() {
69 Long activateTimeStamp = this.activateTimeStamp;
70 if (activateTimeStamp != null) {
71 removeOlderResults(activateTimeStamp);
73 setSmartHomeDevices(accountHandler.updateSmartHomeDeviceList(false));
76 protected void startAutomaticScan() {
77 if (!this.accountHandler.getThing().getThings().isEmpty()) {
81 Connection connection = this.accountHandler.findConnection();
82 if (connection == null) {
85 Date verifyTime = connection.tryGetVerifyTime();
86 if (verifyTime == null) {
89 if (new Date().getTime() - verifyTime.getTime() < 10000) {
96 protected void startBackgroundDiscovery() {
98 startScanStateJob = scheduler.scheduleWithFixedDelay(this::startAutomaticScan, 3000, 1000,
99 TimeUnit.MILLISECONDS);
103 protected void stopBackgroundDiscovery() {
108 ScheduledFuture<?> currentStartScanStateJob = startScanStateJob;
109 if (currentStartScanStateJob != null) {
110 currentStartScanStateJob.cancel(false);
111 startScanStateJob = null;
118 public void activate(@Nullable Map<String, Object> config) {
119 super.activate(config);
120 if (config != null) {
123 Long activateTimeStamp = this.activateTimeStamp;
124 if (activateTimeStamp == null) {
125 this.activateTimeStamp = new Date().getTime();
129 synchronized void setSmartHomeDevices(List<SmartHomeBaseDevice> deviceList) {
130 int smartHomeDeviceDiscoveryMode = accountHandler.getSmartHomeDevicesDiscoveryMode();
131 if (smartHomeDeviceDiscoveryMode == 0) {
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<>();
141 if (smartHomeDevice instanceof SmartHomeDevice) {
142 SmartHomeDevice shd = (SmartHomeDevice) smartHomeDevice;
143 logger.trace("Found SmartHome device: {}", shd);
145 String entityId = shd.entityId;
146 if (entityId == null) {
150 String id = shd.findId();
155 boolean isSkillDevice = false;
156 DriverIdentity driverIdentity = shd.driverIdentity;
157 isSkillDevice = driverIdentity != null && "SKILL".equals(driverIdentity.namespace);
159 if (smartHomeDeviceDiscoveryMode == 1 && isSkillDevice) {
160 // Connected through skill
163 if (!(smartHomeDeviceDiscoveryMode == 2) && "openHAB".equalsIgnoreCase(shd.manufacturerName)) {
168 if (shd.getCapabilities().stream()
169 .noneMatch(capability -> Constants.SUPPORTED_INTERFACES.contains(capability.interfaceName))) {
170 // No supported interface found
174 thingUID = new ThingUID(THING_TYPE_SMART_HOME_DEVICE, bridgeThingUID, entityId.replace(".", "-"));
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;
188 deviceName = "Unknown Device on " + shd.friendlyName;
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;
196 deviceName = shd.friendlyName;
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);
203 String id = shg.findId();
208 Set<SmartHomeDevice> supportedChildren = SmartHomeDeviceHandler.getSupportedSmartHomeDevices(shg,
210 if (supportedChildren.size() == 0) {
211 // No children with an supported interface
214 thingUID = new ThingUID(THING_TYPE_SMART_HOME_DEVICE_GROUP, bridgeThingUID, id.replace(".", "-"));
215 deviceName = shg.applianceGroupName;
216 props.put(DEVICE_PROPERTY_ID, id);
219 if (thingUID != null) {
220 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel(deviceName)
221 .withProperties(props).withBridge(bridgeThingUID).build();
223 logger.debug("Device [{}] found.", deviceName);
225 thingDiscovered(result);