#!/usr/bin/env python

# performs a simple device inquiry, followed by a remote name request of each
# discovered device

# apt-get install bluetooth, bluez, python-bluez, python-setuptools
# easy_install requests

import os
import sys
import struct
import bluetooth._bluetooth as bluez
import requests
import json

# Define a global addresses dictionary
addresses 	= dict()
whitelist 	= ("30:F7:C5:8E:74:FF", "88:9B:39:DD:25:DC")
socket_url 	= "http://192.168.0.110:8000/gm"

def printpacket(pkt):
    for c in pkt:
        sys.stdout.write("%02x " % struct.unpack("B",c)[0])
    print 


def read_inquiry_mode(sock):
    """returns the current mode, or -1 on failure"""
    # save current filter
    old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)

    # Setup socket filter to receive only events related to the
    # read_inquiry_mode command
    flt = bluez.hci_filter_new()
    opcode = bluez.cmd_opcode_pack(bluez.OGF_HOST_CTL, 
            bluez.OCF_READ_INQUIRY_MODE)
    bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
    bluez.hci_filter_set_event(flt, bluez.EVT_CMD_COMPLETE);
    bluez.hci_filter_set_opcode(flt, opcode)
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )

    # first read the current inquiry mode.
    bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, 
            bluez.OCF_READ_INQUIRY_MODE )

    pkt = sock.recv(255)

    status,mode = struct.unpack("xxxxxxBB", pkt)
    if status != 0: mode = -1

    # restore old filter
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
    return mode

def write_inquiry_mode(sock, mode):
    """returns 0 on success, -1 on failure"""
    # save current filter
    old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)

    # Setup socket filter to receive only events related to the
    # write_inquiry_mode command
    flt = bluez.hci_filter_new()
    opcode = bluez.cmd_opcode_pack(bluez.OGF_HOST_CTL, 
            bluez.OCF_WRITE_INQUIRY_MODE)
    bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
    bluez.hci_filter_set_event(flt, bluez.EVT_CMD_COMPLETE);
    bluez.hci_filter_set_opcode(flt, opcode)
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )

    # send the command!
    bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, 
            bluez.OCF_WRITE_INQUIRY_MODE, struct.pack("B", mode) )

    pkt = sock.recv(255)

    status = struct.unpack("xxxxxxB", pkt)[0]

    # restore old filter
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
    if status != 0: return -1
    return 0

def device_inquiry_with_with_rssi(sock):
    # save current filter
    old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)

    # perform a device inquiry on bluetooth device #0
    # The inquiry should last 8 * 1.28 = 10.24 seconds
    # before the inquiry is performed, bluez should flush its cache of
    # previously discovered devices
    flt = bluez.hci_filter_new()
    bluez.hci_filter_all_events(flt)
    bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )

    duration = 4
    max_responses = 255
    cmd_pkt = struct.pack("BBBBB", 0x33, 0x8b, 0x9e, duration, max_responses)
    bluez.hci_send_cmd(sock, bluez.OGF_LINK_CTL, bluez.OCF_INQUIRY, cmd_pkt)

    results = []

    done = False
    while not done:
        # try:
        pkt = sock.recv(255)
        ptype, event, plen = struct.unpack("BBB", pkt[:3])
        if event == bluez.EVT_INQUIRY_RESULT_WITH_RSSI:
            pkt = pkt[3:]
            nrsp = struct.unpack("B", pkt[0])[0]
            for i in range(nrsp):
                addr = bluez.ba2str( pkt[1+6*i:1+6*i+6] )
                rssi = struct.unpack("b", pkt[1+13*nrsp+i])[0]
                results.append( ( addr, rssi ) )

                # Add the results into our tracking array
                appendResult(addr, rssi);

                # Print the output
                # print "[%s] RSSI: [%d]" % (addr, rssi)

        elif event == bluez.EVT_INQUIRY_COMPLETE:
            done = True
        elif event == bluez.EVT_CMD_STATUS:
            status, ncmd, opcode = struct.unpack("BBH", pkt[3:7])
            if status != 0:
                print "uh oh..."
                printpacket(pkt[3:7])
                done = True
        elif event == bluez.EVT_INQUIRY_RESULT:
            pkt = pkt[3:]
            nrsp = struct.unpack("B", pkt[0])[0]
            for i in range(nrsp):
                addr = bluez.ba2str( pkt[1+6*i:1+6*i+6] )
                results.append( ( addr, -1 ) )
                print "[%s] (no RRSI)" % addr
        else:
            print "unrecognized packet type 0x%02x" % ptype
	    print "event ", event
        # except:
            # pass

    # restore old filter
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )

    return results

def appendResult(addr, rssi):
    # Global in the addresses, since we're going to use it
    global addresses, socket_url

    # If it's not an address we care about, don't do anything!
    if addr not in whitelist:
        return

    # If the address isn't in the dict
    if addr not in addresses:
        print "Adding %s to dictionary" % addr
        addresses[addr] = list()

    # Push the rssi value into the list
    addresses[addr].insert(0, rssi)

    # Truncate the list to the 10 most recent readings
    del addresses[addr][10:]

    l   = len(addresses[addr])
    avg = sum(addresses[addr]) / l

    #url     = "http://192.168.2.1:8000/gm"
    url = socket_url
    payload = { 
        "channel":  "/rssi",
        "data": {
            "address":  addr,
            "value":     avg
        }
    }

    headers = {
        'Content-Type':     'application/json'
    }

    r = requests.post(url, data=json.dumps(payload), headers=headers)
    print r.content
    #print "[%s] RSSI: [%d] {%s} (%d) :: %d" % (addr, rssi, addresses[addr], len(addresses[addr]), avg)

dev_id = 0
try:
    sock = bluez.hci_open_dev(dev_id)
except:
    print "error accessing bluetooth device..."
    sys.exit(1)

try:
    mode = read_inquiry_mode(sock)
except Exception, e:
    print "error reading inquiry mode.  "
    print "Are you sure this a bluetooth 1.2 device?"
    print e
    sys.exit(1)
print "current inquiry mode is %d" % mode

if mode != 1:
    print "writing inquiry mode..."
    try:
        result = write_inquiry_mode(sock, 1)
    except Exception, e:
        print "error writing inquiry mode.  Are you sure you're root?"
        print e
        sys.exit(1)
    if result != 0:
        print "error while setting inquiry mode"
    print "result: %d" % result




# Phone address 88:9B:39:DD:25:DC
while True:
    device_inquiry_with_with_rssi(sock)
