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 = "";
55 public enum BoxModel {
56 FBXGW_R1_FULL, // Freebox Server (v6) revision 1
57 FBXGW_R2_FULL, // Freebox Server (v6) revision 2
58 FBXGW_R1_MINI, // Freebox Mini revision 1
59 FBXGW_R2_MINI, // Freebox Mini revision 2
60 FBXGW_R1_ONE, // Freebox One revision 1
61 FBXGW_R2_ONE, // Freebox One revision 2
62 FBXGW7_R1_FULL, // Freebox v7 revision 1
66 public static record ApiVersion(String apiBaseUrl, @Nullable String apiDomain, String apiVersion, BoxModel boxModel,
67 @Nullable String boxModelName, String deviceName, String deviceType, boolean httpsAvailable, int httpsPort,
71 * @return a string like eg: '/api/v8'
73 private String baseUrl() {
74 return "%sv%s".formatted(apiBaseUrl, apiVersion.split("\\.")[0]);
78 public FreeboxOsSession(ApiHandler apiHandler) {
79 this.apiHandler = apiHandler;
82 public void initialize(FreeboxOsConfiguration config) throws FreeboxException, InterruptedException {
83 ApiVersion version = apiHandler.executeUri(config.getUriBuilder(API_VERSION_PATH).build(), HttpMethod.GET,
84 ApiVersion.class, null, null);
85 this.uriBuilder = config.getUriBuilder(version.baseUrl());
86 getManager(LoginManager.class);
87 getManager(NetShareManager.class);
88 getManager(LanManager.class);
89 getManager(WifiManager.class);
90 getManager(FreeplugManager.class);
91 getManager(AirMediaManager.class);
94 public void openSession(String appToken) throws FreeboxException {
95 Session newSession = getManager(LoginManager.class).openSession(appToken);
96 getManager(WebSocketManager.class).openSession(newSession.sessionToken());
98 this.appToken = appToken;
101 public String grant() throws FreeboxException {
102 return getManager(LoginManager.class).checkGrantStatus();
105 public void closeSession() {
106 Session currentSession = session;
107 if (currentSession != null) {
109 getManager(WebSocketManager.class).closeSession();
110 getManager(LoginManager.class).closeSession();
112 } catch (FreeboxException e) {
113 logger.warn("Error closing session: {}", e.getMessage());
117 restManagers.clear();
120 private synchronized <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
121 boolean retryAuth, int retryCount, @Nullable Object aPayload) throws FreeboxException {
123 T response = apiHandler.executeUri(uri, method, clazz, getSessionToken(), aPayload);
124 if (response.getErrorCode() == ErrorCode.INTERNAL_ERROR && retryCount > 0) {
125 return execute(uri, method, clazz, false, retryCount - 1, aPayload);
126 } else if (retryAuth && response.getErrorCode() == ErrorCode.AUTH_REQUIRED) {
127 openSession(appToken);
128 return execute(uri, method, clazz, false, retryCount, aPayload);
130 if (!response.isSuccess()) {
131 throw new FreeboxException("Api request failed: %s", response.getMsg());
133 return response.getResult();
134 } catch (FreeboxException e) {
135 if (ErrorCode.AUTH_REQUIRED.equals(e.getErrorCode())) {
136 openSession(appToken);
137 return execute(uri, method, clazz, false, retryCount, aPayload);
140 } catch (InterruptedException ignored) {
145 public <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
146 @Nullable Object aPayload) throws FreeboxException {
147 return execute(uri, method, clazz, getSessionToken() != null, 3, aPayload);
150 @SuppressWarnings("unchecked")
151 public synchronized <T extends RestManager> T getManager(Class<T> clazz) throws FreeboxException {
152 RestManager manager = restManagers.get(clazz);
153 if (manager == null) {
155 Constructor<T> managerConstructor = clazz.getConstructor(FreeboxOsSession.class);
156 manager = addManager(clazz, managerConstructor.newInstance(this));
157 } catch (InvocationTargetException e) {
158 Throwable cause = e.getCause();
159 if (cause instanceof PermissionException exception) {
162 throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
163 } catch (ReflectiveOperationException e) {
164 throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
170 public <T extends RestManager> T addManager(Class<T> clazz, T manager) {
171 restManagers.put(clazz, manager);
175 boolean hasPermission(LoginManager.Permission required) {
176 Session currentSession = session;
177 return currentSession != null ? currentSession.hasPermission(required) : false;
180 private @Nullable String getSessionToken() {
181 Session currentSession = session;
182 return currentSession != null ? currentSession.sessionToken() : null;
185 public UriBuilder getUriBuilder() {
186 return uriBuilder.clone();
189 public ApiHandler getApiHandler() {