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.elroconnects.internal.discovery;
15 import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
17 import java.time.Instant;
18 import java.util.HashMap;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants;
26 import org.openhab.binding.elroconnects.internal.devices.ElroConnectsConnector;
27 import org.openhab.binding.elroconnects.internal.handler.ElroConnectsAccountHandler;
28 import org.openhab.core.config.discovery.AbstractDiscoveryService;
29 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingUID;
32 import org.openhab.core.thing.binding.ThingHandler;
33 import org.openhab.core.thing.binding.ThingHandlerService;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * {@link ElroConnectsBridgeDiscoveryService} is used to discover K1 Hubs attached to an ELRO Connects cloud account.
40 * @author Mark Herwege - Initial Contribution
43 public class ElroConnectsBridgeDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
45 private final Logger logger = LoggerFactory.getLogger(ElroConnectsBridgeDiscoveryService.class);
47 private @Nullable ElroConnectsAccountHandler accountHandler;
49 private volatile @Nullable ScheduledFuture<?> discoveryJob;
51 private static final int TIMEOUT_S = 5;
52 private static final int INITIAL_DELAY_S = 5; // initial delay for polling to allow time for login and retrieval in
53 // ElroConnectsAccountHandler to complete
54 private static final int REFRESH_INTERVAL_S = 60;
56 public ElroConnectsBridgeDiscoveryService() {
57 super(ElroConnectsBindingConstants.SUPPORTED_CONNECTOR_TYPES_UIDS, TIMEOUT_S);
58 logger.debug("Bridge discovery service started");
62 protected void startScan() {
63 scheduler.execute(this::discoverConnectors); // If background account polling is not enabled for the handler,
64 // this will trigger http requests, therefore do in separate thread
65 // to be able to return quickly
68 private void discoverConnectors() {
69 logger.debug("Starting hub discovery scan");
70 ElroConnectsAccountHandler account = accountHandler;
71 if (account == null) {
75 ThingUID bridgeUID = account.getThing().getUID();
77 Map<String, ElroConnectsConnector> connectors = account.getDevices();
78 if (connectors == null) {
82 connectors.entrySet().forEach(c -> {
83 if (c.getValue().online) {
84 String connectorId = c.getKey();
85 String firmwareVersion = c.getValue().binVersion;
86 boolean legacy = false;
88 legacy = Integer.valueOf(firmwareVersion.substring(firmwareVersion.lastIndexOf(".") + 1)) <= 14;
89 } catch (NumberFormatException e) {
90 // Assume new firmware if we cannot decode firmwareVersion
91 logger.debug("Cannot get firmware version from {}, assume new firmware", firmwareVersion);
93 final Map<String, Object> properties = new HashMap<>();
94 properties.put(CONFIG_CONNECTOR_ID, connectorId);
95 properties.put(CONFIG_LEGACY_FIRMWARE, legacy);
96 properties.put("binVersion", c.getValue().binVersion);
97 properties.put("binType", c.getValue().binType);
98 properties.put("sdkVer", c.getValue().sdkVer);
99 properties.put(Thing.PROPERTY_MODEL_ID, c.getValue().model);
100 properties.put("desc", c.getValue().desc);
103 DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_CONNECTOR, bridgeUID, connectorId))
104 .withLabel(c.getValue().desc).withBridge(bridgeUID).withProperties(properties)
105 .withRepresentationProperty(CONFIG_CONNECTOR_ID).build());
111 protected synchronized void stopScan() {
113 removeOlderResults(getTimestampOfLastScan());
117 public void startBackgroundDiscovery() {
118 logger.debug("Start bridge background discovery");
119 ScheduledFuture<?> job = discoveryJob;
120 if (job == null || job.isCancelled()) {
121 discoveryJob = scheduler.scheduleWithFixedDelay(this::discoverConnectors, INITIAL_DELAY_S,
122 REFRESH_INTERVAL_S, TimeUnit.SECONDS);
127 public void stopBackgroundDiscovery() {
128 logger.debug("Stop bridge background discovery");
129 ScheduledFuture<?> job = discoveryJob;
130 if (job != null && !job.isCancelled()) {
137 public void deactivate() {
138 removeOlderResults(Instant.now().toEpochMilli());
143 public void setThingHandler(@Nullable ThingHandler handler) {
144 if (handler instanceof ElroConnectsAccountHandler accountHandler) {
145 this.accountHandler = accountHandler;
146 accountHandler.setDiscoveryService(this);
151 public @Nullable ThingHandler getThingHandler() {
152 return accountHandler;