]> git.basschouten.com Git - openhab-addons.git/blob
586460858f8f9d0605219d5769bbc39a6932360e
[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.TimeoutException;
19
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;
25
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;
38
39 /**
40  * Methods for accessing the HTTP interface of the Roku
41  *
42  * @author Michael Lobstein - Initial contribution
43  */
44 @NonNullByDefault
45 public class RokuCommunicator {
46     private final HttpClient httpClient;
47
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;
57
58     public RokuCommunicator(HttpClient httpClient, String host, int port) {
59         this.httpClient = httpClient;
60
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";
71     }
72
73     /**
74      * Send a keypress command to the Roku
75      *
76      * @param key The key code to send
77      *
78      */
79     public void keyPress(String key) throws RokuHttpException {
80         postCommand(urlKeyPress + key);
81     }
82
83     /**
84      * Send a launch app command to the Roku
85      *
86      * @param appId The appId of the app to launch
87      *
88      */
89     public void launchApp(String appId) throws RokuHttpException {
90         postCommand(urlLaunchApp + appId);
91     }
92
93     /**
94      * Send a TV channel change command to the Roku TV
95      *
96      * @param channelNumber The channel number of the channel to tune into, ie: 2.1
97      *
98      */
99     public void launchTvChannel(String channelNumber) throws RokuHttpException {
100         postCommand(urlLaunchTvChannel + channelNumber);
101     }
102
103     /**
104      * Send a command to get device-info from the Roku and return a DeviceInfo object
105      *
106      * @return A DeviceInfo object populated with information about the connected Roku
107      * @throws RokuHttpException
108      */
109     public DeviceInfo getDeviceInfo() throws RokuHttpException {
110         try {
111             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_DEVICE_INFO;
112             if (ctx != null) {
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) {
119                         return device;
120                     }
121                 }
122             }
123             throw new RokuHttpException("No DeviceInfo model in response");
124         } catch (JAXBException | XMLStreamException e) {
125             throw new RokuHttpException("Exception creating DeviceInfo Unmarshaller: " + e.getLocalizedMessage());
126         }
127     }
128
129     /**
130      * Send a command to get active-app from the Roku and return an ActiveApp object
131      *
132      * @return An ActiveApp object populated with information about the current running app on the Roku
133      * @throws RokuHttpException
134      */
135     public ActiveApp getActiveApp() throws RokuHttpException {
136         try {
137             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_ACTIVE_APP;
138             if (ctx != null) {
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) {
145                         return activeApp;
146                     }
147                 }
148             }
149             throw new RokuHttpException("No ActiveApp model in response");
150         } catch (JAXBException | XMLStreamException e) {
151             throw new RokuHttpException("Exception creating ActiveApp Unmarshaller: " + e.getLocalizedMessage());
152         }
153     }
154
155     /**
156      * Send a command to get the installed app list from the Roku and return a List of App objects
157      *
158      * @return A List of App objects for all apps currently installed on the Roku
159      * @throws RokuHttpException
160      */
161     public List<App> getAppList() throws RokuHttpException {
162         try {
163             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_APPS;
164             if (ctx != null) {
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();
172                     }
173                 }
174             }
175             throw new RokuHttpException("No AppList model in response");
176         } catch (JAXBException | XMLStreamException e) {
177             throw new RokuHttpException("Exception creating AppList Unmarshaller: " + e.getLocalizedMessage());
178         }
179     }
180
181     /**
182      * Send a command to get media-player from the Roku and return a Player object
183      *
184      * @return A Player object populated with information about the current stream playing on the Roku
185      * @throws RokuHttpException
186      */
187     public Player getPlayerInfo() throws RokuHttpException {
188         try {
189             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_PLAYER;
190             if (ctx != null) {
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) {
197                         return playerInfo;
198                     }
199                 }
200             }
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());
204         }
205     }
206
207     /**
208      * Send a command to get tv-active-channel from the Roku TV and return a TvChannel object
209      *
210      * @return A TvChannel object populated with information about the current active TV Channel
211      * @throws RokuHttpException
212      */
213     public TvChannel getActiveTvChannel() throws RokuHttpException {
214         try {
215             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNEL;
216             if (ctx != null) {
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;
224                     }
225                 }
226             }
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());
230         }
231     }
232
233     /**
234      * Send a command to get tv-channels from the Roku TV and return a list of Channel objects
235      *
236      * @return A List of Channel objects for all TV channels currently available on the Roku TV
237      * @throws RokuHttpException
238      */
239     public List<Channel> getTvChannelList() throws RokuHttpException {
240         try {
241             JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNELS;
242             if (ctx != null) {
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();
250                     }
251                 }
252             }
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());
256         }
257     }
258
259     /**
260      * Sends a GET command to the Roku
261      *
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
265      */
266     private String getCommand(String url) throws RokuHttpException {
267         try {
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);
274         }
275     }
276
277     /**
278      * Sends a POST command to the Roku
279      *
280      * @param url The url to send with the command embedded in the URI
281      * @throws RokuHttpException
282      */
283     private void postCommand(String url) throws RokuHttpException {
284         try {
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);
291         }
292     }
293 }