commit
c7a09e3474
|
@ -10,8 +10,10 @@ import sleepdata, vibrate
|
||||||
auth_key_filename = 'auth_key.txt'
|
auth_key_filename = 'auth_key.txt'
|
||||||
mac_filename = 'mac.txt'
|
mac_filename = 'mac.txt'
|
||||||
|
|
||||||
|
maximize_graph = False
|
||||||
|
|
||||||
vibration_settings = {
|
vibration_settings = {
|
||||||
'interval_minutes': 0.2,
|
'interval_minutes': 45,
|
||||||
'duration_seconds': 5,
|
'duration_seconds': 5,
|
||||||
'type': 'random'
|
'type': 'random'
|
||||||
}
|
}
|
||||||
|
@ -68,7 +70,7 @@ def sleep_monitor_callback(data):
|
||||||
if not sleepdata.last_tick_time:
|
if not sleepdata.last_tick_time:
|
||||||
sleepdata.last_tick_time = time.time()
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
if data[0] == "GYRO":
|
if data[0] == "GYRO_RAW":
|
||||||
sleepdata.process_gyro_data(data[1], tick_time)
|
sleepdata.process_gyro_data(data[1], tick_time)
|
||||||
elif data[0] == "HR":
|
elif data[0] == "HR":
|
||||||
sleepdata.process_heartrate_data(data[1], tick_time)
|
sleepdata.process_heartrate_data(data[1], tick_time)
|
||||||
|
@ -120,7 +122,7 @@ if __name__ == "__main__":
|
||||||
connect()
|
connect()
|
||||||
threading.Thread(target=start_data_pull).start()
|
threading.Thread(target=start_data_pull).start()
|
||||||
threading.Thread(target=start_vibration).start()
|
threading.Thread(target=start_vibration).start()
|
||||||
#sleepdata.init_graph()
|
sleepdata.init_graph(maximize=maximize_graph, graph_displaytime_mins=5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -88,3 +88,5 @@ class QUEUE_TYPES(object):
|
||||||
HEART = 'heart'
|
HEART = 'heart'
|
||||||
RAW_ACCEL = 'raw_accel'
|
RAW_ACCEL = 'raw_accel'
|
||||||
RAW_HEART = 'raw_heart'
|
RAW_HEART = 'raw_heart'
|
||||||
|
RAW_GYRO = 'raw_gyro'
|
||||||
|
AVG_GYRO = 'avg_gyro'
|
79
miband.py
79
miband.py
|
@ -1,7 +1,6 @@
|
||||||
import sys, os, time
|
import sys, os, time
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
import binascii
|
|
||||||
|
|
||||||
from bytepatterns import miband4 as bytepattern
|
from bytepatterns import miband4 as bytepattern
|
||||||
|
|
||||||
|
@ -50,14 +49,26 @@ class Delegate(DefaultDelegate):
|
||||||
self.device.queue.put((QUEUE_TYPES.RAW_ACCEL, data))
|
self.device.queue.put((QUEUE_TYPES.RAW_ACCEL, data))
|
||||||
elif len(data) == 16:
|
elif len(data) == 16:
|
||||||
self.device.queue.put((QUEUE_TYPES.RAW_HEART, data))
|
self.device.queue.put((QUEUE_TYPES.RAW_HEART, data))
|
||||||
|
else:
|
||||||
|
print("Unhandled data on handle 0x38: {}".format(data))
|
||||||
elif hnd == self.device._char_hz.getHandle():
|
elif hnd == self.device._char_hz.getHandle():
|
||||||
if len(data) == 20 and struct.unpack('b', data[0:1])[0] == 1:
|
if len(data) == 20 and struct.unpack('b', data[0:1])[0] == 1:
|
||||||
self.device.queue.put((QUEUE_TYPES.RAW_ACCEL, data))
|
self.device.queue.put((QUEUE_TYPES.RAW_ACCEL, data))
|
||||||
|
elif len(data) == 11:
|
||||||
|
#print("Unknown data: {}".format(bytes.hex(data, " ")))
|
||||||
|
#print(struct.unpack('BBBBBBBBBB', data[1:]))
|
||||||
|
# Seems to be a counter of the time the gyro is enabled.
|
||||||
|
#print(struct.unpack(">x2L", data))
|
||||||
|
#print(struct.unpack("<x5H", data))
|
||||||
|
...
|
||||||
|
elif len(data) == 8:
|
||||||
|
self.device.queue.put((QUEUE_TYPES.AVG_GYRO, data))
|
||||||
|
else:
|
||||||
|
#print("Unknown sensor data ({}): {}".format(len(data), bytes.hex(data, " ")))
|
||||||
|
...
|
||||||
else:
|
else:
|
||||||
print ("Unhandled handle: " + str(hnd) + " | Data: " + str(data))
|
#print ("Unhandled handle: " + str(hnd) + " | Data: " + bytes.hex(data, " "))
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class miband(Peripheral):
|
class miband(Peripheral):
|
||||||
|
@ -80,6 +91,7 @@ class miband(Peripheral):
|
||||||
self.heart_measure_callback = None
|
self.heart_measure_callback = None
|
||||||
self.heart_raw_callback = None
|
self.heart_raw_callback = None
|
||||||
self.gyro_raw_callback = None
|
self.gyro_raw_callback = None
|
||||||
|
self.gyro_avg_callback = None
|
||||||
self.auth_key = key
|
self.auth_key = key
|
||||||
self.queue = Queue()
|
self.queue = Queue()
|
||||||
self.write_queue = Queue()
|
self.write_queue = Queue()
|
||||||
|
@ -120,6 +132,7 @@ class miband(Peripheral):
|
||||||
self.waitForNotifications(0.1)
|
self.waitForNotifications(0.1)
|
||||||
self.setDelegate( Delegate(self) )
|
self.setDelegate( Delegate(self) )
|
||||||
|
|
||||||
|
|
||||||
def _auth_notif(self, enabled):
|
def _auth_notif(self, enabled):
|
||||||
if enabled:
|
if enabled:
|
||||||
self._log.info("Enabling Auth Service notifications status...")
|
self._log.info("Enabling Auth Service notifications status...")
|
||||||
|
@ -130,6 +143,7 @@ class miband(Peripheral):
|
||||||
else:
|
else:
|
||||||
self._log.error("Something went wrong while changing the Auth Service notifications status...")
|
self._log.error("Something went wrong while changing the Auth Service notifications status...")
|
||||||
|
|
||||||
|
|
||||||
def _auth_previews_data_notif(self, enabled):
|
def _auth_previews_data_notif(self, enabled):
|
||||||
if enabled:
|
if enabled:
|
||||||
self._log.info("Enabling Fetch Char notifications status...")
|
self._log.info("Enabling Fetch Char notifications status...")
|
||||||
|
@ -144,6 +158,7 @@ class miband(Peripheral):
|
||||||
self._desc_activity.write(bytepattern.stop, True)
|
self._desc_activity.write(bytepattern.stop, True)
|
||||||
self.activity_notif_enabled = False
|
self.activity_notif_enabled = False
|
||||||
|
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self._req_rdn()
|
self._req_rdn()
|
||||||
while True:
|
while True:
|
||||||
|
@ -158,11 +173,13 @@ class miband(Peripheral):
|
||||||
self._log.error(self.state)
|
self._log.error(self.state)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _req_rdn(self):
|
def _req_rdn(self):
|
||||||
self._log.info("Requesting random number...")
|
self._log.info("Requesting random number...")
|
||||||
self._char_auth.write(bytepattern.request_random_number)
|
self._char_auth.write(bytepattern.request_random_number)
|
||||||
self.waitForNotifications(self.timeout)
|
self.waitForNotifications(self.timeout)
|
||||||
|
|
||||||
|
|
||||||
def _send_enc_rdn(self, data):
|
def _send_enc_rdn(self, data):
|
||||||
self._log.info("Sending encrypted random number")
|
self._log.info("Sending encrypted random number")
|
||||||
cmd = bytepattern.auth_key_prefix + self._encrypt(data)
|
cmd = bytepattern.auth_key_prefix + self._encrypt(data)
|
||||||
|
@ -170,10 +187,12 @@ class miband(Peripheral):
|
||||||
self._char_auth.write(send_cmd)
|
self._char_auth.write(send_cmd)
|
||||||
self.waitForNotifications(self.timeout)
|
self.waitForNotifications(self.timeout)
|
||||||
|
|
||||||
|
|
||||||
def _encrypt(self, message):
|
def _encrypt(self, message):
|
||||||
aes = AES.new(self.auth_key, AES.MODE_ECB)
|
aes = AES.new(self.auth_key, AES.MODE_ECB)
|
||||||
return aes.encrypt(message)
|
return aes.encrypt(message)
|
||||||
|
|
||||||
|
|
||||||
def _get_from_queue(self, _type):
|
def _get_from_queue(self, _type):
|
||||||
try:
|
try:
|
||||||
res = self.queue.get(False)
|
res = self.queue.get(False)
|
||||||
|
@ -184,30 +203,51 @@ class miband(Peripheral):
|
||||||
return None
|
return None
|
||||||
return res[1]
|
return res[1]
|
||||||
|
|
||||||
|
|
||||||
def _parse_queue(self):
|
def _parse_queue(self):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
res = self.queue.get(False)
|
queue_data = self.queue.get(False)
|
||||||
_type = res[0]
|
_type = queue_data[0]
|
||||||
if self.heart_measure_callback and _type == QUEUE_TYPES.HEART:
|
if self.heart_measure_callback and _type == QUEUE_TYPES.HEART:
|
||||||
self.heart_measure_callback(self._parse_heart_measure(res[1]))
|
self.heart_measure_callback(self._parse_heart_measure(queue_data[1]))
|
||||||
elif self.gyro_raw_callback and _type == QUEUE_TYPES.RAW_ACCEL:
|
elif self.gyro_raw_callback and _type == QUEUE_TYPES.RAW_ACCEL:
|
||||||
self.gyro_raw_callback(self._parse_raw_gyro(res[1]))
|
self.gyro_raw_callback(self._parse_raw_gyro(queue_data[1]))
|
||||||
|
elif self.gyro_avg_callback and _type == QUEUE_TYPES.AVG_GYRO:
|
||||||
|
self.gyro_avg_callback(self._parse_avg_gyro(queue_data[1]))
|
||||||
except Empty:
|
except Empty:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def _parse_avg_gyro(self, bytes):
|
||||||
|
gyro_avg_data = struct.unpack('<b3h', bytes[1:])
|
||||||
|
gyro_dict = {
|
||||||
|
'gyro_time': gyro_avg_data[0],
|
||||||
|
'gyro_avg_x': gyro_avg_data[1],
|
||||||
|
'gyro_avg_y': gyro_avg_data[2],
|
||||||
|
'gyro_avg_z': gyro_avg_data[3]
|
||||||
|
}
|
||||||
|
return_tuple = ['GYRO_AVG', gyro_dict]
|
||||||
|
return return_tuple
|
||||||
|
|
||||||
|
|
||||||
def _parse_heart_measure(self, bytes):
|
def _parse_heart_measure(self, bytes):
|
||||||
res = struct.unpack('bb', bytes)[1]
|
res = struct.unpack('bb', bytes)[1]
|
||||||
return_tuple = ["HR", res]
|
return_tuple = ["HR", res]
|
||||||
print("BPM: {}".format(res))
|
print("BPM: {}".format(res))
|
||||||
return return_tuple
|
return return_tuple
|
||||||
|
|
||||||
|
|
||||||
def _parse_raw_gyro(self, bytes):
|
def _parse_raw_gyro(self, bytes):
|
||||||
res = []
|
gyro_raw_data_list = []
|
||||||
for i in range(3):
|
for i in range(2, 20, 6):
|
||||||
g = struct.unpack('hhh', bytes[2 + i * 6:8 + i * 6])
|
gyro_raw_data = struct.unpack("3h", bytes[i:(i+6)])
|
||||||
res.append({'x': g[0], 'y': g[1], 'z': g[2]})
|
gyro_dict = {
|
||||||
return_tuple = ["GYRO", res]
|
'gyro_raw_x': gyro_raw_data[0],
|
||||||
|
'gyro_raw_y': gyro_raw_data[1],
|
||||||
|
'gyro_raw_z': gyro_raw_data[2]
|
||||||
|
}
|
||||||
|
gyro_raw_data_list.append(gyro_dict)
|
||||||
|
return_tuple = ["GYRO_RAW", gyro_raw_data_list]
|
||||||
return return_tuple
|
return return_tuple
|
||||||
|
|
||||||
|
|
||||||
|
@ -224,6 +264,7 @@ class miband(Peripheral):
|
||||||
except Empty:
|
except Empty:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def vibrate(self, value):
|
def vibrate(self, value):
|
||||||
if value == 255 or value == 0:
|
if value == 255 or value == 0:
|
||||||
# '255' means 'continuous vibration'
|
# '255' means 'continuous vibration'
|
||||||
|
@ -242,12 +283,14 @@ class miband(Peripheral):
|
||||||
self.write_cmd(self._char_alert, bytepattern.vibration(value), queued=True)
|
self.write_cmd(self._char_alert, bytepattern.vibration(value), queued=True)
|
||||||
time.sleep(vibration_duration)
|
time.sleep(vibration_duration)
|
||||||
|
|
||||||
|
|
||||||
def write_cmd(self, characteristic, data, response=False, queued=False):
|
def write_cmd(self, characteristic, data, response=False, queued=False):
|
||||||
if queued:
|
if queued:
|
||||||
self.write_queue.put(['write_cmd', [characteristic, data, response]])
|
self.write_queue.put(['write_cmd', [characteristic, data, response]])
|
||||||
else:
|
else:
|
||||||
characteristic.write(data, withResponse=response)
|
characteristic.write(data, withResponse=response)
|
||||||
|
|
||||||
|
|
||||||
def write_req(self, handle, data, response=True, queued=False):
|
def write_req(self, handle, data, response=True, queued=False):
|
||||||
if queued:
|
if queued:
|
||||||
self.write_queue.put(['write_req', [handle, data, response]])
|
self.write_queue.put(['write_req', [handle, data, response]])
|
||||||
|
@ -259,6 +302,7 @@ class miband(Peripheral):
|
||||||
self.process_write_queue()
|
self.process_write_queue()
|
||||||
self.waitForNotifications(wait)
|
self.waitForNotifications(wait)
|
||||||
|
|
||||||
|
|
||||||
def send_gyro_start(self, sensitivity):
|
def send_gyro_start(self, sensitivity):
|
||||||
if not self.gyro_started_flag:
|
if not self.gyro_started_flag:
|
||||||
self._log.info("Starting gyro...")
|
self._log.info("Starting gyro...")
|
||||||
|
@ -266,10 +310,12 @@ class miband(Peripheral):
|
||||||
self.write_req(self._steps_handle, bytepattern.start)
|
self.write_req(self._steps_handle, bytepattern.start)
|
||||||
self.write_req(self._hz_handle, bytepattern.start)
|
self.write_req(self._hz_handle, bytepattern.start)
|
||||||
self.gyro_started_flag = True
|
self.gyro_started_flag = True
|
||||||
self.write_cmd(self._char_sensor, bytepattern.gyro_start(sensitivity))
|
#self.write_cmd(self._char_sensor, bytepattern.gyro_start(sensitivity))
|
||||||
|
self.write_cmd(self._char_sensor, bytes.fromhex('010119'))
|
||||||
self.write_req(self._sensor_handle, bytepattern.stop)
|
self.write_req(self._sensor_handle, bytepattern.stop)
|
||||||
self.write_cmd(self._char_sensor, b'\x02')
|
self.write_cmd(self._char_sensor, b'\x02')
|
||||||
|
|
||||||
|
|
||||||
def send_heart_measure_start(self):
|
def send_heart_measure_start(self):
|
||||||
self._log.info("Starting heart measure...")
|
self._log.info("Starting heart measure...")
|
||||||
self.write_cmd(self._char_heart_ctrl, bytepattern.stop_heart_measure_manual, response=True)
|
self.write_cmd(self._char_heart_ctrl, bytepattern.stop_heart_measure_manual, response=True)
|
||||||
|
@ -281,12 +327,13 @@ class miband(Peripheral):
|
||||||
def send_heart_measure_keepalive(self):
|
def send_heart_measure_keepalive(self):
|
||||||
self.write_cmd(self._char_heart_ctrl, bytepattern.heart_measure_keepalive, response=True)
|
self.write_cmd(self._char_heart_ctrl, bytepattern.heart_measure_keepalive, response=True)
|
||||||
|
|
||||||
|
|
||||||
def start_heart_and_gyro(self, sensitivity, callback):
|
def start_heart_and_gyro(self, sensitivity, callback):
|
||||||
self.heart_measure_callback = callback
|
self.heart_measure_callback = callback
|
||||||
self.gyro_raw_callback = callback
|
self.gyro_raw_callback = callback
|
||||||
|
|
||||||
self.send_gyro_start(sensitivity)
|
self.send_gyro_start(sensitivity)
|
||||||
self.send_heart_measure_start()
|
#self.send_heart_measure_start()
|
||||||
|
|
||||||
heartbeat_time = time.time()
|
heartbeat_time = time.time()
|
||||||
while True:
|
while True:
|
||||||
|
|
61
sleepdata.py
61
sleepdata.py
|
@ -1,6 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os import path
|
from os import path
|
||||||
import csv
|
import csv, time
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.animation as animation
|
import matplotlib.animation as animation
|
||||||
|
@ -17,14 +17,10 @@ sleep_data = {
|
||||||
'value_name': 'movement',
|
'value_name': 'movement',
|
||||||
'periods': [10, 30, 60],
|
'periods': [10, 30, 60],
|
||||||
'raw_data': [],
|
'raw_data': [],
|
||||||
'averaged_data': [],
|
'averaged_data': []
|
||||||
'workspace': {
|
|
||||||
'gyro_last_x' : 0,
|
|
||||||
'gyro_last_y' : 0,
|
|
||||||
'gyro_last_z' : 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tick_seconds = 0.5
|
tick_seconds = 0.5
|
||||||
last_tick_time = None
|
last_tick_time = None
|
||||||
|
@ -35,9 +31,13 @@ csv_filename_format = '{}_{}.csv'
|
||||||
|
|
||||||
plt.style.use('dark_background')
|
plt.style.use('dark_background')
|
||||||
graph_figure = plt.figure()
|
graph_figure = plt.figure()
|
||||||
|
graph_figure.canvas.set_window_title('blesleep')
|
||||||
|
|
||||||
graph_axes = graph_figure.add_subplot(1, 1, 1)
|
graph_axes = graph_figure.add_subplot(1, 1, 1)
|
||||||
graph_data = {}
|
graph_data = {}
|
||||||
|
|
||||||
|
graph_displaytime_minutes = None
|
||||||
|
|
||||||
last_heartrate = 0
|
last_heartrate = 0
|
||||||
|
|
||||||
class Average_Gyro_Data():
|
class Average_Gyro_Data():
|
||||||
|
@ -50,12 +50,12 @@ class Average_Gyro_Data():
|
||||||
def process(self, gyro_data):
|
def process(self, gyro_data):
|
||||||
gyro_movement = 0
|
gyro_movement = 0
|
||||||
for gyro_datum in gyro_data:
|
for gyro_datum in gyro_data:
|
||||||
gyro_delta_x = abs(gyro_datum['x'] - self.gyro_last_x)
|
gyro_delta_x = abs(gyro_datum['gyro_raw_x'] - self.gyro_last_x)
|
||||||
self.gyro_last_x = gyro_datum['x']
|
self.gyro_last_x = gyro_datum['gyro_raw_x']
|
||||||
gyro_delta_y = abs(gyro_datum['y'] - self.gyro_last_y)
|
gyro_delta_y = abs(gyro_datum['gyro_raw_y'] - self.gyro_last_y)
|
||||||
self.gyro_last_y = gyro_datum['y']
|
self.gyro_last_y = gyro_datum['gyro_raw_y']
|
||||||
gyro_delta_z = abs(gyro_datum['z'] - self.gyro_last_z)
|
gyro_delta_z = abs(gyro_datum['gyro_raw_z'] - self.gyro_last_z)
|
||||||
self.gyro_last_z = gyro_datum['z']
|
self.gyro_last_z = gyro_datum['gyro_raw_z']
|
||||||
gyro_delta_sum = gyro_delta_x + gyro_delta_y + gyro_delta_z
|
gyro_delta_sum = gyro_delta_x + gyro_delta_y + gyro_delta_z
|
||||||
gyro_movement += gyro_delta_sum
|
gyro_movement += gyro_delta_sum
|
||||||
return gyro_movement
|
return gyro_movement
|
||||||
|
@ -107,6 +107,22 @@ def flush_old_raw_data(tick_time):
|
||||||
write_csv(old_raw_data, 'raw')
|
write_csv(old_raw_data, 'raw')
|
||||||
|
|
||||||
|
|
||||||
|
def flush_old_graph_data(graph_displaytime_minutes):
|
||||||
|
graph_displaytime_seconds = graph_displaytime_minutes * 60
|
||||||
|
tick_time = time.time()
|
||||||
|
for data_type in sleep_data:
|
||||||
|
s_data = sleep_data[data_type]
|
||||||
|
cleaned_graph_data = []
|
||||||
|
old_graph_data = []
|
||||||
|
for avg_datum in s_data['averaged_data']:
|
||||||
|
datum_age = tick_time - datetime.timestamp(avg_datum['time'])
|
||||||
|
if datum_age < graph_displaytime_seconds:
|
||||||
|
cleaned_graph_data.append(avg_datum)
|
||||||
|
else:
|
||||||
|
old_graph_data.append(avg_datum)
|
||||||
|
s_data['averaged_data'] = cleaned_graph_data
|
||||||
|
|
||||||
|
|
||||||
def average_raw_data(tick_time):
|
def average_raw_data(tick_time):
|
||||||
global last_heartrate
|
global last_heartrate
|
||||||
timestamp = datetime.fromtimestamp(tick_time)
|
timestamp = datetime.fromtimestamp(tick_time)
|
||||||
|
@ -142,7 +158,6 @@ def average_raw_data(tick_time):
|
||||||
csv_out[csv_header_field_name] = zero_to_nan(period_data_average)
|
csv_out[csv_header_field_name] = zero_to_nan(period_data_average)
|
||||||
|
|
||||||
s_data['averaged_data'].append(period_averages_dict)
|
s_data['averaged_data'].append(period_averages_dict)
|
||||||
|
|
||||||
write_csv([csv_out], 'avg')
|
write_csv([csv_out], 'avg')
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,12 +190,13 @@ def zero_to_nan(value):
|
||||||
|
|
||||||
def update_graph_data():
|
def update_graph_data():
|
||||||
for data_type in sleep_data:
|
for data_type in sleep_data:
|
||||||
s_data = sleep_data[data_type] # Re-referenced to shorten name
|
s_data = sleep_data[data_type]
|
||||||
|
|
||||||
avg_data = s_data['averaged_data']
|
avg_data = s_data['averaged_data']
|
||||||
|
|
||||||
if len(avg_data) > 1:
|
if len(avg_data) > 1:
|
||||||
|
|
||||||
g_data = graph_data[data_type] # Re-referenced to short name
|
g_data = graph_data[data_type]
|
||||||
data_periods = s_data['periods']
|
data_periods = s_data['periods']
|
||||||
|
|
||||||
starting_index = max([(len(g_data['time']) - 1), 0])
|
starting_index = max([(len(g_data['time']) - 1), 0])
|
||||||
|
@ -207,13 +223,11 @@ def init_graph_data():
|
||||||
|
|
||||||
|
|
||||||
def graph_animation(i):
|
def graph_animation(i):
|
||||||
global graph_axes
|
|
||||||
global graph_data
|
|
||||||
plotflag = False
|
|
||||||
|
|
||||||
if len(graph_data) == 0:
|
if len(graph_data) == 0:
|
||||||
init_graph_data()
|
init_graph_data()
|
||||||
|
|
||||||
|
flush_old_graph_data(graph_displaytime_minutes)
|
||||||
update_graph_data()
|
update_graph_data()
|
||||||
|
|
||||||
for data_type in graph_data:
|
for data_type in graph_data:
|
||||||
|
@ -221,6 +235,7 @@ def graph_animation(i):
|
||||||
graph_axes.clear()
|
graph_axes.clear()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
plotflag = False
|
||||||
for data_type in sleep_data:
|
for data_type in sleep_data:
|
||||||
s_data = sleep_data[data_type]
|
s_data = sleep_data[data_type]
|
||||||
g_data = graph_data[data_type]
|
g_data = graph_data[data_type]
|
||||||
|
@ -237,7 +252,13 @@ def graph_animation(i):
|
||||||
plt.legend()
|
plt.legend()
|
||||||
|
|
||||||
|
|
||||||
def init_graph():
|
def init_graph(graph_displaytime_mins=60, maximize=False):
|
||||||
|
global graph_displaytime_minutes
|
||||||
|
graph_displaytime_minutes = graph_displaytime_mins
|
||||||
|
if maximize:
|
||||||
|
figure_manager = plt.get_current_fig_manager()
|
||||||
|
figure_manager.full_screen_toggle()
|
||||||
|
|
||||||
ani = animation.FuncAnimation(graph_figure, graph_animation, interval=1000)
|
ani = animation.FuncAnimation(graph_figure, graph_animation, interval=1000)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue