Merge pull request #2563 from nxdefiant/master

sleepphasealarm 0.14
master
Gordon Williams 2023-02-08 13:12:15 +00:00 committed by GitHub
commit cb8e733738
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 133 additions and 32 deletions

View File

@ -14,3 +14,6 @@
0.11: Minor tweaks
0.12: Support javascript command to execute as defined in scheduler 'js' configuration
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

View File

@ -23,6 +23,11 @@ Replacing the watch strap with a more comfortable one (e.g. made of nylon) is re
## 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.
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:
![](screenshot.jpg)
![](screenshot_log.png)
The logs can also be viewed with the download button in the App Loader:
![](interface.jpg)

View File

@ -14,6 +14,7 @@ const active = alarms.filter(alarm => require("sched").getTimeToAlarm(alarm));
const schedSettings = require("sched").getSettings();
let buzzCount = schedSettings.buzzCount;
let logs = [];
let drawTimeTimeout;
// 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.
@ -26,7 +27,7 @@ const nomothresh=0.023; // Original implementation: 6, resolution 11 bit, scale
const sleepthresh=600;
var ess_values = [];
var slsnds = 0;
function calc_ess(acc_magn) {
function calc_ess(acc_magn) {"ram"
ess_values.push(acc_magn);
if (ess_values.length == winwidth) {
@ -90,10 +91,12 @@ function drawApp() {
layout.alarm_date.label = `${LABEL_WAKEUP_TIME}: ${alarmHour}:${alarmMinute}`;
layout.render();
function drawTime() {
function drawTime() {"ram"
const drawSeconds = !Bangle.isLocked();
if (Bangle.isLCDOn()) {
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 diffHour = Math.floor((diff % 86400000) / 3600000).toString();
const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString();
@ -101,10 +104,21 @@ function drawApp() {
layout.render();
}
setTimeout(()=>{
drawTime();
}, 1000 - (Date.now() % 1000));
const period = drawSeconds ? 1000 : 60000;
if (this.drawTimeTimeout !== undefined) {
clearTimeout(this.drawTimeTimeout);
}
drawTimeTimeout = setTimeout(()=>{
drawTimeTimeout = undefined;
drawTime();
}, period - (Date.now() % period));
}
Bangle.on('lock', function(on) {
if (on === false) {
drawTime();
}
});
drawTime();
}
@ -132,8 +146,9 @@ function addLog(time, type) {
var minAlarm = new Date();
var measure = true;
if (nextAlarmDate !== undefined) {
config.logs[nextAlarmDate.getDate()] = []; // overwrite log on each day of month
logs = config.logs[nextAlarmDate.getDate()];
const logday = BANGLEJS2 ? nextAlarmDate.getDate() : 0;
config.logs[logday] = []; // overwrite log on each day of month
logs = config.logs[logday];
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -146,7 +161,7 @@ if (nextAlarmDate !== undefined) {
layout.render();
Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval
Bangle.setPollInterval(80); // 12.5Hz
Bangle.on('accel', (accelData) => {
Bangle.on('accel', (accelData) => {"ram"
const now = new Date();
const acc = accelData.mag;
const swest = calc_ess(acc);

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -2,7 +2,7 @@
"id": "sleepphasealarm",
"name": "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.",
"icon": "app.png",
"tags": "alarm",
@ -15,5 +15,6 @@
{"name":"sleepphasealarm.img","url":"app-icon.js","evaluate":true}
],
"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

View File

@ -13,6 +13,79 @@
require('Storage').writeJSON(CONFIGFILE, config);
}
function draw(log) {
const step = 10*60*1000; // resolution 10min
const yTicks = ["sleep", "awake", "alarm"];
const starttime = new Date(log[0].time);
const endtime = new Date(log[log.length-1].time);
let logidx = 0;
let curtime = starttime;
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);
}
data[i++] = curval;
curtime = new Date(curtime + step);
}
data[i] = 1; // always end with awake
Bangle.setUI({
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" },
@ -33,5 +106,9 @@
writeSettings();
}
},
/*LANG*/'Select day': () => selectday(),
});
}
settingsmenu();
})