]> git.basschouten.com Git - openhab-addons.git/blob
91c80dade6dd34f741c9ed66093bd534362e614d
[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.roku.internal.communication;
14
15 import java.io.StringReader;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
20
21 import javax.xml.bind.JAXBContext;
22 import javax.xml.bind.JAXBException;
23 import javax.xml.bind.Unmarshaller;
24 import javax.xml.stream.XMLStreamException;
25 import javax.xml.stream.XMLStreamReader;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jetty.client.HttpClient;
29 import org.eclipse.jetty.http.HttpMethod;
30 import org.openhab.binding.roku.internal.RokuHttpException;
31 import org.openhab.binding.roku.internal.dto.ActiveApp;
32 import org.openhab.binding.roku.internal.dto.Apps;
33 import org.openhab.binding.roku.internal.dto.Apps.App;
34 import org.openhab.binding.roku.internal.dto.DeviceInfo;
35 import org.openhab.binding.roku.internal.dto.Player;
36 import org.openhab.binding.roku.internal.dto.TvChannel;
37 import org.openhab.binding.roku.internal.dto.TvChannels;
38 import org.openhab.binding.roku.internal.dto.TvChannels.Channel;
39
40 /**
41  * Methods for accessing the HTTP interface of the Roku
42  *
43  * @author Michael Lobstein - Initial contribution
44  */
45 @NonNullByDefault
46 public class RokuCommunicator {
47     private static final int REQUEST_TIMEOUT = 5000;
48
49     private final HttpClient httpClient;
50
51     private final String urlKeyPress;
52     private final String urlLaunchApp;
53     private final String urlLaunchTvChannel;
54     private final String urlQryDevice;
55     private final String urlQryActiveApp;
56     private final String urlQryApps;
57     private final String urlQryPlayer;
58     private final String urlQryActiveTvChannel;
59     private final String urlQryTvChannels;
60
61     public RokuCommunicator(HttpClient httpClient, String host, int port) {
62         this.httpClient = httpClient;
63
64         final String baseUrl = "http://" + host + ":" + port;
65         urlKeyPress = baseUrl + "/keypress/";
66         urlLaunchApp = baseUrl + "/launch/";
67         urlLaunchTvChannel = baseUrl + "/launch/tvinput.dtv?ch=";
68         urlQryDevice = baseUrl + "/query/device-info";
69         urlQryActiveApp = baseUrl + "/query/active-app";
70         urlQryApps = baseUrl + "/query/apps";
71         urlQryPlayer = baseUrl + "/query/media-player";
72         urlQryActiveTvChannel = baseUrl + "/query/tv-active-channel";
73         urlQryTvChannels = baseUrl + "/query/tv-channels";
74     }
75
76     /**
77      * Send a keypress command to the Roku
78      *
79      * @param key The key code to send
80      *
81      */
82     public void keyPress(String key) throws RokuHttpException {
83         postCommand(urlKeyPress + key);
84     }
85
86     /**
87      * Send a launch app command to the Roku
88      *
89      * @param appId The appId of the app to launch
90      *
91      */
92     public void launchApp(String appId) throws RokuHttpException {
93         postCommand(urlLaunchApp + appId);
94     }
95
96     /**
97      * Send a TV channel change command to the Roku TV
98      *
99      * @param channelNumber The channel number of the channel to tune into, ie: 2.1
100      *
101      */
102     public void launchTvChannel(String channelNumber) throws RokuHttpException {
103         postCommand(urlLaunchTvChannel + channelNumber);
104     }
105
106     /**
107      * Send a command to get device-info from the Roku and return a DeviceInfo object
108      *
109      * @return A DeviceInfo object populated with information about the connected Roku
110      * @throws RokuHttpException
111      */
112     public DeviceInfo getDeviceInfo() throws RokuHttpException {
113         try {
114             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_DEVICE_INFO;
115             if (ctx != null) {
116                 final String response = getCommand(urlQryDevice);
117                 Unmarshaller unmarshaller = ctx.createUnmarshaller();
118                 if (unmarshaller != null) {
119                     XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
120                     DeviceInfo device = (DeviceInfo) unmarshaller.unmarshal(xsr);
121                     if (device != null) {
122                         return device;
123                     }
124                 }
125             }
126             throw new RokuHttpException("No DeviceInfo model in response");
127         } catch (JAXBException | XMLStreamException e) {
128             throw new RokuHttpException("Exception creating DeviceInfo Unmarshaller: " + e.getLocalizedMessage());
129         }
130     }
131
132     /**
133      * Send a command to get active-app from the Roku and return an ActiveApp object
134      *
135      * @return An ActiveApp object populated with information about the current running app on the Roku
136      * @throws RokuHttpException
137      */
138     public ActiveApp getActiveApp() throws RokuHttpException {
139         try {
140             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_ACTIVE_APP;
141             if (ctx != null) {
142                 final String response = getCommand(urlQryActiveApp);
143                 Unmarshaller unmarshaller = ctx.createUnmarshaller();
144                 if (unmarshaller != null) {
145                     XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
146                     ActiveApp activeApp = (ActiveApp) unmarshaller.unmarshal(xsr);
147                     if (activeApp != null) {
148                         return activeApp;
149                     }
150                 }
151             }
152             throw new RokuHttpException("No ActiveApp model in response");
153         } catch (JAXBException | XMLStreamException e) {
154             throw new RokuHttpException("Exception creating ActiveApp Unmarshaller: " + e.getLocalizedMessage());
155         }
156     }
157
158     /**
159      * Send a command to get the installed app list from the Roku and return a List of App objects
160      *
161      * @return A List of App objects for all apps currently installed on the Roku
162      * @throws RokuHttpException
163      */
164     public List<App> getAppList() throws RokuHttpException {
165         try {
166             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_APPS;
167             if (ctx != null) {
168                 final String response = getCommand(urlQryApps);
169                 Unmarshaller unmarshaller = ctx.createUnmarshaller();
170                 if (unmarshaller != null) {
171                     XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
172                     Apps appList = (Apps) unmarshaller.unmarshal(xsr);
173                     if (appList != null) {
174                         return appList.getApp();
175                     }
176                 }
177             }
178             throw new RokuHttpException("No AppList model in response");
179         } catch (JAXBException | XMLStreamException e) {
180             throw new RokuHttpException("Exception creating AppList Unmarshaller: " + e.getLocalizedMessage());
181         }
182     }
183
184     /**
185      * Send a command to get media-player from the Roku and return a Player object
186      *
187      * @return A Player object populated with information about the current stream playing on the Roku
188      * @throws RokuHttpException
189      */
190     public Player getPlayerInfo() throws RokuHttpException {
191         try {
192             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_PLAYER;
193             if (ctx != null) {
194                 final String response = getCommand(urlQryPlayer);
195                 Unmarshaller unmarshaller = ctx.createUnmarshaller();
196                 if (unmarshaller != null) {
197                     XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
198                     Player playerInfo = (Player) unmarshaller.unmarshal(xsr);
199                     if (playerInfo != null) {
200                         return playerInfo;
201                     }
202                 }
203             }
204             throw new RokuHttpException("No Player info model in response");
205         } catch (JAXBException | XMLStreamException e) {
206             throw new RokuHttpException("Exception creating Player info Unmarshaller: " + e.getLocalizedMessage());
207         }
208     }
209
210     /**
211      * Send a command to get tv-active-channel from the Roku TV and return a TvChannel object
212      *
213      * @return A TvChannel object populated with information about the current active TV Channel
214      * @throws RokuHttpException
215      */
216     public TvChannel getActiveTvChannel() throws RokuHttpException {
217         try {
218             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNEL;
219             if (ctx != null) {
220                 final String response = getCommand(urlQryActiveTvChannel);
221                 Unmarshaller unmarshaller = ctx.createUnmarshaller();
222                 if (unmarshaller != null) {
223                     XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
224                     TvChannel tvChannelInfo = (TvChannel) unmarshaller.unmarshal(xsr);
225                     if (tvChannelInfo != null) {
226                         return tvChannelInfo;
227                     }
228                 }
229             }
230             throw new RokuHttpException("No TvChannel info model in response");
231         } catch (JAXBException | XMLStreamException e) {
232             throw new RokuHttpException("Exception creating TvChannel info Unmarshaller: " + e.getLocalizedMessage());
233         }
234     }
235
236     /**
237      * Send a command to get tv-channels from the Roku TV and return a list of Channel objects
238      *
239      * @return A List of Channel objects for all TV channels currently available on the Roku TV
240      * @throws RokuHttpException
241      */
242     public List<Channel> getTvChannelList() throws RokuHttpException {
243         try {
244             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNELS;
245             if (ctx != null) {
246                 final String response = getCommand(urlQryTvChannels);
247                 Unmarshaller unmarshaller = ctx.createUnmarshaller();
248                 if (unmarshaller != null) {
249                     XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
250                     TvChannels tvChannels = (TvChannels) unmarshaller.unmarshal(xsr);
251                     if (tvChannels != null) {
252                         return tvChannels.getChannel();
253                     }
254                 }
255             }
256             throw new RokuHttpException("No TvChannels info model in response");
257         } catch (JAXBException | XMLStreamException e) {
258             throw new RokuHttpException("Exception creating TvChannel info Unmarshaller: " + e.getLocalizedMessage());
259         }
260     }
261
262     /**
263      * Sends a GET command to the Roku
264      *
265      * @param url The url to send with the command embedded in the URI
266      * @return The response content of the http request
267      * @throws RokuHttpException
268      */
269     private String getCommand(String url) throws RokuHttpException {
270         try {
271             return httpClient.newRequest(url).method(HttpMethod.GET).timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS)
272                     .send().getContentAsString();
273         } catch (TimeoutException | ExecutionException e) {
274             throw new RokuHttpException("Error executing GET command for URL: " + url, e);
275         } catch (InterruptedException e) {
276             Thread.currentThread().interrupt();
277             throw new RokuHttpException("InterruptedException executing GET command for URL: " + url, e);
278         }
279     }
280
281     /**
282      * Sends a POST command to the Roku
283      *
284      * @param url The url to send with the command embedded in the URI
285      * @throws RokuHttpException
286      */
287     private void postCommand(String url) throws RokuHttpException {
288         try {
289             httpClient.POST(url).method(HttpMethod.POST).timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).send();
290         } catch (TimeoutException | ExecutionException e) {
291             throw new RokuHttpException("Error executing POST command, URL: " + url, e);
292         } catch (InterruptedException e) {
293             Thread.currentThread().interrupt();
294             throw new RokuHttpException("InterruptedException executing POST command for URL: " + url, e);
295         }
296     }
297 }