2 * Copyright (c) 2010-2023 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.freeboxos.internal.api.rest;
15 import java.lang.reflect.Constructor;
16 import java.lang.reflect.InvocationTargetException;
18 import java.util.HashMap;
19 import java.util.List;
22 import javax.ws.rs.core.UriBuilder;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.openhab.binding.freeboxos.internal.api.ApiHandler;
28 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
29 import org.openhab.binding.freeboxos.internal.api.PermissionException;
30 import org.openhab.binding.freeboxos.internal.api.Response;
31 import org.openhab.binding.freeboxos.internal.api.Response.ErrorCode;
32 import org.openhab.binding.freeboxos.internal.api.rest.LoginManager.Session;
33 import org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * The {@link FreeboxOsSession} is responsible for sending requests toward a given url and transform the answer in
41 * @author Gaƫl L'Hopital - Initial contribution
44 public class FreeboxOsSession {
45 private static final String API_VERSION_PATH = "api_version";
47 private final Logger logger = LoggerFactory.getLogger(FreeboxOsSession.class);
48 private final Map<Class<? extends RestManager>, RestManager> restManagers = new HashMap<>();
49 private final ApiHandler apiHandler;
51 private @NonNullByDefault({}) UriBuilder uriBuilder;
52 private @Nullable Session session;
53 private String appToken = "";
54 private int wsReconnectInterval;
56 public enum BoxModel {
57 FBXGW_R1_FULL, // Freebox Server (v6) revision 1
58 FBXGW_R2_FULL, // Freebox Server (v6) revision 2
59 FBXGW_R1_MINI, // Freebox Mini revision 1
60 FBXGW_R2_MINI, // Freebox Mini revision 2
61 FBXGW_R1_ONE, // Freebox One revision 1
62 FBXGW_R2_ONE, // Freebox One revision 2
63 FBXGW7_R1_FULL, // Freebox v7 revision 1
67 public static record ApiVersion(String apiBaseUrl, @Nullable String apiDomain, String apiVersion, BoxModel boxModel,
68 @Nullable String boxModelName, String deviceName, String deviceType, boolean httpsAvailable, int httpsPort,
72 * @return a string like eg: '/api/v8'
74 private String baseUrl() {
75 return "%sv%s".formatted(apiBaseUrl, apiVersion.split("\\.")[0]);
79 public FreeboxOsSession(ApiHandler apiHandler) {
80 this.apiHandler = apiHandler;
83 public void initialize(FreeboxOsConfiguration config) throws FreeboxException, InterruptedException {
84 ApiVersion version = apiHandler.executeUri(config.getUriBuilder(API_VERSION_PATH).build(), HttpMethod.GET,
85 ApiVersion.class, null, null);
86 this.uriBuilder = config.getUriBuilder(version.baseUrl());
87 this.wsReconnectInterval = config.wsReconnectInterval;
88 getManager(LoginManager.class);
89 getManager(NetShareManager.class);
90 getManager(LanManager.class);
91 getManager(WifiManager.class);
92 getManager(FreeplugManager.class);
93 getManager(AirMediaManager.class);
96 public void openSession(String appToken) throws FreeboxException {
97 Session newSession = getManager(LoginManager.class).openSession(appToken);
98 getManager(WebSocketManager.class).openSession(newSession.sessionToken(), wsReconnectInterval);
100 this.appToken = appToken;
103 public String grant() throws FreeboxException {
104 return getManager(LoginManager.class).checkGrantStatus();
107 public void closeSession() {
108 Session currentSession = session;
109 if (currentSession != null) {
111 getManager(WebSocketManager.class).dispose();
112 getManager(LoginManager.class).closeSession();
114 } catch (FreeboxException e) {
115 logger.warn("Error closing session: {}", e.getMessage());
119 restManagers.clear();
122 private synchronized <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
123 boolean retryAuth, int retryCount, @Nullable Object aPayload) throws FreeboxException {
125 T response = apiHandler.executeUri(uri, method, clazz, getSessionToken(), aPayload);
126 if (response.getErrorCode() == ErrorCode.INTERNAL_ERROR && retryCount > 0) {
127 return execute(uri, method, clazz, false, retryCount - 1, aPayload);
128 } else if (retryAuth && response.getErrorCode() == ErrorCode.AUTH_REQUIRED) {
129 openSession(appToken);
130 return execute(uri, method, clazz, false, retryCount, aPayload);
132 if (!response.isSuccess()) {
133 throw new FreeboxException("Api request failed: %s", response.getMsg());
135 return response.getResult();
136 } catch (FreeboxException e) {
137 if (ErrorCode.AUTH_REQUIRED.equals(e.getErrorCode())) {
138 openSession(appToken);
139 return execute(uri, method, clazz, false, retryCount, aPayload);
142 } catch (InterruptedException ignored) {
147 public <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
148 @Nullable Object aPayload) throws FreeboxException {
149 return execute(uri, method, clazz, getSessionToken() != null, 3, aPayload);
152 @SuppressWarnings("unchecked")
153 public synchronized <T extends RestManager> T getManager(Class<T> clazz) throws FreeboxException {
154 RestManager manager = restManagers.get(clazz);
155 if (manager == null) {
157 Constructor<T> managerConstructor = clazz.getConstructor(FreeboxOsSession.class);
158 manager = addManager(clazz, managerConstructor.newInstance(this));
159 } catch (InvocationTargetException e) {
160 Throwable cause = e.getCause();
161 if (cause instanceof PermissionException exception) {
164 throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
165 } catch (ReflectiveOperationException e) {
166 throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
172 public <T extends RestManager> T addManager(Class<T> clazz, T manager) {
173 restManagers.put(clazz, manager);
177 boolean hasPermission(LoginManager.Permission required) {
178 Session currentSession = session;
179 return currentSession != null ? currentSession.hasPermission(required) : false;
182 private @Nullable String getSessionToken() {
183 Session currentSession = session;
184 return currentSession != null ? currentSession.sessionToken() : null;
187 public UriBuilder getUriBuilder() {
188 return uriBuilder.clone();
191 public ApiHandler getApiHandler() {