]> git.basschouten.com Git - openhab-addons.git/blob
0c47a7a3b0d57a47ae7a7be6d8e172206bbf11b4
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.api.model;
14
15 import java.util.Collection;
16
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;
33
34 import com.google.gson.FieldNamingPolicy;
35 import com.google.gson.Gson;
36 import com.google.gson.GsonBuilder;
37
38 /**
39  * The {@link UniFiController} is the main communication point with an external instance of the Ubiquiti Networks
40  * Controller Software.
41  *
42  * @author Matthew Bowman - Initial contribution
43  * @author Patrik Wimnell - Blocking / Unblocking client support
44  */
45 @NonNullByDefault
46 public class UniFiController {
47
48     private final Logger logger = LoggerFactory.getLogger(UniFiController.class);
49
50     private UniFiSiteCache sitesCache = new UniFiSiteCache();
51
52     private UniFiDeviceCache devicesCache = new UniFiDeviceCache();
53
54     private UniFiClientCache clientsCache = new UniFiClientCache();
55
56     private UniFiClientCache insightsCache = new UniFiClientCache();
57
58     private final HttpClient httpClient;
59
60     private final String host;
61
62     private final int port;
63
64     private final String username;
65
66     private final String password;
67
68     private final boolean unifios;
69
70     private String csrfToken;
71
72     private final Gson gson;
73
74     public UniFiController(HttpClient httpClient, String host, int port, String username, String password,
75             boolean unifios) {
76         this.httpClient = httpClient;
77         this.host = host;
78         this.port = port;
79         this.username = username;
80         this.password = password;
81         this.unifios = unifios;
82         this.csrfToken = "";
83         UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(this);
84         UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(this);
85         UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(this);
86         this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
87                 .registerTypeAdapter(UniFiSite.class, siteInstanceCreator)
88                 .registerTypeAdapter(UniFiDevice.class, deviceInstanceCreator)
89                 .registerTypeAdapter(UniFiClient.class, new UniFiClientDeserializer())
90                 .registerTypeAdapter(UniFiUnknownClient.class, clientInstanceCreator)
91                 .registerTypeAdapter(UniFiWiredClient.class, clientInstanceCreator)
92                 .registerTypeAdapter(UniFiWirelessClient.class, clientInstanceCreator).create();
93     }
94
95     // Public API
96
97     public void start() throws UniFiException {
98         login();
99     }
100
101     public void stop() throws UniFiException {
102         logout();
103     }
104
105     public void login() throws UniFiException {
106         csrfToken = "";
107
108         UniFiControllerRequest<Void> req = newRequest(Void.class);
109         req.setPath(unifios ? "/api/auth/login" : "/api/login");
110         req.setBodyParameter("username", username);
111         req.setBodyParameter("password", password);
112         // scurb: Changed strict = false to make blocking feature work
113         req.setBodyParameter("strict", false);
114         req.setBodyParameter("remember", false);
115         executeRequest(req);
116     }
117
118     public void logout() throws UniFiException {
119         csrfToken = "";
120         UniFiControllerRequest<Void> req = newRequest(Void.class);
121         req.setPath(unifios ? "/api/auth/logout" : "/logout");
122         executeRequest(req);
123     }
124
125     public void refresh() throws UniFiException {
126         synchronized (this) {
127             sitesCache = getSites();
128             devicesCache = getDevices();
129             clientsCache = getClients();
130             insightsCache = getInsights();
131         }
132     }
133
134     // Site API
135
136     public @Nullable UniFiSite getSite(@Nullable String id) {
137         UniFiSite site = null;
138         if (StringUtils.isNotBlank(id)) {
139             synchronized (this) {
140                 site = sitesCache.get(id);
141             }
142             if (site == null) {
143                 logger.debug("Could not find a matching site for id = '{}'", id);
144             }
145         }
146         return site;
147     }
148
149     // Device API
150
151     public @Nullable UniFiDevice getDevice(@Nullable String id) {
152         UniFiDevice device = null;
153         if (StringUtils.isNotBlank(id)) {
154             synchronized (this) {
155                 device = devicesCache.get(id);
156             }
157             if (device == null) {
158                 logger.debug("Could not find a matching device for id = '{}'", id);
159             }
160         }
161         return device;
162     }
163
164     // Client API
165
166     public @Nullable UniFiClient getClient(@Nullable String id) {
167         UniFiClient client = null;
168         if (StringUtils.isNotBlank(id)) {
169             synchronized (this) {
170                 // mgb: first check active clients and fallback to insights if not found
171                 client = clientsCache.get(id);
172                 if (client == null) {
173                     client = insightsCache.get(id);
174                 }
175             }
176             if (client == null) {
177                 logger.debug("Could not find a matching client for id = {}", id);
178             }
179         }
180         return client;
181     }
182
183     protected void block(UniFiClient client, boolean blocked) throws UniFiException {
184         UniFiControllerRequest<Void> req = newRequest(Void.class);
185         req.setAPIPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
186         req.setBodyParameter("cmd", blocked ? "block-sta" : "unblock-sta");
187         req.setBodyParameter("mac", client.getMac());
188         executeRequest(req);
189     }
190
191     protected void reconnect(UniFiClient client) throws UniFiException {
192         UniFiControllerRequest<Void> req = newRequest(Void.class);
193         req.setAPIPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
194         req.setBodyParameter("cmd", "kick-sta");
195         req.setBodyParameter("mac", client.getMac());
196         executeRequest(req);
197     }
198
199     // Internal API
200
201     private <T> UniFiControllerRequest<T> newRequest(Class<T> responseType) {
202         return new UniFiControllerRequest<>(responseType, gson, httpClient, host, port, csrfToken, unifios);
203     }
204
205     private <T> @Nullable T executeRequest(UniFiControllerRequest<T> request) throws UniFiException {
206         T result;
207         try {
208             result = request.execute();
209             csrfToken = request.getCsrfToken();
210         } catch (UniFiExpiredSessionException e) {
211             login();
212             result = executeRequest(request);
213         } catch (UniFiNotAuthorizedException e) {
214             logger.warn("Not Authorized! Please make sure your controller credentials have administrator rights");
215             result = null;
216         }
217         return result;
218     }
219
220     private UniFiSiteCache getSites() throws UniFiException {
221         UniFiControllerRequest<UniFiSite[]> req = newRequest(UniFiSite[].class);
222         req.setAPIPath("/api/self/sites");
223         UniFiSite[] sites = executeRequest(req);
224         UniFiSiteCache cache = new UniFiSiteCache();
225         if (sites != null) {
226             logger.debug("Found {} UniFi Site(s): {}", sites.length, lazyFormatAsList(sites));
227             for (UniFiSite site : sites) {
228                 cache.put(site);
229             }
230         }
231         return cache;
232     }
233
234     private UniFiDeviceCache getDevices() throws UniFiException {
235         UniFiDeviceCache cache = new UniFiDeviceCache();
236         Collection<UniFiSite> sites = sitesCache.values();
237         for (UniFiSite site : sites) {
238             cache.putAll(getDevices(site));
239         }
240         return cache;
241     }
242
243     private UniFiDeviceCache getDevices(UniFiSite site) throws UniFiException {
244         UniFiControllerRequest<UniFiDevice[]> req = newRequest(UniFiDevice[].class);
245         req.setAPIPath("/api/s/" + site.getName() + "/stat/device");
246         UniFiDevice[] devices = executeRequest(req);
247         UniFiDeviceCache cache = new UniFiDeviceCache();
248         if (devices != null) {
249             logger.debug("Found {} UniFi Device(s): {}", devices.length, lazyFormatAsList(devices));
250             for (UniFiDevice device : devices) {
251                 cache.put(device);
252             }
253         }
254         return cache;
255     }
256
257     private UniFiClientCache getClients() throws UniFiException {
258         UniFiClientCache cache = new UniFiClientCache();
259         Collection<UniFiSite> sites = sitesCache.values();
260         for (UniFiSite site : sites) {
261             cache.putAll(getClients(site));
262         }
263         return cache;
264     }
265
266     private UniFiClientCache getClients(UniFiSite site) throws UniFiException {
267         UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
268         req.setAPIPath("/api/s/" + site.getName() + "/stat/sta");
269         UniFiClient[] clients = executeRequest(req);
270         UniFiClientCache cache = new UniFiClientCache();
271         if (clients != null) {
272             logger.debug("Found {} UniFi Client(s): {}", clients.length, lazyFormatAsList(clients));
273             for (UniFiClient client : clients) {
274                 cache.put(client);
275             }
276         }
277         return cache;
278     }
279
280     private UniFiClientCache getInsights() throws UniFiException {
281         UniFiClientCache cache = new UniFiClientCache();
282         Collection<UniFiSite> sites = sitesCache.values();
283         for (UniFiSite site : sites) {
284             cache.putAll(getInsights(site));
285         }
286         return cache;
287     }
288
289     private UniFiClientCache getInsights(UniFiSite site) throws UniFiException {
290         UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
291         req.setAPIPath("/api/s/" + site.getName() + "/stat/alluser");
292         req.setQueryParameter("within", 168); // scurb: Changed to 7 days.
293         UniFiClient[] clients = executeRequest(req);
294         UniFiClientCache cache = new UniFiClientCache();
295         if (clients != null) {
296             logger.debug("Found {} UniFi Insights(s): {}", clients.length, lazyFormatAsList(clients));
297             for (UniFiClient client : clients) {
298                 cache.put(client);
299             }
300         }
301         return cache;
302     }
303
304     private static Object lazyFormatAsList(Object[] arr) {
305         return new Object() {
306
307             @Override
308             public String toString() {
309                 String value = "";
310                 for (Object o : arr) {
311                     value += "\n - " + o.toString();
312                 }
313                 return value;
314             }
315         };
316     }
317 }