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.roku.internal.communication;
15 import java.io.StringReader;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeoutException;
20 import javax.xml.bind.JAXBContext;
21 import javax.xml.bind.JAXBException;
22 import javax.xml.bind.Unmarshaller;
23 import javax.xml.stream.XMLStreamException;
24 import javax.xml.stream.XMLStreamReader;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.eclipse.jetty.http.HttpMethod;
29 import org.openhab.binding.roku.internal.RokuHttpException;
30 import org.openhab.binding.roku.internal.dto.ActiveApp;
31 import org.openhab.binding.roku.internal.dto.Apps;
32 import org.openhab.binding.roku.internal.dto.Apps.App;
33 import org.openhab.binding.roku.internal.dto.DeviceInfo;
34 import org.openhab.binding.roku.internal.dto.Player;
35 import org.openhab.binding.roku.internal.dto.TvChannel;
36 import org.openhab.binding.roku.internal.dto.TvChannels;
37 import org.openhab.binding.roku.internal.dto.TvChannels.Channel;
40 * Methods for accessing the HTTP interface of the Roku
42 * @author Michael Lobstein - Initial contribution
45 public class RokuCommunicator {
46 private final HttpClient httpClient;
48 private final String urlKeyPress;
49 private final String urlLaunchApp;
50 private final String urlLaunchTvChannel;
51 private final String urlQryDevice;
52 private final String urlQryActiveApp;
53 private final String urlQryApps;
54 private final String urlQryPlayer;
55 private final String urlQryActiveTvChannel;
56 private final String urlQryTvChannels;
58 public RokuCommunicator(HttpClient httpClient, String host, int port) {
59 this.httpClient = httpClient;
61 final String baseUrl = "http://" + host + ":" + port;
62 urlKeyPress = baseUrl + "/keypress/";
63 urlLaunchApp = baseUrl + "/launch/";
64 urlLaunchTvChannel = baseUrl + "/launch/tvinput.dtv?ch=";
65 urlQryDevice = baseUrl + "/query/device-info";
66 urlQryActiveApp = baseUrl + "/query/active-app";
67 urlQryApps = baseUrl + "/query/apps";
68 urlQryPlayer = baseUrl + "/query/media-player";
69 urlQryActiveTvChannel = baseUrl + "/query/tv-active-channel";
70 urlQryTvChannels = baseUrl + "/query/tv-channels";
74 * Send a keypress command to the Roku
76 * @param key The key code to send
79 public void keyPress(String key) throws RokuHttpException {
80 postCommand(urlKeyPress + key);
84 * Send a launch app command to the Roku
86 * @param appId The appId of the app to launch
89 public void launchApp(String appId) throws RokuHttpException {
90 postCommand(urlLaunchApp + appId);
94 * Send a TV channel change command to the Roku TV
96 * @param channelNumber The channel number of the channel to tune into, ie: 2.1
99 public void launchTvChannel(String channelNumber) throws RokuHttpException {
100 postCommand(urlLaunchTvChannel + channelNumber);
104 * Send a command to get device-info from the Roku and return a DeviceInfo object
106 * @return A DeviceInfo object populated with information about the connected Roku
107 * @throws RokuHttpException
109 public DeviceInfo getDeviceInfo() throws RokuHttpException {
111 JAXBContext ctx = JAXBUtils.JAXBCONTEXT_DEVICE_INFO;
113 final String response = getCommand(urlQryDevice);
114 Unmarshaller unmarshaller = ctx.createUnmarshaller();
115 if (unmarshaller != null) {
116 XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
117 DeviceInfo device = (DeviceInfo) unmarshaller.unmarshal(xsr);
118 if (device != null) {
123 throw new RokuHttpException("No DeviceInfo model in response");
124 } catch (JAXBException | XMLStreamException e) {
125 throw new RokuHttpException("Exception creating DeviceInfo Unmarshaller: " + e.getLocalizedMessage());
130 * Send a command to get active-app from the Roku and return an ActiveApp object
132 * @return An ActiveApp object populated with information about the current running app on the Roku
133 * @throws RokuHttpException
135 public ActiveApp getActiveApp() throws RokuHttpException {
137 JAXBContext ctx = JAXBUtils.JAXBCONTEXT_ACTIVE_APP;
139 final String response = getCommand(urlQryActiveApp);
140 Unmarshaller unmarshaller = ctx.createUnmarshaller();
141 if (unmarshaller != null) {
142 XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
143 ActiveApp activeApp = (ActiveApp) unmarshaller.unmarshal(xsr);
144 if (activeApp != null) {
149 throw new RokuHttpException("No ActiveApp model in response");
150 } catch (JAXBException | XMLStreamException e) {
151 throw new RokuHttpException("Exception creating ActiveApp Unmarshaller: " + e.getLocalizedMessage());
156 * Send a command to get the installed app list from the Roku and return a List of App objects
158 * @return A List of App objects for all apps currently installed on the Roku
159 * @throws RokuHttpException
161 public List<App> getAppList() throws RokuHttpException {
163 JAXBContext ctx = JAXBUtils.JAXBCONTEXT_APPS;
165 final String response = getCommand(urlQryApps);
166 Unmarshaller unmarshaller = ctx.createUnmarshaller();
167 if (unmarshaller != null) {
168 XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
169 Apps appList = (Apps) unmarshaller.unmarshal(xsr);
170 if (appList != null) {
171 return appList.getApp();
175 throw new RokuHttpException("No AppList model in response");
176 } catch (JAXBException | XMLStreamException e) {
177 throw new RokuHttpException("Exception creating AppList Unmarshaller: " + e.getLocalizedMessage());
182 * Send a command to get media-player from the Roku and return a Player object
184 * @return A Player object populated with information about the current stream playing on the Roku
185 * @throws RokuHttpException
187 public Player getPlayerInfo() throws RokuHttpException {
189 JAXBContext ctx = JAXBUtils.JAXBCONTEXT_PLAYER;
191 final String response = getCommand(urlQryPlayer);
192 Unmarshaller unmarshaller = ctx.createUnmarshaller();
193 if (unmarshaller != null) {
194 XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
195 Player playerInfo = (Player) unmarshaller.unmarshal(xsr);
196 if (playerInfo != null) {
201 throw new RokuHttpException("No Player info model in response");
202 } catch (JAXBException | XMLStreamException e) {
203 throw new RokuHttpException("Exception creating Player info Unmarshaller: " + e.getLocalizedMessage());
208 * Send a command to get tv-active-channel from the Roku TV and return a TvChannel object
210 * @return A TvChannel object populated with information about the current active TV Channel
211 * @throws RokuHttpException
213 public TvChannel getActiveTvChannel() throws RokuHttpException {
215 JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNEL;
217 final String response = getCommand(urlQryActiveTvChannel);
218 Unmarshaller unmarshaller = ctx.createUnmarshaller();
219 if (unmarshaller != null) {
220 XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
221 TvChannel tvChannelInfo = (TvChannel) unmarshaller.unmarshal(xsr);
222 if (tvChannelInfo != null) {
223 return tvChannelInfo;
227 throw new RokuHttpException("No TvChannel info model in response");
228 } catch (JAXBException | XMLStreamException e) {
229 throw new RokuHttpException("Exception creating TvChannel info Unmarshaller: " + e.getLocalizedMessage());
234 * Send a command to get tv-channels from the Roku TV and return a list of Channel objects
236 * @return A List of Channel objects for all TV channels currently available on the Roku TV
237 * @throws RokuHttpException
239 public List<Channel> getTvChannelList() throws RokuHttpException {
241 JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNELS;
243 final String response = getCommand(urlQryTvChannels);
244 Unmarshaller unmarshaller = ctx.createUnmarshaller();
245 if (unmarshaller != null) {
246 XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response));
247 TvChannels tvChannels = (TvChannels) unmarshaller.unmarshal(xsr);
248 if (tvChannels != null) {
249 return tvChannels.getChannel();
253 throw new RokuHttpException("No TvChannels info model in response");
254 } catch (JAXBException | XMLStreamException e) {
255 throw new RokuHttpException("Exception creating TvChannel info Unmarshaller: " + e.getLocalizedMessage());
260 * Sends a GET command to the Roku
262 * @param url The url to send with the command embedded in the URI
263 * @return The response content of the http request
264 * @throws RokuHttpException
266 private String getCommand(String url) throws RokuHttpException {
268 return httpClient.GET(url).getContentAsString();
269 } catch (TimeoutException | ExecutionException e) {
270 throw new RokuHttpException("Error executing GET command for URL: " + url, e);
271 } catch (InterruptedException e) {
272 Thread.currentThread().interrupt();
273 throw new RokuHttpException("InterruptedException executing GET command for URL: " + url, e);
278 * Sends a POST command to the Roku
280 * @param url The url to send with the command embedded in the URI
281 * @throws RokuHttpException
283 private void postCommand(String url) throws RokuHttpException {
285 httpClient.POST(url).method(HttpMethod.POST).send();
286 } catch (TimeoutException | ExecutionException e) {
287 throw new RokuHttpException("Error executing POST command, URL: " + url, e);
288 } catch (InterruptedException e) {
289 Thread.currentThread().interrupt();
290 throw new RokuHttpException("InterruptedException executing POST command for URL: " + url, e);