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.api;
15 import java.util.Collection;
16 import java.util.List;
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;
41 import com.google.gson.FieldNamingPolicy;
42 import com.google.gson.Gson;
43 import com.google.gson.GsonBuilder;
46 * The {@link UniFiController} is the main communication point with an external instance of the Ubiquiti Networks
47 * Controller Software.
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
55 public class UniFiController {
57 private static final int INSIGHT_WITHIN_HOURS = 7 * 24; // scurb: Changed to 7 days.
59 private final Logger logger = LoggerFactory.getLogger(UniFiController.class);
61 private final HttpClient httpClient;
62 private final UniFiControllerCache cache = new UniFiControllerCache();
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;
72 private String csrfToken;
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;
79 this.username = username;
80 this.password = password;
81 this.unifios = unifios;
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();
101 public void start() throws UniFiException {
109 public void stop() throws UniFiException {
113 public void obtainCsrfToken() throws UniFiException {
116 final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.GET, gson);
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);
132 public void logout() throws UniFiException {
134 final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.GET, gson);
135 req.setPath(unifios ? "/api/auth/logout" : "/logout");
139 public void refresh() throws UniFiException {
140 synchronized (this) {
142 final Collection<UniFiSite> sites = refreshSites();
144 refreshDevices(sites);
145 refreshClients(sites);
146 refreshInsights(sites);
150 public UniFiControllerCache getCache() {
154 public @Nullable Map<Integer, UniFiPortTable> getSwitchPorts(@Nullable final String deviceId) {
155 return cache.getSwitchPorts(deviceId);
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());
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());
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());
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);
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");
205 private <T> UniFiControllerRequest<T> newRequest(final Class<T> responseType, final HttpMethod method,
207 return new UniFiControllerRequest<>(responseType, gson, httpClient, method, host, port, csrfToken, unifios);
210 private <T> @Nullable T executeRequest(final UniFiControllerRequest<T> request) throws UniFiException {
211 return executeRequest(request, false);
214 private <T> @Nullable T executeRequest(final UniFiControllerRequest<T> request, final boolean fromLogin)
215 throws UniFiException {
218 result = request.execute();
219 csrfToken = request.getCsrfToken();
220 } catch (final UniFiExpiredSessionException e) {
222 // if this exception is thrown from a login attempt something is wrong, because the login should init
224 throw new UniFiCommunicationException(e);
227 result = executeRequest(request);
229 } catch (final UniFiNotAuthorizedException e) {
230 logger.warn("Not Authorized! Please make sure your controller credentials have administrator rights");
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));
242 private void refreshWlans(final Collection<UniFiSite> sites) throws UniFiException {
243 for (final UniFiSite site : sites) {
244 cache.putWlans(getWlans(site));
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);
254 private void refreshDevices(final Collection<UniFiSite> sites) throws UniFiException {
255 for (final UniFiSite site : sites) {
256 cache.putDevices(getDevices(site));
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);
266 private void refreshClients(final Collection<UniFiSite> sites) throws UniFiException {
267 for (final UniFiSite site : sites) {
268 cache.putClients(getClients(site));
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);
278 private void refreshInsights(final Collection<UniFiSite> sites) throws UniFiException {
279 for (final UniFiSite site : sites) {
280 cache.putInsights(getInsights(site));
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);