From 74787f7b5ef5f05cad87d7efc268e944878469e3 Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 12 Sep 2022 23:15:39 +0200 Subject: [PATCH 01/15] [sleeplog] Add option for onChange functions Execute functions in `sleeplog.onChange[fn({timestamp, status, consecutive})]` on a status change --- apps/sleeplog/ChangeLog | 1 + apps/sleeplog/boot.js | 23 +++++++++++++++++++++-- apps/sleeplog/metadata.json | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index a0698d471..bff278e60 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -7,3 +7,4 @@ 0.10: Complete rework off this app! 0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats 0.11: Prevent module not found error +0.12: Execute functions in `sleeplog.onChange[fn({timestamp, status, consecutive})]` on a status change \ No newline at end of file diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index c1f8a2d2d..f4d9dff46 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -258,6 +258,18 @@ if (sleeplog.conf.enabled) { // check if the status has changed if (data.status !== this.status || data.consecutive !== this.consecutive) { + // check for onChange functions + if ((this.onChange || []).length) { + this.onChange.forEach(fn => { + // setup timeouts to start onChange functions if fn is a function + if (typeof fn === "function") setTimeout(fn, 100, { + timestamp: new Date(data.timestamp), + status: data.status === this.status ? undefined : data.status, + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive + }); + }); + } + // append status this.appendStatus(data.timestamp, data.status, data.consecutive); @@ -268,7 +280,7 @@ if (sleeplog.conf.enabled) { // reset saveUpToDate status delete this.info.saveUpToDate; } - + // send status to gadgetbridge var gb_kinds = "unknown,not_worn,activity,light_sleep,deep_sleep"; Bluetooth.println(JSON.stringify({ @@ -319,7 +331,14 @@ if (sleeplog.conf.enabled) { } // return stats cache return this.statsCache; - } + }, + + // define array for functions to execute after a status change (changes had hapened 10min earlier) + // changed values will be passed as object with the following properties: + // timestamp: as date object, + // status: if changed 0-4 else undefined, + // consecutive: if changed 0-2 else undefined + onChange: [] }, sleeplog); // initial starting diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index f6ce661e8..353476446 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -2,7 +2,7 @@ "id":"sleeplog", "name":"Sleep Log", "shortName": "SleepLog", - "version": "0.11", + "version": "0.12", "description": "Log and view your sleeping habits. This app is using the built in movement calculation.", "icon": "app.png", "type": "app", From 99663e4583ae34cacffe0e4d72d749f4e1c9741a Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 00:33:51 +0100 Subject: [PATCH 02/15] [sleeplog] onChange: + previous values and readme --- apps/sleeplog/README.md | 25 +++++++++++++++++++++++-- apps/sleeplog/boot.js | 10 ++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 45aeb316b..7f122add3 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -87,7 +87,7 @@ Available through the App Loader when your watch is connected. Deletes the logfile from the watch. __Please backup your data first!__ --- -### Timestamps and files +### Timestamps and Files --- 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: @@ -110,8 +110,10 @@ Available through the App Loader when your watch is connected. --- -### Access statistics (developer information) +### Developer Information --- + +#### Access statistics - Last Asleep Time [Date]: `Date(sleeplog.awakeSince)` - Last Awake Duration [ms]: @@ -149,6 +151,25 @@ Available through the App Loader when your watch is connected. require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); ``` +#### Add functions triggered by status changes +With the following code it is possible to add functions that will be called on status changes. +``` +// first ensure that the sleeplog object is available +if (typeof (global.sleeplog || {}).onChange === "object") { + // then add your function to the onChange-array + sleeplog.onChange.push( function(data) { print(data); } ); +} +``` +The passed data object has the following properties: +- timestamp: of the status change as date object, + (should be around 10min. before "now", the actual call of the function) +- status: if changed the value of the new status (0-4) else undefined, + (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) +- consecutive: if changed the value of the new status (0-2) else undefined, + (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) +- prevStatus: value of the previous status (0-4), +- prevConsecutive: value of the previous status (0-2) + --- ### Worth Mentioning diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index f4d9dff46..3d4685ea4 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -265,7 +265,9 @@ if (sleeplog.conf.enabled) { if (typeof fn === "function") setTimeout(fn, 100, { timestamp: new Date(data.timestamp), status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, + prevStatus: this.status, + prevConsecutive: this.consecutive }); }); } @@ -337,7 +339,11 @@ if (sleeplog.conf.enabled) { // changed values will be passed as object with the following properties: // timestamp: as date object, // status: if changed 0-4 else undefined, - // consecutive: if changed 0-2 else undefined + // (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) + // consecutive: if changed 0-2 else undefined, + // (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) + // prevStatus: value of the previous status 0-4, + // prevConsecutive: value of the previous status 0-2 onChange: [] }, sleeplog); From 81aece08ee34867530cf621f091654ab312d6dc7 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 01:27:37 +0100 Subject: [PATCH 03/15] [sleeplog] Correct README.md --- apps/sleeplog/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 7f122add3..1c14865ae 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -161,11 +161,11 @@ if (typeof (global.sleeplog || {}).onChange === "object") { } ``` The passed data object has the following properties: -- timestamp: of the status change as date object, +- timestamp: of the status change as date object, (should be around 10min. before "now", the actual call of the function) -- status: if changed the value of the new status (0-4) else undefined, +- status: if changed the value of the new status (0-4) else undefined, (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) -- consecutive: if changed the value of the new status (0-2) else undefined, +- consecutive: if changed the value of the new status (0-2) else undefined, (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) - prevStatus: value of the previous status (0-4), - prevConsecutive: value of the previous status (0-2) From 58330df11c95aaac8ed880be37c2d3cff6d43fac Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 17:04:54 +0100 Subject: [PATCH 04/15] [sleeplog] Change .onChange from array to object --- apps/sleeplog/ChangeLog | 2 +- apps/sleeplog/README.md | 110 ++++++++++++++++++++-------------------- apps/sleeplog/boot.js | 29 ++++++----- 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index bff278e60..66eeccfc2 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -7,4 +7,4 @@ 0.10: Complete rework off this app! 0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats 0.11: Prevent module not found error -0.12: Execute functions in `sleeplog.onChange[fn({timestamp, status, consecutive})]` on a status change \ No newline at end of file +0.12: Execute functions in `sleeplog.onChange` object on a status change \ No newline at end of file diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 1c14865ae..00c07ef4a 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -1,6 +1,6 @@ # Sleep Log -This app logs and displays the following states: +This app logs and displays the following states: - sleepling status: _unknown, not worn, awake, light sleep, deep sleep_ - consecutive sleep status: _unknown, not consecutive, consecutive_ @@ -20,55 +20,55 @@ Logfiles are not removed on un-/reinstall to prevent data loss. --- #### On the main app screen: - - __swipe left & right__ + - __swipe left & right__ to change the displayed day - - __touch the "title"__ (e.g. `Night to Fri 20/05/2022`) + - __touch the "title"__ (e.g. `Night to Fri 20/05/2022`) to enter day selection prompt - - __touch the info area__ - to change the displayed information + - __touch the info area__ + to change the displayed information (by default: consecutive & true sleeping) - - __touch the wrench__ (upper right corner) + - __touch the wrench__ (upper right corner) to enter the settings - - __use back button widget__ (upper left corner) + - __use back button widget__ (upper left corner) exit the app #### Inside the settings: - - __Thresholds__ submenu + - __Thresholds__ submenu Changes take effect from now on, not retrospective! - - __Max Awake__ | maximal awake duration + - __Max Awake__ | maximal awake duration _10min_ / _20min_ / ... / __60min__ / ... / _120min_ - - __Min Consecutive__ | minimal consecutive sleep duration + - __Min Consecutive__ | minimal consecutive sleep duration _10min_ / _20min_ / ... / __30min__ / ... / _120min_ - - __Deep Sleep__ | deep sleep threshold + - __Deep Sleep__ | deep sleep threshold _30_ / _31_ / ... / __100__ / ... / _200_ - - __Light Sleep__ | light sleep threshold - _100_ / _110_ / ... / __200__ / ... / _400_ + - __Light Sleep__ | light sleep threshold + _100_ / _110_ / ... / __200__ / ... / _400_ - __Reset to Default__ | reset to bold values above - - __BreakToD__ | time of day to break view + - __BreakToD__ | time of day to break view _0:00_ / _1:00_ / ... / __12:00__ / ... / _23:00_ - - __App Timeout__ | app specific lock timeout + - __App Timeout__ | app specific lock timeout __0s__ / _10s_ / ... / _120s_ - - __Enabled__ | completely en-/disables the background service + - __Enabled__ | completely en-/disables the background service __on__ / _off_ - - __Debugging__ submenu - - __View log__ | display logfile data - Select the logfile by its starting time. - Thresholds are shown as line with its value. - - __swipe left & right__ + - __Debugging__ submenu + - __View log__ | display logfile data + Select the logfile by its starting time. + Thresholds are shown as line with its value. + - __swipe left & right__ to change displayed duration - - __swipe up & down__ + - __swipe up & down__ to change displayed value range - - __touch the graph__ + - __touch the graph__ to change between light & dark colors - - __use back button widget__ (upper left corner) + - __use back button widget__ (upper left corner) to go back to the logfile selection - - __Enabled__ | en-/disables debugging + - __Enabled__ | en-/disables debugging _on_ / __off__ - - __write File__ | toggles if a logfile is written + - __write File__ | toggles if a logfile is written _on_ / __off__ - - __Duration__ | duration for writing into logfile - _1h_ / _2h_ / ... / __12h__ / _96_ - - The following data is logged to a csv-file: + - __Duration__ | duration for writing into logfile + _1h_ / _2h_ / ... / __12h__ / _96_ + - The following data is logged to a csv-file: _timestamp_ (in days since 1900-01-01 00:00 UTC used by office software) _, movement, status, consecutive, asleepSince, awakeSince, bpm, bpmConfidence_ @@ -78,34 +78,34 @@ Logfiles are not removed on un-/reinstall to prevent data loss. Available through the App Loader when your watch is connected. -- __view data__ +- __view data__ Display the data to each timestamp in a table. -- __save csv-file__ - Download a csv-file with the data to each timestamp. - The time format is chooseable beneath the file list. -- __delete file__ +- __save csv-file__ + Download a csv-file with the data to each timestamp. + The time format is chooseable beneath the file list. +- __delete file__ Deletes the logfile from the watch. __Please backup your data first!__ --- ### Timestamps and Files --- -1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: +1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: seconds since 1970-01-01 00:00 UTC -2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: +2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: 10 minutes since 1970-01-01 00:00 UTC (`Bangle / (10 * 60 * 1000)`) 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: hours since 1970-01-01 00:00 UTC (`Bangle / (60 * 60 * 1000)`) -4. logged timestamps inside the debug .csv file are formatted for office calculation software: +4. logged timestamps inside the debug .csv file are formatted for office calculation software: days since 1900-01-01 00:00 UTC (`Bangle / (24 * 60 * 60 * 1000) + 25569`) -5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: +5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(Bangle)` and `require("sleeplog").fnToMs(fortnight)`) -- __Logfiles from before 0.10:__ +- __Logfiles from before 0.10:__ timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` -- __View logged data:__ - if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data +- __View logged data:__ + if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data since & until in Bangle timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h @@ -114,14 +114,14 @@ Available through the App Loader when your watch is connected. --- #### Access statistics -- Last Asleep Time [Date]: +- Last Asleep Time [Date]: `Date(sleeplog.awakeSince)` -- Last Awake Duration [ms]: +- Last Awake Duration [ms]: `Date() - sleeplog.awakeSince` -- Last Statistics [object]: +- Last Statistics [object]: ``` // get stats of the last night (period as displayed inside the app) - // as this might be the mostly used function the data is cached inside the global object + // as this might be the mostly used function the data is cached inside the global object sleeplog.getStats(); // get stats of the last 24h @@ -132,20 +132,20 @@ Available through the App Loader when your watch is connected. ={ calculatedAt: 1653123553810, deepSleep: 250, lightSleep: 150, awakeSleep: 10, consecSleep: 320, awakeTime: 1030, notWornTime: 0, unknownTime: 0, logDuration: 1440, firstDate: 1653036600000, lastDate: 1653111600000 } - + // to get the start of a period defined by "Break TOD" of any date var startOfBreak = require("sleeplog").getLastBreak(); // same as var startOfBreak = require("sleeplog").getLastBreak(Date.now()); // output as date =Date: Sat May 21 2022 12:00:00 GMT+0200 - + // get stats of this period as displayed inside the app require("sleeplog").getStats(require("sleeplog").getLastBreak(), 24*60*60*1000); // or any other day require("sleeplog").getStats(require("sleeplog").getLastBreak(Date(2022,4,10)), 24*60*60*1000); ``` -- Total Statistics [object]: +- Total Statistics [object]: ``` // use with caution, may take a long time ! require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); @@ -154,18 +154,18 @@ Available through the App Loader when your watch is connected. #### Add functions triggered by status changes With the following code it is possible to add functions that will be called on status changes. ``` -// first ensure that the sleeplog object is available +// first ensure that the sleeplog onChange object is available if (typeof (global.sleeplog || {}).onChange === "object") { - // then add your function to the onChange-array - sleeplog.onChange.push( function(data) { print(data); } ); + // then add your function to the onChange object + sleeplog.onChange["my app name"] = function(data) { print(data); }; } ``` The passed data object has the following properties: -- timestamp: of the status change as date object, +- timestamp: of the status change as date object, (should be around 10min. before "now", the actual call of the function) -- status: if changed the value of the new status (0-4) else undefined, +- status: if changed the value of the new status (0-4) else undefined, (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) -- consecutive: if changed the value of the new status (0-2) else undefined, +- consecutive: if changed the value of the new status (0-2) else undefined, (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) - prevStatus: value of the previous status (0-4), - prevConsecutive: value of the previous status (0-2) @@ -177,7 +177,7 @@ The passed data object has the following properties: #### To do list - Check translations. - Add more functionallities to interface.html. -- Enable recieving data on the Gadgetbridge side + testing. +- Enable recieving data on the Gadgetbridge side + testing. __Help appreciated!__ #### Requests, Bugs and Feedback diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 3d4685ea4..33452ff91 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -124,7 +124,7 @@ if (sleeplog.conf.enabled) { if (!sleeplog.info.saveUpToDate || force) { // save status, consecutive status and info timestamps to restore on reload var save = [sleeplog.info.lastCheck, sleeplog.info.awakeSince, sleeplog.info.asleepSince]; - // add debuging status if active + // add debuging status if active if (sleeplog.debug) save.push(sleeplog.debug.writeUntil, sleeplog.debug.fileid); // stringify entries @@ -258,19 +258,20 @@ if (sleeplog.conf.enabled) { // check if the status has changed if (data.status !== this.status || data.consecutive !== this.consecutive) { - // check for onChange functions - if ((this.onChange || []).length) { - this.onChange.forEach(fn => { - // setup timeouts to start onChange functions if fn is a function - if (typeof fn === "function") setTimeout(fn, 100, { - timestamp: new Date(data.timestamp), - status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, - prevStatus: this.status, - prevConsecutive: this.consecutive - }); + // read and check for onChange functions + var onChange = Object.keys(this.onChange) || []; + if (onChange.length) onChange.forEach(key => { + // read function to key + var fn = this.onChange[key]; + // setup timeouts to start onChange functions if fn is a function + if (typeof fn === "function") setTimeout(fn, 100, { + timestamp: new Date(data.timestamp), + status: data.status === this.status ? undefined : data.status, + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, + prevStatus: this.status, + prevConsecutive: this.consecutive }); - } + }); // append status this.appendStatus(data.timestamp, data.status, data.consecutive); @@ -335,7 +336,7 @@ if (sleeplog.conf.enabled) { return this.statsCache; }, - // define array for functions to execute after a status change (changes had hapened 10min earlier) + // define object for functions to execute after a status change (changes had hapened 10min earlier) // changed values will be passed as object with the following properties: // timestamp: as date object, // status: if changed 0-4 else undefined, From 56e69cc32cdbd918dcfbc1378fe574bf00a05e01 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 17:16:44 +0100 Subject: [PATCH 05/15] [sleeplog] Initialise .onChange as object --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 33452ff91..6a9c7c6e6 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -345,7 +345,7 @@ if (sleeplog.conf.enabled) { // (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) // prevStatus: value of the previous status 0-4, // prevConsecutive: value of the previous status 0-2 - onChange: [] + onChange: {} }, sleeplog); // initial starting From 9105c94e1b8f002bd9b6cff08ff9db3f9239a598 Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Nov 2022 22:46:49 +0100 Subject: [PATCH 06/15] [sleeplog] Replace onChange with trigger object --- apps/sleeplog/README.md | 18 ++++++++----- apps/sleeplog/boot.js | 58 ++++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 00c07ef4a..9e32ad37f 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -151,13 +151,19 @@ Available through the App Loader when your watch is connected. require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); ``` -#### Add functions triggered by status changes -With the following code it is possible to add functions that will be called on status changes. +#### Add functions triggered by status changes or inside a specified time period +With the following code it is possible to add functions that will be called every 10 minutes after new movement data when meeting the specified parameters on each : ``` -// first ensure that the sleeplog onChange object is available -if (typeof (global.sleeplog || {}).onChange === "object") { - // then add your function to the onChange object - sleeplog.onChange["my app name"] = function(data) { print(data); }; +// first ensure that the sleeplog trigger object is available (sleeplog is enabled) +if (typeof (global.sleeplog || {}).trigger === "object") { + // then add your parameters with the function to call as object into the trigger object + sleeplog.trigger["my app name"] = { + onChange: false, // false as default, if true call fn only on a status change + from: 0, // 0 as default, in ms, first time fn will be called + to: 24*60*60*1000, // 24h as default, in ms, last time fn will be called + // reference time to from & to is rounded to full minutes + fn: function(data) { print(data); } // function to be executed + }; } ``` The passed data object has the following properties: diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 6a9c7c6e6..d787b8dff 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -253,26 +253,38 @@ if (sleeplog.conf.enabled) { } } + // check if the status has changed + var changed = data.status !== this.status || data.consecutive !== this.consecutive; + + // read and check trigger entries + var triggers = Object.keys(this.trigger) || []; + if (triggers.length) { + // calculate time from timestamp in ms on full minutes + var time = data.timestamp; + time = ((time.getHours() * 60) + time.getMinutes() * 60) * 1000; + // go through all triggers + triggers.forEach(key => { + // read entry to key + var entry = this.trigger[key]; + // check if the event matches the entries requirements + if (typeof entry.fn === "function" && (changed || !entry.onChange) && + (entry.from || 0) <= time && (entry.to || 24 * 60 * 60 * 1000) >= time) + // and call afterwards with status data + setTimeout(fn, 100, { + timestamp: new Date(data.timestamp), + status: data.status === this.status ? undefined : data.status, + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, + prevStatus: this.status, + prevConsecutive: this.consecutive + }); + }); + } + // cache change into a known consecutive state var changeIntoConsec = data.consecutive; - // check if the status has changed - if (data.status !== this.status || data.consecutive !== this.consecutive) { - // read and check for onChange functions - var onChange = Object.keys(this.onChange) || []; - if (onChange.length) onChange.forEach(key => { - // read function to key - var fn = this.onChange[key]; - // setup timeouts to start onChange functions if fn is a function - if (typeof fn === "function") setTimeout(fn, 100, { - timestamp: new Date(data.timestamp), - status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, - prevStatus: this.status, - prevConsecutive: this.consecutive - }); - }); - + // actions on a status change + if (changed) { // append status this.appendStatus(data.timestamp, data.status, data.consecutive); @@ -336,16 +348,8 @@ if (sleeplog.conf.enabled) { return this.statsCache; }, - // define object for functions to execute after a status change (changes had hapened 10min earlier) - // changed values will be passed as object with the following properties: - // timestamp: as date object, - // status: if changed 0-4 else undefined, - // (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) - // consecutive: if changed 0-2 else undefined, - // (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) - // prevStatus: value of the previous status 0-4, - // prevConsecutive: value of the previous status 0-2 - onChange: {} + // define trigger object + trigger: {} }, sleeplog); // initial starting From a2fd46e35ccc8412b807b884fb54fe2605862b8e Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Nov 2022 23:26:23 +0100 Subject: [PATCH 07/15] [sleeplog] Correct calculation of time in trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index d787b8dff..17388da44 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -261,7 +261,7 @@ if (sleeplog.conf.enabled) { if (triggers.length) { // calculate time from timestamp in ms on full minutes var time = data.timestamp; - time = ((time.getHours() * 60) + time.getMinutes() * 60) * 1000; + time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000; // go through all triggers triggers.forEach(key => { // read entry to key From abb719319f871363da0394ae4f6c9007c8a968a4 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 08:36:22 +0100 Subject: [PATCH 08/15] [sleeplog] Correct time as date for trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 17388da44..75383c7df 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -260,7 +260,7 @@ if (sleeplog.conf.enabled) { var triggers = Object.keys(this.trigger) || []; if (triggers.length) { // calculate time from timestamp in ms on full minutes - var time = data.timestamp; + var time = new Date(data.timestamp); time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000; // go through all triggers triggers.forEach(key => { From 528716322a0656e9530d657b7960912bb8a92b51 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 09:26:51 +0100 Subject: [PATCH 09/15] [sleeplog] Correct fn call for trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 75383c7df..17e5fe70a 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -270,7 +270,7 @@ if (sleeplog.conf.enabled) { if (typeof entry.fn === "function" && (changed || !entry.onChange) && (entry.from || 0) <= time && (entry.to || 24 * 60 * 60 * 1000) >= time) // and call afterwards with status data - setTimeout(fn, 100, { + setTimeout(entry.fn, 100, { timestamp: new Date(data.timestamp), status: data.status === this.status ? undefined : data.status, consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, From dfe57c0e77f2095c823fbb77a6ac5500119ad57e Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 10:04:57 +0100 Subject: [PATCH 10/15] [sleeplog] Correct generation of time in trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 17e5fe70a..2ab8f9eff 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -260,7 +260,7 @@ if (sleeplog.conf.enabled) { var triggers = Object.keys(this.trigger) || []; if (triggers.length) { // calculate time from timestamp in ms on full minutes - var time = new Date(data.timestamp); + var time = new Date(); time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000; // go through all triggers triggers.forEach(key => { From 855a2c4263d2cfa5a22701707d1db82b18623fd9 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 10:26:52 +0100 Subject: [PATCH 11/15] [sleeplog] Switch unchanged data behav. in trigger --- apps/sleeplog/README.md | 8 ++++---- apps/sleeplog/boot.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 9e32ad37f..66d26e7f0 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -169,12 +169,12 @@ if (typeof (global.sleeplog || {}).trigger === "object") { The passed data object has the following properties: - timestamp: of the status change as date object, (should be around 10min. before "now", the actual call of the function) -- status: if changed the value of the new status (0-4) else undefined, +- status: value of the new status (0-4), (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) -- consecutive: if changed the value of the new status (0-2) else undefined, +- consecutive: value of the new status (0-2), (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) -- prevStatus: value of the previous status (0-4), -- prevConsecutive: value of the previous status (0-2) +- prevStatus: if changed the value of the previous status (0-4) else undefined, +- prevConsecutive: if changed the value of the previous status (0-2) else undefined --- diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 2ab8f9eff..78fd23450 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -272,10 +272,10 @@ if (sleeplog.conf.enabled) { // and call afterwards with status data setTimeout(entry.fn, 100, { timestamp: new Date(data.timestamp), - status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, - prevStatus: this.status, - prevConsecutive: this.consecutive + status: data.status, + consecutive: data.consecutive, + prevStatus: data.status === this.status ? undefined : this.status, + prevConsecutive: data.consecutive === this.consecutive ? undefined : this.consecutive }); }); } From 361efb6c25d55e50f0b379dd2f68ab4079d2240d Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 18:12:59 +0100 Subject: [PATCH 12/15] [sleeplog] Add more details to README.md --- apps/sleeplog/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 66d26e7f0..4310ead58 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -6,6 +6,14 @@ This app logs and displays the following states: It is using the built in movement calculation to decide your sleeping state. While charging it is assumed that you are not wearing the watch and if the status changes to _deep sleep_ the internal heartrate sensor is used to detect if you are wearing the watch. +#### Explanations +* __Detection of Sleep__ + The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or eaqual to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake. +* __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 very light sleeping periods when an awake period is detected based on the registered movements. All periods after a sleeping period will be summarized until the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consecutive__) it is not considered, otherwise it will be added to the consecutive sleep value. + Logfiles are not removed on un-/reinstall to prevent data loss. | Filename (* _example_) | Content | Removeable in | From 30abb6706baf7691d956dbdfdb2b4d72eaebd291 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 24 Nov 2022 23:56:16 +0100 Subject: [PATCH 13/15] [sleeplog] Improve README, remove convertOldLog --- apps/sleeplog/ChangeLog | 2 +- apps/sleeplog/README.md | 22 +++++++++-- apps/sleeplog/lib.js | 85 ----------------------------------------- 3 files changed, 19 insertions(+), 90 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index 66eeccfc2..aa700f931 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -7,4 +7,4 @@ 0.10: Complete rework off this app! 0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats 0.11: Prevent module not found error -0.12: Execute functions in `sleeplog.onChange` object on a status change \ No newline at end of file +0.12: Improve README, option to add functions triggered by status changes or time periods, remove old log (<0.10) conversion \ No newline at end of file diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 4310ead58..1970d8dde 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -24,10 +24,20 @@ Logfiles are not removed on un-/reinstall to prevent data loss. --- -### App Usage +### Main App Usage --- -#### On the main app screen: +#### View: +| Status | Color | Height | +|-------------|:------:|----------:| +| unknown | black | 0% | +| not worn | red | 40% | +| awake | green | 60% | +| light sleep | cyan | 80% | +| deep sleep | blue | 100% | +| consecutive | violet | as status | + +#### Controls: - __swipe left & right__ to change the displayed day - __touch the "title"__ (e.g. `Night to Fri 20/05/2022`) @@ -40,7 +50,11 @@ Logfiles are not removed on un-/reinstall to prevent data loss. - __use back button widget__ (upper left corner) exit the app -#### Inside the settings: + +--- +### Settings Usage +--- + - __Thresholds__ submenu Changes take effect from now on, not retrospective! - __Max Awake__ | maximal awake duration @@ -198,7 +212,7 @@ The passed data object has the following properties: Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)). #### Creator -Storm64 ([Mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64)) +Storm64 ([mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64)) #### Contributors myxor ([github](https://github.com/myxor)) diff --git a/apps/sleeplog/lib.js b/apps/sleeplog/lib.js index 1919e7483..83c45de66 100644 --- a/apps/sleeplog/lib.js +++ b/apps/sleeplog/lib.js @@ -149,14 +149,6 @@ exports = { // define move log function, move StorageFile content into files seperated by fortnights moveLog: function(force) { - /** convert old logfile (< v0.10) if present **/ - if (require("Storage").list("sleeplog.log", { - sf: false - }).length) { - convertOldLog(); - } - /** may be removed in later versions **/ - // first day of this fortnight period var thisFirstDay = this.fnToMs(this.msToFn(Date.now())); @@ -384,82 +376,5 @@ exports = { "unknown,not worn,awake,light sleep,deep sleep".split(",")[entry[1]].padEnd(12) + "for" + (duration + "min").padStart(8)); }); - }, - - /** convert old (< v0.10) to new logfile data **/ - convertOldLog: function() { - // read old logfile - var oldLog = require("Storage").read("sleeplog.log") || ""; - // decode data if needed - if (!oldLog.startsWith("[")) oldLog = atob(oldLog); - // delete old logfile and return if it is empty or corrupted - if (!oldLog.startsWith("[[") || !oldLog.endsWith("]]")) { - require("Storage").erase("sleeplog.log"); - return; - } - - // transform into StorageFile and clear oldLog to have more free ram accessable - require("Storage").open("sleeplog_old.log", "w").write(JSON.parse(oldLog).reverse().join("\n")); - oldLog = undefined; - - // calculate fortnight from now - var fnOfNow = this.msToFn(Date.now()); - - // open StorageFile with old log data - var file = require("Storage").open("sleeplog_old.log", "r"); - // define active fortnight and file cache - var activeFn = true; - var fileCache = []; - // loop through StorageFile entries - while (activeFn) { - // define fortnight for this entry - var thisFn = false; - // cache new line - var line = file.readLine(); - // check if line is filled - if (line) { - // parse line - line = line.substr(0, 15).split(",").map(e => parseInt(e)); - // calculate fortnight for this entry - thisFn = this.msToFn(line[0]); - // convert timestamp into 10min steps - line[0] = line[0] / 6E5 | 0; - // set consecutive to unknown - line.push(0); - } - // check if active fortnight and file cache is set, fortnight has changed and - // active fortnight is not fortnight from now - if (activeFn && fileCache.length && activeFn !== thisFn && activeFn !== fnOfNow) { - // write file cache into new file according to fortnight - require("Storage").writeJSON("sleeplog_" + activeFn + ".log", fileCache); - // clear file cache - fileCache = []; - } - // add line to file cache if it is filled - if (line) fileCache.push(line); - // set active fortnight - activeFn = thisFn; - } - // check if entries are leftover - if (fileCache.length) { - // format fileCache entries into a string - fileCache = fileCache.map(e => e.join(",")).join("\n"); - // read complete new log StorageFile as string - file = require("Storage").open("sleeplog.log", "r"); - var newLogString = file.read(file.getLength()); - // add entries at the beginning of the new log string - newLogString = fileCache + "\n" + newLogString; - // rewrite new log StorageFile - require("Storage").open("sleeplog.log", "w").write(newLogString); - } - - // free ram - file = undefined; - fileCache = undefined; - - // clean up old files - require("Storage").erase("sleeplog.log"); - require("Storage").open("sleeplog_old.log", "w").erase(); } - /** may be removed in later versions **/ }; From 4f78515a716b7edb9933e5e44e960a4197f42469 Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 28 Nov 2022 21:21:36 +0100 Subject: [PATCH 14/15] [sleeplog] Reorder in README.md --- apps/sleeplog/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 1970d8dde..ceb3418d9 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -27,16 +27,6 @@ Logfiles are not removed on un-/reinstall to prevent data loss. ### Main App Usage --- -#### View: -| Status | Color | Height | -|-------------|:------:|----------:| -| unknown | black | 0% | -| not worn | red | 40% | -| awake | green | 60% | -| light sleep | cyan | 80% | -| deep sleep | blue | 100% | -| consecutive | violet | as status | - #### Controls: - __swipe left & right__ to change the displayed day @@ -50,6 +40,16 @@ Logfiles are not removed on un-/reinstall to prevent data loss. - __use back button widget__ (upper left corner) exit the app +#### View: +| Status | Color | Height | +|-------------|:------:|----------:| +| unknown | black | 0% | +| not worn | red | 40% | +| awake | green | 60% | +| light sleep | cyan | 80% | +| deep sleep | blue | 100% | +| consecutive | violet | as status | + --- ### Settings Usage From 9c42820dd12c4d6afce3d46df41fdc0e2a3fea49 Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 28 Nov 2022 22:08:11 +0100 Subject: [PATCH 15/15] [sleeplog] Correct typos found by @thyttan --- apps/sleeplog/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index ceb3418d9..71a7ddde2 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -8,9 +8,9 @@ It is using the built in movement calculation to decide your sleeping state. Whi #### Explanations * __Detection of Sleep__ - The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or eaqual to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake. + The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or equal to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake. * __True Sleep__ - The true sleep value is a simple addition of all registert sleeping periods. + The true sleep value is a simple addition of all registered sleeping periods. * __Consecutive Sleep__ In addition the consecutive sleep value tries to predict the complete time you were asleep, even the very light sleeping periods when an awake period is detected based on the registered movements. All periods after a sleeping period will be summarized until the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consecutive__) it is not considered, otherwise it will be added to the consecutive sleep value. @@ -205,7 +205,7 @@ The passed data object has the following properties: #### To do list - Check translations. - Add more functionallities to interface.html. -- Enable recieving data on the Gadgetbridge side + testing. +- Enable receiving data on the Gadgetbridge side + testing. __Help appreciated!__ #### Requests, Bugs and Feedback