]> git.basschouten.com Git - openhab-addons.git/blob
d0c6678a0b6ba47d511f5a46e101977dd43c1971
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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;
14
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Map;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.eclipse.jetty.http.HttpMethod;
23 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
24 import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverride;
25 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
26 import org.openhab.binding.unifi.internal.api.dto.UniFiDevice;
27 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTable;
28 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
29 import org.openhab.binding.unifi.internal.api.dto.UniFiUnknownClient;
30 import org.openhab.binding.unifi.internal.api.dto.UniFiWiredClient;
31 import org.openhab.binding.unifi.internal.api.dto.UniFiWirelessClient;
32 import org.openhab.binding.unifi.internal.api.dto.UniFiWlan;
33 import org.openhab.binding.unifi.internal.api.util.UniFiClientDeserializer;
34 import org.openhab.binding.unifi.internal.api.util.UniFiClientInstanceCreator;
35 import org.openhab.binding.unifi.internal.api.util.UniFiDeviceInstanceCreator;
36 import org.openhab.binding.unifi.internal.api.util.UniFiSiteInstanceCreator;
37 import org.openhab.binding.unifi.internal.api.util.UniFiWlanInstanceCreator;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import com.google.gson.FieldNamingPolicy;
42 import com.google.gson.Gson;
43 import com.google.gson.GsonBuilder;
44
45 /**
46  * The {@link UniFiController} is the main communication point with an external instance of the Ubiquiti Networks
47  * Controller Software.
48  *
49  * @author Matthew Bowman - Initial contribution
50  * @author Patrik Wimnell - Blocking / Unblocking client support
51  * @author Jacob Laursen - Fix online/blocked channels (broken by UniFi Controller 5.12.35)
52  * @author Hilbrand Bouwkamp - Added POEPort support, moved generic cache related code to cache object
53  */
54 @NonNullByDefault
55 public class UniFiController {
56
57     private static final int INSIGHT_WITHIN_HOURS = 7 * 24; // scurb: Changed to 7 days.
58
59     private final Logger logger = LoggerFactory.getLogger(UniFiController.class);
60
61     private final HttpClient httpClient;
62     private final UniFiControllerCache cache = new UniFiControllerCache();
63
64     private final String host;
65     private final int port;
66     private final String username;
67     private final String password;
68     private final boolean unifios;
69     private final Gson gson;
70     private final Gson poeGson;
71
72     private String csrfToken;
73
74     public UniFiController(final HttpClient httpClient, final String host, final int port, final String username,
75             final String password, final 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         final UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(cache);
84         final UniFiWlanInstanceCreator wlanInstanceCreator = new UniFiWlanInstanceCreator(cache);
85         final UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(cache);
86         final UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(cache);
87         this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
88                 .registerTypeAdapter(UniFiSite.class, siteInstanceCreator)
89                 .registerTypeAdapter(UniFiWlan.class, wlanInstanceCreator)
90                 .registerTypeAdapter(UniFiDevice.class, deviceInstanceCreator)
91                 .registerTypeAdapter(UniFiClient.class, new UniFiClientDeserializer())
92                 .registerTypeAdapter(UniFiUnknownClient.class, clientInstanceCreator)
93                 .registerTypeAdapter(UniFiWiredClient.class, clientInstanceCreator)
94                 .registerTypeAdapter(UniFiWirelessClient.class, clientInstanceCreator).create();
95         this.poeGson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
96                 .excludeFieldsWithoutExposeAnnotation().create();
97     }
98
99     // Public API
100
101     public void start() throws UniFiException {
102         if (unifios) {
103             obtainCsrfToken();
104         }
105
106         login();
107     }
108
109     public void stop() throws UniFiException {
110         logout();
111     }
112
113     public void obtainCsrfToken() throws UniFiException {
114         csrfToken = "";
115
116         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.GET, gson);
117         req.setPath("/");
118         executeRequest(req);
119     }
120
121     public void login() throws UniFiException {
122         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
123         req.setPath(unifios ? "/api/auth/login" : "/api/login");
124         req.setBodyParameter("username", username);
125         req.setBodyParameter("password", password);
126         // scurb: Changed strict = false to make blocking feature work
127         req.setBodyParameter("strict", false);
128         req.setBodyParameter("remember", false);
129         executeRequest(req, true);
130     }
131
132     public void logout() throws UniFiException {
133         csrfToken = "";
134         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.GET, gson);
135         req.setPath(unifios ? "/api/auth/logout" : "/logout");
136         executeRequest(req);
137     }
138
139     public void refresh() throws UniFiException {
140         synchronized (this) {
141             cache.clear();
142             final Collection<UniFiSite> sites = refreshSites();
143             refreshWlans(sites);
144             refreshDevices(sites);
145             refreshClients(sites);
146             refreshInsights(sites);
147         }
148     }
149
150     public UniFiControllerCache getCache() {
151         return cache;
152     }
153
154     public @Nullable Map<Integer, UniFiPortTable> getSwitchPorts(@Nullable final String deviceId) {
155         return cache.getSwitchPorts(deviceId);
156     }
157
158     public void block(final UniFiClient client, final boolean blocked) throws UniFiException {
159         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
160         req.setAPIPath(String.format("/api/s/%s/cmd/stamgr", client.getSite().getName()));
161         req.setBodyParameter("cmd", blocked ? "block-sta" : "unblock-sta");
162         req.setBodyParameter("mac", client.getMac());
163         executeRequest(req);
164         refresh();
165     }
166
167     public void reconnect(final UniFiClient client) throws UniFiException {
168         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
169         req.setAPIPath(String.format("/api/s/%s/cmd/stamgr", client.getSite().getName()));
170         req.setBodyParameter("cmd", "kick-sta");
171         req.setBodyParameter("mac", client.getMac());
172         executeRequest(req);
173         refresh();
174     }
175
176     public void poeMode(final UniFiDevice device, final Map<Integer, UnfiPortOverride> data) throws UniFiException {
177         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.PUT, poeGson);
178         req.setAPIPath(String.format("/api/s/%s/rest/device/%s", device.getSite().getName(), device.getId()));
179         req.setBodyParameter("port_overrides", data.values());
180         executeRequest(req);
181         refresh();
182     }
183
184     public void poePowerCycle(final UniFiDevice device, final Integer portIdx) throws UniFiException {
185         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
186         req.setAPIPath(String.format("/api/s/%s/cmd/devmgr", device.getSite().getName()));
187         req.setBodyParameter("cmd", "power-cycle");
188         req.setBodyParameter("mac", device.getMac());
189         req.setBodyParameter("port_idx", portIdx);
190         executeRequest(req);
191         refresh();
192     }
193
194     public void enableWifi(final UniFiWlan wlan, final boolean enable) throws UniFiException {
195         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.PUT, poeGson);
196         req.setAPIPath(String.format("/api/s/%s/rest/wlanconf/%s", wlan.getSite().getName(), wlan.getId()));
197         req.setBodyParameter("_id", wlan.getId());
198         req.setBodyParameter("enabled", enable ? "true" : "false");
199         executeRequest(req);
200         refresh();
201     }
202
203     // Internal API
204
205     private <T> UniFiControllerRequest<T> newRequest(final Class<T> responseType, final HttpMethod method,
206             final Gson gson) {
207         return new UniFiControllerRequest<>(responseType, gson, httpClient, method, host, port, csrfToken, unifios);
208     }
209
210     private <T> @Nullable T executeRequest(final UniFiControllerRequest<T> request) throws UniFiException {
211         return executeRequest(request, false);
212     }
213
214     private <T> @Nullable T executeRequest(final UniFiControllerRequest<T> request, final boolean fromLogin)
215             throws UniFiException {
216         T result;
217         try {
218             result = request.execute();
219             csrfToken = request.getCsrfToken();
220         } catch (final UniFiExpiredSessionException e) {
221             if (fromLogin) {
222                 // if this exception is thrown from a login attempt something is wrong, because the login should init
223                 // the session.
224                 throw new UniFiCommunicationException(e);
225             } else {
226                 login();
227                 result = executeRequest(request);
228             }
229         } catch (final UniFiNotAuthorizedException e) {
230             logger.warn("Not Authorized! Please make sure your controller credentials have administrator rights");
231             result = null;
232         }
233         return result;
234     }
235
236     private List<UniFiSite> refreshSites() throws UniFiException {
237         final UniFiControllerRequest<UniFiSite[]> req = newRequest(UniFiSite[].class, HttpMethod.GET, gson);
238         req.setAPIPath("/api/self/sites");
239         return cache.setSites(executeRequest(req));
240     }
241
242     private void refreshWlans(final Collection<UniFiSite> sites) throws UniFiException {
243         for (final UniFiSite site : sites) {
244             cache.putWlans(getWlans(site));
245         }
246     }
247
248     private UniFiWlan @Nullable [] getWlans(final UniFiSite site) throws UniFiException {
249         final UniFiControllerRequest<UniFiWlan[]> req = newRequest(UniFiWlan[].class, HttpMethod.GET, gson);
250         req.setAPIPath(String.format("/api/s/%s/rest/wlanconf", site.getName()));
251         return executeRequest(req);
252     }
253
254     private void refreshDevices(final Collection<UniFiSite> sites) throws UniFiException {
255         for (final UniFiSite site : sites) {
256             cache.putDevices(getDevices(site));
257         }
258     }
259
260     private UniFiDevice @Nullable [] getDevices(final UniFiSite site) throws UniFiException {
261         final UniFiControllerRequest<UniFiDevice[]> req = newRequest(UniFiDevice[].class, HttpMethod.GET, gson);
262         req.setAPIPath(String.format("/api/s/%s/stat/device", site.getName()));
263         return executeRequest(req);
264     }
265
266     private void refreshClients(final Collection<UniFiSite> sites) throws UniFiException {
267         for (final UniFiSite site : sites) {
268             cache.putClients(getClients(site));
269         }
270     }
271
272     private UniFiClient @Nullable [] getClients(final UniFiSite site) throws UniFiException {
273         final UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class, HttpMethod.GET, gson);
274         req.setAPIPath(String.format("/api/s/%s/stat/sta", site.getName()));
275         return executeRequest(req);
276     }
277
278     private void refreshInsights(final Collection<UniFiSite> sites) throws UniFiException {
279         for (final UniFiSite site : sites) {
280             cache.putInsights(getInsights(site));
281         }
282     }
283
284     private UniFiClient @Nullable [] getInsights(final UniFiSite site) throws UniFiException {
285         final UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class, HttpMethod.GET, gson);
286         req.setAPIPath(String.format("/api/s/%s/stat/alluser", site.getName()));
287         req.setQueryParameter("within", INSIGHT_WITHIN_HOURS);
288         return executeRequest(req);
289     }
290 }