Skip to content

Instantly share code, notes, and snippets.

@lhchavez
Last active August 29, 2015 06:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lhchavez/25e2bc7cd0589ae024da to your computer and use it in GitHub Desktop.
Save lhchavez/25e2bc7cd0589ae024da to your computer and use it in GitHub Desktop.
#!/usr/bin/python
# vim: set expandtab:ts=2:sw=2
import argparse
import bluetooth
import struct
import sys
def checksum(msg):
checksum = 0
for c in msg:
checksum ^= ord(c)
return checksum
def format_request(args):
msg = struct.pack('BBBBBB',
1, # group
args.gender == 'M', # 0 = Female, 1 = Male
1, # level. ???
args.height, # height, in cm
args.age, # age. in years
1) # units. 1 = KG, 2 = LB, 4 = ST
return '\xfe' + msg + struct.pack('B', checksum(msg))
def parse_reply(data):
assert data[0] == 0xcf or len(data) < 16, 'Invalid reply'
height = data[3]
weight = (data[4] << 8 | data[5]) / 10.0
return {
'level': data[1] >> 4,
'group': data[1] & 0xF,
'gender': ["Female", "Male"][data[2] >> 7],
'age': data[2] & 0x7F,
'height': height,
'weight': weight,
'body_fat': (data[6] << 8 | data[7]) / 10.0,
'bone': data[8] / 10.0,
'muscle': (data[9] << 8 | data[10]) / 10.0,
'visceral_fat': data[11],
'body_water': (data[12] << 8 | data[13]) / 10.0,
'base_metabolic_rate': (data[14] << 8 | data[15]),
'body_mass_index': weight / (height / 100.0)**2
}
def close():
return '\xfd\x35\x00\x00\x00\x00\x00\x35'
def get_weight(args):
uuid = '00001101-0000-1000-8000-00805F9B34FB'
if not args.addr:
print 'Scanning...'
nearby_devices = bluetooth.discover_devices(duration=4,
lookup_names=True)
for addr, name in nearby_devices:
if name == 'Electronic Scale':
args.addr = addr
print 'Found', args.addr
break
assert args.addr, 'Scale not found'
service_matches = bluetooth.find_service(uuid=uuid,
address=args.addr)
assert len(service_matches) > 0, 'Scale service not found'
service = service_matches[0]
host = service['host']
port = service['port']
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect((host, port))
sock.send(format_request(args))
parsed_data = parse_reply(map(ord, sock.recv(1024)))
try:
sock.send(close())
sock.close()
except:
pass
return parsed_data
def main():
parser = argparse.ArgumentParser(description='Bathroom scale')
parser.add_argument('age', type=int, help='Age (in years)')
parser.add_argument('gender', choices=['M', 'F'], help='Gender')
parser.add_argument('height', type=int, help='Height (in cm)')
parser.add_argument('--addr', help='Bluetooth address of the scale')
args = parser.parse_args()
for k,v in get_weight(args).iteritems():
print k, v
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment