Skip to content

Commit 047a088

Browse files
committed
Initial commit of the Pairing Protocol implementation.
1 parent 9693ea9 commit 047a088

File tree

13 files changed

+2688
-0
lines changed

13 files changed

+2688
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.pyc

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Google TV: Python implementation of Pairing Protocol and Anymote Protocol #
2+
3+
## Installation ##
4+
5+
sudo python setup.py install
6+
7+
## Pairing Protocol ##
8+
9+
The "scripts" directory contains some sample scripts to aid with the Google TV
10+
pairing process.
11+
12+
### Discovery Phase ###
13+
14+
If you're not sure what IP address or hostname your Google TV is running on, you
15+
can use the "discover" script to determine its IP and port. The script requires
16+
[pybonjour](http://code.google.com/p/pybonjour/).
17+
18+
googletv/scripts$ ./discover
19+
20+
(Only tested on Mac.)
21+
22+
### Identification and Authentication Phases ###
23+
24+
Next, you'll need to start the pairing process by authenticating a certificate
25+
with Google TV. The "pair" script provides an example of communicating with
26+
Google TV using the
27+
[Pairing Protocol](http://code.google.com/tv/remote/docs/pairing.html).
28+
29+
If you do not have a cert, the "pair" script can auto-generate a self-signed
30+
cert that you can use. [OpenSSL](http://www.openssl.org/) is required for
31+
certificate generation.
32+
33+
The Pairing Protocol server typically runs on the port one more than the Anymote
34+
server. For example, if the Anymore server is on 9551, then the Pairing Protocol
35+
server listens on 9552.
36+
37+
googletv/scripts$ ./pair --cert=mycert.pem
38+
39+
## Anymote Protocol ##
40+
41+
TBD

googletv/__init__.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright 2012 Steven Le ([email protected])
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""Python clients for the Google TV Pairing and Anymote protocols."""
18+
19+
__author__ = '[email protected] (Steven Le)'
20+
21+
import socket
22+
import ssl
23+
import struct
24+
from googletv.proto import polo_pb2
25+
from googletv.proto import remote_pb2
26+
27+
ENCODING_TYPE_HEXADECIMAL = polo_pb2.Options.Encoding.ENCODING_TYPE_HEXADECIMAL
28+
ROLE_TYPE_INPUT = polo_pb2.Options.ROLE_TYPE_INPUT
29+
30+
31+
class Error(Exception):
32+
"""Base class for all exceptions in this module."""
33+
34+
35+
class BaseProtocol(object):
36+
"""Base class for protocols used by this module.
37+
38+
Attributes:
39+
host: The host of the Google TV server.
40+
port: The port to connect to. Default is 9551 for Anymote Protocol and 9552
41+
for Pairing Protocol.
42+
sock: A socket.socket object.
43+
ssl: SSL-wrapped socket.socket object.
44+
"""
45+
46+
def __init__(self, host, port, certfile):
47+
self.host = host
48+
self.port = port
49+
self.sock = socket.socket()
50+
self.ssl = ssl.wrap_socket(self.sock, certfile=certfile)
51+
self.ssl.connect((self.host, self.port))
52+
53+
def __enter__(self):
54+
return self
55+
56+
def __exit__(self):
57+
self.Close()
58+
59+
def Close(self):
60+
self.ssl.close()
61+
62+
def Send(self, data):
63+
data_len = struct.pack('!I', len(data))
64+
sent = self.ssl.write(data_len + data)
65+
assert sent == len(data) + 4
66+
return sent
67+
68+
69+
class PairingProtocol(BaseProtocol):
70+
"""Google TV Pairing Protocol.
71+
72+
More info:
73+
http://code.google.com/tv/remote/docs/pairing.html
74+
"""
75+
76+
def __init__(self, host, certfile, port=9552):
77+
super(PairingProtocol, self).__init__(host, port, certfile)
78+
79+
def SendPairingRequest(self, client_name, service_name='AnyMote'):
80+
"""Initiates a new PairingRequest with the Google TV server.
81+
82+
Args:
83+
client_name: A string that can be used to identify the client making reqs.
84+
service_name: The name of the service to pair with.
85+
"""
86+
req = polo_pb2.PairingRequest()
87+
req.service_name = service_name
88+
req.client_name = client_name
89+
self._SendMessage(req, polo_pb2.OuterMessage.MESSAGE_TYPE_PAIRING_REQUEST)
90+
91+
def SendOptions(self, *args, **kwargs):
92+
"""Sends an Options message to the Google TV server.
93+
94+
Currently, only a 4-length HEXADECIMAL message is supported. Will support
95+
other types in the future.
96+
"""
97+
options = polo_pb2.Options()
98+
encoding = options.input_encodings.add()
99+
encoding.type = ENCODING_TYPE_HEXADECIMAL
100+
encoding.symbol_length = 4
101+
self._SendMessage(options, polo_pb2.OuterMessage.MESSAGE_TYPE_OPTIONS)
102+
103+
def SendConfiguration(self, encoding_type=ENCODING_TYPE_HEXADECIMAL,
104+
symbol_length=4, client_role=ROLE_TYPE_INPUT):
105+
"""Sends a Configuration message to the Google TV server.
106+
107+
Currently, only a 4-length HEXADECIMAL message is supported. Will support
108+
other types in the future.
109+
"""
110+
req = polo_pb2.Configuration()
111+
req.encoding.type = encoding_type
112+
req.encoding.symbol_length = symbol_length
113+
req.client_role = client_role
114+
self._SendMessage(req, polo_pb2.OuterMessage.MESSAGE_TYPE_CONFIGURATION)
115+
116+
def SendSecret(self, code):
117+
"""Sends a Secret message to the Google TV server.
118+
119+
Args:
120+
code: Hex code string displayed by the Google TV.
121+
"""
122+
req = polo_pb2.Secret()
123+
req.secret = self._EncodeHexSecret(code)
124+
self._SendMessage(req, polo_pb2.OuterMessage.MESSAGE_TYPE_SECRET)
125+
126+
def _EncodeHexSecret(self, secret):
127+
"""Encodes a hex secret.
128+
129+
Args:
130+
secret: The hex code string displayed by the Google TV.
131+
132+
Returns:
133+
An encoded value that can be used in the Secret message.
134+
"""
135+
# TODO(stevenle): Something further encodes the secret to a 64-char hex
136+
# string. For now, use adb logcat to figure out what the expected challenge
137+
# is. Eventually, make sure the encoding matches the server reference
138+
# implementation:
139+
# http://code.google.com/p/google-tv-pairing-protocol/source/browse/src/com/google/polo/pairing/PoloChallengeResponse.java
140+
result = bytearray(len(secret) / 2)
141+
for i in xrange(len(result)):
142+
start_index = 2 * i
143+
end_index = 2 * (i + 1)
144+
result[i] = int(code[start_index:end_index], 16)
145+
return bytes(result)
146+
147+
def _SendMessage(self, message, message_type):
148+
"""Sends a message to the Google TV server.
149+
150+
Args:
151+
message: A proto request message.
152+
message_type: A polo_pb2.OuterMessage.MESSAGE_TYPE_* constant.
153+
154+
Returns:
155+
The amount of data sent, in bytes.
156+
"""
157+
req = polo_pb2.OuterMessage()
158+
req.protocol_version = 1
159+
req.status = polo_pb2.OuterMessage.STATUS_OK
160+
req.type = message_type
161+
req.payload = message.SerializeToString()
162+
data = req.SerializeToString()
163+
return self.Send(data)
164+
165+
166+
class AnymoteProtocol(BaseProtocol):
167+
168+
def __init__(self):
169+
# Work in progress...
170+
raise NotImplementedError
File renamed without changes.

0 commit comments

Comments
 (0)