BangleApps/apps/sleeplog
storm64 ab42b6555f sleeplog: Fix to be able to switch logging as intended
Update lib.js
 - fix error in `setEnabled(...)` when logfile=false
Update settings.js
 - use correct value for settings.logfile
2022-02-14 09:10:26 +01:00
..
ChangeLog sleeplog: Prevent removing other kill listeners #1445 2022-02-13 22:39:57 +01:00
README.md sleeplog: Prevent removing other kill listeners #1445 2022-02-13 22:39:57 +01:00
app-icon.js sleeplog: Add Sleep Log App 2022-02-11 09:29:02 +01:00
app.js sleeplog: Complete power saving mode + move all read/write log actions into lib.js 2022-02-13 20:54:26 +01:00
app.png sleeplog: Add Sleep Log App 2022-02-11 09:29:02 +01:00
boot.js sleeplog: remove logging always, minimized workaround in settings menu 2022-02-14 08:18:16 +01:00
lib.js sleeplog: Fix to be able to switch logging as intended 2022-02-14 09:10:26 +01:00
metadata.json sleeplog: Complete power saving mode + move all read/write log actions into lib.js 2022-02-13 20:54:26 +01:00
screenshot1.png sleeplog: Add Sleep Log App 2022-02-11 09:29:02 +01:00
screenshot2.png sleeplog: Add Sleep Log App 2022-02-11 09:29:02 +01:00
screenshot3.png sleeplog: Add Sleep Log App 2022-02-11 09:29:02 +01:00
settings.js sleeplog: Fix to be able to switch logging as intended 2022-02-14 09:10:26 +01:00

README.md

Sleep Log

This app logs and displays the four following states:
unknown, not worn, awake, sleeping
It derived from the SleepPhaseAlarm and uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS) and also provides a power saving mode using the built in movement calculation. The internal temperature is used to decide if the status is sleeping or not worn.

Operating Principle

  • ESS calculation
    The accelerometer polls values with 12.5Hz. On each poll the magnitude value is saved. When 13 values are collected, every 1.04 seconds, the standard deviation over this values is calculated.
    Is the calculated standard deviation lower than the "no movement" threshold (NoMoThresh) a "no movement" counter is incremented. Each time the "no movement" threshold is reached the "no movement" counter will be reset. The first time no movement is detected the actual timestamp is cached (in sleeplog.firstnomodate) for logging.
    When the "no movement" counter reaches the sleep threshold the watch is considered as resting. (The sleep threshold is calculated from the MinDuration setting, Example: sleep threshold = MinDuration * 60 / calculation interval => 10min * 60s/min / 1.04s ~= 576,9 rounded up to 577)
  • Power Saving Mode
    On power saving mode the movement value of bangle's build in health event is checked against the maximal movement threshold (MaxMove). The event is only triggered every 10 minutes which decreases the battery impact but also reduces accurracy.
  • Sleeping or Not Worn
    To check if a resting watch indicates a sleeping status, the internal temperature must be greater than the temperature threshold (TempThresh). Otherwise the watch is considered as not worn.
  • True Sleep
    The true sleep value is a simple addition of all registert sleeping periods.
  • Consecutive Sleep
    In addition the consecutive sleep value tries to predict the complete time you were asleep, even the light sleeping phases with registered movements. All periods after a sleeping period will be summarized til the first following non sleeping period that is longer then the maximal awake duration (MaxAwake). If this sum is lower than the minimal consecutive sleep duration (MinConsec) it is not considered, otherwise it will be added to the consecutive sleep value.
  • Logging
    To minimize the log size only a changed state is logged. The logged timestamp is matching the beginning of its measurement period.
    When not on power saving mode a movement is detected nearly instantaneous and the detection of a no movement period is delayed by the minimal no movement duration. To match the beginning of the measurement period a cached timestamp (sleeplog.firstnomodate) is logged.
    On power saving mode the measurement period is fixed to 10 minutes and all logged timestamps are also set back 10 minutes.

Control


  • Swipe
    Swipe left/right to display the previous/following day.
  • Touch / BTN
    Touch the screen to open the settings menu to exit or change settings.

