]> git.basschouten.com Git - openhab-addons.git/blob
a38f37106effe529a2c54e3e02865d97128c2b3b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.androidtv.internal.protocol.googletv;
14
15 import static org.openhab.binding.androidtv.internal.protocol.googletv.GoogleTVConstants.*;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 /**
22  * Class responsible for parsing incoming GoogleTV messages. Calls back to an object implementing the
23  * GoogleTVMessageParserCallbacks interface.
24  *
25  * Adapted from Lutron Leap binding
26  *
27  * @author Ben Rosenblum - Initial contribution
28  */
29
30 @NonNullByDefault
31 public class GoogleTVMessageParser {
32     private final Logger logger = LoggerFactory.getLogger(GoogleTVMessageParser.class);
33
34     private final GoogleTVConnectionManager callback;
35
36     public GoogleTVMessageParser(GoogleTVConnectionManager callback) {
37         this.callback = callback;
38     }
39
40     public void handleMessage(String msg) {
41         if (msg.trim().isEmpty()) {
42             return; // Ignore empty lines
43         }
44
45         String thingId = callback.getThingID();
46         char[] charArray = msg.toCharArray();
47         String lenString = "" + charArray[0] + charArray[1];
48         int len = Integer.parseInt(lenString, 16);
49         msg = msg.substring(2);
50         charArray = msg.toCharArray();
51
52         logger.trace("{} - Received GoogleTV message - Length: {} Message: {}", thingId, len, msg);
53
54         callback.validMessageReceived();
55
56         try {
57             if (msg.startsWith(DELIMITER_1A)) {
58                 logger.warn("{} - GoogleTV Error Message: {}", thingId, msg);
59             } else if (msg.startsWith(DELIMITER_0A)) {
60                 // First message on connection from GTV
61                 //
62                 // 0a 5b08 ff 041256 0a 11 534849454c4420416e64726f6964205456 12 06 4e5649444941 18 01 22 02 3131 2a
63                 // ---------------------LEN-SHIELD Android TV--------------------LEN-NVIDIA---------LEN---LEN-Android
64                 // 24 636f6d2e676f6f676c652e616e64726f69642e74762e72656d6f74652e73657276696365 32
65                 // LEN-com.google.android.tv.remote.service
66                 // 0d 352e322e343733323534313333
67                 // LEN-5.2.473254133
68                 //
69                 // 0a 5308 ff 04124e 0a 0c 42524156494120344b204742 12 04 536f6e79 18 01 22 01 39 2a
70                 // ---------------------LEN-BRAVIA 4K GB---------------LEN-Sony-------LEN---LEN-Android Version
71                 // 24 636f6d2e676f6f676c652e616e64726f69642e74762e72656d6f74652e73657276696365 32
72                 // 0d 352e322e343733323534313333
73                 //
74                 // 0a 5408 ff 04124f 0a 0a 4368726f6d6563617374 12 06 476f6f676c65 18 01 22 02 3132 2a
75                 // ---------------------LEN-Chromecast-------------LEN-Google---------LEN---LEN-Android Version
76                 // 24 636f6d2e676f6f676c652e616e64726f69642e74762e72656d6f74652e73657276696365 32
77                 // 0d 352e322e343733323534313333
78                 //
79                 // 0a 5708 ff 041252 0a 0d 4368726f6d6563617374204844 12 06 476f6f676c65 18 01 22 02 3132 2a
80                 // ---------------------LEN-Chromecast HD----------------LEN-Google---------LEN---LEN-Android Version
81                 // 24 636f6d2e676f6f676c652e616e64726f69642e74762e72656d6f74652e73657276696365 32
82                 // 0d352e322e343733323534313333
83
84                 if (callback.getLoggedIn()) {
85                     logger.warn("{} - Unexpected Login Message: {}", thingId, msg);
86                 } else {
87                     callback.sendCommand(
88                             new GoogleTVCommand(GoogleTVRequest.encodeMessage(GoogleTVRequest.loginRequest(4))));
89                 }
90
91                 String st = "";
92                 int length = 0;
93                 StringBuilder preambleSb = new StringBuilder();
94                 StringBuilder manufacturerSb = new StringBuilder();
95                 StringBuilder modelSb = new StringBuilder();
96                 StringBuilder androidVersionSb = new StringBuilder();
97                 StringBuilder remoteServerSb = new StringBuilder();
98                 StringBuilder remoteServerVersionSb = new StringBuilder();
99
100                 int i = 0;
101                 int current = 0;
102
103                 for (; i < 14; i++) {
104                     preambleSb.append(charArray[i]);
105                 }
106
107                 i += 2; // 0a delimiter
108
109                 st = "" + charArray[i] + charArray[i + 1];
110                 length = Integer.parseInt(st, 16) * 2;
111                 i += 2;
112                 current = i;
113
114                 for (; i < current + length; i++) {
115                     modelSb.append(charArray[i]);
116                 }
117
118                 i += 2; // 12 delimiter
119
120                 st = "" + charArray[i] + charArray[i + 1];
121                 length = Integer.parseInt(st, 16) * 2;
122                 i += 2;
123                 current = i;
124
125                 for (; i < current + length; i++) {
126                     manufacturerSb.append(charArray[i]);
127                 }
128
129                 i += 6; // 18 01 22
130
131                 st = "" + charArray[i] + charArray[i + 1];
132                 length = Integer.parseInt(st, 16) * 2;
133                 i += 2;
134                 current = i;
135
136                 for (; i < current + length; i++) {
137                     androidVersionSb.append(charArray[i]);
138                 }
139
140                 i += 2; // 2a delimiter
141
142                 st = "" + charArray[i] + charArray[i + 1];
143                 length = Integer.parseInt(st, 16) * 2;
144                 i += 2;
145                 current = i;
146
147                 for (; i < current + length; i++) {
148                     remoteServerSb.append(charArray[i]);
149                 }
150
151                 i += 2; // 32 delimiter
152
153                 st = "" + charArray[i] + charArray[i + 1];
154                 length = Integer.parseInt(st, 16) * 2;
155                 i += 2;
156                 current = i;
157
158                 for (; i < current + length; i++) {
159                     remoteServerVersionSb.append(charArray[i]);
160                 }
161
162                 String preamble = preambleSb.toString();
163                 String model = GoogleTVRequest.encodeMessage(modelSb.toString());
164                 String manufacturer = GoogleTVRequest.encodeMessage(manufacturerSb.toString());
165                 String androidVersion = GoogleTVRequest.encodeMessage(androidVersionSb.toString());
166                 String remoteServer = GoogleTVRequest.encodeMessage(remoteServerSb.toString());
167                 String remoteServerVersion = GoogleTVRequest.encodeMessage(remoteServerVersionSb.toString());
168
169                 logger.debug("{} - {} \"{}\" \"{}\" {} {} {}", thingId, preamble, model, manufacturer, androidVersion,
170                         remoteServer, remoteServerVersion);
171
172                 callback.setModel(model);
173                 callback.setManufacturer(manufacturer);
174                 callback.setAndroidVersion(androidVersion);
175                 callback.setRemoteServer(remoteServer);
176                 callback.setRemoteServerVersion(remoteServerVersion);
177
178             } else if (msg.startsWith(DELIMITER_12)) {
179                 // Second message on connection from GTV
180                 // Login successful
181                 callback.sendCommand(
182                         new GoogleTVCommand(GoogleTVRequest.encodeMessage(GoogleTVRequest.loginRequest(5))));
183                 logger.info("{} - Login Successful", thingId);
184                 callback.setLoggedIn(true);
185             } else if (msg.startsWith(DELIMITER_92)) {
186                 // Third message on connection from GTV
187                 // Also sent on power state change (to ON only unless keypress triggers)i
188                 // 9203 21 08 02 10 02 1a 11 534849454c4420416e64726f6964205456 20 02 2800 30 0f 38 0e 40 00
189                 // --------DD----DD----DD-LEN-SHIELD Android TV
190                 // 9203 1e 08 9610 10 09 1a 0d 4368726f6d6563617374204844 20 02 2800 30 19 38 0a 40 00
191                 // --------DD------DD----DD-LEN-Chromecast HD
192                 // 9203 1a 08 f304 10 09 1a 11 534849454c4420416e64726f6964205456 20 01
193                 // 9203 1a 08 8205 10 09 1a 11 534849454c4420416e64726f6964205456 20 01
194                 // --------DD------DD----DD-LEN-SHIELD Android TV
195                 //
196                 // VOLUME:
197                 // ---------------DD----DD----DD-LEN-BRAVIA 4K GB------------DD---------DD-MAX---VOL---MUTE
198                 // 00 --- 9203 1c 08 03 10 06 1a 0c 42524156494120344b204742 20 02 2800 30 64 38 00 40 00
199                 // 01 --- 9203 1c 08 03 10 06 1a 0c 42524156494120344b204742 20 02 2800 30 64 38 01 40 00
200                 // 100 -- 9203 1c 08 03 10 06 1a 0c 42524156494120344b204742 20 02 2800 30 64 38 64 40 00
201                 // MUTE - 9203 1c 08 03 10 06 1a 0c 42524156494120344b204742 20 02 2800 30 64 38 00 40 01
202
203                 String st = "";
204                 int length = 0;
205
206                 StringBuilder preambleSb = new StringBuilder();
207                 StringBuilder modelSb = new StringBuilder();
208                 String volMax = "";
209                 String volCurr = "";
210                 String volMute = "";
211                 String audioMode = "";
212
213                 int i = 0;
214                 int current = 0;
215
216                 for (; i < 12; i++) {
217                     preambleSb.append(charArray[i]);
218                 }
219
220                 st = "" + charArray[i] + charArray[i + 1];
221                 do {
222                     if (!DELIMITER_1A.equals(st)) {
223                         preambleSb.append(st);
224                         i += 2;
225                         st = "" + charArray[i] + charArray[i + 1];
226                     }
227                 } while (!DELIMITER_1A.equals(st));
228
229                 i += 2; // 1a delimiter
230
231                 st = "" + charArray[i] + charArray[i + 1];
232                 length = Integer.parseInt(st, 16) * 2;
233                 i += 2;
234                 current = i;
235
236                 for (; i < current + length; i++) {
237                     modelSb.append(charArray[i]);
238                 }
239
240                 i += 2; // 20 delimiter
241
242                 st = "" + charArray[i] + charArray[i + 1];
243
244                 audioMode = st; // 01 remote audio - 02 local audio
245
246                 if (DELIMITER_02.equals(st)) {
247                     i += 2; // 02 longer message
248                     i += 4; // Unknown 2800 message
249                     i += 2; // 30 delimiter
250                     volMax = "" + charArray[i] + charArray[i + 1];
251                     i += 4; // volMax + 38 delimiter
252                     volCurr = "" + charArray[i] + charArray[i + 1];
253                     i += 4; // volCurr + 40 delimiter
254                     volMute = "" + charArray[i] + charArray[i + 1];
255
256                     callback.setVolMax(volMax);
257                     callback.setVolCurr(volCurr);
258                     callback.setVolMute(volMute);
259                 }
260
261                 String preamble = preambleSb.toString();
262                 String model = GoogleTVRequest.encodeMessage(modelSb.toString());
263                 logger.debug("{} - Device Update: {} \"{}\" {} {} {} {}", thingId, preamble, model, audioMode, volMax,
264                         volCurr, volMute);
265                 callback.setAudioMode(audioMode);
266
267             } else if (msg.startsWith(DELIMITER_08)) {
268                 // PIN Process Messages. Only used on 6467.
269                 if (msg.startsWith(MESSAGE_PINSUCCESS)) {
270                     // PIN Process Successful
271                     logger.debug("{} - PIN Process Successful!", thingId);
272                     callback.finishPinProcess();
273                 } else {
274                     // 080210c801a201081204080310061801
275                     // 080210c801fa0100
276                     logger.debug("{} - PIN Intermediary Message: {}", thingId, msg);
277                 }
278             } else if (msg.startsWith(DELIMITER_C2)) {
279                 // Power State
280                 // c202020800 - OFF
281                 // c202020801 - ON
282                 if (MESSAGE_POWEROFF.equals(msg)) {
283                     callback.setPower(false);
284                 } else if (MESSAGE_POWERON.equals(msg)) {
285                     callback.setPower(true);
286                 } else {
287                     logger.info("{} - Unknown power state received. {}", thingId, msg);
288                 }
289             } else if (msg.startsWith(DELIMITER_42)) {
290                 // Keepalive request
291                 callback.sendKeepAlive(msg);
292             } else if (msg.startsWith(DELIMITER_A2)) {
293                 // Current app name. Sent on keypress and power change.
294                 // a201 21 0a 1f 62 1d 636f6d2e676f6f676c652e616e64726f69642e796f75747562652e7476
295                 // -----------------LEN-com.google.android.youtube.tv
296                 // a201 21 0a 1f 62 1d 636f6d2e676f6f676c652e616e64726f69642e74766c61756e63686572
297                 // -----------------LEN-com.google.android.tvlauncher
298                 // a201 14 0a 12 62 10 636f6d2e736f6e792e6474762e747678
299                 // -----------------LEN-com.sony.dtv.tvx
300                 // a201 15 0a 13 62 11 636f6d2e6e6574666c69782e6e696e6a61
301                 // -----------------LEN-com.netflix.ninja
302
303                 StringBuilder preambleSb = new StringBuilder();
304                 StringBuilder appNameSb = new StringBuilder();
305                 int i = 0;
306                 int current = 0;
307
308                 for (; i < 10; i++) {
309                     preambleSb.append(charArray[i]);
310                 }
311
312                 i += 2; // 62 delimiter
313
314                 String st = "" + charArray[i] + charArray[i + 1];
315                 int length = Integer.parseInt(st, 16) * 2;
316                 i += 2;
317                 current = i;
318
319                 for (; i < current + length; i++) {
320                     appNameSb.append(charArray[i]);
321                 }
322
323                 String preamble = preambleSb.toString();
324                 String appName = GoogleTVRequest.encodeMessage(appNameSb.toString());
325
326                 logger.debug("{} - Current App: {} {}", thingId, preamble, appName);
327                 callback.setCurrentApp(appName);
328             } else {
329                 logger.info("{} - Unknown payload received. {} {}", thingId, len, msg);
330             }
331         } catch (Exception e) {
332             logger.debug("{} - Message Parser Exception on {}", thingId, msg);
333             logger.debug("Message Parser Caught Exception", e);
334         }
335     }
336 }