From 57e325548f9a4a89b5a3405f10b97f638e0c677e Mon Sep 17 00:00:00 2001 From: hughbarney Date: Wed, 2 Feb 2022 22:35:06 +0000 Subject: [PATCH] Lazybones: idle timer based on step threshold --- apps/lazybones/ChangeLog | 1 + apps/lazybones/README.md | 5 + apps/lazybones/lazybones.app.js | 204 +++++++++++++++++++++++++++++++ apps/lazybones/lazybones.icon.js | 1 + apps/lazybones/lazybones.png | Bin 0 -> 1547 bytes apps/lazybones/metadata.json | 15 +++ 6 files changed, 226 insertions(+) create mode 100644 apps/lazybones/ChangeLog create mode 100644 apps/lazybones/README.md create mode 100644 apps/lazybones/lazybones.app.js create mode 100644 apps/lazybones/lazybones.icon.js create mode 100644 apps/lazybones/lazybones.png create mode 100644 apps/lazybones/metadata.json diff --git a/apps/lazybones/ChangeLog b/apps/lazybones/ChangeLog new file mode 100644 index 000000000..9db0e26c5 --- /dev/null +++ b/apps/lazybones/ChangeLog @@ -0,0 +1 @@ +0.01: first release diff --git a/apps/lazybones/README.md b/apps/lazybones/README.md new file mode 100644 index 000000000..2fe597234 --- /dev/null +++ b/apps/lazybones/README.md @@ -0,0 +1,5 @@ +# Simplest Clock + +The simplest working clock, acts as a tutorial piece + +![](screenshot.jpg) diff --git a/apps/lazybones/lazybones.app.js b/apps/lazybones/lazybones.app.js new file mode 100644 index 000000000..6af26d6bc --- /dev/null +++ b/apps/lazybones/lazybones.app.js @@ -0,0 +1,204 @@ +Graphics.prototype.setFontRoboto = function(scale) { + // Actual height 21 (20 - 0) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAH/zA/+YAAAAAAAHwAAwAAHwAA+AAAAAAAAAAAQACDAAYbADP4B/8A/zAGYZADH4A/+A/7AHYYADCAAAAAAAQAeHgH4eBzgwMMHnhw88GGBw4wHj+AcPgAAAAAAAAAAB4AA/gAGMAAwhwGMcAfuABzgABzgAc+AOMYBhBAAMYAB/AAHwAAAAAHwD5+A/8YGPDAw8YGPzA/HYD4fAADwAB/AAOYAABAAAAHwAA4AAAAAAAAAAH/gD//B8A+cAA7AADAAAAAAAYAAbwAHHgHwf/4A/8AAAAEAABiAAGwAA8AA/AAH+AAGwAByAAEAAAAAAAMAABgAAMAABgAH/wA/+AAMAABgAAMAABgAAAAAAAIAAfAADwAAAABgAAMAABgAAMAABgAAAAAAAAAAAAADAAAYAAAAAAAAADgAB8AB+AA+AA+AA/AAHAAAgAAAAAAB8AB/8Af/wHAHAwAYGADAwAYHAHAf/wB/8AAAAAAAAAAABgAAcAADAAAYAAH//A//4AAAAAAAAAAAAAAAAAAAAABwDAeA4HAPAwHYGBzAwcYHHDAfwYB8DAAAYAAAAAAABgOAcBwHADAwwYGGDAwwYHPHAf/wB58AAAAAAAAADAAB4AAfAAPYAHjAB4YA8DAH//A//4AAYAADAAAAAAAAAEMA/xwH+HAxgYGMDAxgYGODAw/4GD+AAHAAAAAAAAAf8AP/wD2HA5wYGMDAxgYGOHAA/wAD8AAAAAAAAAAAGAAAwAAGADAwB4GB+Aw+AGfAA/gAHwAAwAAAAAAADAB5+Af/wHPDAwwYGGDAwwYHPHAfvwB58AAAAAAAAAAAB+AAf4AHDjAwMYGBjAwM4HDOAf/gB/4AAAAAAAAAAAAYDADAYAAAAAAAAAAYDAfAYHwAAAABAAAcAADgAA+AAGwAB3AAMYABjgAYMAAAAAAAAAAAAAAABmAAMwABmAAMwABmAAMwABmAAMwAAiAAAAAAAAAYMADjgAMYAB3AAGwAA2AADgAAcAABAAAAAAAAAMAADgAA4AAGBzAweYGHAA/wAD8AAEAAAAwAB/4A/PwOAGDgAYYPxmH/Mw4ZmMDMxgZmM+Mx/5mHDAYAIDgDAPBwAf8AAMAAAAAAAYAAfAAPwAP4AH+AH4wA8GAH4wAP2AAPwAAfwAAfAAAYAAAAAAAAAAA//4H//AwwYGGDAwwYGGDAwwYH/HAf/wB58AAAAADAAH/AD/+AcBwHADAwAYGADAwAYGADA4A4DweAODgAAAAAAAAAAAAAAH//A//4GADAwAYGADAwAYGADAYAwD4+AP/gAfwAAAAAAAAAAAH//A//4GDDAwYYGDDAwYYGDDAwYYGCDAgAYAAAAAAAH//A//4GDAAwYAGDAAwYAGDAAwYAGAAAAAAAAAAH/AD/8AcBwHAHAwAYGADAwYYGDDA4YYDz/AOfwAAAAAAAAAAA//4H//A//4ADAAAYAADAAAYAADAAAYAADAA//4H//AAAAAAAAAAAAAAA//4H//AAAAAAAAABAAAeAAB4AADAAAYAADAAAYAAHA//wH/8AAAAAAAAAAAAAAA//4H//AAcAAPAAD4AA/wAOPADg8A4B4GAHAgAYAAAAAAAH//A//4AADAAAYAADAAAYAADAAAYAADAAAAAAAA//4H//A+AAB+AAD8AAD8AAH4AAPAAH4AH4AD8AD8AA+AAH//A//4AAAAAAAH//A//4H//AeAAB8AADwAAPgAAeAAA8AADwH//A//4AAAAAAAAAAAH/AB/8AeDwHAHAwAYGADAwAYGADA4A4DweAP/gA/4AAAAAAAAAAAH//A//4GBgAwMAGBgAwMAGBgAwcAH/AAfwAA8AAAAAA/4AP/gDgOA4A4GADAwAYGADAwAYHAHgeD+B/8wD+GAAAAAAAAAAA//4H//AwYAGDAAwYAGDgAweAHH8Afz4B8HAAAIAAYAPDwD8OA5w4GGDAwwYGHDAwYYHDnAePwBw8AAAAGAAAwAAGAAAwAAGAAA//4H//AwAAGAAAwAAGAAAwAAAAAAAAAH/4A//wAAPAAAYAADAAAYAADAAAYAAPA//wH/8AAAAAAAAgAAHAAA/AAB/AAD+AAD+AAD4AAfAAfwAfwAfwAH4AA4AAEAAA+AAH/AAH/gAD/AAD4AD+AH+AH8AA+AAH+AAD+AAD/AAD4AH/AP/AH+AA8AAAAAAAAAGADA4A4HweAPPgA/wAB8AAfwAPvgDweA8B4GADAAAIGAAA4AAHwAAPgAAfAAA/4AH/AD4AB8AA+AAHgAAwAAAAAAAAAGADAwB4GAfAwPYGDzAx4YGeDA/AYHwDA4AYGADAAAAAAAA///3//+wAA2AAGAAAGAAA+AAD8AAD8AAD4AAH4AAHgAAMAAAAwAA2AAG///3//+AAAAAAAAAAAOAAHwAD4AA8AAD8AADwAAGAAAAAAABgAAMAABgAAMAABgAAMAABgAAMAABgAAAEAAAwAADAAAIAAAAAAAAAAEeABn4Ad3ADMYAZjADMYAZmAB/4AP/AAAAAAAA//4H//ABgwAYDADAYAYDADg4AP+AA/gABwAAAAAAAAA/gAP+ADg4AYDADAYAYDADAYAOOABxwAAAAAEAAH8AB/wAcHADAYAYDADAYAcDA//4H//AAAAAAAAAAAAH8AB/wAdnADMYAZjADMYAZjAB84AHmAAMAAMAABgAB//gf/8HMAAxgAGIAAAAAAH8IB/zAcHMDAZgYDMDAZgcHcD//Af/wAAAAAAAAAAH//A//4AMAADAAAYAADAAAcAAD/4AP/AAAAAAAAAAAGf/Az/4AAAAAAAAAAMz//mf/4AAAAAAAAAAH//A//4ABwAAeAAH4ABzwAcPACAYAABAAAAAAAA//4H//AAAAAAAAAAAAf/AD/4AMAADAAAYAADAAAcAAD/4AP/ABgAAYAADAAAYAADgAAP/AA/4AAAAAAAAf/AD/4AMAADAAAYAADAAAcAAD/4AP/AAAAAAAAAAAAH8AB/wAcHADAYAYDADAYAYDADx4AP+AA/gAAAAAAAAf/8D//gYDADAYAYDADAYAcHAB/wAH8AAEAAAAAAEAAH8AB/wAcHADAYAYDADAYAYDAD//gf/8AAAAAAAAAAAf/AD/4AcAADAAAYAACAAAAEAB5wAfnADMYAZjADGYAYzADn4AOeAAAAAAAADAAAYAAf/wD//ADAYAYDAAAAAAAAD/gAf/AAA4AADAAAYAADAAAwAf/AD/4AAAAAAAAYAAD4AAP4AAP4AAPAAH4AH4AD8AAcAAAAAAQAADwAAf4AAf4AAPAAP4AP4ADwAAfgAA/gAA/AAD4AH+AD+AAeAAAAAAAAACAYAcHADzwAH8AAfAAH8ADx4AcHACAIAcAMD4BgP4MAP/AAPwAP4AP4AD4AAcAAAAAAAAADAYAYHADD4AY7ADOYAfjADwYAcDADAYAAAAADAAA4AH//B/v8cABzAACAAAH//w//+AAAAAAACAACcAAx/n+H//AA4AAHAAAAAAAAAAAAAOAADgAAYAADAAAcAABgAAGAAAwAAGAADwAAcAAAAA"), 32, atob("BQUHDQwPDQQHBwkMBAYGCQwMDAwMDAwMDAwFBAsMCwoTDg0ODgwMDg8GDA0LEg8ODQ4NDA0ODRMNDQ0GCQYJCQYLDAsMCwcMDAUFCwUSDAwMDAcLBwwKEAoKCgcFBw4A"), 21+(scale<<8)+(1<<16)); + return this; +} + +function setSmallFont() { + g.setFontRoboto(); +} + +const h = g.getHeight(); +const w = g.getWidth(); + +function draw() { + if (!idle) + drawClock(); + else + drawIdle(); + queueDraw(); +} + +function drawClock() { + var date = new Date(); + var timeStr = require("locale").time(date,1); + + g.reset(); + g.setColor(g.theme.bg); + g.fillRect(Bangle.appRect); + + g.setFont('Vector', w/3); + g.setFontAlign(0, 0); + g.setColor(g.theme.fg); + g.drawString(timeStr, w/2, h/2); + setSmallFont(); + g.drawString('Last Step ' + lastStepTime, w/2, 3*h/4); +} + +///////////////// IDLE TIMER ///////////////////////////////////// + +function log_debug(o) { + //print(o); +} + +// variable for controlling idle alert +let lastStep = getTime(); +let lastStepTime = '??'; +let warned = 0; +let idle = false; +let IDLE_MINUTES = 26; + +function drawIdle() { + let mins = Math.round((getTime() - lastStep) / 60); + g.reset(); + g.setColor(g.theme.bg); + g.fillRect(Bangle.appRect); + g.setColor(g.theme.fg); + setSmallFont(); + g.setFontAlign(0, 0); + g.drawString('Last step was', w/2, (h/3)); + g.drawString(mins + ' minutes ago', w/2, 20+(h/3)); + dismissBtn.draw(); +} + +/////////////// BUTTON CLASS /////////////////////////////////////////// + +// simple on screen button class +function BUTTON(name,x,y,w,h,c,f,tx) { + this.name = name; + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.color = c; + this.callback = f; + this.text = tx; +} + +// if pressed the callback +BUTTON.prototype.check = function(x,y) { + //console.log(this.name + ":check() x=" + x + " y=" + y +"\n"); + + if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) { + log_debug(this.name + ":callback\n"); + this.callback(); + return true; + } + return false; +}; + +BUTTON.prototype.draw = function() { + g.setColor(this.color); + g.fillRect(this.x, this.y, this.x + this.w, this.y + this.h); + g.setColor("#000"); // the icons and boxes are drawn black + setSmallFont(); + g.setFontAlign(0, 0); + g.drawString(this.text, (this.x + this.w/2), (this.y + this.h/2)); + g.drawRect(this.x, this.y, (this.x + this.w), (this.y + this.h)); +}; + +function dismissPrompt() { + idle = false; + warned = false; + lastStep = getTime(); + Bangle.buzz(100); + draw(); +} + +var dismissBtn = new BUTTON("big",0, 3*h/4 ,w, h/4, "#0ff", dismissPrompt, "Dismiss"); + +Bangle.on('touch', function(button, xy) { + if (idle && dismissBtn.check(xy.x, xy.y)) return; +}); + +// if we get a step then we are not idle +Bangle.on('step', s => { + setLastStepTime(); + lastStep = getTime(); + // redraw if we had been idle + if (idle == true) { + dismissPrompt(); + } + idle = false; + warned = 0; +}); + +function setLastStepTime() { + var date = new Date(); + lastStepTime = require("locale").time(date,1); +} + +function checkIdle() { + let hour = (new Date()).getHours(); + let active = (hour >= 9 && hour < 21); + //let active = true; + let dur = getTime() - lastStep; + + if (active && dur > IDLE_MINUTES * 60) { + drawIdle(); + if (warned++ < 3) { + buzzer(warned); + log_debug("checkIdle: warned=" + warned); + Bangle.setLocked(false); + } + idle = true; + } else { + idle = false; + warned = 0; + } +} + +setLastStepTime(); + +// timeout for multi-buzzer +var buzzTimeout; + +// n buzzes +function buzzer(n) { + log_debug("buzzer n=" + n); + + if (n-- < 1) return; + Bangle.buzz(250); + + if (buzzTimeout) clearTimeout(buzzTimeout); + buzzTimeout = setTimeout(function() { + buzzTimeout = undefined; + buzzer(n); + }, 500); +} + + +/////////////////////////////////////////////////////////////////////////////// + + +// timeout used to update every minute +var drawTimeout; +const DRAW_PERIOD = 60000; + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + checkIdle(); + draw(); + }, DRAW_PERIOD - (Date.now() % DRAW_PERIOD)); +} + +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +g.clear(); + +// Show launcher when middle button pressed +Bangle.setUI("clock"); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); diff --git a/apps/lazybones/lazybones.icon.js b/apps/lazybones/lazybones.icon.js new file mode 100644 index 000000000..ff3755705 --- /dev/null +++ b/apps/lazybones/lazybones.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwMB///4EDAYP//gNBn4FB+E//kPAoPBAQODAQOPAQPx//+j4FB/F//l/AoP+DQImC/8//IFDz/zAofPAovnAofnApfPAofzAshgBLwIFB/EH8Ef+fO+Pj4+PufmuYOB51n8nn/Of88nAovwAocP+IFDx/5AoefAoIjBAoYvBAoXwg1gh4FB/EcnEfAoLGBYgOfU4IjBU4P+v/xx4DB/8P+VP+BnBw/wh/hAoPj/Ef45zCWQQCB/YCB74CBZgQqBAo34AQLsCIAJCCAAMAgCeD4EDAYIA=")) diff --git a/apps/lazybones/lazybones.png b/apps/lazybones/lazybones.png new file mode 100644 index 0000000000000000000000000000000000000000..ecca084c665974e3e21b80433654a6160e6c1ea2 GIT binary patch literal 1547 zcmV+m2K4!fP)T3ZAos4Z;)5u{QdQ9saBe4xcNl1Mb- zgKA>LSAr!(k!aK?5?_fQP~@9_FqB9X(-NZ;F%*f7F+LiDiHcZCo0yskZK>^TZ`Thq z>z;1+?xXki(tmPucIV8@`JcUe=FFUpkvd9%8-OZc8E^qG$M_|He}L24_5jC#cAypL zP6LO?s06kFzW|oc?+nl%pmPj(8CaSL;;VpzE&`{3Pk=4J&A?^Ag+R7pm#ebH>SL?g zbQ*rffG>cXrh@oF;4>HLmylN`RaC04Q-=R$pdt;v0ay?8spCI^dgO_w8@cMc1LyU7 zAQgu(V7od?0B->Ksc{U2d>t#HW523gBI8YUa2lvh7rO|kMg9c~cs;7|6KdECT$x^8 zk#IE7D?T9Fw)J%yyAVk^Swt02WYRPkrV$MJKG z9Av|@fI2_L7FAlC0a8(M7sBoHWD4cTZg>)z@pMBD(1mcEZws>wh1E#B{TLX{D7A1H z1@<8v{}1ct1XgN$FoUF`;()d{dUduV?^sF3=tM^u!tn>M&a=P(liap5gc!0P^t(H9 zak9+mj&6i&E;wu8JZ%Th1*3=<(6$hq2@Gpnka0TEQK;>(DU2cM>x6e}8e#wmsT0;} zH?m304C$yA_yCDs$B}Yy6Hw%1Qv@_1<)mHMzYp9Na?ApJ*6P>Vd2I+M%_J|<|G>M{ zTn3!*v1=mu6~0b&TfI7WYUjgxKOZR}pEPu`kG|Gl_fzDf}ypdkc8SYXbG!xm)iy==}~uXC2N)QtaU)hEx1+HsqUC zwjb|(rWp89bv79C9WGg;-rw&vfs2rZ+>6B6Jxr{GzEjyPUJev1RHx05eXFt+K6a~A z=X*o;xXK!V8Ax@O0Olrha##)Ok=1Tye)IIc(}x5|Q9gnbuR->g5rPi|I&}=2V42?c z0}m0RsJREHkhg%NMqi!EJ`5mXZ})bn%R-*oS%TA)-8#nYhU}<|)7_?lnT>*kw=Q6o z-ZvV3rP`hdAW?#Z;x3#Mw=N{Y1SN{L?@~jyN84G*KeW#@a8FY*Swra{gm3 zU>kOHToPsig~-8HACTwKUk!YzgV|&fI*5JbkqsO%`j6l|@j9LKn9(0spI-Y!J`zR; ziI6QfspDsSbPA9sQpLmsZj;)#bdc$#vS0REy)yJzB+uhJm5~|!r-K~3npQk z3=6f5Jb}qzF7m#10q3W|-)!VWWElAvo)OqN%{RcJ6#Oj0S>!v@z$-Ln;^aD`NSNJ- zZ>) xr4%{g$i+E&=|j?jzmQY4c4SvRfJ|=E_z%s}z^MCt-emv)002ovPDHLkV1oDD-4Xx* literal 0 HcmV?d00001 diff --git a/apps/lazybones/metadata.json b/apps/lazybones/metadata.json new file mode 100644 index 000000000..e1df6dc5d --- /dev/null +++ b/apps/lazybones/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "lazybones", + "name": "Lazybones", + "version": "0.01", + "description": "Idleness timer, warns when you have been sat too long", + "icon": "lazybones.png", + "screenshots": [{"url":"screenshot_lazybones.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS","BANGLEJS2"], + "storage": [ + {"name":"lazybones.app.js","url":"lazybones.app.js"}, + {"name":"lazybones.img","url":"lazybones.icon.js","evaluate":true} + ] +}