forked from FOSS/BangleApps
sleeplog: Complete power saving mode + move all read/write log actions into lib.js
* Complete new power saving mode after tests * Move all read/write log operations into lib/module Update app.js - update `readLog(...)` call Update boot.js - update maxmove default value - delete `log(..)`, merging into `writeLog(...)` from lib.js - replace all read/write actions to the logfile with `readLog(...)`/`writeLog(...)` from lib.js - add restoring the status after a restart (<5 min ago) to last known state - add deletion of unused settings on power saving mode - update log timestamp on power saving mode to always be 10min before now Update ChangeLog Update lib.js - update `readLog(...)`, prevent errors on reading, minimize workload if unfiltered - add `writeLog(...)` to append or replace log depending on input with plausibility checks on input - merging `log(...)` from boot.js into `writeLog(...)` - replace all read/write actions to the logfile with `readLog(...)`/`writeLog(...)` Update metadata.json - update version number - add power saving mode to description Update README.md - add power saving mode description - move sleeping/not worn decision description into its own section - add description for timestamp values to Logging section - update Global Object and Module Functions section Update settings.js - update maxmove setting and default valuemaster
parent
b8721fbdcf
commit
0ec1d57add
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Fix crash on start
|
||||
0.03: Added power saving mode + move all read/write log actions into lib/module
|
||||
|
|
|
@ -2,20 +2,26 @@
|
|||
|
||||
This app logs and displays the four following states:
|
||||
_unknown, not worn, awake, sleeping_
|
||||
It derived from the [SleepPhaseAlarm](https://banglejs.com/apps/#sleepphasealarm) and uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments ([ESS](https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en)) and the internal temperature to decide _sleeping_ or _not worn_ when the watch is resting.
|
||||
It derived from the [SleepPhaseAlarm](https://banglejs.com/apps/#sleepphasealarm) and uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments ([ESS](https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en)) 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.
|
||||
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_)
|
||||
To check if a resting watch indicates as sleeping, the internal temperature must be greater than the temperature threshold (__TempThresh__). Otherwise the watch is considered as not worn.
|
||||
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.
|
||||
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
|
||||
|
@ -28,28 +34,34 @@ It derived from the [SleepPhaseAlarm](https://banglejs.com/apps/#sleepphasealarm
|
|||
---
|
||||
### Settings
|
||||
---
|
||||
* __BreakTod__ break at time of day
|
||||
* __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
|
||||
* __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
|
||||
* __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
|
||||
* __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_.
|
||||
* __NoMoThresh__ no movement threshold
|
||||
* __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
|
||||
* __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 battery, but might make this app useless._
|
||||
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_.
|
||||
|
@ -66,7 +78,7 @@ For easy access from the console or other apps the following parameters, values
|
|||
logfile: "sleeplog.log", // string / used logfile
|
||||
resting: false, // bool / indicates if the watch is resting
|
||||
status: 2, // int / actual status: 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping
|
||||
firstnomodate: 1644435877595, // number / Date.now() from first recognised no movement
|
||||
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
|
||||
...
|
||||
|
@ -74,42 +86,54 @@ For easy access from the console or other apps the following parameters, values
|
|||
|
||||
>require("sleeplog")
|
||||
={
|
||||
setEnabled: function (enable, logfile) { ... },
|
||||
// en-/disable the service and/or logging
|
||||
// * enable / bool / service status to change to
|
||||
// * logfile / bool or string
|
||||
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
|
||||
// returns: bool or undefined
|
||||
// - true = changes executed
|
||||
// - false = no changes needed
|
||||
// * (powersaving) / bool / new power saving status, default: false
|
||||
// returns: true or undefined
|
||||
// - true = service restart executed
|
||||
// - undefined = no global.sleeplog found
|
||||
readLog: function (since, until) { ... },
|
||||
readLog: function (logfile, since, until) { ... },
|
||||
// read the raw log data for a specific time period
|
||||
// * since / Date or number / startpoint of period
|
||||
// * until / Date or number / endpoint of 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 found
|
||||
getReadableLog: function (printLog, since, until) { ... }
|
||||
// * [] = 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
|
||||
// * since / Date or number / startpoint of period
|
||||
// * until / Date or number / endpoint of 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 / name of the logfile that will be restored
|
||||
// * (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 / name of the logfile
|
||||
// * tempthresh / float / new 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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -25,7 +25,7 @@ function drawLog(topY, viewUntil) {
|
|||
var y = topY + graphHeight;
|
||||
|
||||
// read 12h wide log
|
||||
var log = require("sleeplog").readLog(timestamp0, viewUntil.valueOf());
|
||||
var log = require("sleeplog").readLog(0, timestamp0, viewUntil.valueOf());
|
||||
|
||||
// format log array if not empty
|
||||
if (log.length) {
|
||||
|
|
|
@ -12,7 +12,7 @@ global.sleeplog = Object.assign({
|
|||
winwidth: 13, // 13 values, read with 12.5Hz = every 1.04s
|
||||
nomothresh: 0.012, // values lower than 0.008 getting triggert by noise
|
||||
sleepthresh: 577, // 577 times no movement * 1.04s window width > 10min
|
||||
maxmove: 44, // movement threshold on power saving mode
|
||||
maxmove: 100, // movement threshold on power saving mode
|
||||
tempthresh: 27, // every temperature above ist registered as worn
|
||||
}, require("Storage").readJSON("sleeplog.json", true) || {});
|
||||
|
||||
|
@ -28,41 +28,6 @@ if (sleeplog.enabled) {
|
|||
resting: undefined,
|
||||
status: undefined,
|
||||
|
||||
// define logging function
|
||||
log: function(date, status, temperature, info) {
|
||||
// exit on wrong this
|
||||
if (this.enabled === undefined) return;
|
||||
// skip logging if logfile is undefined or does not end with ".log"
|
||||
if (!this.logfile || !this.logfile.endsWith(".log")) return;
|
||||
// prevent logging on implausible date
|
||||
if (date < 9E11 || Date() < 9E11) return;
|
||||
|
||||
// set default value for status
|
||||
status = status || 0;
|
||||
|
||||
// define storage
|
||||
var storage = require("Storage");
|
||||
|
||||
// read previous logfile
|
||||
var log = storage.read(this.logfile) || "";
|
||||
log = log ? JSON.parse(atob(log)) || [] : [];
|
||||
|
||||
// remove last state if it was unknown and is less then 10min ago
|
||||
if (log.length > 0 && log[0][1] === 0 &&
|
||||
Math.floor(Date.now()) - log[0][0] < 600000) log.shift();
|
||||
|
||||
// add actual status at the first position if it has changed
|
||||
if (log.length === 0 || log[0][1] !== status)
|
||||
log.unshift(info ? [date, status, temperature, info] : temperature ? [date, status, temperature] : [date, status]);
|
||||
|
||||
// write log to storage
|
||||
storage.write(this.logfile, btoa(JSON.stringify(log)));
|
||||
|
||||
// clear variables
|
||||
log = undefined;
|
||||
storage = undefined;
|
||||
},
|
||||
|
||||
// define stop function (logging will restart if enabled and boot file is executed)
|
||||
stop: function() {
|
||||
// remove all listeners
|
||||
|
@ -72,7 +37,7 @@ if (sleeplog.enabled) {
|
|||
// exit on missing global object
|
||||
if (!global.sleeplog) return;
|
||||
// write log with undefined sleeping status
|
||||
sleeplog.log(Math.floor(Date.now()));
|
||||
require("sleeplog").writeLog(0, [Math.floor(Date.now()), 0]);
|
||||
// reset always used cached values
|
||||
sleeplog.resting = undefined;
|
||||
sleeplog.status = undefined;
|
||||
|
@ -91,14 +56,20 @@ if (sleeplog.enabled) {
|
|||
if (sleeplog.accel) Bangle.on('accel', sleeplog.accel);
|
||||
// add kill listener
|
||||
E.on('kill', sleeplog.stop);
|
||||
// set status to unknown
|
||||
sleeplog.status = 0;
|
||||
// read log since 5min ago and restore status to last known state or unknown
|
||||
sleeplog.status = (require("sleeplog").readLog(0, Date.now() - 3E5)[1] || [0, 0])[1]
|
||||
// update resting according to status
|
||||
sleeplog.resting = sleeplog.status % 2;
|
||||
// write restored status to log
|
||||
require("sleeplog").writeLog(0, [Math.floor(Date.now()), sleeplog.status]);
|
||||
}
|
||||
});
|
||||
|
||||
// check for power saving mode
|
||||
if (sleeplog.powersaving) {
|
||||
// power saving mode using build in movement detection
|
||||
// delete unused settings
|
||||
["winwidth", "nomothresh", "sleepthresh"].forEach(property => delete sleeplog[property]);
|
||||
// add cached values and functions to global object
|
||||
sleeplog = Object.assign(sleeplog, {
|
||||
// define health listener function
|
||||
|
@ -107,24 +78,27 @@ if (sleeplog.enabled) {
|
|||
var gObj = global.sleeplog;
|
||||
if (!gObj) return;
|
||||
|
||||
// calculate timestamp for this measurement
|
||||
var timestamp = Math.floor(Date.now() - 6E5);
|
||||
|
||||
// check for non-movement according to the threshold
|
||||
if (data.movement <= gObj.maxmove) {
|
||||
// check resting state
|
||||
if (gObj.resting !== true) {
|
||||
if (true || gObj.resting !== true) { // log always for testing
|
||||
// change resting state
|
||||
gObj.resting = true;
|
||||
// set status to sleeping or worn
|
||||
gObj.status = E.getTemperature() > gObj.tempthresh ? 3 : 1;
|
||||
// write status to log, correct timestamp by health interval in ms
|
||||
gObj.log(Math.floor(Date.now() - 6E5), gObj.status, E.getTemperature());
|
||||
// write status to log,
|
||||
require("sleeplog").writeLog(0, [timestamp, gObj.status, E.getTemperature(), data.movement]);
|
||||
}
|
||||
} else {
|
||||
// check resting state
|
||||
if (gObj.resting !== false) {
|
||||
// change resting state, set status and write to log as awake
|
||||
if (true || gObj.resting !== false) { // log always for testing
|
||||
// change resting state, set status and write status to log
|
||||
gObj.resting = false;
|
||||
gObj.status = 2;
|
||||
gObj.log(Math.floor(Date.now()), 2);
|
||||
require("sleeplog").writeLog(0, [timestamp, 2, E.getTemperature(), data.movement]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,18 +138,19 @@ if (sleeplog.enabled) {
|
|||
this.resting = true;
|
||||
// set status to sleeping or worn
|
||||
this.status = E.getTemperature() > this.tempthresh ? 3 : 1;
|
||||
// write status to log, correct timestamp by health interval in ms
|
||||
this.log(this.firstnomodate, this.status, E.getTemperature());
|
||||
// write status to log, with first no movement timestamp
|
||||
require("sleeplog").writeLog(0, [this.firstnomodate, this.status, E.getTemperature()]);
|
||||
}
|
||||
} else {
|
||||
// reset non-movement sections count
|
||||
this.nomocount = 0;
|
||||
// check resting state
|
||||
if (this.resting !== false) {
|
||||
// change resting state, set status and write to log as awake
|
||||
// change resting state and set status
|
||||
this.resting = false;
|
||||
this.status = 2;
|
||||
this.log(Math.floor(Date.now()), 2);
|
||||
// write status to log
|
||||
require("sleeplog").writeLog(0, [Math.floor(Date.now()), 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ exports = {
|
|||
storage.writeJSON(filename, Object.assign(storage.readJSON(filename, true) || {}, {
|
||||
enabled: enable,
|
||||
logfile: logfile,
|
||||
powersaving: powersaving
|
||||
powersaving: powersaving || false
|
||||
}));
|
||||
|
||||
// force changes to take effect by executing the boot script
|
||||
|
@ -39,33 +39,77 @@ exports = {
|
|||
// - int // status: 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping
|
||||
// - float // internal temperature
|
||||
// - string // additional information
|
||||
readLog: function(since, until) {
|
||||
// set logfile
|
||||
var logfile = (global.sleeplog || {}).logfile || "sleeplog.log";
|
||||
readLog: function(logfile, since, until) {
|
||||
// check/set logfile
|
||||
logfile = typeof logfile === "string" && logfile.endsWith(".log") ? logfile :
|
||||
(global.sleeplog || {}).logfile || "sleeplog.log";
|
||||
|
||||
// check if since is in the future
|
||||
if (since > Date()) return [];
|
||||
|
||||
// read log json to array
|
||||
var log = storage.read(this.logfile) || "";
|
||||
log = log ? JSON.parse(atob(log)) || [] : [];
|
||||
// read logfile
|
||||
var log = require("Storage").read(logfile);
|
||||
// return empty log
|
||||
if (!log) return [];
|
||||
// decode data if needed
|
||||
if (log[0] !== "[") log = atob(log);
|
||||
// do a simple check before parsing
|
||||
if (!log.startsWith("[[") || !log.endsWith("]]")) return [];
|
||||
log = JSON.parse(log) || [];
|
||||
|
||||
// search for latest entry befor since
|
||||
since = (log.find(element => element[0] <= since) || [0])[0];
|
||||
|
||||
// filter selected time period
|
||||
log = log.filter(element => (element[0] >= since) && (element[0] <= (until || 1E14)));
|
||||
// check if filtering is needed
|
||||
if (since || until) {
|
||||
// search for latest entry befor since
|
||||
if (since) since = (log.find(element => element[0] <= since) || [0])[0];
|
||||
// filter selected time period
|
||||
log = log.filter(element => (element[0] >= since) && (element[0] <= (until || 1E14)));
|
||||
}
|
||||
|
||||
// output log
|
||||
return log;
|
||||
},
|
||||
|
||||
// define write log function, append or replace log depending on input
|
||||
// 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
|
||||
writeLog: function(logfile, input) {
|
||||
// check/set logfile
|
||||
logfile = typeof logfile === "string" && logfile.endsWith(".log") ? logfile :
|
||||
(global.sleeplog || {}).logfile || "sleeplog.log";
|
||||
|
||||
// check if input is an array
|
||||
if (typeof input !== "object" || typeof input.length !== "number") return;
|
||||
|
||||
// check for entry plausibility
|
||||
if (input.length > 1 && input[0] * 1 > 9E11) {
|
||||
// read log
|
||||
var log = this.readLog(logfile);
|
||||
|
||||
// remove last state if it was unknown and less then 5min ago
|
||||
if (log.length > 0 && log[0][1] === 0 &&
|
||||
Math.floor(Date.now()) - log[0][0] < 3E5) log.shift();
|
||||
|
||||
// add entry at the first position if it has changed
|
||||
if (log.length === 0 || input.some((e, index) => index > 0 && input[index] !== log[0][index])) log.unshift(input);
|
||||
|
||||
// map log as input
|
||||
input = log;
|
||||
}
|
||||
|
||||
// simple check for log plausibility
|
||||
if (input[0].length > 1 && input[0][0] * 1 > 9E11) {
|
||||
// write log to storage
|
||||
require("Storage").write(logfile, btoa(JSON.stringify(input)));
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
// define log to humanreadable string function
|
||||
// sorting: latest last, format:
|
||||
// "{substring of ISO date} - {status} for {duration}min\n..."
|
||||
getReadableLog: function(printLog, since, until) {
|
||||
getReadableLog: function(printLog, since, until, logfile) {
|
||||
// read log and check
|
||||
var log = this.readLog(since, until);
|
||||
var log = this.readLog(logfile, since, until);
|
||||
if (!log.length) return;
|
||||
// reverse array to set last timestamp to the end
|
||||
log.reverse();
|
||||
|
@ -79,8 +123,11 @@ exports = {
|
|||
logString[index] = "" +
|
||||
Date(element[0] - Date().getTimezoneOffset() * 6E4).toISOString().substr(0, 19).replace("T", " ") + " - " +
|
||||
statusText[element[1]] +
|
||||
(index === log.length - 1 ? "" : " for " + Math.round((log[index + 1][0] - element[0]) / 60000) + "min") +
|
||||
(element[2] ? " | Temp: " + element[2] + "°C" : "") +
|
||||
(index === log.length - 1 ?
|
||||
element.length < 3 ? "" : " ".repeat(12) :
|
||||
" for " + ("" + Math.round((log[index + 1][0] - element[0]) / 60000)).padStart(4) + "min"
|
||||
) +
|
||||
(element[2] ? " | Temp: " + ("" + element[2]).padEnd(5) + "°C" : "") +
|
||||
(element[3] ? " | " + element[3] : "");
|
||||
});
|
||||
logString = logString.join("\n");
|
||||
|
@ -98,12 +145,9 @@ exports = {
|
|||
|
||||
// define function to eliminate some errors inside the log
|
||||
restoreLog: function(logfile) {
|
||||
// define storage
|
||||
var storage = require("Storage");
|
||||
|
||||
// read log json to array
|
||||
var log = storage.read(this.logfile) || "";
|
||||
log = log ? JSON.parse(atob(log)) || [] : [];
|
||||
// read log and check
|
||||
var log = this.readLog(logfile);
|
||||
if (!log.length) return;
|
||||
|
||||
// define output variable to show number of changes
|
||||
var output = log.length;
|
||||
|
@ -111,8 +155,8 @@ exports = {
|
|||
// remove non decremental entries
|
||||
log = log.filter((element, index) => log[index][0] >= (log[index + 1] || [0])[0]);
|
||||
|
||||
// write log to storage
|
||||
storage.write(logfile, btoa(JSON.stringify(log)));
|
||||
// write log
|
||||
this.writeLog(logfile, log);
|
||||
|
||||
// return difference in length
|
||||
return output - log.length;
|
||||
|
@ -120,12 +164,12 @@ exports = {
|
|||
|
||||
// define function to reinterpret worn status based on given temperature threshold
|
||||
reinterpretTemp: function(logfile, tempthresh) {
|
||||
// define storage
|
||||
var storage = require("Storage");
|
||||
// read log and check
|
||||
var log = this.readLog(logfile);
|
||||
if (!log.length) return;
|
||||
|
||||
// read log json to array
|
||||
var log = storage.read(this.logfile) || "";
|
||||
log = log ? JSON.parse(atob(log)) || [] : [];
|
||||
// set default tempthresh
|
||||
tempthresh = tempthresh || (global.sleeplog ? sleeplog.tempthresh : 27);
|
||||
|
||||
// define output variable to show number of changes
|
||||
var output = 0;
|
||||
|
@ -140,8 +184,8 @@ exports = {
|
|||
return element;
|
||||
});
|
||||
|
||||
// write log to storage
|
||||
storage.write(logfile, btoa(JSON.stringify(log)));
|
||||
// write log
|
||||
this.writeLog(logfile, log);
|
||||
|
||||
// return output
|
||||
return output;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
"id":"sleeplog",
|
||||
"name":"Sleep Log",
|
||||
"shortName": "SleepLog",
|
||||
"version": "0.02",
|
||||
"description": "Log and view your sleeping habits. This app derived from SleepPhaseAlarm and uses also the principe of Estimation of Stationary Sleep-segments (ESS).",
|
||||
"version": "0.03",
|
||||
"description": "Log and view your sleeping habits. This app derived from SleepPhaseAlarm and uses also the principe of Estimation of Stationary Sleep-segments (ESS). It also provides a power saving mode using the built in movement calculation.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,boot",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
minconsec: 18E5, // 30min in ms
|
||||
tempthresh: 27, // every temperature above ist registered as worn
|
||||
powersaving: false, // disables ESS and uses build in movement detection
|
||||
maxmove: 44, // movement threshold on power saving mode
|
||||
maxmove: 100, // movement threshold on power saving mode
|
||||
nomothresh: 0.012, // values lower than 0.008 getting triggert by noise
|
||||
sleepthresh: 577, // 577 times no movement * 1.04s window width > 10min
|
||||
winwidth: 13, // 13 values, read with 12.5Hz = every 1.04s
|
||||
|
@ -97,9 +97,9 @@
|
|||
},
|
||||
"MaxMove": {
|
||||
value: settings.maxmove,
|
||||
step: 44,
|
||||
step: 1,
|
||||
onchange: function(v) {
|
||||
this.value = v = circulate(40, 100, v);
|
||||
this.value = v = circulate(50, 200, v);
|
||||
writeSetting("maxmove", v);
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue