2022-06-14 22:42:50 -04:00
screenshots Add 'external/SleepTk/' from commit '7ce31f74f19b943b43d1f65ad778f68b9820ef38' 2022-06-14 22:20:31 -04:00
.gitignore Add 'external/SleepTk/' from commit '7ce31f74f19b943b43d1f65ad778f68b9820ef38' 2022-06-14 22:20:31 -04:00
LICENSE Add 'external/SleepTk/' from commit '7ce31f74f19b943b43d1f65ad778f68b9820ef38' 2022-06-14 22:20:31 -04:00
pull_sleep_data.py Add 'external/SleepTk/' from commit '7ce31f74f19b943b43d1f65ad778f68b9820ef38' 2022-06-14 22:20:31 -04:00
README.md Add 'external/SleepTk/' from commit '7ce31f74f19b943b43d1f65ad778f68b9820ef38' 2022-06-14 22:20:31 -04:00
rm_sleep_data.py Add 'external/SleepTk/' from commit '7ce31f74f19b943b43d1f65ad778f68b9820ef38' 2022-06-14 22:20:31 -04:00
SleepTk.py SleepTk: Adapt for mainline accel_xyz() impl 2022-06-14 22:42:50 -04:00

SleepTk : a sleep tracker and smart alarm for wasp-os

Goal: privacy friendly sleep tracker with smart alarm for the pinetime smartwatch by Pine64, on python, to run on wasp-os.


  • Sleep tracking: logs your body movement during the night, infers your sleep cycle and write it all down in a .csv file.
  • Heart tracking: tracks your heart rate throughout the night.
  • Flexible: does not make too many assumption regarding time to fall asleep, sleep cycle duration etc. SleepTk tries various data to see what fits best for your profile. If you still want to customize things, all the hardcoded and commented settings are easily accessible at the top of the file.
  • Privacy friendly: your data is not sent to anyone, it is stored and analyzed directly on the watch (but you can still download it if needed).
  • Completely open source
  • Best alarm time suggestion: suggests wake up time according to average sleep cycles length.
  • Gradual wake: vibrates the watch a tiny bit a few times before the alarm to lift you gently back to consciousness.
  • Easy to snooze but hard to stop You have to swipe several times to make it stop, but can snooze easily.
  • Smart alarm clock (alpha): adaptative alarm that wakes you at the best time of your sleep cycle (up to 40 minutes before the set time) to make sure you wake up feeling refreshed.
  • Insomnia insights: if you turn on the screen during the night, SleepTk will tell you how long you slept and in what part of the sleep cycle you are supposed to be.


How to install:

(for now you need my slightly forked wasp-os that allows to use accelerometer data)

  • download the latest forked wasp-os, or at least implement the relevant commits (624d1c675343cd4d95caea5ac4cec32766b3119b).
  • download the latest SleepTk.py and put it in wasp-os/wasp/apps/SleepTk.py
  • open SleepTk.py to edit the user settings (at the top of the file)
  • compile wasp-os: make submodules && make softdevice && make BOARD=pinetime all && echo "SUCCESS"
  • upload it to your pinetime: ./tools/ota-dfu/dfu.py -z build-pinetime/micropython.zip -a XX:XX:XX:XX:XX:XX --legacy
  • reboot the watch and enjoy SleepTk
  • optional: download your latest sleep data using the script pull_sleep_data.py
  • optional: delete all the sleep data present in your watch using the script rm_sleep_data.py

Note to reader:

  • Note that the watch assumes an average of 14 minutes to fall asleep. You have to take this into account if you feel super sleepy at the time of setting the alarm.
  • If you're interested or have any kind of things to say about this, please open an issue and tell me all about it :)
  • Status as of end of May 2022: UI (done), regular alarm (done), heart tracking (done but might be a bit dodgy), smart alarm (mostly done but untested)
  • you can download your sleep data file using the file pull_sleep_data. A suggested workflow to load it into pandas can be found at the bottom of the page.
  • the notifications are set to "silent" during the tracking session and are restored to the previously used level when the alarm is ringing
  • by default, the Bluetooth is turned off when tracking to save battery. Wasp OS doesn't allow to turn it back on without restarting the watch.
  • it seems the simulator is having a rough time with daylight saving mode or time management. I personnaly have a 1h offset between sleep estimation on the simulator compared to the pinetime, don't worry.


settings settings2 tracking night example



  • greatly simplify the code by simply adding a large tick function every second instead of managing tons of counters.

  • investigate adding a simple feature to wake you up only after a certain movement threshold was passed

  • add a "nap tracking" mode that records sleep tracking with more precision

    • add a "power nap" mode that wakes you as soon as there has been no movement for 5 minutes OR (like steelball) when your heart rate drops
  • implement a simple Neural Network or decision tree to compute best wake up time, using one found in the scientific literature.

  • implement downsampling to 15 minutes precision to compute best wake up time

  • log smart alarm data to file? log user rating of how well he/she felt fresh at wake?

  • ability to send in real time to Bluetooth device the current sleep stage you're probably in. For use in Targeted Memory Reactivation?

to read :

Pandas integration:

Commands the author uses to take a look a the data using pandas:

fname = "./logs/sleep/YOUR_TIME.csv"

import pandas as pd
import plotly.express as plt

#df = pd.read_csv(fname, names=["motion", "elapsed", "x_avg", "y_avg", "z_avg", "battery"])
df = pd.read_csv(fname, names=["motion", "elapsed", "heart_rate"])
start_time = int(fname.split("/")[-1].split(".csv")[0])

df["time"] = pd.to_datetime(df["elapsed"]+start_time, unit='s')
df["human_time"] = df["time"].dt.time

month = df.iloc[0]["time"].month_name()
dayname = str(df.iloc[0]["time"].day_name())
daynumber = str(df.iloc[0]["time"].day)
if daynumber == 1:
    daynumber = str(daynumber) + "st"
elif daynumber.endswith("2"):
    daynumber = str(daynumber) + "nd"
elif daynumber.endswith("3"):
    daynumber = str(daynumber) + "rd"
    daynumber = str(daynumber) + "th"
date = f"{month} {daynumber} ({dayname})"

fig = px.line(df,
              labels={"motion": "Body motion", "time":"Time"},
              title=f"Night starting on {date}")

df_HR = df.set_index("human_time")["heart_rate"]
df_HR = df_HR[~df_HR.isna()]

Now, to play around with the signal processing function:

import array
data = array.array("f", df["motion"])
data = data[:15] # remove the last few data points as the signal
# processor does not yet have access to them when finding best wake up time


from matplotlib import pyplot as plt
for i in x_maximas: