From 970b7300141ad31cae42767a4a610d08b46c8b4b Mon Sep 17 00:00:00 2001 From: hughbarney Date: Tue, 8 Feb 2022 23:12:18 +0000 Subject: [PATCH] Pastel: incorpprated lazybones idle timer --- apps/pastel/ChangeLog | 1 + apps/pastel/metadata.json | 2 +- apps/pastel/pastel.app.js | 171 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 166 insertions(+), 8 deletions(-) diff --git a/apps/pastel/ChangeLog b/apps/pastel/ChangeLog index 570c37507..63636b1c1 100644 --- a/apps/pastel/ChangeLog +++ b/apps/pastel/ChangeLog @@ -13,3 +13,4 @@ which requires 2.11.27 firmware to reset at midnight 0.13: call process.memory(false) to avoid triggering a GC of memory supported in pre 2.12.13 firmware +0.14: incorporated lazybones idle timer, configuration settings to come diff --git a/apps/pastel/metadata.json b/apps/pastel/metadata.json index 61b636842..19458f30d 100644 --- a/apps/pastel/metadata.json +++ b/apps/pastel/metadata.json @@ -2,7 +2,7 @@ "id": "pastel", "name": "Pastel Clock", "shortName": "Pastel", - "version": "0.13", + "version": "0.14", "description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times. Requires firmware 2.11.27", "icon": "pastel.png", "dependencies": {"mylocation":"app","weather":"app"}, diff --git a/apps/pastel/pastel.app.js b/apps/pastel/pastel.app.js index 5def5737c..ac3f5c3ee 100644 --- a/apps/pastel/pastel.app.js +++ b/apps/pastel/pastel.app.js @@ -4,9 +4,19 @@ const storage = require('Storage'); const locale = require("locale"); const SETTINGS_FILE = "pastel.json"; const LOCATION_FILE = "mylocation.json"; +const w = g.getWidth(); +const h = g.getHeight(); let settings; let location; +// variable for controlling idle alert +let lastStep = getTime(); +let lastStepTime = '??'; +let warned = 0; +let idle = false; +let checkIdleness = true; +let IDLE_MINUTES = 26; + // cloud, sun, partSun, snow, rain, storm, error // create 1 bit, max contrast, brightness set to 85 var cloudIcon = require("heatshrink").decompress(atob("kEggIfcj+AAYM/8ADBuFwAYPAmADCCAMBwEf8ADBhFwg4aBnEPAYMYjAVBhgDDDoQDHCYc4jwDB+EP///FYIDBMTgA==")); @@ -16,6 +26,10 @@ var snowIcon = require("heatshrink").decompress(atob("kEggITQj/AAYM98ADBsEwAYPAj var rainIcon = require("heatshrink").decompress(atob("kEggIPMh+AAYM/8ADBuFwAYPgmADB4EbAYOAj/ggOAhnwg4aBnAeCjEcCIMMjADCDoQDHjAPCnAXCuEP///8EDAYJECAAXBwkAgPDhwDBwUMgEEhkggEOjFgFgMQLYQAOA==")); var errIcon = require("heatshrink").decompress(atob("kEggILIgOAAYsD4ADBg/gAYMGsADBhkwAYsYjADCjgDBmEMAYNxxwDBsOGAYPBwYDEgOBwOAgYDB4EDHYPAgwDBsADDhgDBFIcwjAHBjE4AYMcmADBhhNCKIcG/4AGOw4A==")); +// saves having to recode all the small font calls +function setSmallFont() { + g.setFontLatoSmall(); +} function loadSettings() { settings = require("Storage").readJSON(SETTINGS_FILE,1)||{}; @@ -71,17 +85,18 @@ function getSteps() { if (WIDGETS.wpedom !== undefined) return WIDGETS.wpedom.getSteps(); else - return '???' + return '???'; } } const infoData = { ID_BLANK: { calc: () => '' }, - ID_DATE: { calc: () => {var d = (new Date).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} }, - ID_DAY: { calc: () => {var d = require("locale").dow(new Date).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} }, + ID_DATE: { calc: () => {var d = (new Date()).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} }, + ID_DAY: { calc: () => {var d = require("locale").dow(new Date()).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} }, ID_SR: { calc: () => 'Sunrise: ' + sunRise }, ID_SS: { calc: () => 'Sunset: ' + sunSet }, ID_STEP: { calc: () => 'Steps: ' + getSteps() }, + ID_LAST: { calc: () => 'Last Step: ' + lastStepTime }, ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' }, ID_MEM: { calc: () => {var val = process.memory(false); return 'Ram: ' + Math.round(val.usage*100/val.total) + '%';} }, ID_ID: { calc: () => {var val = NRF.getAddress().split(':'); return 'Id: ' + val[4] + val[5];} }, @@ -152,6 +167,14 @@ function getWeather() { } function draw() { + if (!idle) + drawClock(); + else + drawIdle(); + queueDraw(); +} + +function drawClock() { var d = new Date(); var da = d.toString().split(" "); var time = da[4].substr(0,5); @@ -166,11 +189,8 @@ function draw() { if (parseInt(hh) > 12) hh = h2.substr(h2.length -2); - var w = g.getWidth(); - var h = g.getHeight(); var x = (g.getWidth()/2); var y = (g.getHeight()/3); - var weatherJson = getWeather(); var w_temp; var w_icon; @@ -190,7 +210,8 @@ function draw() { } g.reset(); - g.clearRect(0, 30, w, h - 24); + g.setColor(g.theme.bg); + g.fillRect(Bangle.appRect); // draw a grid like graph paper if (settings.grid && process.env.HWVERSION !=1) { @@ -249,6 +270,141 @@ function draw() { queueDraw(); } + +///////////////// IDLE TIMER ///////////////////////////////////// + +function log_debug(o) { + //print(o); +} + +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() { + if (!checkIdleness) { + idle = false; + warned = false; + return; + } + + 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; @@ -258,6 +414,7 @@ function queueDraw() { drawTimeout = setTimeout(function() { drawTimeout = undefined; prevInfo(); + checkIdle(); draw(); }, 60000 - (Date.now() % 60000)); }