]> git.basschouten.com Git - openhab-addons.git/blob
5eaddc8e27937da3706d730eef8bebedca6d08a0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.knx.internal.discovery;
14
15 import static org.openhab.binding.knx.internal.KNXBindingConstants.THING_TYPE_IP_BRIDGE;
16
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.Set;
21 import java.util.concurrent.Future;
22
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;
32
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;
38
39 /**
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.
43  *
44  * @implNote Discovery is based on the functionality provided by Calimero library.
45  * @author Holger Friedrich - Initial contribution
46  */
47 @Component(service = DiscoveryService.class, configurationPid = "discovery.knx")
48 @NonNullByDefault
49 public class KNXnetDiscoveryService extends AbstractDiscoveryService {
50     private final Logger logger = LoggerFactory.getLogger(KNXnetDiscoveryService.class);
51
52     private @Nullable Future<?> scanFuture = null;
53
54     public KNXnetDiscoveryService() {
55         super(Set.of(THING_TYPE_IP_BRIDGE), 3, true);
56     }
57
58     @Override
59     protected void startBackgroundDiscovery() {
60         // only start once at startup
61         startScan();
62     }
63
64     @Override
65     protected void stopBackgroundDiscovery() {
66         stopScan();
67     }
68
69     @Override
70     protected void startScan() {
71         if (scanFuture == null) {
72             scanFuture = scheduler.submit(this::startDiscovery);
73         } else {
74             logger.debug("KNXnet/IP background discovery scan in progress");
75         }
76     }
77
78     @Override
79     protected void stopScan() {
80         Future<?> tmpScanFuture = scanFuture;
81         if (tmpScanFuture != null) {
82             tmpScanFuture.cancel(false);
83             scanFuture = null;
84         }
85     }
86
87     private synchronized void startDiscovery() {
88         try {
89             logger.debug("Starting KNXnet/IP discovery scan");
90             Discoverer discovererUdp = new Discoverer(0, false);
91             discovererUdp.startSearch(3, true);
92
93             List<Result<SearchResponse>> responses = discovererUdp.getSearchResponses();
94
95             for (Result<SearchResponse> r : responses) {
96                 @Nullable
97                 SearchResponse response = r.getResponse();
98                 if (response == null) {
99                     continue;
100                 }
101                 Map<ServiceFamily, Integer> services = response.getServiceFamilies().families();
102
103                 if (services.containsKey(ServiceFamiliesDIB.ServiceFamily.Tunneling)
104                         || services.containsKey(ServiceFamiliesDIB.ServiceFamily.Routing)) {
105                     String serial = Objects.toString(response.getDevice().serialNumber()).replace(':', '-');
106
107                     if (logger.isTraceEnabled()) {
108                         logger.trace("Discovered device {}", response);
109                     } else {
110                         logger.debug("Discovered device {}, {}, {}", response.getDevice().getName(), serial,
111                                 response.getDevice().getMACAddressString());
112                     }
113
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());
122                     }
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());
130                     }
131                 } else {
132                     logger.trace("Ignoring device {}", response);
133                 }
134             }
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);
138         } finally {
139             scanFuture = null;
140             removeOlderResults(getTimestampOfLastScan());
141         }
142     }
143 }