2 * Copyright (c) 2010-2024 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.homematic.internal.discovery;
15 import static org.openhab.binding.homematic.internal.HomematicBindingConstants.BINDING_ID;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.openhab.binding.homematic.internal.common.HomematicConfig;
23 import org.openhab.binding.homematic.internal.communicator.HomematicGateway;
24 import org.openhab.binding.homematic.internal.handler.HomematicBridgeHandler;
25 import org.openhab.binding.homematic.internal.model.HmDevice;
26 import org.openhab.binding.homematic.internal.type.UidUtils;
27 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
28 import org.openhab.core.config.discovery.DiscoveryResult;
29 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.thing.ThingUID;
34 import org.osgi.service.component.annotations.Component;
35 import org.osgi.service.component.annotations.ServiceScope;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link HomematicDeviceDiscoveryService} is used to discover devices that are connected to a Homematic gateway.
42 * @author Gerhard Riegler - Initial contribution
44 @Component(scope = ServiceScope.PROTOTYPE, service = HomematicDeviceDiscoveryService.class)
45 public class HomematicDeviceDiscoveryService
46 extends AbstractThingHandlerDiscoveryService<@NonNull HomematicBridgeHandler> {
47 private final Logger logger = LoggerFactory.getLogger(HomematicDeviceDiscoveryService.class);
48 private static final int DISCOVER_TIMEOUT_SECONDS = 300;
50 private Future<?> loadDevicesFuture;
51 private volatile boolean isInInstallMode = false;
52 private volatile Object installModeSync = new Object();
54 public HomematicDeviceDiscoveryService() {
55 super(HomematicBridgeHandler.class, Set.of(new ThingTypeUID(BINDING_ID, "-")), DISCOVER_TIMEOUT_SECONDS, false);
59 public void initialize() {
60 thingHandler.setDiscoveryService(this);
65 protected void startScan() {
66 logger.debug("Starting Homematic discovery scan");
72 * Will set controller in <i>installMode==true</i>, but only if the bridge
73 * is ONLINE (e.g. not during INITIALIZATION).
75 private void enableInstallMode() {
77 HomematicGateway gateway = thingHandler.getGateway();
78 Thing bridge = thingHandler.getThing();
79 ThingStatus bridgeStatus = bridge.getStatus();
81 if (ThingStatus.ONLINE == bridgeStatus) {
82 gateway.setInstallMode(true, getInstallModeDuration());
84 int remaining = gateway.getInstallMode();
87 logger.debug("Successfully put controller in install mode. Remaining time: {} seconds", remaining);
89 logger.warn("Controller did not accept requested install mode");
92 logger.debug("Will not attempt to set controller in install mode, because bridge is not ONLINE.");
94 } catch (Exception ex) {
95 logger.warn("Failed to set Homematic controller in install mode", ex);
99 private int getInstallModeDuration() {
100 return thingHandler.getThing().getConfiguration().as(HomematicConfig.class).getInstallModeDuration();
104 public int getScanTimeout() {
105 return getInstallModeDuration();
109 public synchronized void stopScan() {
110 logger.debug("Stopping Homematic discovery scan");
111 disableInstallMode();
112 thingHandler.getGateway().cancelLoadAllDeviceMetadata();
113 waitForScanFinishing();
117 private void disableInstallMode() {
119 synchronized (installModeSync) {
120 if (isInInstallMode) {
121 isInInstallMode = false;
122 installModeSync.notify();
123 thingHandler.getGateway().setInstallMode(false, 0);
126 } catch (Exception ex) {
127 logger.warn("Failed to disable Homematic controller's install mode", ex);
131 private void setIsInInstallMode() {
132 synchronized (installModeSync) {
133 isInInstallMode = true;
137 private void waitForInstallModeFinished(int timeout) throws InterruptedException {
138 synchronized (installModeSync) {
139 while (isInInstallMode) {
140 installModeSync.wait(timeout);
145 private void waitForLoadDevicesFinished() throws InterruptedException, ExecutionException {
146 if (loadDevicesFuture != null) {
147 loadDevicesFuture.get();
152 * Waits for the discovery scan to finish and then returns.
154 public void waitForScanFinishing() {
155 logger.debug("Waiting for finishing Homematic device discovery scan");
157 waitForInstallModeFinished(DISCOVER_TIMEOUT_SECONDS * 1000);
158 waitForLoadDevicesFinished();
159 } catch (ExecutionException | InterruptedException ex) {
161 } catch (Exception ex) {
162 logger.error("Error waiting for device discovery scan: {}", ex.getMessage(), ex);
164 HomematicBridgeHandler bridgeHandler = thingHandler;
165 String gatewayId = bridgeHandler != null && bridgeHandler.getGateway() != null
166 ? bridgeHandler.getGateway().getId()
168 logger.debug("Finished Homematic device discovery scan on gateway '{}'", gatewayId);
172 * Starts a thread which loads all Homematic devices connected to the gateway.
174 public void loadDevices() {
175 if (loadDevicesFuture == null && thingHandler.getGateway() != null) {
176 loadDevicesFuture = scheduler.submit(() -> {
178 final HomematicGateway gateway = thingHandler.getGateway();
179 gateway.loadAllDeviceMetadata();
180 thingHandler.getTypeGenerator().validateFirmwares();
181 } catch (Throwable ex) {
182 logger.error("{}", ex.getMessage(), ex);
184 loadDevicesFuture = null;
185 thingHandler.setOfflineStatus();
186 removeOlderResults(getTimestampOfLastScan());
190 logger.debug("Homematic devices discovery scan in progress");
195 * Removes the Homematic device.
197 public void deviceRemoved(HmDevice device) {
198 ThingUID thingUID = UidUtils.generateThingUID(device, thingHandler.getThing());
199 thingRemoved(thingUID);
203 * Generates the DiscoveryResult from a Homematic device.
205 public void deviceDiscovered(HmDevice device) {
206 ThingUID bridgeUID = thingHandler.getThing().getUID();
207 ThingTypeUID typeUid = UidUtils.generateThingTypeUID(device);
208 ThingUID thingUID = new ThingUID(typeUid, bridgeUID, device.getAddress());
209 String label = device.getName() != null ? device.getName() : device.getAddress();
210 long timeToLive = thingHandler.getThing().getConfiguration().as(HomematicConfig.class).getDiscoveryTimeToLive();
212 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID).withLabel(label)
213 .withProperty(Thing.PROPERTY_SERIAL_NUMBER, device.getAddress())
214 .withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER).withTTL(timeToLive).build();
215 thingDiscovered(discoveryResult);