]> git.basschouten.com Git - openhab-addons.git/blob
a3d4a17966c50a067693f156ab6cee114e3b0a07
[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.unifi.internal.handler;
14
15 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.DEVICE_TYPE_UAP;
16 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_CID;
17 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_MAC_ADDRESS;
18 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_PORT_NUMBER;
19 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_SID;
20 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_SITE;
21 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_WID;
22 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_WIFI_NAME;
23
24 import java.util.Map;
25 import java.util.concurrent.TimeUnit;
26 import java.util.regex.Pattern;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.unifi.internal.UniFiBindingConstants;
31 import org.openhab.binding.unifi.internal.api.UniFiController;
32 import org.openhab.binding.unifi.internal.api.UniFiException;
33 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
34 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
35 import org.openhab.binding.unifi.internal.api.dto.UniFiDevice;
36 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTuple;
37 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
38 import org.openhab.binding.unifi.internal.api.dto.UniFiSwitchPorts;
39 import org.openhab.binding.unifi.internal.api.dto.UniFiWlan;
40 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
41 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
42 import org.openhab.core.thing.ThingUID;
43 import org.osgi.service.component.annotations.Component;
44 import org.osgi.service.component.annotations.ServiceScope;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * Discovery service for detecting things connected to a UniFi controller.
50  *
51  * @author Hilbrand Bouwkamp - Initial contribution
52  */
53 @Component(scope = ServiceScope.PROTOTYPE, service = UniFiThingDiscoveryService.class)
54 @NonNullByDefault
55 public class UniFiThingDiscoveryService extends AbstractThingHandlerDiscoveryService<UniFiControllerThingHandler> {
56
57     /**
58      * Timeout for discovery time.
59      */
60     private static final int UNIFI_DISCOVERY_TIMEOUT_SECONDS = 30;
61     private static final long TTL_SECONDS = TimeUnit.MINUTES.toSeconds(5);
62     private static final int THING_ID_LENGTH = 8;
63     private static final Pattern DEFAULT_PORTNAME = Pattern.compile("Port \\d+");
64
65     private final Logger logger = LoggerFactory.getLogger(UniFiThingDiscoveryService.class);
66
67     public UniFiThingDiscoveryService() {
68         super(UniFiControllerThingHandler.class, UniFiBindingConstants.THING_TYPE_SUPPORTED,
69                 UNIFI_DISCOVERY_TIMEOUT_SECONDS, false);
70     }
71
72     @Override
73     protected void startScan() {
74         removeOlderResults(getTimestampOfLastScan());
75         final UniFiController controller = thingHandler.getController();
76         if (controller == null) {
77             return;
78         }
79         try {
80             controller.refresh();
81             final UniFiControllerCache cache = controller.getCache();
82             final ThingUID bridgeUID = thingHandler.getThing().getUID();
83
84             discoverSites(cache, bridgeUID);
85             discoverWlans(cache, bridgeUID);
86             discoverClients(cache, bridgeUID);
87             discoverPoePorts(cache, bridgeUID);
88             discoverAccessPoints(cache, bridgeUID);
89         } catch (final UniFiException e) {
90             logger.debug("Exception during discovery of UniFi Things", e);
91         }
92     }
93
94     private void discoverSites(final UniFiControllerCache cache, final ThingUID bridgeUID) {
95         for (final UniFiSite site : cache.getSites()) {
96             final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_SITE, bridgeUID,
97                     stripIdShort(site.getId()));
98             final Map<String, Object> properties = Map.of(PARAMETER_SID, site.getId());
99
100             thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(UniFiBindingConstants.THING_TYPE_SITE)
101                     .withBridge(bridgeUID).withRepresentationProperty(PARAMETER_SID).withTTL(TTL_SECONDS)
102                     .withProperties(properties).withLabel(site.getName()).build());
103         }
104     }
105
106     private void discoverWlans(final UniFiControllerCache cache, final ThingUID bridgeUID) {
107         for (final UniFiWlan wlan : cache.getWlans()) {
108             final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_WLAN, bridgeUID,
109                     stripIdShort(wlan.getId()));
110             final String siteName = wlan.getSite() == null ? "" : wlan.getSite().getName();
111             final Map<String, Object> properties = Map.of(PARAMETER_WID, wlan.getId(), PARAMETER_SITE, siteName,
112                     PARAMETER_WIFI_NAME, wlan.getName());
113
114             thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(UniFiBindingConstants.THING_TYPE_WLAN)
115                     .withBridge(bridgeUID).withRepresentationProperty(PARAMETER_WID).withTTL(TTL_SECONDS)
116                     .withProperties(properties).withLabel(wlan.getName()).build());
117         }
118     }
119
120     private void discoverClients(final UniFiControllerCache cache, final ThingUID bridgeUID) {
121         for (final UniFiClient uc : cache.getClients()) {
122             final var thingTypeUID = uc.isWireless() ? UniFiBindingConstants.THING_TYPE_WIRELESS_CLIENT
123                     : UniFiBindingConstants.THING_TYPE_WIRED_CLIENT;
124             final ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, stripIdShort(uc.getId()));
125             final Map<String, Object> properties = Map.of(PARAMETER_CID, uc.getMac(), PARAMETER_SITE,
126                     uc.getSite().getName());
127
128             thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withBridge(bridgeUID)
129                     .withRepresentationProperty(PARAMETER_CID).withTTL(TTL_SECONDS).withProperties(properties)
130                     .withLabel(uc.getName()).build());
131         }
132     }
133
134     private void discoverAccessPoints(final UniFiControllerCache cache, final ThingUID bridgeUID) {
135         for (final UniFiDevice ud : cache.getDevices()) {
136             if (DEVICE_TYPE_UAP.equals(ud.getType())) {
137                 final var thingTypeUID = UniFiBindingConstants.THING_TYPE_ACCESS_POINT;
138                 final ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, stripIdShort(ud.getId()));
139                 final Map<String, Object> properties = Map.of(PARAMETER_SITE, ud.getSite().getName(),
140                         PARAMETER_MAC_ADDRESS, ud.getMac());
141                 thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
142                         .withBridge(bridgeUID).withRepresentationProperty(PARAMETER_MAC_ADDRESS).withTTL(TTL_SECONDS)
143                         .withProperties(properties).withLabel(ud.getName()).build());
144             }
145         }
146     }
147
148     /**
149      * Shorten the id to make it a bit more comprehensible.
150      *
151      * @param id id to shorten.
152      * @return shortened id or if to short the original id
153      */
154     private static String stripIdShort(final String id) {
155         return id != null && id.length() > THING_ID_LENGTH ? id.substring(id.length() - THING_ID_LENGTH) : id;
156     }
157
158     private void discoverPoePorts(final UniFiControllerCache cache, final ThingUID bridgeUID) {
159         for (final UniFiSwitchPorts uc : cache.getSwitchPorts()) {
160             for (final UniFiPortTuple pt : uc.getPoePorts()) {
161                 final String deviceMac = pt.getDevice().getMac();
162                 final String id = deviceMac.replace(":", "") + "_" + pt.getPortIdx();
163                 final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_POE_PORT, bridgeUID, id);
164                 final Map<String, Object> properties = Map.of(PARAMETER_PORT_NUMBER, pt.getPortIdx(),
165                         PARAMETER_MAC_ADDRESS, deviceMac);
166
167                 thingDiscovered(DiscoveryResultBuilder.create(thingUID)
168                         .withThingType(UniFiBindingConstants.THING_TYPE_POE_PORT).withBridge(bridgeUID)
169                         .withTTL(TTL_SECONDS).withProperties(properties).withLabel(portName(pt)).build());
170             }
171         }
172     }
173
174     /**
175      * If the PoE port hasn't it's own name, but is named Port with a number the name is prefixed with the device name.
176      *
177      * @param pt port object
178      * @return label for the discovered PoE port
179      */
180     private @Nullable String portName(final UniFiPortTuple pt) {
181         final String portName = pt.getTable().getName();
182
183         return DEFAULT_PORTNAME.matcher(portName).find() ? pt.getDevice().getName() + " " + portName : portName;
184     }
185 }