Added heartrate alarm
This commit is contained in:
parent
bca9e148fa
commit
c3bc050593
|
@ -1,5 +1,6 @@
|
||||||
mac.txt
|
mac*.txt
|
||||||
auth_key.txt
|
auth_key*.txt
|
||||||
|
*.sh
|
||||||
*.pyc
|
*.pyc
|
||||||
/__pycache__
|
/__pycache__
|
||||||
*.swp
|
*.swp
|
||||||
|
@ -8,3 +9,6 @@ auth_key.txt
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
/.vscode
|
/.vscode
|
||||||
/data
|
/data
|
||||||
|
datafilter.py
|
||||||
|
crymonitor.py
|
||||||
|
|
||||||
|
|
16
bluesleep.py
16
bluesleep.py
|
@ -15,9 +15,10 @@ mac_filename = 'mac.txt'
|
||||||
maximize_graph = False
|
maximize_graph = False
|
||||||
|
|
||||||
vibration_settings = {
|
vibration_settings = {
|
||||||
'interval_minutes': 0.1,
|
'interval_minutes': 20,
|
||||||
'duration_seconds': 5,
|
'duration_seconds': 10,
|
||||||
'type': 'random'
|
'type': 'random',
|
||||||
|
'heartrate_alarm_pct': 17
|
||||||
}
|
}
|
||||||
|
|
||||||
band = None
|
band = None
|
||||||
|
@ -65,7 +66,7 @@ def average_data(tick_time):
|
||||||
sleepdata.average_raw_data(tick_time)
|
sleepdata.average_raw_data(tick_time)
|
||||||
sleepdata.last_tick_time = time.time()
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
def sleep_monitor_callback(data):
|
def sleep_monitor_callback(data):
|
||||||
tick_time = time.time()
|
tick_time = time.time()
|
||||||
|
|
||||||
|
@ -79,6 +80,9 @@ def sleep_monitor_callback(data):
|
||||||
|
|
||||||
average_data(tick_time)
|
average_data(tick_time)
|
||||||
|
|
||||||
|
vibration.heartrate_increase_pct = sleepdata.analyze_heartrate(10)
|
||||||
|
print("HR increase percent: {}".format(vibration.heartrate_increase_pct))
|
||||||
|
|
||||||
|
|
||||||
def connect():
|
def connect():
|
||||||
global band
|
global band
|
||||||
|
@ -113,7 +117,9 @@ def start_data_pull():
|
||||||
def start_vibration():
|
def start_vibration():
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
vibration.timed_vibration(vibration_settings)
|
#vibration.timed_vibration(vibration_settings)
|
||||||
|
vibration.heartrate_alarm(vibration_settings)
|
||||||
|
|
||||||
except BTLEDisconnectError:
|
except BTLEDisconnectError:
|
||||||
print("Vibration thread waiting for band reconnect...")
|
print("Vibration thread waiting for band reconnect...")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time, re, threading
|
||||||
|
from bluepy.btle import BTLEDisconnectError
|
||||||
|
from miband import miband
|
||||||
|
import sleepdata
|
||||||
|
from vibrate import Vibrate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auth_key_filename = 'auth_key.txt'
|
||||||
|
mac_filename = 'mac.txt'
|
||||||
|
|
||||||
|
maximize_graph = False
|
||||||
|
|
||||||
|
vibration_settings = {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
'interval_minutes': 0.1,
|
||||||
|
'duration_seconds': 5,
|
||||||
|
'type': 'random'
|
||||||
|
=======
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
'interval_minutes': 20,
|
||||||
|
'duration_seconds': 10,
|
||||||
|
'type': 'random',
|
||||||
|
'heartrate_alarm_pct': 17
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
||||||| constructed merge base
|
||||||
|
'interval_minutes': 45,
|
||||||
|
'duration_seconds': 5,
|
||||||
|
'type': 'random'
|
||||||
|
=======
|
||||||
|
'interval_minutes': 20,
|
||||||
|
'duration_seconds': 10,
|
||||||
|
'type': 'random',
|
||||||
|
'heartrate_alarm_pct': 17
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
}
|
||||||
|
|
||||||
|
band = None
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
class regex_patterns():
|
||||||
|
mac_regex_pattern = re.compile(r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})')
|
||||||
|
authkey_regex_pattern = re.compile(r'([0-9a-fA-F]){32}')
|
||||||
|
|
||||||
|
|
||||||
|
def get_mac_address(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
hwaddr_search = re.search(regex_patterns.mac_regex_pattern, f.read().strip())
|
||||||
|
if hwaddr_search:
|
||||||
|
MAC_ADDR = hwaddr_search[0]
|
||||||
|
else:
|
||||||
|
print ("No valid MAC address found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("MAC file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return MAC_ADDR
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_key(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
key_search = re.search(regex_patterns.authkey_regex_pattern, f.read().strip())
|
||||||
|
if key_search:
|
||||||
|
AUTH_KEY = bytes.fromhex(key_search[0])
|
||||||
|
else:
|
||||||
|
print ("No valid auth key found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("Auth key file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return AUTH_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def average_data(tick_time):
|
||||||
|
if (tick_time - sleepdata.last_tick_time) >= sleepdata.tick_seconds:
|
||||||
|
sleepdata.average_raw_data(tick_time)
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def sleep_monitor_callback(data):
|
||||||
|
tick_time = time.time()
|
||||||
|
|
||||||
|
if not sleepdata.last_tick_time:
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
if data[0] == "GYRO_RAW":
|
||||||
|
sleepdata.process_gyro_data(data[1], tick_time)
|
||||||
|
elif data[0] == "HR":
|
||||||
|
sleepdata.process_heartrate_data(data[1], tick_time)
|
||||||
|
|
||||||
|
average_data(tick_time)
|
||||||
|
|
||||||
|
vibration.heartrate_increase_pct = sleepdata.analyze_heartrate(10)
|
||||||
|
print("HR increase percent: {}".format(vibration.heartrate_increase_pct))
|
||||||
|
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
global band
|
||||||
|
success = False
|
||||||
|
timeout = 3
|
||||||
|
msg = 'Connection to the band failed. Trying again in {} seconds'
|
||||||
|
|
||||||
|
MAC_ADDR = get_mac_address(mac_filename)
|
||||||
|
AUTH_KEY = get_auth_key(auth_key_filename)
|
||||||
|
|
||||||
|
while not success:
|
||||||
|
try:
|
||||||
|
band = miband(MAC_ADDR, AUTH_KEY, debug=True)
|
||||||
|
success = band.initialize()
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print(msg.format(timeout))
|
||||||
|
time.sleep(timeout)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nExit.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def start_data_pull():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
band.start_heart_and_gyro(sensitivity=1, callback=sleep_monitor_callback)
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
band.gyro_started_flag = False
|
||||||
|
connect()
|
||||||
|
|
||||||
|
|
||||||
|
def start_vibration():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
#vibration.timed_vibration(vibration_settings)
|
||||||
|
vibration.heartrate_alarm(vibration_settings)
|
||||||
|
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print("Vibration thread waiting for band reconnect...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
connect()
|
||||||
|
vibration = Vibrate(band)
|
||||||
|
threading.Thread(target=start_data_pull).start()
|
||||||
|
threading.Thread(target=start_vibration).start()
|
||||||
|
sleepdata.init_graph(maximize=maximize_graph, graph_displaytime_mins=5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#import simpleaudio as sa
|
||||||
|
# comfort_wav = 'comfort.wav'
|
||||||
|
# wave_obj = sa.WaveObject.from_wave_file(comfort_wav)
|
||||||
|
# comfort_delay = 30
|
||||||
|
# comfort_lasttime = time.time()
|
|
@ -0,0 +1,153 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time, re, threading
|
||||||
|
from bluepy.btle import BTLEDisconnectError
|
||||||
|
from miband import miband
|
||||||
|
import sleepdata
|
||||||
|
from vibrate import Vibrate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auth_key_filename = 'auth_key.txt'
|
||||||
|
mac_filename = 'mac.txt'
|
||||||
|
|
||||||
|
maximize_graph = False
|
||||||
|
|
||||||
|
vibration_settings = {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
'interval_minutes': 0.1,
|
||||||
|
'duration_seconds': 5,
|
||||||
|
'type': 'random'
|
||||||
|
=======
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
'interval_minutes': 20,
|
||||||
|
'duration_seconds': 10,
|
||||||
|
'type': 'random',
|
||||||
|
'heartrate_alarm_pct': 17
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
}
|
||||||
|
|
||||||
|
band = None
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
class regex_patterns():
|
||||||
|
mac_regex_pattern = re.compile(r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})')
|
||||||
|
authkey_regex_pattern = re.compile(r'([0-9a-fA-F]){32}')
|
||||||
|
|
||||||
|
|
||||||
|
def get_mac_address(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
hwaddr_search = re.search(regex_patterns.mac_regex_pattern, f.read().strip())
|
||||||
|
if hwaddr_search:
|
||||||
|
MAC_ADDR = hwaddr_search[0]
|
||||||
|
else:
|
||||||
|
print ("No valid MAC address found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("MAC file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return MAC_ADDR
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_key(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
key_search = re.search(regex_patterns.authkey_regex_pattern, f.read().strip())
|
||||||
|
if key_search:
|
||||||
|
AUTH_KEY = bytes.fromhex(key_search[0])
|
||||||
|
else:
|
||||||
|
print ("No valid auth key found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("Auth key file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return AUTH_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def average_data(tick_time):
|
||||||
|
if (tick_time - sleepdata.last_tick_time) >= sleepdata.tick_seconds:
|
||||||
|
sleepdata.average_raw_data(tick_time)
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def sleep_monitor_callback(data):
|
||||||
|
tick_time = time.time()
|
||||||
|
|
||||||
|
if not sleepdata.last_tick_time:
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
if data[0] == "GYRO_RAW":
|
||||||
|
sleepdata.process_gyro_data(data[1], tick_time)
|
||||||
|
elif data[0] == "HR":
|
||||||
|
sleepdata.process_heartrate_data(data[1], tick_time)
|
||||||
|
|
||||||
|
average_data(tick_time)
|
||||||
|
|
||||||
|
vibration.heartrate_increase_pct = sleepdata.analyze_heartrate(10)
|
||||||
|
print("HR increase percent: {}".format(vibration.heartrate_increase_pct))
|
||||||
|
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
global band
|
||||||
|
success = False
|
||||||
|
timeout = 3
|
||||||
|
msg = 'Connection to the band failed. Trying again in {} seconds'
|
||||||
|
|
||||||
|
MAC_ADDR = get_mac_address(mac_filename)
|
||||||
|
AUTH_KEY = get_auth_key(auth_key_filename)
|
||||||
|
|
||||||
|
while not success:
|
||||||
|
try:
|
||||||
|
band = miband(MAC_ADDR, AUTH_KEY, debug=True)
|
||||||
|
success = band.initialize()
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print(msg.format(timeout))
|
||||||
|
time.sleep(timeout)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nExit.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def start_data_pull():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
band.start_heart_and_gyro(sensitivity=1, callback=sleep_monitor_callback)
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
band.gyro_started_flag = False
|
||||||
|
connect()
|
||||||
|
|
||||||
|
|
||||||
|
def start_vibration():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
#vibration.timed_vibration(vibration_settings)
|
||||||
|
vibration.heartrate_alarm(vibration_settings)
|
||||||
|
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print("Vibration thread waiting for band reconnect...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
connect()
|
||||||
|
vibration = Vibrate(band)
|
||||||
|
threading.Thread(target=start_data_pull).start()
|
||||||
|
threading.Thread(target=start_vibration).start()
|
||||||
|
sleepdata.init_graph(maximize=maximize_graph, graph_displaytime_mins=5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#import simpleaudio as sa
|
||||||
|
# comfort_wav = 'comfort.wav'
|
||||||
|
# wave_obj = sa.WaveObject.from_wave_file(comfort_wav)
|
||||||
|
# comfort_delay = 30
|
||||||
|
# comfort_lasttime = time.time()
|
|
@ -0,0 +1,135 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time, re, threading
|
||||||
|
from bluepy.btle import BTLEDisconnectError
|
||||||
|
from miband import miband
|
||||||
|
import sleepdata
|
||||||
|
from vibrate import Vibrate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auth_key_filename = 'auth_key.txt'
|
||||||
|
mac_filename = 'mac.txt'
|
||||||
|
|
||||||
|
maximize_graph = False
|
||||||
|
|
||||||
|
vibration_settings = {
|
||||||
|
'interval_minutes': 45,
|
||||||
|
'duration_seconds': 5,
|
||||||
|
'type': 'random'
|
||||||
|
}
|
||||||
|
|
||||||
|
band = None
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
class regex_patterns():
|
||||||
|
mac_regex_pattern = re.compile(r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})')
|
||||||
|
authkey_regex_pattern = re.compile(r'([0-9a-fA-F]){32}')
|
||||||
|
|
||||||
|
|
||||||
|
def get_mac_address(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
hwaddr_search = re.search(regex_patterns.mac_regex_pattern, f.read().strip())
|
||||||
|
if hwaddr_search:
|
||||||
|
MAC_ADDR = hwaddr_search[0]
|
||||||
|
else:
|
||||||
|
print ("No valid MAC address found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("MAC file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return MAC_ADDR
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_key(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
key_search = re.search(regex_patterns.authkey_regex_pattern, f.read().strip())
|
||||||
|
if key_search:
|
||||||
|
AUTH_KEY = bytes.fromhex(key_search[0])
|
||||||
|
else:
|
||||||
|
print ("No valid auth key found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("Auth key file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return AUTH_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def average_data(tick_time):
|
||||||
|
if (tick_time - sleepdata.last_tick_time) >= sleepdata.tick_seconds:
|
||||||
|
sleepdata.average_raw_data(tick_time)
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def sleep_monitor_callback(data):
|
||||||
|
tick_time = time.time()
|
||||||
|
|
||||||
|
if not sleepdata.last_tick_time:
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
if data[0] == "GYRO_RAW":
|
||||||
|
sleepdata.process_gyro_data(data[1], tick_time)
|
||||||
|
elif data[0] == "HR":
|
||||||
|
sleepdata.process_heartrate_data(data[1], tick_time)
|
||||||
|
|
||||||
|
average_data(tick_time)
|
||||||
|
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
global band
|
||||||
|
success = False
|
||||||
|
timeout = 3
|
||||||
|
msg = 'Connection to the band failed. Trying again in {} seconds'
|
||||||
|
|
||||||
|
MAC_ADDR = get_mac_address(mac_filename)
|
||||||
|
AUTH_KEY = get_auth_key(auth_key_filename)
|
||||||
|
|
||||||
|
while not success:
|
||||||
|
try:
|
||||||
|
band = miband(MAC_ADDR, AUTH_KEY, debug=True)
|
||||||
|
success = band.initialize()
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print(msg.format(timeout))
|
||||||
|
time.sleep(timeout)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nExit.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def start_data_pull():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
band.start_heart_and_gyro(sensitivity=1, callback=sleep_monitor_callback)
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
band.gyro_started_flag = False
|
||||||
|
connect()
|
||||||
|
|
||||||
|
|
||||||
|
def start_vibration():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
vibration.timed_vibration(vibration_settings)
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print("Vibration thread waiting for band reconnect...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
connect()
|
||||||
|
vibration = Vibrate(band)
|
||||||
|
threading.Thread(target=start_data_pull).start()
|
||||||
|
threading.Thread(target=start_vibration).start()
|
||||||
|
sleepdata.init_graph(maximize=maximize_graph, graph_displaytime_mins=5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#import simpleaudio as sa
|
||||||
|
# comfort_wav = 'comfort.wav'
|
||||||
|
# wave_obj = sa.WaveObject.from_wave_file(comfort_wav)
|
||||||
|
# comfort_delay = 30
|
||||||
|
# comfort_lasttime = time.time()
|
|
@ -0,0 +1,147 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time, re, threading
|
||||||
|
from bluepy.btle import BTLEDisconnectError
|
||||||
|
from miband import miband
|
||||||
|
import sleepdata
|
||||||
|
from vibrate import Vibrate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auth_key_filename = 'auth_key.txt'
|
||||||
|
mac_filename = 'mac.txt'
|
||||||
|
|
||||||
|
maximize_graph = False
|
||||||
|
|
||||||
|
vibration_settings = {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
'interval_minutes': 0.1,
|
||||||
|
'duration_seconds': 5,
|
||||||
|
'type': 'random'
|
||||||
|
=======
|
||||||
|
'interval_minutes': 20,
|
||||||
|
'duration_seconds': 10,
|
||||||
|
'type': 'random',
|
||||||
|
'heartrate_alarm_pct': 17
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
}
|
||||||
|
|
||||||
|
band = None
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
class regex_patterns():
|
||||||
|
mac_regex_pattern = re.compile(r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})')
|
||||||
|
authkey_regex_pattern = re.compile(r'([0-9a-fA-F]){32}')
|
||||||
|
|
||||||
|
|
||||||
|
def get_mac_address(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
hwaddr_search = re.search(regex_patterns.mac_regex_pattern, f.read().strip())
|
||||||
|
if hwaddr_search:
|
||||||
|
MAC_ADDR = hwaddr_search[0]
|
||||||
|
else:
|
||||||
|
print ("No valid MAC address found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("MAC file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return MAC_ADDR
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_key(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
key_search = re.search(regex_patterns.authkey_regex_pattern, f.read().strip())
|
||||||
|
if key_search:
|
||||||
|
AUTH_KEY = bytes.fromhex(key_search[0])
|
||||||
|
else:
|
||||||
|
print ("No valid auth key found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("Auth key file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return AUTH_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def average_data(tick_time):
|
||||||
|
if (tick_time - sleepdata.last_tick_time) >= sleepdata.tick_seconds:
|
||||||
|
sleepdata.average_raw_data(tick_time)
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def sleep_monitor_callback(data):
|
||||||
|
tick_time = time.time()
|
||||||
|
|
||||||
|
if not sleepdata.last_tick_time:
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
if data[0] == "GYRO_RAW":
|
||||||
|
sleepdata.process_gyro_data(data[1], tick_time)
|
||||||
|
elif data[0] == "HR":
|
||||||
|
sleepdata.process_heartrate_data(data[1], tick_time)
|
||||||
|
|
||||||
|
average_data(tick_time)
|
||||||
|
|
||||||
|
vibration.heartrate_increase_pct = sleepdata.analyze_heartrate(10)
|
||||||
|
print("HR increase percent: {}".format(vibration.heartrate_increase_pct))
|
||||||
|
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
global band
|
||||||
|
success = False
|
||||||
|
timeout = 3
|
||||||
|
msg = 'Connection to the band failed. Trying again in {} seconds'
|
||||||
|
|
||||||
|
MAC_ADDR = get_mac_address(mac_filename)
|
||||||
|
AUTH_KEY = get_auth_key(auth_key_filename)
|
||||||
|
|
||||||
|
while not success:
|
||||||
|
try:
|
||||||
|
band = miband(MAC_ADDR, AUTH_KEY, debug=True)
|
||||||
|
success = band.initialize()
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print(msg.format(timeout))
|
||||||
|
time.sleep(timeout)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nExit.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def start_data_pull():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
band.start_heart_and_gyro(sensitivity=1, callback=sleep_monitor_callback)
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
band.gyro_started_flag = False
|
||||||
|
connect()
|
||||||
|
|
||||||
|
|
||||||
|
def start_vibration():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
#vibration.timed_vibration(vibration_settings)
|
||||||
|
vibration.heartrate_alarm(vibration_settings)
|
||||||
|
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print("Vibration thread waiting for band reconnect...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
connect()
|
||||||
|
vibration = Vibrate(band)
|
||||||
|
threading.Thread(target=start_data_pull).start()
|
||||||
|
threading.Thread(target=start_vibration).start()
|
||||||
|
sleepdata.init_graph(maximize=maximize_graph, graph_displaytime_mins=5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#import simpleaudio as sa
|
||||||
|
# comfort_wav = 'comfort.wav'
|
||||||
|
# wave_obj = sa.WaveObject.from_wave_file(comfort_wav)
|
||||||
|
# comfort_delay = 30
|
||||||
|
# comfort_lasttime = time.time()
|
|
@ -0,0 +1,141 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time, re, threading
|
||||||
|
from bluepy.btle import BTLEDisconnectError
|
||||||
|
from miband import miband
|
||||||
|
import sleepdata
|
||||||
|
from vibrate import Vibrate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auth_key_filename = 'auth_key.txt'
|
||||||
|
mac_filename = 'mac.txt'
|
||||||
|
|
||||||
|
maximize_graph = False
|
||||||
|
|
||||||
|
vibration_settings = {
|
||||||
|
'interval_minutes': 20,
|
||||||
|
'duration_seconds': 10,
|
||||||
|
'type': 'random',
|
||||||
|
'heartrate_alarm_pct': 17
|
||||||
|
}
|
||||||
|
|
||||||
|
band = None
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
class regex_patterns():
|
||||||
|
mac_regex_pattern = re.compile(r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})')
|
||||||
|
authkey_regex_pattern = re.compile(r'([0-9a-fA-F]){32}')
|
||||||
|
|
||||||
|
|
||||||
|
def get_mac_address(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
hwaddr_search = re.search(regex_patterns.mac_regex_pattern, f.read().strip())
|
||||||
|
if hwaddr_search:
|
||||||
|
MAC_ADDR = hwaddr_search[0]
|
||||||
|
else:
|
||||||
|
print ("No valid MAC address found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("MAC file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return MAC_ADDR
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_key(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
key_search = re.search(regex_patterns.authkey_regex_pattern, f.read().strip())
|
||||||
|
if key_search:
|
||||||
|
AUTH_KEY = bytes.fromhex(key_search[0])
|
||||||
|
else:
|
||||||
|
print ("No valid auth key found in {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print ("Auth key file not found: {}".format(filename))
|
||||||
|
exit(1)
|
||||||
|
return AUTH_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def average_data(tick_time):
|
||||||
|
if (tick_time - sleepdata.last_tick_time) >= sleepdata.tick_seconds:
|
||||||
|
sleepdata.average_raw_data(tick_time)
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def sleep_monitor_callback(data):
|
||||||
|
tick_time = time.time()
|
||||||
|
|
||||||
|
if not sleepdata.last_tick_time:
|
||||||
|
sleepdata.last_tick_time = time.time()
|
||||||
|
|
||||||
|
if data[0] == "GYRO_RAW":
|
||||||
|
sleepdata.process_gyro_data(data[1], tick_time)
|
||||||
|
elif data[0] == "HR":
|
||||||
|
sleepdata.process_heartrate_data(data[1], tick_time)
|
||||||
|
|
||||||
|
average_data(tick_time)
|
||||||
|
|
||||||
|
vibration.heartrate_increase_pct = sleepdata.analyze_heartrate(10)
|
||||||
|
print("HR increase percent: {}".format(vibration.heartrate_increase_pct))
|
||||||
|
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
global band
|
||||||
|
success = False
|
||||||
|
timeout = 3
|
||||||
|
msg = 'Connection to the band failed. Trying again in {} seconds'
|
||||||
|
|
||||||
|
MAC_ADDR = get_mac_address(mac_filename)
|
||||||
|
AUTH_KEY = get_auth_key(auth_key_filename)
|
||||||
|
|
||||||
|
while not success:
|
||||||
|
try:
|
||||||
|
band = miband(MAC_ADDR, AUTH_KEY, debug=True)
|
||||||
|
success = band.initialize()
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print(msg.format(timeout))
|
||||||
|
time.sleep(timeout)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nExit.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def start_data_pull():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
band.start_heart_and_gyro(sensitivity=1, callback=sleep_monitor_callback)
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
band.gyro_started_flag = False
|
||||||
|
connect()
|
||||||
|
|
||||||
|
|
||||||
|
def start_vibration():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
#vibration.timed_vibration(vibration_settings)
|
||||||
|
vibration.heartrate_alarm(vibration_settings)
|
||||||
|
|
||||||
|
except BTLEDisconnectError:
|
||||||
|
print("Vibration thread waiting for band reconnect...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
connect()
|
||||||
|
vibration = Vibrate(band)
|
||||||
|
threading.Thread(target=start_data_pull).start()
|
||||||
|
threading.Thread(target=start_vibration).start()
|
||||||
|
sleepdata.init_graph(maximize=maximize_graph, graph_displaytime_mins=5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#import simpleaudio as sa
|
||||||
|
# comfort_wav = 'comfort.wav'
|
||||||
|
# wave_obj = sa.WaveObject.from_wave_file(comfort_wav)
|
||||||
|
# comfort_delay = 30
|
||||||
|
# comfort_lasttime = time.time()
|
15
miband.py
15
miband.py
|
@ -231,7 +231,7 @@ class miband(Peripheral):
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -301,6 +301,16 @@ class miband(Peripheral):
|
||||||
self.waitForNotifications(wait)
|
self.waitForNotifications(wait)
|
||||||
|
|
||||||
|
|
||||||
|
# def send_gyro_start(self, sensitivity):
|
||||||
|
# if not self.gyro_started_flag:
|
||||||
|
# self._log.info("Starting gyro...")
|
||||||
|
# self.write_req(self._sensor_handle, BYTEPATTERNS.start)
|
||||||
|
# self.write_req(self._steps_handle, BYTEPATTERNS.start)
|
||||||
|
# self.write_req(self._hz_handle, BYTEPATTERNS.start)
|
||||||
|
# self.gyro_started_flag = True
|
||||||
|
# self.write_cmd(self._char_sensor, BYTEPATTERNS.gyro_start(sensitivity))
|
||||||
|
# self.write_req(self._sensor_handle, BYTEPATTERNS.stop)
|
||||||
|
# self.write_cmd(self._char_sensor, b'\x02')
|
||||||
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...")
|
||||||
|
@ -309,9 +319,10 @@ class miband(Peripheral):
|
||||||
self.write_req(self._hz_handle, BYTEPATTERNS.start)
|
self.write_req(self._hz_handle, BYTEPATTERNS.start)
|
||||||
self.gyro_started_flag = True
|
self.gyro_started_flag = True
|
||||||
self.write_cmd(self._char_sensor, BYTEPATTERNS.gyro_start(sensitivity))
|
self.write_cmd(self._char_sensor, BYTEPATTERNS.gyro_start(sensitivity))
|
||||||
|
self.write_cmd(self._char_sensor, bytes.fromhex("010100000019"))
|
||||||
self.write_req(self._sensor_handle, BYTEPATTERNS.stop)
|
self.write_req(self._sensor_handle, BYTEPATTERNS.stop)
|
||||||
self.write_cmd(self._char_sensor, b'\x02')
|
self.write_cmd(self._char_sensor, b'\x02')
|
||||||
|
self.write_cmd(self._char_sensor, b'\x03')
|
||||||
|
|
||||||
def send_heart_measure_start(self):
|
def send_heart_measure_start(self):
|
||||||
self._log.info("Starting heart measure...")
|
self._log.info("Starting heart measure...")
|
||||||
|
|
17
sleepdata.py
17
sleepdata.py
|
@ -12,6 +12,7 @@ sleep_data = {
|
||||||
'periods': [2, 5, 10, 15],
|
'periods': [2, 5, 10, 15],
|
||||||
'raw_data': [],
|
'raw_data': [],
|
||||||
'averaged_data': [],
|
'averaged_data': [],
|
||||||
|
'last_hr': []
|
||||||
},
|
},
|
||||||
'movement':{
|
'movement':{
|
||||||
'value_name': 'movement',
|
'value_name': 'movement',
|
||||||
|
@ -173,6 +174,7 @@ def process_gyro_data(gyro_data, tick_time):
|
||||||
|
|
||||||
|
|
||||||
def process_heartrate_data(heartrate_data, tick_time):
|
def process_heartrate_data(heartrate_data, tick_time):
|
||||||
|
last_heartrate_count = 20
|
||||||
print("BPM: " + str(heartrate_data))
|
print("BPM: " + str(heartrate_data))
|
||||||
if heartrate_data > 0:
|
if heartrate_data > 0:
|
||||||
value_name = sleep_data['heartrate']['value_name']
|
value_name = sleep_data['heartrate']['value_name']
|
||||||
|
@ -181,6 +183,21 @@ def process_heartrate_data(heartrate_data, tick_time):
|
||||||
value_name: heartrate_data
|
value_name: heartrate_data
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
if len(sleep_data['heartrate']['last_hr']) > last_heartrate_count:
|
||||||
|
sleep_data['heartrate']['last_hr'].pop(0)
|
||||||
|
sleep_data['heartrate']['last_hr'].append(heartrate_data)
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_heartrate(hr_count):
|
||||||
|
# Finds the pct change between the lowest HR in the last $hr_count samples and the current HR
|
||||||
|
pct_heartrate_increase = 0
|
||||||
|
if len(sleep_data['heartrate']['last_hr']) >= hr_count:
|
||||||
|
last_heartrate_list = sleep_data['heartrate']['last_hr'][-hr_count:]
|
||||||
|
last_heartrate_min = min(last_heartrate_list)
|
||||||
|
current_heartrate = last_heartrate_list[-1]
|
||||||
|
pct_heartrate_increase = int((current_heartrate - last_heartrate_min)/last_heartrate_min*100)
|
||||||
|
return pct_heartrate_increase
|
||||||
|
|
||||||
|
|
||||||
def zero_to_nan(value):
|
def zero_to_nan(value):
|
||||||
if value == 0:
|
if value == 0:
|
||||||
|
|
45
vibrate.py
45
vibrate.py
|
@ -12,6 +12,8 @@ import logging
|
||||||
class Vibrate():
|
class Vibrate():
|
||||||
vibrate_band = None
|
vibrate_band = None
|
||||||
vibration_log = None
|
vibration_log = None
|
||||||
|
heartrate_increase_pct = 0
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, band):
|
def __init__(self, band):
|
||||||
self.vibrate_band = band
|
self.vibrate_band = band
|
||||||
|
@ -22,12 +24,53 @@ class Vibrate():
|
||||||
self.vibration_log = logging.getLogger(__name__)
|
self.vibration_log = logging.getLogger(__name__)
|
||||||
self.vibration_log.setLevel(vibration_log_level)
|
self.vibration_log.setLevel(vibration_log_level)
|
||||||
|
|
||||||
|
|
||||||
|
def heartrate_alarm(self, settings):
|
||||||
|
interval_minutes = settings['interval_minutes']
|
||||||
|
duration_seconds = settings['duration_seconds']
|
||||||
|
type = settings['type']
|
||||||
|
heartrate_alarm_pct = settings['heartrate_alarm_pct']
|
||||||
|
|
||||||
|
tick_time = time.time()
|
||||||
|
buzz_delay = interval_minutes * 60
|
||||||
|
buzz_timer = tick_time - buzz_delay
|
||||||
|
|
||||||
|
|
||||||
|
self.vibration_log.info("Starting heartrate alarm timer, alarming at {} percent for {} seconds with a {} minute interval".format(
|
||||||
|
heartrate_alarm_pct,
|
||||||
|
duration_seconds,
|
||||||
|
interval_minutes))
|
||||||
|
if type not in ['random', 'pattern', 'rolling', 'continuous']:
|
||||||
|
self.vibration_log.warn("Invalid or no vibration type specified: {}".format(type))
|
||||||
|
self.vibration_log.warn("Must be one of these: random, pattern, rolling, continuous")
|
||||||
|
return
|
||||||
|
|
||||||
|
while True:
|
||||||
|
elapsed_time = tick_time - buzz_timer
|
||||||
|
if elapsed_time >= buzz_delay and self.heartrate_increase_pct >= heartrate_alarm_pct:
|
||||||
|
self.vibration_log.info("Heartrate alarm triggered at {} percent, buzzing".format(self.heartrate_increase_pct))
|
||||||
|
if type == 'random':
|
||||||
|
self.vibrate_random(duration_seconds)
|
||||||
|
elif type == 'pattern':
|
||||||
|
self.vibrate_pattern(duration_seconds)
|
||||||
|
elif type == 'rolling':
|
||||||
|
self.vibrate_rolling(duration_seconds)
|
||||||
|
elif type == 'continuous':
|
||||||
|
self.vibrate_continuous(duration_seconds)
|
||||||
|
buzz_timer = tick_time
|
||||||
|
elif not elapsed_time >= buzz_delay and self.heartrate_increase_pct >= heartrate_alarm_pct:
|
||||||
|
self.vibration_log.info("Heartrate alarm threshold reached ({} percent) but timout not expired".format(self.heartrate_increase_pct))
|
||||||
|
else:
|
||||||
|
tick_time = time.time()
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
|
||||||
def timed_vibration(self, settings):
|
def timed_vibration(self, settings):
|
||||||
interval_minutes = settings['interval_minutes']
|
interval_minutes = settings['interval_minutes']
|
||||||
duration_seconds = settings['duration_seconds']
|
duration_seconds = settings['duration_seconds']
|
||||||
type = settings['type']
|
type = settings['type']
|
||||||
|
|
||||||
buzz_timer = time.time()
|
buzz_timer = time.time()
|
||||||
tick_time = time.time()
|
tick_time = time.time()
|
||||||
buzz_delay = interval_minutes * 60
|
buzz_delay = interval_minutes * 60
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue