commit
cb8e733738
|
|
@ -14,3 +14,6 @@
|
||||||
0.11: Minor tweaks
|
0.11: Minor tweaks
|
||||||
0.12: Support javascript command to execute as defined in scheduler 'js' configuration
|
0.12: Support javascript command to execute as defined in scheduler 'js' configuration
|
||||||
0.13: Fix dated events alarm on wrong date
|
0.13: Fix dated events alarm on wrong date
|
||||||
|
0.14: Reduce update interval of current time when seconds are not shown
|
||||||
|
Limit logging on Bangle.js 1 to one day due to low memory
|
||||||
|
Add plot logged data to settings
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@ Replacing the watch strap with a more comfortable one (e.g. made of nylon) is re
|
||||||
## Logging
|
## Logging
|
||||||
|
|
||||||
For each day of month (1..31) the ESS states are logged. An entry will be overwritten in the next month, e.g. an entry on the 4th May will overwrite an entry on the 4th April.
|
For each day of month (1..31) the ESS states are logged. An entry will be overwritten in the next month, e.g. an entry on the 4th May will overwrite an entry on the 4th April.
|
||||||
The logs can be viewed with the download button:
|
On Bangle.js 1 only one day is logged due to low memory.
|
||||||
|
The logs can be plotted from the settings menu:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
The logs can also be viewed with the download button in the App Loader:
|
||||||
|
|
||||||
|

