2 * Copyright (c) 2010-2022 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.unifi.internal.handler;
15 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_CID;
16 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_MAC_ADDRESS;
17 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_PORT_NUMBER;
18 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_SID;
19 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_SITE;
20 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_WID;
21 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_WIFI_NAME;
24 import java.util.Map.Entry;
25 import java.util.concurrent.TimeUnit;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.unifi.internal.UniFiBindingConstants;
30 import org.openhab.binding.unifi.internal.api.UniFiController;
31 import org.openhab.binding.unifi.internal.api.UniFiException;
32 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
33 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
34 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTable;
35 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
36 import org.openhab.binding.unifi.internal.api.dto.UniFiWlan;
37 import org.openhab.core.config.discovery.AbstractDiscoveryService;
38 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
39 import org.openhab.core.config.discovery.DiscoveryService;
40 import org.openhab.core.thing.ThingUID;
41 import org.openhab.core.thing.binding.ThingHandler;
42 import org.openhab.core.thing.binding.ThingHandlerService;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * Discovery service for detecting things connected to a UniFi controller.
49 * @author Hilbrand Bouwkamp - Initial contribution
52 public class UniFiThingDiscoveryService extends AbstractDiscoveryService
53 implements ThingHandlerService, DiscoveryService {
56 * Timeout for discovery time.
58 private static final int UNIFI_DISCOVERY_TIMEOUT_SECONDS = 30;
59 private static final long TTL_SECONDS = TimeUnit.MINUTES.toSeconds(5);
60 private static final int THING_ID_LENGTH = 8;
61 private static final String DEFAULT_PORTNAME = "Port";
63 private final Logger logger = LoggerFactory.getLogger(UniFiThingDiscoveryService.class);
65 private @Nullable UniFiControllerThingHandler bridgeHandler;
67 public UniFiThingDiscoveryService() {
68 super(UniFiBindingConstants.THING_TYPE_SUPPORTED, UNIFI_DISCOVERY_TIMEOUT_SECONDS, false);
72 public void deactivate() {
77 public void setThingHandler(final ThingHandler handler) {
78 if (handler instanceof UniFiControllerThingHandler) {
79 bridgeHandler = (UniFiControllerThingHandler) handler;
84 public @Nullable ThingHandler getThingHandler() {
89 protected void startScan() {
90 removeOlderResults(getTimestampOfLastScan());
91 final UniFiControllerThingHandler bh = bridgeHandler;
95 final UniFiController controller = bh.getController();
96 if (controller == null) {
100 controller.refresh();
101 final UniFiControllerCache cache = controller.getCache();
102 final ThingUID bridgeUID = bh.getThing().getUID();
104 discoverSites(cache, bridgeUID);
105 discoverWlans(cache, bridgeUID);
106 discoverClients(cache, bridgeUID);
107 discoverPoePorts(cache, bridgeUID);
108 } catch (final UniFiException e) {
109 logger.debug("Exception during discovery of UniFi Things", e);
113 private void discoverSites(final UniFiControllerCache cache, final ThingUID bridgeUID) {
114 for (final UniFiSite site : cache.getSites()) {
115 final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_SITE, bridgeUID,
116 stripIdShort(site.getId()));
117 final Map<String, Object> properties = Map.of(PARAMETER_SID, site.getId());
119 thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(UniFiBindingConstants.THING_TYPE_SITE)
120 .withBridge(bridgeUID).withRepresentationProperty(PARAMETER_SID).withTTL(TTL_SECONDS)
121 .withProperties(properties).withLabel(site.getName()).build());
125 private void discoverWlans(final UniFiControllerCache cache, final ThingUID bridgeUID) {
126 for (final UniFiWlan wlan : cache.getWlans()) {
127 final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_WLAN, bridgeUID,
128 stripIdShort(wlan.getId()));
129 final Map<String, Object> properties = Map.of(PARAMETER_WID, wlan.getId(), PARAMETER_SITE,
130 wlan.getSite().getName(), PARAMETER_WIFI_NAME, wlan.getName());
132 thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(UniFiBindingConstants.THING_TYPE_WLAN)
133 .withBridge(bridgeUID).withRepresentationProperty(PARAMETER_WID).withTTL(TTL_SECONDS)
134 .withProperties(properties).withLabel(wlan.getName()).build());
138 private void discoverClients(final UniFiControllerCache cache, final ThingUID bridgeUID) {
139 for (final UniFiClient uc : cache.getClients()) {
140 final var thingTypeUID = uc.isWireless() ? UniFiBindingConstants.THING_TYPE_WIRELESS_CLIENT
141 : UniFiBindingConstants.THING_TYPE_WIRED_CLIENT;
142 final ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, stripIdShort(uc.getId()));
143 final Map<String, Object> properties = Map.of(PARAMETER_CID, uc.getMac(), PARAMETER_SITE,
144 uc.getSite().getName());
146 thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withBridge(bridgeUID)
147 .withRepresentationProperty(PARAMETER_CID).withTTL(TTL_SECONDS).withProperties(properties)
148 .withLabel(uc.getAlias()).build());
153 * Shorten the id to make it a bit more comprehensible.
155 * @param id id to shorten.
156 * @return shortened id or if to short the original id
158 private static String stripIdShort(final String id) {
159 return id.length() > THING_ID_LENGTH ? id.substring(id.length() - THING_ID_LENGTH) : id;
162 private void discoverPoePorts(final UniFiControllerCache cache, final ThingUID bridgeUID) {
163 for (final Map<Integer, UniFiPortTable> uc : cache.getSwitchPorts()) {
164 for (final Entry<Integer, UniFiPortTable> sp : uc.entrySet()) {
165 final UniFiPortTable pt = sp.getValue();
166 final String deviceMac = pt.getDevice().getMac();
167 final String id = deviceMac.replace(":", "") + "_" + pt.getPortIdx();
168 final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_POE_PORT, bridgeUID, id);
169 final Map<String, Object> properties = Map.of(PARAMETER_PORT_NUMBER, pt.getPortIdx(),
170 PARAMETER_MAC_ADDRESS, deviceMac);
172 thingDiscovered(DiscoveryResultBuilder.create(thingUID)
173 .withThingType(UniFiBindingConstants.THING_TYPE_POE_PORT).withBridge(bridgeUID)
174 .withTTL(TTL_SECONDS).withProperties(properties).withLabel(portName(pt)).build());
180 * 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.
182 * @param pt port object
183 * @return label for the discovered PoE port
185 private static @Nullable String portName(final UniFiPortTable pt) {
186 final String portName = pt.getName();
188 return portName.startsWith(DEFAULT_PORTNAME) ? pt.getDevice().getName() + " " + portName : portName;