#!/usr/bin/python
# Quick an dirty SpotConnect client in Python for pybluez.
# Written by Travis Goodspeed on--not for--the Nokia N900.
# Extended by Richard Ulrich for tracking on ubuntu phone.

# See Alexei Karpenko's article on reversing the SPOT.
# http://natrium42.com/projects/spot/

# This protocol is different from the STX2 one he documented, but the
# principles are the same.  The verbs are different, and checksums may
# be freely omitted.

# Verbs:
# 0x01 -- Get 4-byte ID.
# 0x10 -- Unknown. Used during startup.
# 0x25 -- Get Coordinates.
# 0x26 -- Send text message.
# 0x40 -- Unknown.
# 0x51 -- Start tracking mode.
# 0x52 -- Unknown, probably a status update.


import bluetooth, sys, time, os;

btaddr=os.environ.get("SPOTCONNECT");

class SpotConnect:
   def __init__(self, btaddr=None):
      while btaddr==None or btaddr=="none":
         print "performing inquiry..."
         print "This only works when Bluetooth LED is solid and device is discoverable."
         print "Please set $SPOTCONNECT to the MAC address to avoid the search."
         nearby_devices = bluetooth.discover_devices(lookup_names = True)
         print "found %d devices" % len(nearby_devices)
         for addr, name in nearby_devices:
            print "  %s - '%s'" % (addr, name)
            if name=='Connect_466' or name=='Connect_603':
               btaddr=addr;
         print "Identified modem at %s" % btaddr;

      # Manually use the portnumber.
      port=1;
      
      print "Connecting to %s on port %i." % (btaddr, port);
      connected=False;
      while connected==False:
         try:
            sock=bluetooth.BluetoothSocket(bluetooth.RFCOMM);
            self.sock=sock;
            sock.connect((btaddr,port));
            connected=True;
         except bluetooth.btcommon.BluetoothError:
            print "Connection failed. Retrying.";
            time.sleep(0.5);
            pass;
      
      
      print "Connected to Device %i" % self.getid();
      
   def close(self):
      """Close the connection."""
      print "Disconnecting.";
      self.sock.close();
   def checkin(self,string="OK"):
      """Sends a 42-byte text message to the server."""
      packet="\x26\x01\x00\x01\x00\x01"+string;
      packet="\xaa"+chr(len(packet)+2)+packet;
      print "Transmitting coords and [%s]" % string;
      self.tx(packet);
      while True:
         print "Idling, if that's what 0x52 does.";
         self.tx("\xaa\x03\x52");
         self.rx();
   def StartTracking(self):
      """Enables tracking mode: 
         The device sends the position to the server every 10 minutes."""
      packet="\xaa\x04\x51\x0d"
      print "Enabling tracking mode"
      self.tx(packet)
   verbose=1;
   def txstr(self,msg):
      """Transmit normal ASCII.  Useful for modem mode."""
      print "TXSTR: %s" % msg;
      self.sock.send(msg);
   def rxstr(self):
      print "Listening for input."
      data=self.sock.recv(1024);
      print "RXSTR: %s" % data;
      return data;
   def tx(self,msg,rx=True):
      """Transmit a SpotConnect packet.  SFD, Length, and Checksum will be added."""
      
      if ord(msg[1])!=len(msg):
         print "Length mismatch, 0x%02x!=len(%s)." % (len(msg),msg);
         return None;
      
      self.sock.send(msg);
      if self.verbose: print "Sent 0x%02x bytes: %s" % (len(msg),hex(msg));
      if not rx: return None;
      return self.rx();
   def rx(self, longpacket=True):
      data=None;
      #Is there a reply?
      try:
         while data==None:
            data=self.sock.recv(1024);
         while len(data)<3:
            data=data+self.sock.recv(1024);
         try:
            if data[0:2]=="\x41\x54\x2b":
               print "Somehow we've got a Hayes shell."
               print data;
               sys.exit();
         except:
            pass;
         while len(data)<ord(data[1]) and longpacket:
            print "Need more data, got 0x%02x=[%s]" % (len(data),hex(data));
            data=data+self.sock.recv(1024);
      except IOError:
         print "IOError.";
         pass;
      if self.verbose: print "Received [%s]" % hex(data);
      return data;
   def getid(self):
      """Get the 4-byte device ID."""
      uid=0;
      b=self.tx("\xaa\x03\x01");
      for foo in range(3,7):
         uid=uid*256;
         uid=uid+ord(b[foo]);
      return uid;
   def getlatlong(self):
      """Return the (lat,lon) pair as a float."""
      data=self.tx("\xaa\x03\x25");
      if(len(data)!=0x0c):
         print "Error in coord values.";
         return (0,0);
      
      lat=0;
      lon=0;
      for foo in range(3,6):
         lat=lat*256;
         lat=lat+ord(data[foo]);
      for foo in range(6,9):
         lon=lon*256;
         lon=lon+ord(data[foo]);
      lat=lat*90.0/2**23;
      lon=lon*180.0/2**23;
      if lat>45: lat=lat-90;
      if lon>180: lon=lon-360;

      return (lat,lon);
def hex(str):
  """Returns the hex decoded version of a byte string."""
  toret="";
  if str==None: return "none";
  for c in str:
    toret="%s %02x" % (toret,ord(c));
  return toret;


sc=SpotConnect(btaddr);


(lat, lon) = sc.getlatlong();
print "Last seen at %f, %f." % (lat,lon);

#Set message.


if len(sys.argv)>1:
    if sys.argv[1] == 'track':
        sc.StartTracking()
    else:
        sc.checkin(sys.argv[1]);
else:
   #sc.tx("\xaa\x31\x26\x01\x00\x01\x00\x01Mr. Watson, come here. I want to see you.");
   print "Try giving argv[1] to send a message.";

sc.close();

