2 * Copyright (c) 2010-2023 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.knx.internal.discovery;
15 import static org.openhab.binding.knx.internal.KNXBindingConstants.THING_TYPE_IP_BRIDGE;
17 import java.util.List;
19 import java.util.Objects;
21 import java.util.concurrent.Future;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.config.discovery.AbstractDiscoveryService;
26 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
27 import org.openhab.core.config.discovery.DiscoveryService;
28 import org.openhab.core.thing.ThingUID;
29 import org.osgi.service.component.annotations.Component;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import tuwien.auto.calimero.knxnetip.Discoverer;
34 import tuwien.auto.calimero.knxnetip.Discoverer.Result;
35 import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse;
36 import tuwien.auto.calimero.knxnetip.util.ServiceFamiliesDIB;
37 import tuwien.auto.calimero.knxnetip.util.ServiceFamiliesDIB.ServiceFamily;
40 * Discovers KNXnet/IP interfaces or routers and adds the results to the inbox.
41 * Several items per device might be created, as routers typically support routing and tunneling.
42 * Discovery uses multicast traffic to IP 224.0.23.12, port 3671.
44 * @implNote Discovery is based on the functionality provided by Calimero library.
45 * @author Holger Friedrich - Initial contribution
47 @Component(service = DiscoveryService.class, configurationPid = "discovery.knx")
49 public class KNXnetDiscoveryService extends AbstractDiscoveryService {
50 private final Logger logger = LoggerFactory.getLogger(KNXnetDiscoveryService.class);
52 private @Nullable Future<?> scanFuture = null;
54 public KNXnetDiscoveryService() {
55 super(Set.of(THING_TYPE_IP_BRIDGE), 3, true);
59 protected void startBackgroundDiscovery() {
60 // only start once at startup
65 protected void stopBackgroundDiscovery() {
70 protected void startScan() {
71 if (scanFuture == null) {
72 scanFuture = scheduler.submit(this::startDiscovery);
74 logger.debug("KNXnet/IP background discovery scan in progress");
79 protected void stopScan() {
80 Future<?> tmpScanFuture = scanFuture;
81 if (tmpScanFuture != null) {
82 tmpScanFuture.cancel(false);
87 private synchronized void startDiscovery() {
89 logger.debug("Starting KNXnet/IP discovery scan");
90 Discoverer discovererUdp = new Discoverer(0, false);
91 discovererUdp.startSearch(3, true);
93 List<Result<SearchResponse>> responses = discovererUdp.getSearchResponses();
95 for (Result<SearchResponse> r : responses) {
97 SearchResponse response = r.getResponse();
98 if (response == null) {
101 Map<ServiceFamily, Integer> services = response.getServiceFamilies().families();
103 if (services.containsKey(ServiceFamiliesDIB.ServiceFamily.Tunneling)
104 || services.containsKey(ServiceFamiliesDIB.ServiceFamily.Routing)) {
105 String serial = Objects.toString(response.getDevice().serialNumber()).replace(':', '-');
107 if (logger.isTraceEnabled()) {
108 logger.trace("Discovered device {}", response);
110 logger.debug("Discovered device {}, {}, {}", response.getDevice().getName(), serial,
111 response.getDevice().getMACAddressString());
114 if (services.containsKey(ServiceFamiliesDIB.ServiceFamily.Tunneling)) {
115 thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_IP_BRIDGE, serial))
116 .withLabel(response.getDevice().getName()).withProperty("serialNumber", serial)
117 .withProperty("type", "TUNNEL")
118 .withProperty("ipAddress",
119 "" + response.getControlEndpoint().getAddress().getHostAddress())
120 .withProperty("port", "" + response.getControlEndpoint().getPort())
121 .withRepresentationProperty("serialNumber").build());
123 if (services.containsKey(ServiceFamiliesDIB.ServiceFamily.Routing)) {
124 thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_IP_BRIDGE, serial))
125 .withLabel(response.getDevice().getName() + " (router mode)")
126 .withProperty("serialNumber", serial + "-r").withProperty("type", "ROUTER")
127 .withProperty("ipAddress", "224.0.23.12")
128 .withProperty("port", "" + response.getControlEndpoint().getPort())
129 .withRepresentationProperty("serialNumber").build());
132 logger.trace("Ignoring device {}", response);
135 logger.debug("Completed KNXnet/IP discovery scan");
136 } catch (Exception ex) {
137 logger.warn("An error occurred during KNXnet/IP discovery {}", ex.getMessage(), ex);
140 removeOlderResults(getTimestampOfLastScan());