|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ const active = alarms.filter(alarm => require("sched").getTimeToAlarm(alarm));
|
||||||
const schedSettings = require("sched").getSettings();
|
const schedSettings = require("sched").getSettings();
|
||||||
let buzzCount = schedSettings.buzzCount;
|
let buzzCount = schedSettings.buzzCount;
|
||||||
let logs = [];
|
let logs = [];
|
||||||
|
let drawTimeTimeout;
|
||||||
|
|
||||||
// Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS):
|
// Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS):
|
||||||
// Marko Borazio, Eugen Berlin, Nagihan Kücükyildiz, Philipp M. Scholl and Kristof Van Laerhoven, "Towards a Benchmark for Wearable Sleep Analysis with Inertial Wrist-worn Sensing Units", ICHI 2014, Verona, Italy, IEEE Press, 2014.
|
// Marko Borazio, Eugen Berlin, Nagihan Kücükyildiz, Philipp M. Scholl and Kristof Van Laerhoven, "Towards a Benchmark for Wearable Sleep Analysis with Inertial Wrist-worn Sensing Units", ICHI 2014, Verona, Italy, IEEE Press, 2014.
|
||||||
|
|
@ -26,7 +27,7 @@ const nomothresh=0.023; // Original implementation: 6, resolution 11 bit, scale
|
||||||
const sleepthresh=600;
|
const sleepthresh=600;
|
||||||
var ess_values = [];
|
var ess_values = [];
|
||||||
var slsnds = 0;
|
var slsnds = 0;
|
||||||
function calc_ess(acc_magn) {
|
function calc_ess(acc_magn) {"ram"
|
||||||
ess_values.push(acc_magn);
|
ess_values.push(acc_magn);
|
||||||
|
|
||||||
if (ess_values.length == winwidth) {
|
if (ess_values.length == winwidth) {
|
||||||
|
|
@ -90,10 +91,12 @@ function drawApp() {
|
||||||
layout.alarm_date.label = `${LABEL_WAKEUP_TIME}: ${alarmHour}:${alarmMinute}`;
|
layout.alarm_date.label = `${LABEL_WAKEUP_TIME}: ${alarmHour}:${alarmMinute}`;
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
||||||
function drawTime() {
|
function drawTime() {"ram"
|
||||||
|
const drawSeconds = !Bangle.isLocked();
|
||||||
|
|
||||||
if (Bangle.isLCDOn()) {
|
if (Bangle.isLCDOn()) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
layout.date.label = locale.time(now, BANGLEJS2 && Bangle.isLocked() ? 1 : 0); // hide seconds on bangle 2
|
layout.date.label = locale.time(now, !drawSeconds); // hide seconds on bangle 2
|
||||||
const diff = nextAlarmDate - now;
|
const diff = nextAlarmDate - now;
|
||||||
const diffHour = Math.floor((diff % 86400000) / 3600000).toString();
|
const diffHour = Math.floor((diff % 86400000) / 3600000).toString();
|
||||||
const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString();
|
const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString();
|
||||||
|
|
@ -101,11 +104,22 @@ function drawApp() {
|
||||||
layout.render();
|
layout.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(()=>{
|
const period = drawSeconds ? 1000 : 60000;
|
||||||
|
if (this.drawTimeTimeout !== undefined) {
|
||||||
|
clearTimeout(this.drawTimeTimeout);
|
||||||
|
}
|
||||||
|
drawTimeTimeout = setTimeout(()=>{
|
||||||
|
drawTimeTimeout = undefined;
|
||||||
drawTime();
|
drawTime();
|
||||||
}, 1000 - (Date.now() % 1000));
|
}, period - (Date.now() % period));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bangle.on('lock', function(on) {
|
||||||
|
if (on === false) {
|
||||||
|
drawTime();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
drawTime();
|
drawTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,8 +146,9 @@ function addLog(time, type) {
|
||||||
var minAlarm = new Date();
|
var minAlarm = new Date();
|
||||||
var measure = true;
|
var measure = true;
|
||||||
if (nextAlarmDate !== undefined) {
|
if (nextAlarmDate !== undefined) {
|
||||||
config.logs[nextAlarmDate.getDate()] = []; // overwrite log on each day of month
|
const logday = BANGLEJS2 ? nextAlarmDate.getDate() : 0;
|
||||||
logs = config.logs[nextAlarmDate.getDate()];
|
config.logs[logday] = []; // overwrite log on each day of month
|
||||||
|
logs = config.logs[logday];
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
@ -146,7 +161,7 @@ if (nextAlarmDate !== undefined) {
|
||||||
layout.render();
|
layout.render();
|
||||||
Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval
|
Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval
|
||||||
Bangle.setPollInterval(80); // 12.5Hz
|
Bangle.setPollInterval(80); // 12.5Hz
|
||||||
Bangle.on('accel', (accelData) => {
|
Bangle.on('accel', (accelData) => {"ram"
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const acc = accelData.mag;
|
const acc = accelData.mag;
|
||||||
const swest = calc_ess(acc);
|
const swest = calc_ess(acc);
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "sleepphasealarm",
|
"id": "sleepphasealarm",
|
||||||
"name": "SleepPhaseAlarm",
|
"name": "SleepPhaseAlarm",
|
||||||
"shortName": "SleepPhaseAlarm",
|
"shortName": "SleepPhaseAlarm",
|
||||||
"version": "0.13",
|
"version": "0.14",
|
||||||
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "alarm",
|
"tags": "alarm",
|
||||||
|
|
@ -15,5 +15,6 @@
|
||||||
{"name":"sleepphasealarm.img","url":"app-icon.js","evaluate":true}
|
{"name":"sleepphasealarm.img","url":"app-icon.js","evaluate":true}
|
||||||
],
|
],
|
||||||
"data": [{"name":"sleepphasealarm.json"}],
|
"data": [{"name":"sleepphasealarm.json"}],
|
||||||
"interface": "interface.html"
|
"interface": "interface.html",
|
||||||
|
"screenshots": [ {"url":"screenshot.png"}, {"url":"screenshot_log.png"} ]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -13,25 +13,102 @@
|
||||||
require('Storage').writeJSON(CONFIGFILE, config);
|
require('Storage').writeJSON(CONFIGFILE, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the menu
|
function draw(log) {
|
||||||
E.showMenu({
|
const step = 10*60*1000; // resolution 10min
|
||||||
"" : { "title" : "SleepPhaseAlarm" },
|
const yTicks = ["sleep", "awake", "alarm"];
|
||||||
'Keep alarm enabled': {
|
const starttime = new Date(log[0].time);
|
||||||
value: !!config.settings.disableAlarm,
|
const endtime = new Date(log[log.length-1].time);
|
||||||
format: v => v?"No":"Yes",
|
|
||||||
onchange: v => {
|
let logidx = 0;
|
||||||
config.settings.disableAlarm = v;
|
let curtime = starttime;
|
||||||
writeSettings();
|
const data = new Uint8Array(Math.ceil((endtime-curtime)/step) + 1);
|
||||||
|
let curval;
|
||||||
|
let logtime;
|
||||||
|
let i=0;
|
||||||
|
while(curtime < endtime) {
|
||||||
|
if (logtime === undefined || curtime > logtime) {
|
||||||
|
curval = yTicks.indexOf(log[logidx].type);
|
||||||
|
logidx++;
|
||||||
|
logtime = new Date(log[logidx].time);
|
||||||
}
|
}
|
||||||
}, "< Back" : () => back(),
|
|
||||||
'Run before alarm': {
|
data[i++] = curval;
|
||||||
format: v => v === 0 ? 'disabled' : v+'h',
|
curtime = new Date(curtime + step);
|
||||||
value: config.settings.startBeforeAlarm,
|
}
|
||||||
min: 0, max: 23,
|
data[i] = 1; // always end with awake
|
||||||
onchange: v => {
|
|
||||||
config.settings.startBeforeAlarm = v;
|
Bangle.setUI({
|
||||||
writeSettings();
|
mode: "custom",
|
||||||
}
|
back: () => selectday(),
|
||||||
},
|
});
|
||||||
});
|
g.reset().setFont("6x8",1);
|
||||||
|
|
||||||
|
require("graph").drawLine(g, data, {
|
||||||
|
axes: true,
|
||||||
|
x: 4,
|
||||||
|
y: Bangle.appRect.y+8,
|
||||||
|
height: Bangle.appRect.h-20,
|
||||||
|
gridx: 1,
|
||||||
|
gridy: 1,
|
||||||
|
miny: -1,
|
||||||
|
maxy: 2,
|
||||||
|
title: /*LANG*/"Wakeup " + require("locale").date(endtime, 1),
|
||||||
|
ylabel: y => y >= 0 && y <= 1 ? yTicks[y] : "",
|
||||||
|
xlabel: x => {
|
||||||
|
if (x === Math.round(data.length/10)) {
|
||||||
|
return require("locale").time(starttime, 1);
|
||||||
|
} else if (x === (data.length-2)-Math.round(data.length/10)) {
|
||||||
|
return require("locale").time(endtime, 1);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectday() {
|
||||||
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
|
||||||
|
const logs = config.logs.filter(log => log != null && log.filter(entry => entry.type === "alarm").length > 0);
|
||||||
|
logs.sort(function(a, b) { // sort by alarm date desc
|
||||||
|
const adate = new Date(a.filter(entry => entry.type === "alarm")[0].time);
|
||||||
|
const bdate = new Date(b.filter(entry => entry.type === "alarm")[0].time);
|
||||||
|
return bdate - adate;
|
||||||
|
});
|
||||||
|
|
||||||
|
const menu = {};
|
||||||
|
menu[""] = { title: /*LANG*/"Select day" };
|
||||||
|
menu["< Back"] = () => settingsmenu();
|
||||||
|
logs.forEach((log, i) => {
|
||||||
|
const date = new Date(log.filter(entry => entry.type === "alarm")[0].time);
|
||||||
|
menu[require("locale").date(date, 1)] = () => { E.showMenu(); draw(log); };
|
||||||
|
});
|
||||||
|
E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function settingsmenu() {
|
||||||
|
// Show the menu
|
||||||
|
E.showMenu({
|
||||||
|
"" : { "title" : "SleepPhaseAlarm" },
|
||||||
|
'Keep alarm enabled': {
|
||||||
|
value: !!config.settings.disableAlarm,
|
||||||
|
format: v => v?"No":"Yes",
|
||||||
|
onchange: v => {
|
||||||
|
config.settings.disableAlarm = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
}, "< Back" : () => back(),
|
||||||
|
'Run before alarm': {
|
||||||
|
format: v => v === 0 ? 'disabled' : v+'h',
|
||||||
|
value: config.settings.startBeforeAlarm,
|
||||||
|
min: 0, max: 23,
|
||||||
|
onchange: v => {
|
||||||
|
config.settings.startBeforeAlarm = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*LANG*/'Select day': () => selectday(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsmenu();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue