From decc3dcfaf0240ed7f471c32bf33c082070e1bd3 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Sat, 2 May 2020 21:43:16 +0100 Subject: [PATCH 01/13] Create dotmatrix clock --- apps.json | 14 + apps/dotmatrixclock/ChangeLog | 1 + apps/dotmatrixclock/README.md | 16 ++ apps/dotmatrixclock/app.js | 245 ++++++++++++++++++ .../dotmatrix-clock-screen-shot.png | Bin 0 -> 1855 bytes apps/dotmatrixclock/dotmatrixclock-icon.js | 1 + apps/dotmatrixclock/dotmatrixclock.png | Bin 0 -> 10067 bytes 7 files changed, 277 insertions(+) create mode 100644 apps/dotmatrixclock/ChangeLog create mode 100644 apps/dotmatrixclock/README.md create mode 100755 apps/dotmatrixclock/app.js create mode 100755 apps/dotmatrixclock/dotmatrix-clock-screen-shot.png create mode 100644 apps/dotmatrixclock/dotmatrixclock-icon.js create mode 100755 apps/dotmatrixclock/dotmatrixclock.png diff --git a/apps.json b/apps.json index 906331f65..34cd4fe2e 100644 --- a/apps.json +++ b/apps.json @@ -1732,5 +1732,19 @@ "storage": [ {"name":"rndmclk.wid.js","url":"widget.js"} ] + }, + { "id": "dotmatricsclock", + "name": "Dotmatrix Clock", + "icon": "dotmatrixclock.png", + "version":"0.01", + "description": "A clear white-on-blue dotmatrix simulated clock", + "tags": "clock,dotmatrix,retro", + "type": "clock", + "allow_emulator":true, + "readme": "README.md", + "storage": [ + {"name":"dotmatrixclock.app.js","url":"app.js"}, + {"name":"dotmatrixclock.img","url":"dotmatrixclock-icon.js","evaluate":true} + ] } ] diff --git a/apps/dotmatrixclock/ChangeLog b/apps/dotmatrixclock/ChangeLog new file mode 100644 index 000000000..7ab9e14a9 --- /dev/null +++ b/apps/dotmatrixclock/ChangeLog @@ -0,0 +1 @@ +0.01: Create dotmatrix clock app diff --git a/apps/dotmatrixclock/README.md b/apps/dotmatrixclock/README.md new file mode 100644 index 000000000..5c67c8dfe --- /dev/null +++ b/apps/dotmatrixclock/README.md @@ -0,0 +1,16 @@ +# Dotmatrix clock + +A clock face simulating the classic dotmatrix displays. Shows time, date, compass, and heart rate. + +![](dotmatrix-clock-screen-shot.png) + +## Features + +* Easy to read digits +* Simulated white-on-blue dotmatrix display +* Compass +* Heart rate monitor + +## Requests + +If you have any feature requests, please send an email to the author paulcockrell@gmail.com` diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js new file mode 100755 index 000000000..9661f54cf --- /dev/null +++ b/apps/dotmatrixclock/app.js @@ -0,0 +1,245 @@ +/** + * BangleJS DotMatrixCLOCK + * + * + Original Author: Paul Cockrell https://github.com/paulcockrell + * + Created: May 2020 + */ +const storage = require('Storage'); +const settings = (storage.readJSON('setting.json', 1) || {}); +const is12Hour = settings["12hour"] || false; + +const font7x7 = { + "empty": "00000000000000", + "0": "3E61514945433E", + "1": "1808080808081C", + "2": "7E01013E40407F", + "3": "7E01013E01017E", + "4": "4141417F010101", + "5": "7F40407E01017E", + "6": "3E40407E41413E", + "7": "3F010202040408", + "8": "3E41413E41413E", + "9": "3E41413F01013E", +}; + +const font5x5 = { + "empty": "0000000000", + "0": "0E1915130E", + "1": "0C0404040E", + "2": "1E010E101F", + "3": "1E010E011E", + "4": "11111F0101", + "5": "1F101E011E", + "6": "0E101E110E", + "7": "1F01020408", + "8": "0E110E110E", + "9": "0E110F010E", + "A": "040A0E1111", + "B": "1E111E111E", + "C": "0F1010100F", + "D": "1E1111111E", + "E": "1F101E101F", + "F": "1F101E1010", + "G": "0F1013110E", + "H": "11111F1111", + "I": "0E0404040E", + "J": "1F0404140C", + "L": "101010101F", + "M": "111B151111", + "N": "1119151311", + "O": "0E1111110E", + "P": "1E111E1010", + "R": "1E111E1111", + "S": "0F100E011E", + "T": "1F04040404", + "U": "111111110E", + "V": "1111110A04", + "W": "111115150A", + "Y": "110A040404", +}; + +// Char renderer +const COLORS = { + BG: "#0297fe", + DARK: "#3b3ce8", + LIGHT: "#E9ffff", +}; + +// Example +// binToHex(["0111110", "1000000", "1000000", "1111110", "1000001", "1000001", "0111110"]) +function binToHex(bins /* array of binary strings */) { + return bins.map(bin => ("00" + (parseInt(bin, 2).toString(16))).substr(-2).toUpperCase()).join(""); +} + +// Example +// hexToBin("3E40407E41413E") +function hexToBin(hexStr) { + return ( + hexStr + .replace(/../g, el => el + '_') + .slice(0, -1) + .split('_') + .map(hex => ("00000000" + (parseInt(hex, 16)).toString(2)).substr(-8)) + ); +} + +function drawPixel(opts) { + g.setColor(opts.color); + g.fillRect(opts.x, opts.y, opts.x + opts.w, opts.y + opts.h); +} + +function drawGrid(pos /* {x:int, y:int} */, dims /* {rows:int, cols:int} */, charAsBin, opts /* {pxlW:int, pxlH:int, gap:int} */) { + const defaultOpts = { + pxlW: 5, pxlH: 5, + gap: 1, + offColor: COLORS.DARK, onColor: COLORS.LIGHT + }; + const pxl = Object.assign({}, defaultOpts, opts); + + for (let r = 0; r < dims.rows; r++) { + const y = pos.y + ((pxl.pxlH + pxl.gap) * r); + for (let c = 7; c > (7 - dims.cols); c--) { + const x = pos.x + ((pxl.pxlW + pxl.gap) * c); + const color = (charAsBin && parseInt(charAsBin[r][c])) ? pxl.onColor : pxl.offColor; + + drawPixel({ + x: x, y: y, + w: pxl.pxlW, h: pxl.pxlH, + color: color, + }); + } + } +} + +function drawFont(str, font, x, y) { + let fontMap, rows, cols; + + switch(font) { + case "7x7": + fontMap = font7x7; + rows = cols = 7; + break; + case "5x5": + fontMap = font5x5; + rows = cols = 5; + break; + default: + throw "Unknown font type: " + font; + } + + const pxlW = 2; + const pxlH = 2; + const gap = 2; + const gutter = 3; + const charArr = str.split(""); + const gridWidthTotal = (rows * (pxlW + gap)) + gutter; + for (let i = 0; i < charArr.length; i++) { + const charAsBin = fontMap.hasOwnProperty(charArr[i])? + hexToBin(fontMap[charArr[i]]): + fontMap.empty; + + drawGrid( + {x: x + (i * gridWidthTotal), y: y}, + {rows: rows, cols: cols}, + charAsBin || fontMap.empty, + {pxlW: pxlW, pxlH: pxlH, gap: gap} + ); + } +} + +function drawTitles() { + g.setColor("#ffffff"); + g.setFont("6x8"); + g.drawString("COMPASS", 52, 43); + g.drawString("HEART", 122, 43); + g.drawString("TIME", 52, 85); + g.drawString("DATE", 52, 135); +} + +function drawCompass(lastHeading) { + const directions = [ + 'N', + 'NE', + 'E', + 'SE', + 'S', + 'SW', + 'W', + 'NW' + ]; + const cps = Bangle.getCompass(); + let angle = cps.heading; + const heading = angle? + directions[Math.round(((angle %= 360) < 0 ? angle + 360 : angle) / 45) % 8]: + "NE "; + + if (lastHeading != heading) drawFont(heading, "5x5", 40, 58); + setTimeout(drawCompass.bind(null, heading), 1000 * 2); +} + +function drawHeart(hrm) { + drawFont((" " + (hrm ? hrm.bpm : '')).slice(-3), "5x5", 109, 58); +} + +function drawTime(lastHrs, lastMns, toggle) { + const date = new Date(); + const h = date.getHours(); + const hrs = ("00" + ((is12Hour && h > 12) ? h - 12 : h)).substr(-2); + const mns = ("00" + date.getMinutes()).substr(-2); + + if (lastHrs != hrs) { + drawFont(hrs, "7x7", 48, 100); + } + if (lastMns != mns) { + drawFont(mns, "7x7", 124, 100); + } + + const color = toggle? COLORS.LIGHT : COLORS.DARK; + + // This should toggle on/off per second + drawPixel({ + color: color, + x: 118, y: 109, + w: 2, h: 2, + }); + drawPixel({ + color: color, + x: 118, y: 116, + w: 2, h: 2, + }); + + setTimeout(drawTime.bind(null, hrs, mns, !toggle), 1000); +} + +function drawDate(lastDate) { + const locale = require('locale'); + const date = new Date(); + + if (lastDate != date.toISOString().split('T')[0]) { + const dow = locale.dow(date, 1).toUpperCase(); + const dayNum = ("00" + date.getDate()).slice(-2); + const mon = locale.month(date).toUpperCase(); + const yr = date.getFullYear().toString().slice(-2); + drawFont(dow + " " + dayNum, "5x5", 40, 150); + drawFont(mon + " " + yr, "5x5", 40, 180); + } + + setTimeout(drawDate.bind(null, date.toISOString().split('T')), 1000 * 60); +} + +g.setBgColor(COLORS.BG); +g.clear(); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +drawTitles(); +drawTime(); +drawDate(); +drawCompass(); +drawHeart(); + +Bangle.on('HRM', drawHeart); +Bangle.setHRMPower(1); + +Bangle.setCompassPower(1); diff --git a/apps/dotmatrixclock/dotmatrix-clock-screen-shot.png b/apps/dotmatrixclock/dotmatrix-clock-screen-shot.png new file mode 100755 index 0000000000000000000000000000000000000000..e6218f4c9f43b9bbb7ac342f5b88915aa640e285 GIT binary patch literal 1855 zcmd5-drVVT7{3&1ZNYQ|NYH8P#-tIJfv`AU1!NE?!73;fpDixh8JT8aEp4go-5(M& zAr$v%j`K?mb`5IXUP1 zzTe|q*-uX1gxP`t0ALd-B{2gYjjnIw2KXPFIa>w*s12mVJqPn^=Tv*kwj}#{(6&AF z#6K=v@IHNgBZt2C>XJx5Tz*h@wBWrv@ug2nVxEAS2gO~n4!7NzSYT4?#y>^`N<=6i z*9^pqaX`1i9cX6*+YkNMixGDo$f#z; zH&OnU8IWyQ=J0r@RGfODz$vzwv}TGGEPvwe#c4^Q;vuV}gJDmuWKE+B&L+rALB9`v z&t*6xNkpjW7pSd2E~A@S)7|57Vu_G!bJ$z8ZkKl?ai^AN%@YjjkWDG$bv3g2J`HL8 zw@?=C+-}RH+TLLudR4(q*ZjPGr})`C=knA8D&D#La(cqanpzmv#T~KD%*rWwvC*m{ z90nHG$i2YBH*zPCyepw+fLU(V>eL|J)L(|LHgmL*xRuk6>NTagYTkR7#MP1g428;LOR$-zEma< zA(C&^gljnGH?Zf!kLuUwB5h=Wj+PV@z0}glc)OQQWfif;D$}u{X`odZg2e5AEppNP z@?IWxpqEM5c3iXzWR6OD#$;F7{OoArcE2nTD?<6VK6cLl{T7>65Dm?#zj6{$0~IRV zy~bwUSf@d1r=D#40m3X(uqjB2=APCq;@4EAGRuX4{4QN4ofZTdi)>SZED%1{u)se^ zL&hG5SDEbipeBTHRa#!rQHr9pJIrcGa-y}J+P9j73-OL!F2pui;JhDssOep48aEds z@`d({zeli0;o7CFI)NjH@k}f$pNS4MGG82;Zw_ap_^F za6-B~ty4wg)v0jS@+4{I?TV9a2G5vGY^XlnJ8s|bAu-)#vcRn*1{*sws-c`)a3-CV z19Hqyf5tm&YA?R?b=zCKBUC>_-?i`3i}_Lm`c?mU`2>ow&@3TX?=u6wNCAiQV0ycF zZe*WBs6yF=bVpPY!8+!lE^$fbd>RO|0wlr~a+H|So;x|@;ZO(6 zC-W+cE&o;ze~IPZMQ&GaxV6-pAY4+UlWTff2n6F`3vSM;HRPUJv*04KIKXGIJAF`b#w0jk>JBap;N(7E+b2#4D)hJjO5PXO+j;xxjqz1o!7ce@#V;V-|Lxhh??5Wb+Q+tj SrxL;sBS1 Date: Thu, 14 May 2020 19:49:20 +0100 Subject: [PATCH 03/13] Fix spelling error --- apps.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 34cd4fe2e..2be79db83 100644 --- a/apps.json +++ b/apps.json @@ -1568,6 +1568,7 @@ {"name":"hidjoystick.img","url":"app-icon.js","evaluate":true} ] }, +<<<<<<< HEAD { "id": "largeclock", "name": "Large Clock", @@ -1733,7 +1734,7 @@ {"name":"rndmclk.wid.js","url":"widget.js"} ] }, - { "id": "dotmatricsclock", + { "id": "dotmatrixclock", "name": "Dotmatrix Clock", "icon": "dotmatrixclock.png", "version":"0.01", From f465e06cdd2fea953c97f7b5993efe6751f61a9a Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 14 May 2020 19:46:34 +0100 Subject: [PATCH 04/13] Finish dotmatrix clock.. turn off sensors when screen sleeps, enable menu on BTN2 press --- apps/dotmatrixclock/app.js | 103 ++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js index e65377c6e..04091b36a 100755 --- a/apps/dotmatrixclock/app.js +++ b/apps/dotmatrixclock/app.js @@ -8,8 +8,9 @@ const storage = require('Storage'); const settings = (storage.readJSON('setting.json', 1) || {}); const is12Hour = settings["12hour"] || false; + const font7x7 = { - "empty": "00000000000000", + "empty": "00000000", "0": "3E61514945433E", "1": "1808080808081C", "2": "7E01013E40407F", @@ -23,7 +24,7 @@ const font7x7 = { }; const font5x5 = { - "empty": "0000000000", + "empty": "00000000", "0": "0E1915130E", "1": "0C0404040E", "2": "1E010E101F", @@ -74,13 +75,14 @@ function binToHex(bins /* array of binary strings */) { // Example // hexToBin("3E40407E41413E") function hexToBin(hexStr) { - return ( - hexStr - .replace(/../g, el => el + '_') + const regEx = new RegExp("..", "g"); + const bin = hexStr + .replace(regEx, el => el + '_') .slice(0, -1) .split('_') - .map(hex => ("00000000" + (parseInt(hex, 16)).toString(2)).substr(-8)) - ); + .map(hex => ("00000000" + (parseInt(hex, 16)).toString(2)).substr(-8)); + + return bin; } function drawPixel(opts) { @@ -90,22 +92,27 @@ function drawPixel(opts) { function drawGrid(pos /* {x:int, y:int} */, dims /* {rows:int, cols:int} */, charAsBin, opts /* {pxlW:int, pxlH:int, gap:int} */) { const defaultOpts = { - pxlW: 5, pxlH: 5, + pxlW: 5, + pxlH: 5, gap: 1, - offColor: COLORS.DARK, onColor: COLORS.LIGHT + offColor: COLORS.DARK, + onColor: COLORS.LIGHT }; const pxl = Object.assign({}, defaultOpts, opts); for (let r = 0; r < dims.rows; r++) { const y = pos.y + ((pxl.pxlH + pxl.gap) * r); + for (let c = 7; c > (7 - dims.cols); c--) { const x = pos.x + ((pxl.pxlW + pxl.gap) * c); const color = (charAsBin && parseInt(charAsBin[r][c])) ? pxl.onColor : pxl.offColor; drawPixel({ - x: x, y: y, - w: pxl.pxlW, h: pxl.pxlH, - color: color, + x: x, + y: y, + w: pxl.pxlW, + h: pxl.pxlH, + color: color }); } } @@ -141,7 +148,7 @@ function drawFont(str, font, x, y) { drawGrid( {x: x + (i * gridWidthTotal), y: y}, {rows: rows, cols: cols}, - charAsBin || fontMap.empty, + charAsBin, {pxlW: pxlW, pxlH: pxlH, gap: gap} ); } @@ -150,10 +157,10 @@ function drawFont(str, font, x, y) { function drawTitles() { g.setColor("#ffffff"); g.setFont("6x8"); - g.drawString("COMPASS", 52, 43); - g.drawString("HEART", 122, 43); - g.drawString("TIME", 52, 85); - g.drawString("DATE", 52, 135); + g.drawString("COMPASS", 52, 49); + g.drawString("HEART", 122, 49); + g.drawString("TIME", 52, 94); + g.drawString("DATE", 52, 144); } function drawCompass(lastHeading) { @@ -169,16 +176,17 @@ function drawCompass(lastHeading) { ]; const cps = Bangle.getCompass(); let angle = cps.heading; - const heading = angle? - directions[Math.round(((angle %= 360) < 0 ? angle + 360 : angle) / 45) % 8]: - " "; + let heading = angle? + directions[Math.round(((angle %= 360) < 0 ? angle + 360 : angle) / 45) % 8]: + " "; - if (lastHeading != heading) drawFont(heading, "5x5", 40, 58); + heading = (heading + " ").slice(0, 3); + if (lastHeading != heading) drawFont(heading, "5x5", 40, 67); setTimeout(drawCompass.bind(null, heading), 1000 * 2); } function drawHeart(hrm) { - drawFont((" " + (hrm ? hrm.bpm : '')).slice(-3), "5x5", 109, 58); + drawFont((" " + (hrm ? hrm.bpm : '')).slice(-3), "5x5", 109, 67); } function drawTime(lastHrs, lastMns, toggle) { @@ -188,10 +196,10 @@ function drawTime(lastHrs, lastMns, toggle) { const mns = ("00" + date.getMinutes()).substr(-2); if (lastHrs != hrs) { - drawFont(hrs, "7x7", 48, 100); + drawFont(hrs, "7x7", 48, 109); } if (lastMns != mns) { - drawFont(mns, "7x7", 124, 100); + drawFont(mns, "7x7", 124, 109); } const color = toggle? COLORS.LIGHT : COLORS.DARK; @@ -199,12 +207,12 @@ function drawTime(lastHrs, lastMns, toggle) { // This should toggle on/off per second drawPixel({ color: color, - x: 118, y: 109, + x: 118, y: 118, w: 2, h: 2, }); drawPixel({ color: color, - x: 118, y: 116, + x: 118, y: 125, w: 2, h: 2, }); @@ -218,28 +226,53 @@ function drawDate(lastDate) { if (lastDate != date.toISOString().split('T')[0]) { const dow = locale.dow(date, 1).toUpperCase(); const dayNum = ("00" + date.getDate()).slice(-2); - const mon = locale.month(date).toUpperCase(); + const mon = locale.month(date).toUpperCase().slice(-3); const yr = date.getFullYear().toString().slice(-2); - drawFont(dow + " " + dayNum, "5x5", 40, 150); - drawFont(mon + " " + yr, "5x5", 40, 180); + drawFont(dow + " " + dayNum, "5x5", 40, 159); + drawFont(mon + " " + yr, "5x5", 40, 189); } setTimeout(drawDate.bind(null, date.toISOString().split('T')), 1000 * 60); } -g.setBgColor(COLORS.BG); -g.clear(); +function setSensors(state) { + Bangle.setHRMPower(state); + Bangle.setCompassPower(state); +} +// Turn sensors on +setSensors(1); + +// Reset screen +g.clear(); +g.setBgColor(COLORS.BG); +g.clearRect(0, 24, g.getWidth(), g.getHeight()); + +// Load and draw widgets Bangle.loadWidgets(); Bangle.drawWidgets(); +// Draw screen drawTitles(); -drawTime(); -drawDate(); drawCompass(); drawHeart(); +drawTime(); +drawDate(); +// Setup callbacks Bangle.on('HRM', drawHeart); -Bangle.setHRMPower(1); -Bangle.setCompassPower(1); +setWatch(() => { + setSensors(0); + Bangle.setLCDMode(); + Bangle.showLauncher(); +}, BTN2, {repeat: false, edge: "falling"}); + +Bangle.on('lcdPower', (on) => on ? setSensors(1) : setSensors(0)); + +Bangle.on('faceUp', (up) => { + if (up && !Bangle.isLCDOn()) { + setSensors(1); + Bangle.setLCDPower(true); + } +}); \ No newline at end of file From 315131c9d8484106e5a344aa9f62074e41a2385f Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 14 May 2020 19:52:49 +0100 Subject: [PATCH 05/13] Fix apps.json --- apps.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps.json b/apps.json index 2be79db83..c46de601d 100644 --- a/apps.json +++ b/apps.json @@ -1568,7 +1568,6 @@ {"name":"hidjoystick.img","url":"app-icon.js","evaluate":true} ] }, -<<<<<<< HEAD { "id": "largeclock", "name": "Large Clock", From cc400677a1f8f743cd64fe8ef2e50c0879b6d933 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 14 May 2020 20:10:49 +0100 Subject: [PATCH 06/13] Fix month string truncation --- apps/dotmatrixclock/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js index 04091b36a..878a5aad3 100755 --- a/apps/dotmatrixclock/app.js +++ b/apps/dotmatrixclock/app.js @@ -226,7 +226,7 @@ function drawDate(lastDate) { if (lastDate != date.toISOString().split('T')[0]) { const dow = locale.dow(date, 1).toUpperCase(); const dayNum = ("00" + date.getDate()).slice(-2); - const mon = locale.month(date).toUpperCase().slice(-3); + const mon = locale.month(date).toUpperCase().slice(0, 3); const yr = date.getFullYear().toString().slice(-2); drawFont(dow + " " + dayNum, "5x5", 40, 159); drawFont(mon + " " + yr, "5x5", 40, 189); From 2ed65f3eadcb0802935bc7cc9cd7e80702f55470 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 14 May 2020 21:32:36 +0100 Subject: [PATCH 07/13] Welcome: Allow welcome to run after a fresh install + More useful app menu + BTN2 now goes to menu on release --- apps.json | 4 ++-- apps/ncstart/ChangeLog | 1 + apps/ncstart/boot.js | 9 +++------ apps/welcome/ChangeLog | 3 +++ apps/welcome/app.js | 2 +- apps/welcome/boot.js | 9 +++------ apps/welcome/settings.js | 8 ++++++-- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apps.json b/apps.json index 906331f65..7d4bf20cf 100644 --- a/apps.json +++ b/apps.json @@ -78,7 +78,7 @@ { "id": "welcome", "name": "Welcome", "icon": "app.png", - "version":"0.08", + "version":"0.09", "description": "Appears at first boot and explains how to use Bangle.js", "tags": "start,welcome", "allow_emulator":true, @@ -589,7 +589,7 @@ "id": "ncstart", "name": "NCEU Startup", "icon": "start.png", - "version":"0.05", + "version":"0.06", "description": "NodeConfEU 2019 'First Start' Sequence", "tags": "start,welcome", "storage": [ diff --git a/apps/ncstart/ChangeLog b/apps/ncstart/ChangeLog index 522633f7b..152fdc9d1 100644 --- a/apps/ncstart/ChangeLog +++ b/apps/ncstart/ChangeLog @@ -6,3 +6,4 @@ Don't run again when settings app is updated (or absent) Add "Run Now" option to settings 0.05: Don't overwrite existing settings on app update +0.06: Allow welcome to run after a fresh install diff --git a/apps/ncstart/boot.js b/apps/ncstart/boot.js index 094033094..62ac962f6 100644 --- a/apps/ncstart/boot.js +++ b/apps/ncstart/boot.js @@ -1,11 +1,8 @@ (function() { - let s = require('Storage').readJSON('ncstart.json', 1) - || require('Storage').readJSON('setting.json', 1) - || {welcomed: true} // do NOT run if global settings are also absent - if (!s.welcomed && require('Storage').read('ncstart.app.js')) { + let s = require('Storage').readJSON('ncstart.json', 1) || {}; + if (!s.welcomed) { setTimeout(() => { - s.welcomed = true - require('Storage').write('ncstart.json', s) + require('Storage').write('ncstart.json', {welcomed: true}) load('ncstart.app.js') }) } diff --git a/apps/welcome/ChangeLog b/apps/welcome/ChangeLog index a377fc81e..9545dbbfa 100644 --- a/apps/welcome/ChangeLog +++ b/apps/welcome/ChangeLog @@ -8,3 +8,6 @@ Don't run again when settings app is updated (or absent) Add "Run Now" option to settings 0.08: Don't overwrite existing settings on app update +0.09: Allow welcome to run after a fresh install + More useful app menu + BTN2 now goes to menu on release diff --git a/apps/welcome/app.js b/apps/welcome/app.js index a32a6e56f..b4c79ddaa 100644 --- a/apps/welcome/app.js +++ b/apps/welcome/app.js @@ -285,7 +285,7 @@ setWatch(()=>{ if (sceneNumber == scenes.length-1) { load(); } -}, BTN2, {repeat:true,edge:"rising"}); +}, BTN2, {repeat:true,edge:"falling"}); setWatch(()=>move(-1), BTN1, {repeat:true}); (function migrateSettings(){ diff --git a/apps/welcome/boot.js b/apps/welcome/boot.js index f6ba6d2d6..4e3a12231 100644 --- a/apps/welcome/boot.js +++ b/apps/welcome/boot.js @@ -1,11 +1,8 @@ (function() { - let s = require('Storage').readJSON('welcome.json', 1) - || require('Storage').readJSON('setting.json', 1) - || {welcomed: true} // do NOT run if global settings are also absent - if (!s.welcomed && require('Storage').read('welcome.app.js')) { + let s = require('Storage').readJSON('welcome.json', 1) || {}; + if (!s.welcomed) { setTimeout(() => { - s.welcomed = true - require('Storage').write('welcome.json', {welcomed: "yes"}) + require('Storage').write('welcome.json', {welcomed: true}) load('welcome.app.js') }) } diff --git a/apps/welcome/settings.js b/apps/welcome/settings.js index 20c2e9b13..4992e6e7a 100644 --- a/apps/welcome/settings.js +++ b/apps/welcome/settings.js @@ -3,12 +3,16 @@ || require('Storage').readJSON('setting.json', 1) || {} E.showMenu({ '': { 'title': 'Welcome App' }, - 'Run on Next Boot': { + 'Run next boot': { value: !settings.welcomed, - format: v => v ? 'OK' : 'No', + format: v => v ? 'Yes' : 'No', onchange: v => require('Storage').write('welcome.json', {welcomed: !v}), }, 'Run Now': () => load('welcome.app.js'), + 'Turn off, run next boot': () => { + require('Storage').write('welcome.json', {welcomed: false}); + Bangle.off(); + }, '< Back': back, }) }) From a13954b6cbfe24a43c6a613cfffff575f1e6f43a Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 14 May 2020 21:36:33 +0100 Subject: [PATCH 08/13] tweak --- apps/welcome/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/welcome/settings.js b/apps/welcome/settings.js index 4992e6e7a..f269f238e 100644 --- a/apps/welcome/settings.js +++ b/apps/welcome/settings.js @@ -9,7 +9,7 @@ onchange: v => require('Storage').write('welcome.json', {welcomed: !v}), }, 'Run Now': () => load('welcome.app.js'), - 'Turn off, run next boot': () => { + 'Turn off & run next': () => { require('Storage').write('welcome.json', {welcomed: false}); Bangle.off(); }, From 9178e1b632f265c95b6f1706f29a169d505d8ed3 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 15 May 2020 07:38:09 +0100 Subject: [PATCH 09/13] Fix Upload failed, TypeError: Espruino.transform is not a function (fix #420) --- js/espruinotools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/espruinotools.js b/js/espruinotools.js index 8e266f267..0e8df02cb 100644 --- a/js/espruinotools.js +++ b/js/espruinotools.js @@ -124,7 +124,7 @@ Espruino.Core.Status = { hasProgress : function() { return false; }, incrementProgress : function(amt) {} }; -var acorn = (function(){ var exports={}; +var acorn = (function(){ var exports={};var module={}; (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : From 7488970e741f4b96cec1d6dc67a65a417b45b904 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Fri, 15 May 2020 09:33:25 +0100 Subject: [PATCH 10/13] Allow multiple coloured screens --- apps/dotmatrixclock/app.js | 47 +++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js index 878a5aad3..da165572c 100755 --- a/apps/dotmatrixclock/app.js +++ b/apps/dotmatrixclock/app.js @@ -61,11 +61,20 @@ const font5x5 = { // Char renderer const COLORS = { - BG: "#0297fe", - DARK: "#3b3ce8", - LIGHT: "#E9ffff", + blue: { + BG: "#0297fe", + DARK: "#3b3ce8", + LIGHT: "#E9ffff", + }, + orange: { + BG: "#f7b336", + DARK: "#ac721e", + LIGHT: "#f6fc0f", + } }; +let selectedColor = "blue"; + // Example // binToHex(["0111110", "1000000", "1000000", "1111110", "1000001", "1000001", "0111110"]) function binToHex(bins /* array of binary strings */) { @@ -95,8 +104,8 @@ function drawGrid(pos /* {x:int, y:int} */, dims /* {rows:int, cols:int} */, cha pxlW: 5, pxlH: 5, gap: 1, - offColor: COLORS.DARK, - onColor: COLORS.LIGHT + offColor: COLORS[selectedColor].DARK, + onColor: COLORS[selectedColor].LIGHT }; const pxl = Object.assign({}, defaultOpts, opts); @@ -202,7 +211,7 @@ function drawTime(lastHrs, lastMns, toggle) { drawFont(mns, "7x7", 124, 109); } - const color = toggle? COLORS.LIGHT : COLORS.DARK; + const color = toggle? COLORS[selectedColor].LIGHT : COLORS[selectedColor].DARK; // This should toggle on/off per second drawPixel({ @@ -240,26 +249,38 @@ function setSensors(state) { Bangle.setCompassPower(state); } +function drawScreen() { + g.setBgColor(COLORS[selectedColor].BG); + g.clearRect(0, 24, g.getWidth(), g.getHeight()); + + // Draw components + drawTitles(); + drawCompass(); + drawHeart(); + drawTime(); + drawDate(); +} + // Turn sensors on setSensors(1); // Reset screen g.clear(); -g.setBgColor(COLORS.BG); -g.clearRect(0, 24, g.getWidth(), g.getHeight()); // Load and draw widgets Bangle.loadWidgets(); Bangle.drawWidgets(); // Draw screen -drawTitles(); -drawCompass(); -drawHeart(); -drawTime(); -drawDate(); +drawScreen(); // Setup callbacks +Bangle.on('swipe', (sDir) => { + selectedColor = selectedColor === "blue" ? "orange" : "blue"; + clearTimeout(); + drawScreen(); +}); + Bangle.on('HRM', drawHeart); setWatch(() => { From 04e3d3b4552cb1738bb1334edc36d2335d1accb3 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Fri, 15 May 2020 09:37:37 +0100 Subject: [PATCH 11/13] Update README --- apps/dotmatrixclock/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dotmatrixclock/README.md b/apps/dotmatrixclock/README.md index 5c67c8dfe..e862b4e36 100644 --- a/apps/dotmatrixclock/README.md +++ b/apps/dotmatrixclock/README.md @@ -10,6 +10,7 @@ A clock face simulating the classic dotmatrix displays. Shows time, date, compas * Simulated white-on-blue dotmatrix display * Compass * Heart rate monitor +* Multiple colour palletes, swipe to change ## Requests From 40c4673db7aa2938d04c6adbc4985fb358a8c0c5 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Fri, 15 May 2020 09:46:21 +0100 Subject: [PATCH 12/13] Remove inline comments, seems to break pretokenisation or something! --- apps/dotmatrixclock/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js index da165572c..847b5d074 100755 --- a/apps/dotmatrixclock/app.js +++ b/apps/dotmatrixclock/app.js @@ -77,7 +77,7 @@ let selectedColor = "blue"; // Example // binToHex(["0111110", "1000000", "1000000", "1111110", "1000001", "1000001", "0111110"]) -function binToHex(bins /* array of binary strings */) { +function binToHex(bins) { return bins.map(bin => ("00" + (parseInt(bin, 2).toString(16))).substr(-2).toUpperCase()).join(""); } @@ -99,7 +99,7 @@ function drawPixel(opts) { g.fillRect(opts.x, opts.y, opts.x + opts.w, opts.y + opts.h); } -function drawGrid(pos /* {x:int, y:int} */, dims /* {rows:int, cols:int} */, charAsBin, opts /* {pxlW:int, pxlH:int, gap:int} */) { +function drawGrid(pos, dims, charAsBin, opts) { const defaultOpts = { pxlW: 5, pxlH: 5, @@ -121,7 +121,7 @@ function drawGrid(pos /* {x:int, y:int} */, dims /* {rows:int, cols:int} */, cha y: y, w: pxl.pxlW, h: pxl.pxlH, - color: color + color: color, }); } } From 5a7216166fe4105d7270df161d513bcb49852122 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Fri, 15 May 2020 11:23:52 +0100 Subject: [PATCH 13/13] Fix sensors activation/deactivation and fix more pretokenisation issues (doesn't work well with var names with lenght <= 3 --- apps/dotmatrixclock/README.md | 11 +++++ apps/dotmatrixclock/app.js | 77 ++++++++++++++++++++++++++++++----- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/apps/dotmatrixclock/README.md b/apps/dotmatrixclock/README.md index e862b4e36..3af48efc6 100644 --- a/apps/dotmatrixclock/README.md +++ b/apps/dotmatrixclock/README.md @@ -12,6 +12,17 @@ A clock face simulating the classic dotmatrix displays. Shows time, date, compas * Heart rate monitor * Multiple colour palletes, swipe to change +## Usage + +### Sensor readings + +When the display is activated by 'flipping' the watch up, the compass and heart sensors will be activated automatically, but if +you activate the LCD through a button press, then the sensors will remain off until you press button-1. + +### Colours + +The display defaults to blue, but you can change this to orange by swiping the screen + ## Requests If you have any feature requests, please send an email to the author paulcockrell@gmail.com` diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js index 847b5d074..94c628b1b 100755 --- a/apps/dotmatrixclock/app.js +++ b/apps/dotmatrixclock/app.js @@ -7,7 +7,7 @@ const storage = require('Storage'); const settings = (storage.readJSON('setting.json', 1) || {}); const is12Hour = settings["12hour"] || false; - +const timeout = settings.timeout || 20; const font7x7 = { "empty": "00000000", @@ -25,6 +25,7 @@ const font7x7 = { const font5x5 = { "empty": "00000000", + "-": "0000FF0000", "0": "0E1915130E", "1": "0C0404040E", "2": "1E010E101F", @@ -74,6 +75,7 @@ const COLORS = { }; let selectedColor = "blue"; +let displayTimeoutRef, sensorTimeoutRef; // Example // binToHex(["0111110", "1000000", "1000000", "1111110", "1000001", "1000001", "0111110"]) @@ -109,12 +111,12 @@ function drawGrid(pos, dims, charAsBin, opts) { }; const pxl = Object.assign({}, defaultOpts, opts); - for (let r = 0; r < dims.rows; r++) { - const y = pos.y + ((pxl.pxlH + pxl.gap) * r); + for (let rowY = 0; rowY < dims.rows; rowY++) { + const y = pos.y + ((pxl.pxlH + pxl.gap) * rowY); - for (let c = 7; c > (7 - dims.cols); c--) { - const x = pos.x + ((pxl.pxlW + pxl.gap) * c); - const color = (charAsBin && parseInt(charAsBin[r][c])) ? pxl.onColor : pxl.offColor; + for (let colX = 7; colX > (7 - dims.cols); colX--) { + const x = pos.x + ((pxl.pxlW + pxl.gap) * colX); + const color = (charAsBin && parseInt(charAsBin[rowY][colX])) ? pxl.onColor : pxl.offColor; drawPixel({ x: x, @@ -187,7 +189,7 @@ function drawCompass(lastHeading) { let angle = cps.heading; let heading = angle? directions[Math.round(((angle %= 360) < 0 ? angle + 360 : angle) / 45) % 8]: - " "; + "-- "; heading = (heading + " ").slice(0, 3); if (lastHeading != heading) drawFont(heading, "5x5", 40, 67); @@ -195,7 +197,7 @@ function drawCompass(lastHeading) { } function drawHeart(hrm) { - drawFont((" " + (hrm ? hrm.bpm : '')).slice(-3), "5x5", 109, 67); + drawFont((" " + (hrm ? hrm.bpm : "---")).slice(-3), "5x5", 109, 67); } function drawTime(lastHrs, lastMns, toggle) { @@ -245,6 +247,23 @@ function drawDate(lastDate) { } function setSensors(state) { + // Already reading sensors and trying to activate sensors, do nothing + if (sensorTimeoutRef && state === 1) return; + + // If we are activating the sensors, turn them off again in one minute + if (state === 1) { + sensorTimeoutRef = setTimeout(() => { setSensors(0); }, 1000 * 60); + } else { + if (sensorTimeoutRef) { + clearInterval(sensorTimeoutRef); + sensorTimeoutRef = null; + } + // Bit nasty, but we only redraw the heart value on sensor callback + // but we want to blank out when sensor is off, but no callback for + // that so force redraw here + drawHeart(); + } + Bangle.setHRMPower(state); Bangle.setCompassPower(state); } @@ -261,6 +280,28 @@ function drawScreen() { drawDate(); } +function clearTimers(){ + if (displayTimeoutRef) { + clearInterval(displayTimeoutRef); + displayTimeoutRef = null; + } + + if (sensorTimeoutRef) { + clearInterval(sensorTimeoutRef); + sensorTimeoutRef = null; + } +} + +function resetDisplayTimeout() { + if (displayTimeoutRef) clearInterval(displayTimeoutRef); + Bangle.setLCDPower(true); + + displayTimeoutRef = setTimeout(() => { + if (Bangle.isLCDOn()) Bangle.setLCDPower(false); + clearTimers(); + }, 1000 * timeout); +} + // Turn sensors on setSensors(1); @@ -273,27 +314,41 @@ Bangle.drawWidgets(); // Draw screen drawScreen(); +resetDisplayTimeout(); // Setup callbacks Bangle.on('swipe', (sDir) => { selectedColor = selectedColor === "blue" ? "orange" : "blue"; - clearTimeout(); + resetDisplayTimeout(); drawScreen(); }); Bangle.on('HRM', drawHeart); +setWatch(() => { + setSensors(1); + resetDisplayTimeout(); +}, BTN1, {repeat: true, edge: "falling"}); + setWatch(() => { setSensors(0); + clearTimers(); Bangle.setLCDMode(); Bangle.showLauncher(); }, BTN2, {repeat: false, edge: "falling"}); -Bangle.on('lcdPower', (on) => on ? setSensors(1) : setSensors(0)); +Bangle.on('lcdPower', (on) => { + if(on) { + resetDisplayTimeout(); + } else { + clearTimers(); + setSensors(0); + } +}); Bangle.on('faceUp', (up) => { if (up && !Bangle.isLCDOn()) { setSensors(1); - Bangle.setLCDPower(true); + resetDisplayTimeout(); } }); \ No newline at end of file