]> git.basschouten.com Git - openhab-addons.git/blob
756ad47a6d8bcb979cfd56ed0a371ba9ed013adb
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.fsinternetradio.internal.radio;
14
15 import java.io.IOException;
16 import java.util.concurrent.TimeUnit;
17
18 import org.eclipse.jetty.client.HttpClient;
19 import org.eclipse.jetty.client.api.ContentResponse;
20 import org.eclipse.jetty.client.api.Request;
21 import org.eclipse.jetty.http.HttpMethod;
22 import org.eclipse.jetty.http.HttpStatus;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * This class holds the http-connection and session information for controlling the radio.
28  *
29  * @author Rainer Ostendorf
30  * @author Patrick Koenemann
31  * @author Svilen Valkanov - replaced Apache HttpClient with Jetty
32  * @author Mihaela Memova - changed the calling of the stopHttpClient() method, fixed the hardcoded URL path, fixed the
33  *         for loop condition part
34  */
35 public class FrontierSiliconRadioConnection {
36
37     private final Logger logger = LoggerFactory.getLogger(FrontierSiliconRadioConnection.class);
38
39     /** Timeout for HTTP requests in ms */
40     private static final int SOCKET_TIMEOUT = 5000;
41
42     /** Hostname of the radio. */
43     private final String hostname;
44
45     /** Port number, usually 80. */
46     private final int port;
47
48     /** Access pin, passed upon login as GET parameter. */
49     private final String pin;
50
51     /** The session ID we get from the radio after logging in. */
52     private String sessionId;
53
54     /** http clients, store cookies, so it is kept in connection class. */
55     private HttpClient httpClient = null;
56
57     /** Flag indicating if we are successfully logged in. */
58     private boolean isLoggedIn = false;
59
60     public FrontierSiliconRadioConnection(String hostname, int port, String pin, HttpClient httpClient) {
61         this.hostname = hostname;
62         this.port = port;
63         this.pin = pin;
64         this.httpClient = httpClient;
65     }
66
67     public boolean isLoggedIn() {
68         return isLoggedIn;
69     }
70
71     /**
72      * Perform login/establish a new session. Uses the PIN number and when successful saves the assigned sessionID for
73      * future requests.
74      *
75      * @return <code>true</code> if login was successful; <code>false</code> otherwise.
76      * @throws IOException if communication with the radio failed, e.g. because the device is not reachable.
77      */
78     public boolean doLogin() throws IOException {
79         isLoggedIn = false; // reset login flag
80
81         final String url = "http://" + hostname + ":" + port + FrontierSiliconRadioConstants.CONNECTION_PATH
82                 + "/CREATE_SESSION?pin=" + pin;
83
84         logger.trace("opening URL: {}", url);
85
86         Request request = httpClient.newRequest(url).method(HttpMethod.GET).timeout(SOCKET_TIMEOUT,
87                 TimeUnit.MILLISECONDS);
88
89         try {
90             ContentResponse response = request.send();
91             int statusCode = response.getStatus();
92             if (statusCode != HttpStatus.OK_200) {
93                 String reason = response.getReason();
94                 logger.debug("Communication with radio failed: {} {}", statusCode, reason);
95                 if (statusCode == HttpStatus.FORBIDDEN_403) {
96                     throw new IllegalStateException("Radio does not allow connection, maybe wrong pin?");
97                 }
98                 throw new IOException("Communication with radio failed, return code: " + statusCode);
99             }
100
101             final String responseBody = response.getContentAsString();
102             if (!responseBody.isEmpty()) {
103                 logger.trace("login response: {}", responseBody);
104             }
105
106             final FrontierSiliconRadioApiResult result = new FrontierSiliconRadioApiResult(responseBody);
107             if (result.isStatusOk()) {
108                 logger.trace("login successful");
109                 sessionId = result.getSessionId();
110                 isLoggedIn = true;
111                 return true; // login successful :-)
112             }
113
114         } catch (Exception e) {
115             logger.debug("Fatal transport error: {}", e.toString());
116             throw new IOException(e);
117         }
118
119         return false; // login not successful
120     }
121
122     /**
123      * Performs a request to the radio with no further parameters.
124      *
125      * Typically used for polling state info.
126      *
127      * @param requestString REST API request, e.g. "GET/netRemote.sys.power"
128      * @return request result
129      * @throws IOException if the request failed.
130      */
131     public FrontierSiliconRadioApiResult doRequest(String requestString) throws IOException {
132         return doRequest(requestString, null);
133     }
134
135     /**
136      * Performs a request to the radio with addition parameters.
137      *
138      * Typically used for changing parameters.
139      *
140      * @param requestString REST API request, e.g. "SET/netRemote.sys.power"
141      * @param params parameters, e.g. "value=1"
142      * @return request result
143      * @throws IOException if the request failed.
144      */
145     public FrontierSiliconRadioApiResult doRequest(String requestString, String params) throws IOException {
146         // 3 retries upon failure
147         for (int i = 0; i < 3; i++) {
148             if (!isLoggedIn && !doLogin()) {
149                 continue; // not logged in and login was not successful - try again!
150             }
151
152             final String url = "http://" + hostname + ":" + port + FrontierSiliconRadioConstants.CONNECTION_PATH + "/"
153                     + requestString + "?pin=" + pin + "&sid=" + sessionId
154                     + (params == null || params.trim().length() == 0 ? "" : "&" + params);
155
156             logger.trace("calling url: '{}'", url);
157
158             Request request = httpClient.newRequest(url).method(HttpMethod.GET).timeout(SOCKET_TIMEOUT,
159                     TimeUnit.MILLISECONDS);
160
161             try {
162                 ContentResponse response = request.send();
163                 final int statusCode = response.getStatus();
164                 if (statusCode != HttpStatus.OK_200) {
165                     /*-
166                      * Issue: https://github.com/eclipse/smarthome/issues/2548
167                      * If the session expired, we might get a 404 here. That's ok, remember that we are not logged-in
168                      * and try again. Print warning only if this happens in the last iteration.
169                      */
170                     if (i >= 2) {
171                         String reason = response.getReason();
172                         logger.warn("Method failed: {}  {}", statusCode, reason);
173                     }
174                     isLoggedIn = false;
175                     continue;
176                 }
177
178                 final String responseBody = response.getContentAsString();
179                 if (!responseBody.isEmpty()) {
180                     logger.trace("got result: {}", responseBody);
181                 } else {
182                     logger.debug("got empty result");
183                     isLoggedIn = false;
184                     continue;
185                 }
186
187                 final FrontierSiliconRadioApiResult result = new FrontierSiliconRadioApiResult(responseBody);
188                 if (result.isStatusOk()) {
189                     return result;
190                 }
191
192                 isLoggedIn = false;
193                 continue; // try again
194             } catch (Exception e) {
195                 logger.error("Fatal transport error: {}", e.toString());
196                 throw new IOException(e);
197             }
198         }
199         isLoggedIn = false; // 3 tries failed. log in again next time, maybe our session went invalid (radio restarted?)
200         return null;
201     }
202 }