mirror of https://github.com/espruino/BangleApps
[sleeplog] Improving triggers and web interface
to 0.13: - Prevent to stay in consecutive sleep if not worn. - Correct calling triggers where last time is earlier then first time. - Add the trigger object itself as argument to the fn function to be able to store data or functions inside the trigger object. to 0.14: improvements initiated by #2677 - Add a option to delete all logfiles before a specific date from the watch - Correct regex file search from `\d\d\d\d` / `\d{4}` to `\d+`pull/2679/head
parent
a33a6df463
commit
00a9db1bf0
|
@ -7,4 +7,6 @@
|
|||
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: Improve README, option to add functions triggered by status changes or time periods, remove old log (<0.10) conversion
|
||||
0.12: Improve README, option to add functions triggered by status changes or time periods, remove old log (<0.10) conversion
|
||||
0.13: Prevent to stay in consecutive sleep if not worn, correct trigger calling, add trigger object itself as argument to the fn function
|
||||
0.14: Add "Delete all logfiles before" to interface.html, display all logfiles in the interface
|
|
@ -19,7 +19,7 @@ Logfiles are not removed on un-/reinstall to prevent data loss.
|
|||
| Filename (* _example_) | Content | Removeable in |
|
||||
|------------------------------|-----------------|-------------------|
|
||||
| `sleeplog.log (StorageFile)` | recent logfile | App Web Interface |
|
||||
| `sleeplog_1234.log`* | old logfiles | App Web Interface |
|
||||
| `sleeplog_1234.log`* | past logfiles | App Web Interface |
|
||||
| `sleeplog_123456.csv`* | debugging files | Web IDE |
|
||||
|
||||
|
||||
|
@ -28,16 +28,16 @@ Logfiles are not removed on un-/reinstall to prevent data loss.
|
|||
---
|
||||
|
||||
#### Controls:
|
||||
- __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__
|
||||
- __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
|
||||
|
||||
#### View:
|
||||
|
@ -55,42 +55,42 @@ Logfiles are not removed on un-/reinstall to prevent data loss.
|
|||
### Settings Usage
|
||||
---
|
||||
|
||||
- __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
|
||||
- __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
|
||||
- __View log__ | display logfile data
|
||||
Select the logfile by its starting time.
|
||||
Thresholds are shown as line with its value.
|
||||
- __swipe left & right__
|
||||
- __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
|
||||
- __Duration__ | duration for writing into logfile
|
||||
_1h_ / _2h_ / ... / __12h__ / _96_
|
||||
- The following data is logged to a csv-file:
|
||||
- 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_
|
||||
|
||||
|
||||
|
@ -100,33 +100,40 @@ Logfiles are not removed on un-/reinstall to prevent data loss.
|
|||
|
||||
Available through the App Loader when your watch is connected.
|
||||
|
||||
- __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__
|
||||
Deletes the logfile from the watch. __Please backup your data first!__
|
||||
- A list of all found logfiles with following options for each file:
|
||||
- __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__
|
||||
Deletes the logfile from the watch. __Please backup your data first!__
|
||||
- __csv time format__
|
||||
__JavaScript (milliseconds since 1970)__ /
|
||||
_UNIX (seconds since 1970)_ /
|
||||
_Office (days since 1900)_
|
||||
- __delete all logfiles before__
|
||||
Deletes all logfile before the given date 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:
|
||||
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:__
|
||||
- __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
|
||||
|
||||
|
@ -136,11 +143,11 @@ 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
|
||||
|
@ -184,21 +191,25 @@ if (typeof (global.sleeplog || {}).trigger === "object") {
|
|||
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
|
||||
fn: function(data, thisTriggerEntry) { print(data); } // function to be executed
|
||||
};
|
||||
}
|
||||
```
|
||||
The passed data object has the following properties:
|
||||
- timestamp: of the status change as date 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: value of the new status (0-4),
|
||||
- status: value of the new status (0-4),
|
||||
(0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep)
|
||||
- consecutive: value of the new status (0-2),
|
||||
- consecutive: value of the new status (0-2),
|
||||
(0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep)
|
||||
- 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
|
||||
|
||||
|
||||
If you want to use other variables or functions from the trigger object inside the trigger fn function, you will find them inside the __thisTriggerEntry__ object, as the this keyword is not working in this scenario. The function itself (the fn property) is not passed inside the thisTriggerEntry object.
|
||||
|
||||
|
||||
---
|
||||
### Worth Mentioning
|
||||
---
|
||||
|
|
|
@ -235,6 +235,8 @@ if (sleeplog.conf.enabled) {
|
|||
// reset consecutive status
|
||||
data.consecutive = 0;
|
||||
}
|
||||
// reset consecutive sleep if not worn
|
||||
if (data.status === 1) this.consecutive = 1;
|
||||
// check if consecutive unknown
|
||||
if (!this.consecutive) {
|
||||
// check if long enough asleep or too long awake
|
||||
|
@ -265,10 +267,13 @@ if (sleeplog.conf.enabled) {
|
|||
// go through all triggers
|
||||
triggers.forEach(key => {
|
||||
// read entry to key
|
||||
var entry = this.trigger[key];
|
||||
let entry = this.trigger[key];
|
||||
// set from and to values to default if unset
|
||||
let from = entry.from || 0;
|
||||
let to = entry.to || 24 * 60 * 60 * 1000;
|
||||
// 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)
|
||||
(from <= to ? from <= time && time <= to : time <= to || from <= time))
|
||||
// and call afterwards with status data
|
||||
setTimeout(entry.fn, 100, {
|
||||
timestamp: new Date(data.timestamp),
|
||||
|
@ -276,7 +281,7 @@ if (sleeplog.conf.enabled) {
|
|||
consecutive: data.consecutive,
|
||||
prevStatus: data.status === this.status ? undefined : this.status,
|
||||
prevConsecutive: data.consecutive === this.consecutive ? undefined : this.consecutive
|
||||
});
|
||||
}, (e => {delete e.fn; return e;})(entry.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ function viewLog(logData, filename) {
|
|||
<td>` +
|
||||
new Date(entry[0] * 6E5).toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}) + `
|
||||
</td>
|
||||
<td style="text-align: right"><div class="container"` +
|
||||
<td style="text-align: right"><div class="container"` +
|
||||
(duration >= 60 ? ` style="background-color: hsl(192, 50%, ` +
|
||||
(duration > 480 ? 50 : 100 - Math.floor(duration / 60) * 50 / 8) +
|
||||
`%)">` : `>`) +
|
||||
|
@ -84,7 +84,7 @@ function readLog(date, callback) {
|
|||
|
||||
function deleteFile(filename, callback) {
|
||||
if (window.confirm("Do you really want to remove " + filename)) {
|
||||
Util.showModal("Deleting...");
|
||||
Util.showModal("Deleting " + filename + " ...");
|
||||
if (filename.endsWith(" (StorageFile)")) {
|
||||
Util.eraseStorageFile(filename, () => {
|
||||
Util.hideModal();
|
||||
|
@ -99,15 +99,29 @@ function deleteFile(filename, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
function deleteBefore(dateString) {
|
||||
date = new Date(dateString);
|
||||
if (window.confirm("Do you really want to remove all data before" + date.toLocaleDateString(undefined))) {
|
||||
Util.showModal("Deleting all data before" + date.toLocaleDateString(undefined) + " ...");
|
||||
Puck.eval(`require("Storage").list(/^sleeplog_\\d+.log$/).forEach(file => {
|
||||
date = (parseInt(file.match(/\\d+/)[0]) + 0.25) * 12096E5
|
||||
print(date < ` + date + `)
|
||||
})`, () => {
|
||||
Util.hideModal();
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function viewFiles() {
|
||||
Util.showModal("Loading...");
|
||||
domTable.innerHTML = "";
|
||||
Puck.eval(`require("Storage").list(/^sleeplog_\\d\\d\\d\\d\\.log$/)`, files => {
|
||||
Puck.eval(`require("Storage").list(/^sleeplog_\\d+.log$/)`, files => {
|
||||
// add active log
|
||||
files.push("" + Math.floor(Date.now() / 12096E5 - 0.25));
|
||||
files = files.map(file => { return {
|
||||
filename: file.length === 4 ? "sleeplog.log (StorageFile)" : file,
|
||||
date: (parseInt(file.match(/\d{4}/)[0]) + 0.25) * 12096E5
|
||||
date: (parseInt(file.match(/\d+/)[0]) + 0.25) * 12096E5
|
||||
}});
|
||||
files = files.sort((a, b) => a.date - b.date);
|
||||
var html = `
|
||||
|
@ -146,10 +160,10 @@ function viewFiles() {
|
|||
<div class="container">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="col-3 col-sm-12">
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label"><b>csv time format</b></label>
|
||||
</div>
|
||||
<div class="col-9 col-sm-12">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-select" id="csvTime">
|
||||
<option>JavaScript (milliseconds since 1970)</option>
|
||||
<option>UNIX (seconds since 1970)</option>
|
||||
|
@ -158,6 +172,23 @@ function viewFiles() {
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="container">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label"><b>Delete all logfiles before</b></label>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-input" id="delBeforeDate" type="date" value="2022-01-01">
|
||||
</div>
|
||||
<div class="col-mx-auto">
|
||||
<button class="btn tooltip btn-error" data-tooltip="delete old files" task="delBefore" filename="" date="">
|
||||
<i class="icon icon-delete"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>`;
|
||||
domTable.innerHTML = html;
|
||||
Util.hideModal();
|
||||
|
@ -172,6 +203,7 @@ function viewFiles() {
|
|||
if (task === "view") readLog(date, logData => viewLog(logData, filename));
|
||||
else if (task === "csv") readLog(date, logData => saveCSV(logData, date, date + 12096E5));
|
||||
else if (task === "del") deleteFile(filename, () => viewFiles());
|
||||
else if (task === "delBefore") deleteBefore(document.getElementById("delBeforeDate").value);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -123,7 +123,7 @@ exports = {
|
|||
// free ram
|
||||
files = undefined;
|
||||
|
||||
// check if log from files is available
|
||||
// check if log from files is available
|
||||
if (filesLog.length) {
|
||||
// remove unwanted entries
|
||||
filesLog = filesLog.filter((entry, index, filesLog) => (
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id":"sleeplog",
|
||||
"name":"Sleep Log",
|
||||
"shortName": "SleepLog",
|
||||
"version": "0.12",
|
||||
"version": "0.14",
|
||||
"description": "Log and view your sleeping habits. This app is using the built in movement calculation.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
Loading…
Reference in New Issue