2022-01-24 23:17:43 +00:00
const BANGLEJS2 = process . env . HWVERSION == 2 ; //# check for bangle 2
2020-05-27 05:50:48 +00:00
const alarms = require ( "Storage" ) . readJSON ( "alarm.json" , 1 ) || [ ] ;
const active = alarms . filter ( a => a . on ) ;
// Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS):
2020-05-29 15:18:12 +00:00
// 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.
2020-05-27 05:50:48 +00:00
// https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en
//
// Function needs to be called for every measurement but returns a value at maximum once a second (see winwidth)
// start of sleep marker is delayed by sleepthresh due to continous data reading
const winwidth = 13 ;
const nomothresh = 0.006 ;
const sleepthresh = 600 ;
var ess _values = [ ] ;
var slsnds = 0 ;
function calc _ess ( val ) {
2020-05-28 13:38:46 +00:00
ess _values . push ( val ) ;
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
if ( ess _values . length == winwidth ) {
// calculate standard deviation over ~1s
const mean = ess _values . reduce ( ( prev , cur ) => cur + prev ) / ess _values . length ;
const stddev = Math . sqrt ( ess _values . map ( val => Math . pow ( val - mean , 2 ) ) . reduce ( ( prev , cur ) => prev + cur ) / ess _values . length ) ;
ess _values = [ ] ;
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
// check for non-movement according to the threshold
const nonmot = stddev < nomothresh ;
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
// amount of seconds within non-movement sections
if ( nonmot ) {
slsnds += 1 ;
if ( slsnds >= sleepthresh ) {
2022-03-21 10:03:32 +00:00
return true ; // sleep
2020-05-28 13:38:46 +00:00
}
} else {
slsnds = 0 ;
2022-03-21 10:03:32 +00:00
return false ; // awake
2020-05-28 13:38:46 +00:00
}
}
2020-05-27 05:50:48 +00:00
}
// locate next alarm
var nextAlarm ;
active . forEach ( alarm => {
2020-05-28 13:38:46 +00:00
const now = new Date ( ) ;
const alarmHour = alarm . hr / 1 ;
const alarmMinute = Math . round ( ( alarm . hr % 1 ) * 60 ) ;
var dateAlarm = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) , alarmHour , alarmMinute ) ;
if ( dateAlarm < now ) { // dateAlarm in the past, add 24h
dateAlarm . setTime ( dateAlarm . getTime ( ) + ( 24 * 60 * 60 * 1000 ) ) ;
}
if ( nextAlarm === undefined || dateAlarm < nextAlarm ) {
nextAlarm = dateAlarm ;
}
2020-05-27 05:50:48 +00:00
} ) ;
2022-01-24 23:17:43 +00:00
function drawString ( s , y ) { //# replaced x: always centered
g . reset ( ) ; //# moved up to prevent blue background
g . clearRect ( 0 , y - 12 , 239 , y + 8 ) ; //# minimized upper+lower clearing
g . setFont ( "Vector" , 20 ) ;
g . setFontAlign ( 0 , 0 ) ; // align centered
g . drawString ( s , g . getWidth ( ) / 2 , y ) ; //# set x to center
2020-05-27 05:50:48 +00:00
}
function drawApp ( ) {
2022-01-24 23:17:43 +00:00
g . clearRect ( 0 , 24 , 239 , 215 ) ; //# no problem
2020-05-28 13:38:46 +00:00
var alarmHour = nextAlarm . getHours ( ) ;
var alarmMinute = nextAlarm . getMinutes ( ) ;
if ( alarmHour < 10 ) alarmHour = "0" + alarmHour ;
if ( alarmMinute < 10 ) alarmMinute = "0" + alarmMinute ;
2022-01-24 23:17:43 +00:00
const s = "Alarm at " + alarmHour + ":" + alarmMinute + "\n\n" ; //# make distinct to time
2020-05-28 13:38:46 +00:00
E . showMessage ( s , "Sleep Phase Alarm" ) ;
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
function drawTime ( ) {
if ( Bangle . isLCDOn ( ) ) {
const now = new Date ( ) ;
var nowHour = now . getHours ( ) ;
var nowMinute = now . getMinutes ( ) ;
var nowSecond = now . getSeconds ( ) ;
if ( nowHour < 10 ) nowHour = "0" + nowHour ;
if ( nowMinute < 10 ) nowMinute = "0" + nowMinute ;
if ( nowSecond < 10 ) nowSecond = "0" + nowSecond ;
2022-01-24 23:17:43 +00:00
const time = nowHour + ":" + nowMinute + ( BANGLEJS2 ? "" : ":" + nowSecond ) ; //# hide seconds on bangle 2
2022-01-24 23:39:37 +00:00
drawString ( time , BANGLEJS2 ? 85 : 105 ) ; //# remove x, adjust height for bangle 2 an newer firmware
2020-05-28 13:38:46 +00:00
}
}
2020-05-27 05:50:48 +00:00
2022-01-24 23:17:43 +00:00
if ( BANGLEJS2 ) {
drawTime ( ) ;
setTimeout ( _ => {
drawTime ( ) ;
setInterval ( drawTime , 60000 ) ;
} , 60000 - Date . now ( ) % 60000 ) ; //# every new minute on bangle 2
} else {
setInterval ( drawTime , 500 ) ; // 2Hz
}
2020-05-27 05:50:48 +00:00
}
var buzzCount = 19 ;
function buzz ( ) {
2021-03-23 18:56:34 +00:00
if ( ( require ( 'Storage' ) . readJSON ( 'setting.json' , 1 ) || { } ) . quiet > 1 ) return ; // total silence
2020-05-28 13:38:46 +00:00
Bangle . setLCDPower ( 1 ) ;
Bangle . buzz ( ) . then ( ( ) => {
if ( buzzCount -- ) {
setTimeout ( buzz , 500 ) ;
} else {
// back to main after finish
setTimeout ( load , 1000 ) ;
}
} ) ;
2020-05-27 05:50:48 +00:00
}
// run
var minAlarm = new Date ( ) ;
var measure = true ;
if ( nextAlarm !== undefined ) {
2022-01-24 23:17:43 +00:00
Bangle . loadWidgets ( ) ; //# correct widget load draw order
2020-05-28 13:38:46 +00:00
Bangle . drawWidgets ( ) ;
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
// minimum alert 30 minutes early
minAlarm . setTime ( nextAlarm . getTime ( ) - ( 30 * 60 * 1000 ) ) ;
setInterval ( function ( ) {
const now = new Date ( ) ;
const acc = Bangle . getAccel ( ) . mag ;
const swest = calc _ess ( acc ) ;
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
if ( swest !== undefined ) {
if ( Bangle . isLCDOn ( ) ) {
2022-01-24 23:17:43 +00:00
drawString ( swest ? "Sleep" : "Awake" , BANGLEJS2 ? 150 : 180 ) ; //# remove x, adjust height
2020-05-28 13:38:46 +00:00
}
}
2020-05-27 05:50:48 +00:00
2020-05-28 13:38:46 +00:00
if ( now >= nextAlarm ) {
// The alarm widget should handle this one
setTimeout ( load , 1000 ) ;
} else if ( measure && now >= minAlarm && swest === false ) {
buzz ( ) ;
measure = false ;
}
} , 80 ) ; // 12.5Hz
drawApp ( ) ;
2020-05-27 05:50:48 +00:00
} else {
2020-05-28 13:38:46 +00:00
E . showMessage ( 'No Alarm' ) ;
setTimeout ( load , 1000 ) ;
2020-05-27 05:50:48 +00:00
}
2022-01-24 23:17:43 +00:00
// BTN2 to menu, BTN3 to main # on bangle 2 only BTN to main
if ( ! BANGLEJS2 ) setWatch ( Bangle . showLauncher , BTN2 , { repeat : false , edge : "falling" } ) ;
setWatch ( ( ) => load ( ) , BANGLEJS2 ? BTN : BTN3 , { repeat : false , edge : "falling" } ) ;