Settings


  • BreakTod | break at time of day
    0 / 1 / ... / 10 / ... / 12
    Change time of day on wich the lower graph starts and the upper graph ends.
  • MaxAwake | maximal awake duration
    15min / 20min / ... / 60min / ... / 120min
    Adjust the maximal awake duration upon the exceeding of which aborts the consecutive sleep period.
  • MinConsec | minimal consecutive sleep duration
    15min / 20min / ... / 30min / ... / 120min
    Adjust the minimal consecutive sleep duration that will be considered for the consecutive sleep value.
  • TempThresh | temperature threshold
    20°C / 20.5°C / ... / 25°C / ... / 40°C
    The internal temperature must be greater than this threshold to log sleeping, otherwise it is not worn.
  • PowerSaving
    on / off
    En-/Disable power saving mode. Saves battery, but might decrease accurracy.
  • MaxMove | maximal movement threshold
    (only available when on power saving mode)
    50 / 51 / ... / 100 / ... / 200
    On power saving mode the watch is considered resting if this threshold is lower or equal to the movement value of bangle's health event.
  • NoMoThresh | no movement threshold
    (only available when not on power saving mode)
    0.006 / 0.007 / ... / 0.012 / ... / 0.020
    The standard deviation over the measured values needs to be lower then this threshold to count as not moving.
    The defaut threshold value worked best for my watch. A threshold value below 0.008 may get triggert by noise.
  • MinDuration | minimal no movement duration
    (only available when not on power saving mode)
    5min / 6min / ... / 10min / ... / 15min
    If no movement is detected for this duration, the watch is considered as resting.
  • Enabled
    on / off
    En-/Disable the service (all background activities). Saves the most battery, but might make this app useless.
  • Logfile
    default / off
    En-/Disable logging by setting the logfile to sleeplog.log / undefined.
    If the logfile has been customized it is displayed with custom.

Global Object and Module Functions


For easy access from the console or other apps the following parameters, values and functions are noteworthy:

>global.sleeplog
={
  enabled: true,                // bool   / service status indicator
  logfile: "sleeplog.log",      // string / used logfile
  resting: false,               // bool   / indicates if the watch is resting
  status: 2,                    // int    / actual status:
    / undefined = service stopped, 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping
  firstnomodate: 1644435877595, // number / Date.now() from first recognised no movement, not available in power saving mode
  stop: function () { ... },    // funct  / stops the service until the next load()
  start: function () { ... },   // funct  / restarts the service
  ...
 }

>require("sleeplog")
={
  setEnabled: function (enable, logfile, powersaving) { ... },
    // restarts the service with changed settings
    // * enable        / bool / new service status
    // * logfile       / bool or string
    //   - true            = enables logging to "sleeplog.log"
    //   - "some_file.log" = enables logging to "some_file.log"
    //   - false           = disables logging
    // * (powersaving) / bool / new power saving status, default: false
    // returns: true or undefined
    // - true      = service restart executed
    // - undefined = no global.sleeplog found
  readLog: function (logfile, since, until) { ... },
    // read the raw log data for a specific time period
    // * logfile / string / on no string uses logfile from global object or "sleeplog.log" 
    // * (since) / Date or number / startpoint of period, default: 0
    // * (until) / Date or number / endpoint of period, default: 1E14
    // returns: array
    // * [[number, int, string], [...], ... ] / sorting: latest first
    //   - number // timestamp in ms
    //   - int    // status: 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping
    //   - string // additional information
    // * [] = no data available or global.sleeplog not found
  writeLog: function (logfile, input) { ... },
    // append or replace log depending on input
    // * logfile / string / on no string uses logfile from global object or default 
    // * input   / array
    //   - append input if array length >1 and element[0] >9E11
    //   - replace log with input if at least one entry like above is inside another array
    // returns: true or undefined
    // - true      = changest written to storage
    // - undefined = wrong input
  getReadableLog: function (printLog, since, until, logfile) { ... }
    // read the log data as humanreadable string for a specific time period
    // * (printLog) / bool / direct print output with additional information, default: false
    // * (since)    / Date or number / see readLog(..)
    // * (until)    / Date or number / see readLog(..)
    // * (logfile)  / string / see readLog(..)
    // returns: string
    // * "{substring of ISO date} - {status} for {duration}min\n...", sorting: latest last
    // * undefined = no data available or global.sleeplog found
  restoreLog: function (logfile) { ... }
    // eliminate some errors inside a specific logfile
    // * (logfile)  / string / see readLog(..)
    // returns: int / number of changes that were made
  reinterpretTemp: function (logfile, tempthresh) { ... }
    // reinterpret worn status based on given temperature threshold
    // * (logfile)    / string / see readLog(..)
    // * (tempthresh) / float  / new temperature threshold, on default uses tempthresh from global object or 27
    // returns: int / number of changes that were made
 }

Worth Mentioning


To do list

  • Send the logged information to Gadgetbridge. (For now I have no idea how to achieve this, help is appreciated.)
  • View, down- and upload log functions via App Loader.
  • Calculate and display overall sleep statistics.
  • Option to automatically change power saving mode depending on time of day.

Requests, Bugs and Feedback

Please leave requests and bug reports by raising an issue at github.com/storm64/BangleApps or send me a mail.

Creator

Storm64 (Mail, github)

Contributors

nxdefiant (github)

Attributions

License

MIT License