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