Skip to content
This repository
Browse code

Genericise remote support

You can make p.py control a music app like so:

./p.py remote Spotify
  • Loading branch information...
commit ce40fba90de2fdbd85a491a202663e69a96de751 1 parent ffd2824
Liam McLoughlin authored March 15, 2013

Showing 2 changed files with 65 additions and 10 deletions. Show diff stats Hide diff stats

  1. 45  p.py
  2. 30  pebble/pebble.py
45  p.py
... ...
@@ -1,7 +1,9 @@
1 1
 #!/usr/bin/env python
2 2
 
3 3
 import argparse
  4
+import os
4 5
 import pebble as libpebble
  6
+import subprocess
5 7
 import sys
6 8
 import time
7 9
 
@@ -19,6 +21,44 @@ def cmd_load_fw(pebble, args):
19 21
     print 'resetting to apply firmware update...'
20 22
     pebble.reset()
21 23
 
  24
+def cmd_remote(pebble, args):
  25
+    def do_oscacript(command):
  26
+        cmd = "osascript -e 'tell application \""+args.app_name+"\" to "+command+"'"
  27
+        try:
  28
+            return subprocess.check_output(cmd, shell=True)
  29
+        except subprocess.CalledProcessError:
  30
+            print "Failed to send message to "+args.app_name+", is it running?"
  31
+            return False
  32
+
  33
+    def music_control_handler(endpoint, resp):
  34
+        events = {
  35
+            "PLAYPAUSE": "playpause",
  36
+            "PREVIOUS": "previous track",
  37
+            "NEXT": "next track"
  38
+        }
  39
+        do_oscacript(events[resp])
  40
+        update_metadata()
  41
+
  42
+    def update_metadata():
  43
+        artist = do_oscacript("artist of current track as string")
  44
+        title = do_oscacript("name of current track as string")
  45
+        album = do_oscacript("album of current track as string")
  46
+
  47
+        if not artist or not title or not album:
  48
+            pebble.set_nowplaying_metadata("No Music Found", "", "")
  49
+        else:
  50
+            pebble.set_nowplaying_metadata(title, album, artist)
  51
+
  52
+    pebble.register_endpoint("MUSIC_CONTROL", music_control_handler)
  53
+
  54
+    print 'waiting for music control events'
  55
+    try:
  56
+        while True:
  57
+            update_metadata()
  58
+            time.sleep(5)
  59
+    except KeyboardInterrupt:
  60
+        return
  61
+
22 62
 def cmd_logcat(pebble, args):
23 63
     print 'listening for logs...'
24 64
     try:
@@ -102,6 +142,11 @@ def main():
102 142
     set_time_parser.add_argument('timestamp', type=int, help='time stamp to be sent')
103 143
     set_time_parser.set_defaults(func=cmd_set_time)
104 144
 
  145
+    remote_parser = subparsers.add_parser('remote', help='control a music app on this PC using Pebble')
  146
+    remote_parser.add_argument('app_name', type=str, help='title of application to be controlled')
  147
+    remote_parser.set_defaults(func=cmd_remote)
  148
+
  149
+
105 150
     args = parser.parse_args()
106 151
 
107 152
     attempts = 0
30  pebble/pebble.py
@@ -156,6 +156,7 @@ def __init__(self, id = None):
156 156
 			self.endpoints["VERSION"]: self._version_response,
157 157
 			self.endpoints["PHONE_VERSION"]: self._phone_version_response,
158 158
 			self.endpoints["SYSTEM_MESSAGE"]: self._system_message_response,
  159
+			self.endpoints["MUSIC_CONTROL"]: self._music_control_response,
159 160
 			self.endpoints["LOGS"]: self._log_response,
160 161
 			self.endpoints["PING"]: self._ping_response,
161 162
 			self.endpoints["APP_MANAGER"]: self._appbank_status_response
@@ -165,7 +166,6 @@ def __init__(self, id = None):
165 166
 			devicefile = "/dev/tty.Pebble"+id+"-SerialPortSe"
