2 * Copyright (c) 2010-2020 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.api.model;
15 import java.util.Collection;
17 import org.apache.commons.lang.StringUtils;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.eclipse.jetty.client.HttpClient;
21 import org.openhab.binding.unifi.internal.api.UniFiException;
22 import org.openhab.binding.unifi.internal.api.UniFiExpiredSessionException;
23 import org.openhab.binding.unifi.internal.api.UniFiNotAuthorizedException;
24 import org.openhab.binding.unifi.internal.api.cache.UniFiClientCache;
25 import org.openhab.binding.unifi.internal.api.cache.UniFiDeviceCache;
26 import org.openhab.binding.unifi.internal.api.cache.UniFiSiteCache;
27 import org.openhab.binding.unifi.internal.api.util.UniFiClientDeserializer;
28 import org.openhab.binding.unifi.internal.api.util.UniFiClientInstanceCreator;
29 import org.openhab.binding.unifi.internal.api.util.UniFiDeviceInstanceCreator;
30 import org.openhab.binding.unifi.internal.api.util.UniFiSiteInstanceCreator;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import com.google.gson.FieldNamingPolicy;
35 import com.google.gson.Gson;
36 import com.google.gson.GsonBuilder;
39 * The {@link UniFiController} is the main communication point with an external instance of the Ubiquiti Networks
40 * Controller Software.
42 * @author Matthew Bowman - Initial contribution
43 * @author Patrik Wimnell - Blocking / Unblocking client support
46 public class UniFiController {
48 private final Logger logger = LoggerFactory.getLogger(UniFiController.class);
50 private UniFiSiteCache sitesCache = new UniFiSiteCache();
52 private UniFiDeviceCache devicesCache = new UniFiDeviceCache();
54 private UniFiClientCache clientsCache = new UniFiClientCache();
56 private UniFiClientCache insightsCache = new UniFiClientCache();
58 private final HttpClient httpClient;
60 private final String host;
62 private final int port;
64 private final String username;
66 private final String password;
68 private final Gson gson;
70 public UniFiController(HttpClient httpClient, String host, int port, String username, String password) {
71 this.httpClient = httpClient;
74 this.username = username;
75 this.password = password;
76 UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(this);
77 UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(this);
78 UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(this);
79 this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
80 .registerTypeAdapter(UniFiSite.class, siteInstanceCreator)
81 .registerTypeAdapter(UniFiDevice.class, deviceInstanceCreator)
82 .registerTypeAdapter(UniFiClient.class, new UniFiClientDeserializer())
83 .registerTypeAdapter(UniFiUnknownClient.class, clientInstanceCreator)
84 .registerTypeAdapter(UniFiWiredClient.class, clientInstanceCreator)
85 .registerTypeAdapter(UniFiWirelessClient.class, clientInstanceCreator).create();
90 public void start() throws UniFiException {
94 public void stop() throws UniFiException {
98 public void login() throws UniFiException {
99 UniFiControllerRequest<Void> req = newRequest(Void.class);
100 req.setPath("/api/login");
101 req.setBodyParameter("username", username);
102 req.setBodyParameter("password", password);
103 // scurb: Changed strict = false to make blocking feature work
104 req.setBodyParameter("strict", false);
105 req.setBodyParameter("remember", false);
109 public void logout() throws UniFiException {
110 UniFiControllerRequest<Void> req = newRequest(Void.class);
111 req.setPath("/logout");
115 public void refresh() throws UniFiException {
116 synchronized (this) {
117 sitesCache = getSites();
118 devicesCache = getDevices();
119 clientsCache = getClients();
120 insightsCache = getInsights();
126 public @Nullable UniFiSite getSite(@Nullable String id) {
127 UniFiSite site = null;
128 if (StringUtils.isNotBlank(id)) {
129 synchronized (this) {
130 site = sitesCache.get(id);
133 logger.debug("Could not find a matching site for id = '{}'", id);
141 public @Nullable UniFiDevice getDevice(@Nullable String id) {
142 UniFiDevice device = null;
143 if (StringUtils.isNotBlank(id)) {
144 synchronized (this) {
145 device = devicesCache.get(id);
147 if (device == null) {
148 logger.debug("Could not find a matching device for id = '{}'", id);
156 public @Nullable UniFiClient getClient(@Nullable String id) {
157 UniFiClient client = null;
158 if (StringUtils.isNotBlank(id)) {
159 synchronized (this) {
160 // mgb: first check active clients and fallback to insights if not found
161 client = clientsCache.get(id);
162 if (client == null) {
163 client = insightsCache.get(id);
166 if (client == null) {
167 logger.debug("Could not find a matching client for id = {}", id);
173 protected void block(UniFiClient client, boolean blocked) throws UniFiException {
174 UniFiControllerRequest<Void> req = newRequest(Void.class);
175 req.setPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
176 req.setBodyParameter("cmd", blocked ? "block-sta" : "unblock-sta");
177 req.setBodyParameter("mac", client.getMac());
181 protected void reconnect(UniFiClient client) throws UniFiException {
182 UniFiControllerRequest<Void> req = newRequest(Void.class);
183 req.setPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
184 req.setBodyParameter("cmd", "kick-sta");
185 req.setBodyParameter("mac", client.getMac());
191 private <T> UniFiControllerRequest<T> newRequest(Class<T> responseType) {
192 return new UniFiControllerRequest<>(responseType, gson, httpClient, host, port);
195 private <T> @Nullable T executeRequest(UniFiControllerRequest<T> request) throws UniFiException {
198 result = request.execute();
199 } catch (UniFiExpiredSessionException e) {
201 result = executeRequest(request);
202 } catch (UniFiNotAuthorizedException e) {
203 logger.warn("Not Authorized! Please make sure your controller credentials have administrator rights");
209 private UniFiSiteCache getSites() throws UniFiException {
210 UniFiControllerRequest<UniFiSite[]> req = newRequest(UniFiSite[].class);
211 req.setPath("/api/self/sites");
212 UniFiSite[] sites = executeRequest(req);
213 UniFiSiteCache cache = new UniFiSiteCache();
215 logger.debug("Found {} UniFi Site(s): {}", sites.length, lazyFormatAsList(sites));
216 for (UniFiSite site : sites) {
223 private UniFiDeviceCache getDevices() throws UniFiException {
224 UniFiDeviceCache cache = new UniFiDeviceCache();
225 Collection<UniFiSite> sites = sitesCache.values();
226 for (UniFiSite site : sites) {
227 cache.putAll(getDevices(site));
232 private UniFiDeviceCache getDevices(UniFiSite site) throws UniFiException {
233 UniFiControllerRequest<UniFiDevice[]> req = newRequest(UniFiDevice[].class);
234 req.setPath("/api/s/" + site.getName() + "/stat/device");
235 UniFiDevice[] devices = executeRequest(req);
236 UniFiDeviceCache cache = new UniFiDeviceCache();
237 if (devices != null) {
238 logger.debug("Found {} UniFi Device(s): {}", devices.length, lazyFormatAsList(devices));
239 for (UniFiDevice device : devices) {
246 private UniFiClientCache getClients() throws UniFiException {
247 UniFiClientCache cache = new UniFiClientCache();
248 Collection<UniFiSite> sites = sitesCache.values();
249 for (UniFiSite site : sites) {
250 cache.putAll(getClients(site));
255 private UniFiClientCache getClients(UniFiSite site) throws UniFiException {
256 UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
257 req.setPath("/api/s/" + site.getName() + "/stat/sta");
258 UniFiClient[] clients = executeRequest(req);
259 UniFiClientCache cache = new UniFiClientCache();
260 if (clients != null) {
261 logger.debug("Found {} UniFi Client(s): {}", clients.length, lazyFormatAsList(clients));
262 for (UniFiClient client : clients) {
269 private UniFiClientCache getInsights() throws UniFiException {
270 UniFiClientCache cache = new UniFiClientCache();
271 Collection<UniFiSite> sites = sitesCache.values();
272 for (UniFiSite site : sites) {
273 cache.putAll(getInsights(site));
278 private UniFiClientCache getInsights(UniFiSite site) throws UniFiException {
279 UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
280 req.setPath("/api/s/" + site.getName() + "/stat/alluser");
281 req.setQueryParameter("within", 168); // scurb: Changed to 7 days.
282 UniFiClient[] clients = executeRequest(req);
283 UniFiClientCache cache = new UniFiClientCache();
284 if (clients != null) {
285 logger.debug("Found {} UniFi Insights(s): {}", clients.length, lazyFormatAsList(clients));
286 for (UniFiClient client : clients) {
293 private static Object lazyFormatAsList(Object[] arr) {
294 return new Object() {
297 public String toString() {
299 for (Object o : arr) {
300 value += "\n - " + o.toString();