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