166 167
 			log.debug("Attempting to open %s as Pebble device %s" % (devicefile, id))
167 168
 			self._ser = serial.Serial(devicefile, 115200, timeout=1)
168  
-
169 169
 			log.debug("Connected")
170 170
 
171 171
 			log.debug("Initializing reader thread")
@@ -196,7 +196,7 @@ def _reader(self):
196 196
 				if endpoint in self._internal_endpoint_handlers:
197 197
 					resp = self._internal_endpoint_handlers[endpoint](endpoint, resp)
198 198
 
199  
-				if endpoint in self._endpoint_handlers:
  199
+				if endpoint in self._endpoint_handlers and resp:
200 200
 					self._endpoint_handlers[endpoint](endpoint, resp)
201 201
 		except:
202 202
 			traceback.print_exc()
@@ -260,8 +260,10 @@ def set_nowplaying_metadata(self, track, album, artist):
260 260
 		"""Update the song metadata displayed in Pebble's music app."""
261 261
 
262 262
 		parts = [artist, album, track]
  263
+
263 264
 		data = pack("!b", 16)
264 265
 		for part in parts:
  266
+			part = part[0:29] if len(part) > 30 else part
265 267
 			data += pack("!b", len(part))+part
266 268
 		self._send_message("MUSIC_CONTROL", data)
267 269
 
@@ -480,6 +482,7 @@ def _system_message_response(self, endpoint, data):
480 482
 			log.info("Got system message %s" % repr(unpack('!bb', data)))
481 483
 		else:
482 484
 			log.info("Got 'unknown' system message...")
  485
+
483 486
 	def _log_response(self, endpoint, data):
484 487
 		if (len(data) < 8):
485 488
 			log.warn("Unable to decode log message (length %d is less than 8)" % len(data))
@@ -563,10 +566,6 @@ def _phone_version_response(self, endpoint, data):
563 566
 			"SMS" : 32,
564 567
 			"GPS" : 64,
565 568
 			"BTLE" : 128,
566  
-			# XXX: CAMERA_FRONT is 240 in the APK, but this can't
567  
-			# be right, as this will ruin the bitmask.  Check
568  
-			# future app versions
569  
-			"CAMERA_FRONT" : 240,
570 569
 			"CAMERA_REAR" : 256,
571 570
 			"ACCEL" : 512,
572 571
 			"GYRO" : 1024,
@@ -580,17 +579,28 @@ def _phone_version_response(self, endpoint, data):
580 579
 			"LINUX" : 4,
581 580
 			"WINDOWS" : 5,
582 581
 		}
583  
-		# Magic prefix that the app adds
584  
-		prefix = "\x01\xff\xff\xff\xff"
  582
+
585 583
 		# Then session capabilities, android adds GAMMA_RAY and it's
586 584
 		# the only session flag so far
587 585
 		session = session_cap["GAMMA_RAY"]
  586
+
588 587
 		# Then phone capabilities, android app adds TELEPHONY and SMS,
589 588
 		# and the phone type (we know android works for now)
590 589
 		remote = remote_cap["TELEPHONY"] | remote_cap["SMS"] | os["ANDROID"]
591  
-		msg = prefix + pack("!II", session, remote)
  590
+
  591
+		msg = pack("!biII", 1, -1, session, remote)
592 592
 		self._send_message("PHONE_VERSION", msg);
593  
-		return data
  593
+
  594
+	def _music_control_response(self, endpoint, data):
  595
+		event, = unpack("!b", data)
  596
+
  597
+		event_names = {
  598
+			1: "PLAYPAUSE",
  599
+			4: "NEXT",
  600
+			5: "PREVIOUS",
  601
+		}
  602
+
  603
+		return event_names[event] if event in event_names else None
594 604
 
595 605
 class PutBytesClient(object):
596 606
 	states = {

0 notes on commit ce40fba

Please sign in to comment.
Something went wrong with that request. Please try again.