From 134f258d3918b95f6d70fee44c02b6bbd93ffe0b Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Thu, 26 Jan 2023 11:04:50 +0100 Subject: [PATCH] widalarmeta version 0.02 Change font to 5x9 7 segment-style Add settings page Add option to show seconds --- apps/widalarmeta/ChangeLog | 4 ++ apps/widalarmeta/metadata.json | 10 +++-- apps/widalarmeta/screenshot.png | Bin 3491 -> 3269 bytes apps/widalarmeta/settings.js | 45 ++++++++++++++++++++ apps/widalarmeta/widget.js | 71 +++++++++++++++++++++++--------- 5 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 apps/widalarmeta/ChangeLog create mode 100644 apps/widalarmeta/settings.js diff --git a/apps/widalarmeta/ChangeLog b/apps/widalarmeta/ChangeLog new file mode 100644 index 000000000..37b619f7f --- /dev/null +++ b/apps/widalarmeta/ChangeLog @@ -0,0 +1,4 @@ +0.01: New App! +0.02: Change font to 5x9 7 segment-style + Add settings page + Add option to show seconds diff --git a/apps/widalarmeta/metadata.json b/apps/widalarmeta/metadata.json index ef9f55ba8..79387c1c5 100644 --- a/apps/widalarmeta/metadata.json +++ b/apps/widalarmeta/metadata.json @@ -2,8 +2,8 @@ "id": "widalarmeta", "name": "Alarm & Timer ETA", "shortName": "Alarm ETA", - "version": "0.01", - "description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h", + "version": "0.02", + "description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).", "icon": "widget.png", "type": "widget", "tags": "widget", @@ -11,6 +11,8 @@ "provides_widgets" : ["alarm"], "screenshots" : [ { "url":"screenshot.png" } ], "storage": [ - {"name":"widalarmeta.wid.js","url":"widget.js"} - ] + {"name":"widalarmeta.wid.js","url":"widget.js"}, + {"name":"widalarmeta.settings.js","url":"settings.js"} + ], + "data": [{"name":"widalarmeta.json"}] } diff --git a/apps/widalarmeta/screenshot.png b/apps/widalarmeta/screenshot.png index 41a1095570813bd8d950a8a7d4da4d3eb7102fe9..3a23b757a18419990adac4b35f11416faa27a289 100644 GIT binary patch literal 3269 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 Ah5!Hn literal 3491 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 O09Qv(hqrd<)c*sBA&|ZR diff --git a/apps/widalarmeta/settings.js b/apps/widalarmeta/settings.js new file mode 100644 index 000000000..5922d159d --- /dev/null +++ b/apps/widalarmeta/settings.js @@ -0,0 +1,45 @@ +(function(back) { + const CONFIGFILE = "widalarmeta.json"; + // Load settings + const settings = Object.assign({ + maxhours: 24, + drawBell: false, + showSeconds: 0, // 0=never, 1=only when display is unlocked, 2=for less than a minute + }, require("Storage").readJSON(CONFIGFILE,1) || {}); + + function writeSettings() { + require('Storage').writeJSON(CONFIGFILE, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "Alarm & Timer ETA" }, + "< Back" : () => back(), + /*LANG*/'Maximum hours': { + format: v => v === 0 ? /*LANG*/'disabled' : v, + value: settings.maxhours, + min: 0, max: 24, + onchange: v => { + settings.maxhours = v; + writeSettings(); + } + }, + /*LANG*/'Draw bell': { + value: !!settings.drawBell, + format: v => v?/*LANG*/"Yes":/*LANG*/"No", + onchange: v => { + settings.drawBell = v; + writeSettings(); + } + }, + /*LANG*/'Show seconds': { + value: settings.showSeconds, + min: 0, max: 2, + format: v => [/*LANG*/"Never", /*LANG*/"Unlocked", /*LANG*/"Last minute"][v || 0], + onchange: v => { + settings.showSeconds = v; + writeSettings(); + } + }, + }); +}); diff --git a/apps/widalarmeta/widget.js b/apps/widalarmeta/widget.js index 0cddf953a..ff3390d89 100644 --- a/apps/widalarmeta/widget.js +++ b/apps/widalarmeta/widget.js @@ -1,35 +1,68 @@ (() => { + require("Font5x9Numeric7Seg").add(Graphics); const alarms = require("Storage").readJSON("sched.json",1) || []; + const config = Object.assign({ + maxhours: 24, + drawBell: false, + showSeconds: 0, // 0=never, 1=only when display is unlocked, 2=for less than a minute + }, require("Storage").readJSON("widalarmeta.json",1) || {}); function draw() { const times = alarms.map(alarm => require("sched").getTimeToAlarm(alarm)).filter(a => a !== undefined); const next = Math.min.apply(null, times); - if (next > 0 && next < 86400000) { - const hours = Math.floor((next % 86400000) / 3600000).toString(); - const minutes = Math.floor(((next % 86400000) % 3600000) / 60000).toString(); + let calcWidth = 0; + let drawSeconds = false; + + if (next > 0 && next < config.maxhours*60*60*1000) { + const hours = Math.floor((next-1) / 3600000).toString(); + const minutes = Math.floor(((next-1) % 3600000) / 60000).toString(); + const seconds = Math.floor(((next-1) % 60000) / 1000).toString(); + drawSeconds = (config.showSeconds & 0b01 && !Bangle.isLocked()) || (config.showSeconds & 0b10 && next <= 1000*60); g.reset(); // reset the graphics context to defaults (color/font/etc) g.setFontAlign(0,0); // center fonts g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23); var text = hours.padStart(2, '0') + ":" + minutes.padStart(2, '0'); - g.setFont("6x8:1x2"); - g.drawString(text, this.x+this.width/2, this.y+12); - if (this.width === 0) { - this.width = 6*5+2; - Bangle.drawWidgets(); // width changed, re-layout + if (drawSeconds) { + text += ":" + seconds.padStart(2, '0'); } + g.setFont("5x9Numeric7Seg:1x2"); + g.drawString(text, this.x+this.width/2, this.y+12); + + calcWidth = 5*5+2; + if (drawSeconds) { + calcWidth += 3*5; + } + } else if (times.length > 0 && config.drawBell) { + // next alarm too far in future, draw only widalarm bell + g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y); + calcWidth = 24; } + + if (this.width !== calcWidth) { + // width changed, re-layout + this.width = calcWidth; + Bangle.drawWidgets(); + } + + // redraw next full minute or second + const period = drawSeconds ? 1000 : 60000; + let timeout = next > 0 ? next % period : period - (Date.now() % period); + if (timeout === 0) { + timeout += period; + } + setTimeout(()=>{ + WIDGETS["widalarmeta"].draw(WIDGETS["widalarmeta"]); + }, timeout); + } /* draw */ + + if (config.maxhours > 0) { + // add your widget + WIDGETS["widalarmeta"]={ + area:"tl", + width: 0, // hide by default = assume no timer + draw:draw + }; } - - setInterval(function() { - WIDGETS["widalarmeta"].draw(WIDGETS["widalarmeta"]); - }, 30000); // update every half minute - - // add your widget - WIDGETS["widalarmeta"]={ - area:"tl", - width: 0, // hide by default = assume no timer - draw:draw - }; })();