diff --git a/bluesleep.py b/bluesleep.py index 8a19728..a90bf05 100755 --- a/bluesleep.py +++ b/bluesleep.py @@ -51,7 +51,7 @@ last_tick_time = None tick_seconds = 0.5 fieldnames = ['time'] -for data_type, _ in sleep_data.items(): +for data_type in sleep_data: periods = sleep_data[data_type]['periods'] for period in periods: fieldnames.append(data_type + str(period)) @@ -65,12 +65,12 @@ def write_csv(data): global csv_filename if not path.exists(csv_filename): with open(csv_filename, 'w', newline='') as csvfile: - csv_writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + csv_writer = csv.DictWriter(csvfile, fieldnames=fieldnames) csv_writer.writeheader() csv_writer.writerow(data) else: with open(csv_filename, 'a', newline='') as csvfile: - csv_writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + csv_writer = csv.DictWriter(csvfile, fieldnames=fieldnames) csv_writer.writerow(data) @@ -78,14 +78,15 @@ def get_mac_address(filename): mac_regex_pattern = re.compile(r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})') try: with open(filename, "r") as f: - regex_match_from_file = re.search(mac_regex_pattern, f.read().strip()) - if regex_match_from_file: - MAC_ADDR = regex_match_from_file[0] + hwaddr_search = re.search(mac_regex_pattern, f.read().strip()) + + if hwaddr_search: + MAC_ADDR = hwaddr_search[0] else: - print ("No valid MAC address found in " + str(filename)) + print ("No valid MAC address found in {}".format(filename)) exit(1) except FileNotFoundError: - print ("MAC file not found: " + filename) + print ("MAC file not found: {}".format(filename)) exit(1) return MAC_ADDR @@ -94,14 +95,14 @@ def get_auth_key(filename): authkey_regex_pattern = re.compile(r'([0-9a-fA-F]){32}') try: with open(filename, "r") as f: - regex_match_from_file = re.search(authkey_regex_pattern, f.read().strip()) - if regex_match_from_file: - AUTH_KEY = bytes.fromhex(regex_match_from_file[0]) + key_search = re.search(authkey_regex_pattern, f.read().strip()) + if key_search: + AUTH_KEY = bytes.fromhex(key_search[0]) else: - print ("No valid auth key found in " + str(filename)) + print ("No valid auth key found in {}".format(filename)) exit(1) except FileNotFoundError: - print ("Auth key file not found: " + filename) + print ("Auth key file not found: {}".format(filename)) exit(1) return AUTH_KEY @@ -110,21 +111,28 @@ def process_heartrate_data(heartrate_data, tick_time): print("BPM: " + str(heartrate_data)) if heartrate_data > 0: value_name = sleep_data['heartrate']['value_name'] - sleep_data['heartrate']['raw_data'].append({ 'time': tick_time, value_name: heartrate_data } ) + sleep_data['heartrate']['raw_data'].append({ + 'time': tick_time, + value_name: heartrate_data + } ) def process_gyro_data(gyro_data, tick_time): - # Each gyro reading from miband4 comes over as a group of three, each containing x,y,z values - # This function summarizes the values into a single consolidated movement value + # Each gyro reading from miband4 comes over as a group of three, + # each containing x,y,z values. This function summarizes the + # values into a single consolidated movement value. global sleep_data - gyro_last_x = sleep_data['movement']['workspace']['gyro_last_x'] - gyro_last_y = sleep_data['movement']['workspace']['gyro_last_y'] - gyro_last_z = sleep_data['movement']['workspace']['gyro_last_z'] - value_name = sleep_data['movement']['value_name'] + sleep_move = sleep_data['movement'] + sleep_workspace = sleep_move['workspace'] + + gyro_last_x = sleep_workspace['gyro_last_x'] + gyro_last_y = sleep_workspace['gyro_last_y'] + gyro_last_z = sleep_workspace['gyro_last_z'] + value_name = sleep_move['value_name'] gyro_movement = 0 - for gyro_datum in gyro_data: + for gyro_datum in gyro_data: gyro_delta_x = abs(gyro_datum['x'] - gyro_last_x) gyro_last_x = gyro_datum['x'] gyro_delta_y = abs(gyro_datum['y'] - gyro_last_y) @@ -134,27 +142,31 @@ def process_gyro_data(gyro_data, tick_time): gyro_delta_sum = gyro_delta_x + gyro_delta_y + gyro_delta_z gyro_movement += gyro_delta_sum - sleep_data['movement']['workspace']['gyro_last_x'] = gyro_last_x - sleep_data['movement']['workspace']['gyro_last_y'] = gyro_last_y - sleep_data['movement']['workspace']['gyro_last_z'] = gyro_last_z + sleep_workspace['gyro_last_x'] = gyro_last_x + sleep_workspace['gyro_last_y'] = gyro_last_y + sleep_workspace['gyro_last_z'] = gyro_last_z - sleep_data['movement']['raw_data'].append({ 'time': tick_time, value_name: gyro_movement } ) + sleep_move['raw_data'].append({ + 'time': tick_time, + value_name: gyro_movement + }) def flush_old_raw_data(tick_time): global sleep_data - for data_type, _ in sleep_data.items(): - periods = sleep_data[data_type]['periods'] + for data_type in sleep_data: + s_datum = sleep_data[data_type] + periods = s_datum['periods'] cleaned_raw_data = [] - for raw_datum in sleep_data[data_type]['raw_data']: + for raw_datum in s_datum['raw_data']: datum_age = tick_time - raw_datum['time'] if datum_age < max(periods): cleaned_raw_data.append(raw_datum) - sleep_data[data_type]['raw_data'] = cleaned_raw_data + s_datum['raw_data'] = cleaned_raw_data def average_raw_data(tick_time): @@ -163,18 +175,18 @@ def average_raw_data(tick_time): timestamp = datetime.fromtimestamp(tick_time) csv_out = {'time': timestamp } - for data_type, _ in sleep_data.items(): - period_averages_dict = {} - period_averages_dict['time'] = timestamp - periods = sleep_data[data_type]['periods'] - value_name = sleep_data[data_type]['value_name'] + for data_type in sleep_data: + s_datum = sleep_data[data_type] + period_averages_dict = {'time': timestamp} + periods = s_datum['periods'] + value_name = s_datum['value_name'] flush_old_raw_data(tick_time) for period_seconds in periods: period_data = [] period_averages_dict[period_seconds] = 0 - for raw_datum in sleep_data[data_type]['raw_data']: + for raw_datum in s_datum['raw_data']: datum_age_seconds = tick_time - raw_datum['time'] if datum_age_seconds < period_seconds: period_data.append(raw_datum[value_name]) @@ -182,22 +194,22 @@ def average_raw_data(tick_time): if len(period_data) > 0: period_data_average = sum(period_data) / len(period_data) else: - print ("(" + data_type + ") Period data empty: " + str(period_seconds)) + print("({}) Period data empty: {}".format(data_type, + period_seconds)) period_data_average = 0 period_averages_dict[period_seconds] = zero_to_nan(period_data_average) csv_out[data_type + str(period_seconds)] = zero_to_nan(period_data_average) - sleep_data[data_type]['averaged_data'].append(period_averages_dict) + s_datum['averaged_data'].append(period_averages_dict) write_csv(csv_out) def zero_to_nan(value): if value == 0: return (float('nan')) - else: - return int(value) + return int(value) def sleep_monitor_callback(data): @@ -210,8 +222,7 @@ def sleep_monitor_callback(data): if data[0] == "GYRO": process_gyro_data(data[1], tick_time) - - if data[0] == "HR": + elif data[0] == "HR": process_heartrate_data(data[1], tick_time) if (tick_time - last_tick_time) >= tick_seconds: @@ -220,32 +231,40 @@ def sleep_monitor_callback(data): def init_graph_data(): - for data_type, _ in sleep_data.items(): + for data_type in sleep_data: data_periods = sleep_data[data_type]['periods'] graph_data[data_type] = { - 'time': [], - 'data': {} - } + 'time': [], + 'data': {} + } for period in data_periods: graph_data[data_type]['data'][period] = [] + def update_graph_data(): global sleep_data global graph_data - for data_type, _ in sleep_data.items(): - if len(sleep_data[data_type]['averaged_data']) > 1: - - data_periods = sleep_data[data_type]['periods'] + for data_type in sleep_data: + s_datum = sleep_data[data_type] # Re-referenced to shorten name + avg_datum = s_datum['averaged_data'] - starting_index = max([(len(graph_data[data_type]['time']) - 1), 0]) - ending_index = len(sleep_data[data_type]['averaged_data']) - 1 + if len(avg_datum) > 1: - for sleep_datum in sleep_data[data_type]['averaged_data'][starting_index:ending_index]: - graph_data[data_type]['time'].append(sleep_datum['time']) + g_datum = graph_data[data_type] # Re-referenced to short name + data_periods = s_datum['periods'] + + starting_index = max([(len(g_dataum['time']) - 1), 0]) + ending_index = len(avg_datum) - 1 + + # Re-referenced to shorten name + sleep_data_range = avg_datum[starting_index:ending_index] + + for sleep_datum in sleep_data_range: + g_datum['time'].append(sleep_datum['time']) for period in data_periods: - if graph_data[data_type]['data'][period] != 'nan': - graph_data[data_type]['data'][period].append(sleep_datum[period]) + if g_datum['data'][period] != 'nan': + g_datum['data'][period].append(sleep_datum[period]) def graph_animation(i): @@ -259,28 +278,35 @@ def graph_animation(i): update_graph_data() - for data_type, _ in graph_data.items(): + for data_type in graph_data: if len(graph_data[data_type]['time']) > 0: graph_axes.clear() break - for data_type, _ in sleep_data.items(): - if len(graph_data[data_type]['time']) > 0: + for data_type in sleep_data: + s_datum = sleep_data[data_type] + g_datum = graph_data[data_type] + if len(g_datum['time']) > 0: plotflag = True data_periods = sleep_data[data_type]['periods'] for period in data_periods: - axis_label = sleep_data[data_type]['value_name'] + " " + str(period) + "sec" - graph_axes.plot(graph_data[data_type]['time'], graph_data[data_type]['data'][period], label=axis_label) + axis_label = "{} {} sec".format(s_datum['value_name'], period) + graph_axes.plot(g_datum['time'], + g_datum['data'][period], + label=axis_label) if plotflag: plt.legend() + def connect(): global band global mac_filename global auth_key_filename success = False + timeout = 3 + msg = 'Connection to the MIBand failed. Trying again in {} seconds' MAC_ADDR = get_mac_address(mac_filename) AUTH_KEY = get_auth_key(auth_key_filename) @@ -289,15 +315,14 @@ def connect(): try: band = miband(MAC_ADDR, AUTH_KEY, debug=True) success = band.initialize() - break except BTLEDisconnectError: - print('Connection to the MIBand failed. Trying out again in 3 seconds') - time.sleep(3) - continue + print(msg.format(timeout)) + time.sleep(timeout) except KeyboardInterrupt: print("\nExit.") exit() + def start_data_pull(): global band @@ -307,7 +332,7 @@ def start_data_pull(): except BTLEDisconnectError: band.gyro_started_flag = False connect() - + if __name__ == "__main__": connect() diff --git a/miband.py b/miband.py index 3b39940..15c2cec 100644 --- a/miband.py +++ b/miband.py @@ -1,13 +1,20 @@ -import sys,os,time +import sys, os, time import logging -from bluepy.btle import Peripheral, DefaultDelegate, ADDR_TYPE_RANDOM,ADDR_TYPE_PUBLIC, BTLEException, BTLEDisconnectError - -from constants import UUIDS, AUTH_STATES, ALERT_TYPES, QUEUE_TYPES, MUSICSTATE import struct + +from bluepy.btle import ( + Peripheral, DefaultDelegate, + ADDR_TYPE_RANDOM, ADDR_TYPE_PUBLIC, + BTLEException, BTLEDisconnectError +) from datetime import datetime, timedelta from Crypto.Cipher import AES from datetime import datetime +from constants import ( + UUIDS, AUTH_STATES, ALERT_TYPES, QUEUE_TYPES, MUSICSTATE +) + try: from Queue import Queue, Empty except ImportError: @@ -96,6 +103,7 @@ class Delegate(DefaultDelegate): else: print ("Unhandled handle: " + str(hnd) + " | Data: " + str(data)) + class miband(Peripheral): _send_rnd_cmd = struct.pack('<2s', b'\x02\x00') _send_enc_key = struct.pack('<2s', b'\x03\x00')