From c19333f3c04cb5adc1697921cd0b46fbb1f08399 Mon Sep 17 00:00:00 2001 From: hughbarney Date: Tue, 26 Apr 2022 22:23:17 +0100 Subject: [PATCH 001/821] kbswipe library updated q --- apps/kbswipe/lib.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/kbswipe/lib.js b/apps/kbswipe/lib.js index 417ac98d9..9dfb4358e 100644 --- a/apps/kbswipe/lib.js +++ b/apps/kbswipe/lib.js @@ -21,7 +21,8 @@ exports.getStrokes = function(cb) { cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49])); cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62])); cb("p", new Uint8Array([52, 59, 52, 64, 54, 73, 58, 88, 61, 104, 65, 119, 67, 130, 69, 138, 71, 145, 71, 147, 71, 148, 71, 143, 70, 133, 68, 120, 67, 108, 67, 97, 67, 89, 68, 79, 72, 67, 83, 60, 99, 58, 118, 58, 136, 63, 146, 70, 148, 77, 145, 84, 136, 91, 121, 95, 106, 97, 93, 97, 82, 97])); - cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67])); + cb("q", new Uint8Array([127, 77, 127, 72, 126, 68, 118, 63, 104, 62, 85, 64, 71, 75, 66, 88, 67, 100, 84, 114, 116, 117, 135, 114, 135, 101, 115, 84, 91, 73, 78, 69, 79, 69, 100, 69])); +// cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67])); cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150])); cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158])); cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146])); @@ -82,7 +83,6 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) ); g.drawString(l.join("\n"),R.x+4,R.y+4); } - /* // This draws a big image to use in the README (function() { E.defrag(); @@ -98,7 +98,6 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) ); }); b.dump(); })() - */ function show() { g.reset(); From 76398553b4c49558896759ad33779cb97adc1a99 Mon Sep 17 00:00:00 2001 From: hughbarney Date: Tue, 26 Apr 2022 22:26:27 +0100 Subject: [PATCH 002/821] kbswipe library updated q --- apps/kbswipe/ChangeLog | 1 + apps/kbswipe/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/kbswipe/ChangeLog b/apps/kbswipe/ChangeLog index f0dc54b69..00cda49bc 100644 --- a/apps/kbswipe/ChangeLog +++ b/apps/kbswipe/ChangeLog @@ -2,3 +2,4 @@ 0.02: Now keeps user input trace intact by changing how the screen is updated. 0.03: Positioning of marker now takes the height of the widget field into account. 0.04: Fix issue if going back without typing. +0.05: updated q gesture diff --git a/apps/kbswipe/metadata.json b/apps/kbswipe/metadata.json index d4026c815..59622cb96 100644 --- a/apps/kbswipe/metadata.json +++ b/apps/kbswipe/metadata.json @@ -1,6 +1,6 @@ { "id": "kbswipe", "name": "Swipe keyboard", - "version":"0.04", + "version":"0.05", "description": "A library for text input via PalmOS style swipe gestures (beta!)", "icon": "app.png", "type":"textinput", From d9a110fe8293ad37b45cf586e804e2890f539e85 Mon Sep 17 00:00:00 2001 From: hughbarney Date: Tue, 26 Apr 2022 22:41:51 +0100 Subject: [PATCH 003/821] kbswipe library updated q --- apps/kbswipe/lib.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/kbswipe/lib.js b/apps/kbswipe/lib.js index 9dfb4358e..dcadc37ba 100644 --- a/apps/kbswipe/lib.js +++ b/apps/kbswipe/lib.js @@ -101,7 +101,8 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) ); function show() { g.reset(); - g.clearRect(R).setColor("#f00"); + //g.clearRect(R).setColor("#f00"); + g.clearRect(R).setColor(g.theme.fg); var n=0; exports.getStrokes((id,s) => { var x = n%6; From c29d474a81dc7cfdb73152fa5d8b85c4250150bd Mon Sep 17 00:00:00 2001 From: hughbarney Date: Tue, 26 Apr 2022 23:03:01 +0100 Subject: [PATCH 004/821] kbswipe library updated cribsheet to use g.theme.fg --- apps/kbswipe/ChangeLog | 2 +- apps/kbswipe/lib.js | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/kbswipe/ChangeLog b/apps/kbswipe/ChangeLog index 00cda49bc..4af2ea366 100644 --- a/apps/kbswipe/ChangeLog +++ b/apps/kbswipe/ChangeLog @@ -2,4 +2,4 @@ 0.02: Now keeps user input trace intact by changing how the screen is updated. 0.03: Positioning of marker now takes the height of the widget field into account. 0.04: Fix issue if going back without typing. -0.05: updated q gesture +0.05: Gesture cribsheet use g.theme.fg instead of red diff --git a/apps/kbswipe/lib.js b/apps/kbswipe/lib.js index dcadc37ba..0422dea82 100644 --- a/apps/kbswipe/lib.js +++ b/apps/kbswipe/lib.js @@ -21,8 +21,7 @@ exports.getStrokes = function(cb) { cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49])); cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62])); cb("p", new Uint8Array([52, 59, 52, 64, 54, 73, 58, 88, 61, 104, 65, 119, 67, 130, 69, 138, 71, 145, 71, 147, 71, 148, 71, 143, 70, 133, 68, 120, 67, 108, 67, 97, 67, 89, 68, 79, 72, 67, 83, 60, 99, 58, 118, 58, 136, 63, 146, 70, 148, 77, 145, 84, 136, 91, 121, 95, 106, 97, 93, 97, 82, 97])); - cb("q", new Uint8Array([127, 77, 127, 72, 126, 68, 118, 63, 104, 62, 85, 64, 71, 75, 66, 88, 67, 100, 84, 114, 116, 117, 135, 114, 135, 101, 115, 84, 91, 73, 78, 69, 79, 69, 100, 69])); -// cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67])); + cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67])); cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150])); cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158])); cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146])); @@ -83,7 +82,12 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) ); g.drawString(l.join("\n"),R.x+4,R.y+4); } - // This draws a big image to use in the README + /** + This draws a big image to use in the README file + uncomment and each time the library is loaded it will dump to the IDE + you must be connected to the IDE. + */ + /* (function() { E.defrag(); var b = Graphics.createArrayBuffer(500,420,1,{msb:true}); @@ -98,10 +102,10 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) ); }); b.dump(); })() + */ function show() { g.reset(); - //g.clearRect(R).setColor("#f00"); g.clearRect(R).setColor(g.theme.fg); var n=0; exports.getStrokes((id,s) => { From 64289a0cb760d98b352a4f84ad110d3d31808986 Mon Sep 17 00:00:00 2001 From: Emile Cantin Date: Fri, 13 May 2022 09:25:47 -0400 Subject: [PATCH 005/821] Clock & Calendar: Improve colours The red when disconnected is very hard to read (and not documented). I've switched it to have a slight blue tint when connected, and white when disconnected. --- apps/clockcal/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/clockcal/app.js b/apps/clockcal/app.js index 5e8c7f796..6d97f22ce 100644 --- a/apps/clockcal/app.js +++ b/apps/clockcal/app.js @@ -123,7 +123,7 @@ function drawMinutes() { var d = new Date(); var hours = s.MODE24 ? d.getHours().toString().padStart(2, ' ') : ((d.getHours() + 24) % 12 || 12).toString().padStart(2, ' '); var minutes = d.getMinutes().toString().padStart(2, '0'); - var textColor = NRF.getSecurityStatus().connected ? '#fff' : '#f00'; + var textColor = NRF.getSecurityStatus().connected ? '#99f' : '#fff'; var size = 50; var clock_x = (w - 20) / 2; if (dimSeconds) { From 3ec051be9d716f2a36ca4d8a9e6964e277442941 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 17:17:30 +0200 Subject: [PATCH 006/821] [sleeplog] Complete rework, BETA v01 --- apps/sleeplog/ChangeLog | 1 + apps/sleeplog/README.md | 25 +- apps/sleeplog/app.js | 558 ++++++++++++++++++++------------- apps/sleeplog/boot.js | 462 ++++++++++++++++++---------- apps/sleeplog/lib.js | 598 ++++++++++++++++++++++++++---------- apps/sleeplog/metadata.json | 12 +- apps/sleeplog/settings.js | 308 ++++++++++++------- 7 files changed, 1300 insertions(+), 664 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index 8a3da6362..890d54dce 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -4,3 +4,4 @@ 0.04: Fix #1445, display loading info, add icons to display service states 0.05: Fix LOW_MEMORY,MEMORY error on to big log size 0.06: Reduced log size further to 750 entries +0.10: Complete rework off this app! diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index ebbcdde54..753c63133 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -2,8 +2,29 @@ This app logs and displays the four following states: _unknown, not worn, awake, sleeping_ -It derived from the [SleepPhaseAlarm](https://banglejs.com/apps/#sleepphasealarm) and uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments ([ESS](https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en)) and -also provides a power saving mode using the built in movement calculation. The internal temperature is used to decide if the status is _sleeping_ or _not worn_. +It is using the built in movement calculation to decide your sleeping state. To detect if you are wearing the watch the internal heartrate sensor is used, except while charging. In this case it is always assumed that you are not weraing the watch. + +````diff +-+- -+- +-+- !!! BETA Version !!! -+- +-+- -+- + +- The following readme text is not matching this version! + ++++ To do list: +++ +* Change README.md to represent new app version. +* Add cutom interface.html to download logged data. +* Test emulator functionallity. + +Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: + "data": [ + {"name": "sleeplog.log", "storageFile": true}, + {"wildcard": "sleeplog_????.log"}, + {"wildcard": "sleeplog_??????.csv"} + ], + +```` +--- #### Operating Principle * __ESS calculation__ diff --git a/apps/sleeplog/app.js b/apps/sleeplog/app.js index 8077b4d62..097897d35 100644 --- a/apps/sleeplog/app.js +++ b/apps/sleeplog/app.js @@ -1,234 +1,354 @@ -// set storage and define settings -var storage = require("Storage"); -var breaktod, maxawake, minconsec; - -// read required settings from storage -function readSettings(settings) { - breaktod = settings.breaktod || (settings.breaktod === 0 ? 0 : 10); // time of day when to start/end graphs - maxawake = settings.maxawake || 36E5; // 60min in ms - minconsec = settings.minconsec || 18E5; // 30min in ms -} - -// define draw log function -function drawLog(topY, viewUntil) { - // set default view time - viewUntil = viewUntil || Date(); - - // define parameters - var statusValue = [0, 0.4, 0.6, 1]; // unknown, not worn, awake, sleeping, consecutive sleep - var statusColor = [0, 63488, 2016, 32799, 31]; // black, red, green, violet, blue - var period = 432E5; // 12h - var graphHeight = 18; - var labelHeight = 12; - var width = g.getWidth(); - var timestamp0 = viewUntil.valueOf() - period; - var y = topY + graphHeight; - - // read 12h wide log - var log = require("sleeplog").readLog(0, timestamp0, viewUntil.valueOf()); - - // format log array if not empty - if (log.length) { - // if the period goes into the future add unknown status at the beginning - if (viewUntil > Date()) log.unshift([Date().valueOf(), 0]); - - // check if the period goes earlier than logged data - if (log[log.length - 1][0] < timestamp0) { - // set time of last entry according to period - log[log.length - 1][0] = timestamp0; - } else { - // add entry with unknown status at the end - log.push([timestamp0, 0]); - } - - // remap each element to [status, relative beginning, relative end, duration] - log = log.map((element, index) => [ - element[1], - element[0] - timestamp0, - (log[index - 1] || [viewUntil.valueOf()])[0] - timestamp0, - (log[index - 1] || [viewUntil.valueOf()])[0] - element[0] - ]); - - // start with the oldest entry to build graph left to right - log.reverse(); - } - - // clear area - g.reset().clearRect(0, topY, width, y + labelHeight); - // draw x axis - g.drawLine(0, y + 1, width, y + 1); - // draw x label - var hours = period / 36E5; - var stepwidth = width / hours; - var startHour = 24 + viewUntil.getHours() - hours; - for (var x = 0; x < hours; x++) { - g.fillRect(x * stepwidth, y + 2, x * stepwidth, y + 4); - g.setFontAlign(-1, -1).setFont("6x8") - .drawString((startHour + x) % 24, x * stepwidth + 1, y + 6); - } - - // define variables for sleep calculation - var consecutive = 0; - var output = [0, 0]; // [estimated, true] - var i, nosleepduration; - - // draw graph - log.forEach((element, index) => { - // set bar color depending on type - g.setColor(statusColor[consecutive ? 4 : element[0]]); - - // check for sleeping status - if (element[0] === 3) { - // count true sleeping hours - output[1] += element[3]; - // count duration of subsequent non sleeping periods - i = index + 1; - nosleepduration = 0; - while (log[i] !== undefined && log[i][0] < 3 && nosleepduration < maxawake) { - nosleepduration += log[i++][3]; - } - // check if counted duration lower than threshold to start/stop counting - if (log[i] !== undefined && nosleepduration < maxawake) { - // start counting consecutive sleeping hours - consecutive += element[3]; - // correct color to match consecutive sleeping - g.setColor(statusColor[4]); - } else { - // check if counted consecutive sleeping greater then threshold - if (consecutive >= minconsec) { - // write verified consecutive sleeping hours to output - output[0] += consecutive + element[3]; +// touch listener for specific areas +function touchListener(b, c) { + // check if inside any area + for (var i = 0; i < aaa.length; i++) { + if (!(c.x < aaa[i].x0 || c.x > aaa[i].x1 || c.y < aaa[i].y0 || c.y > aaa[i].y1)) { + // check if drawing ongoing + if (drawingID > 0) { + // check if interrupt is set + if (aaa[i].interrupt) { + // stop ongoing drawing + drawingID++; + if (ATID) ATID = clearTimeout(ATID); } else { - // correct color to display a canceled consecutive sleeping period - g.setColor(statusColor[3]); + // do nothing + return; } - // stop counting consecutive sleeping hours - consecutive = 0; } - } else { - // count durations of non sleeping periods for consecutive sleeping - if (consecutive) consecutive += element[3]; + // give feedback + Bangle.buzz(25); + // execute action + aaa[i].funct(); } - - // calculate points - var x1 = Math.ceil(element[1] / period * width); - var x2 = Math.floor(element[2] / period * width); - var y2 = y - graphHeight * statusValue[element[0]]; - // draw bar - g.clearRect(x1, topY, x2, y); - g.fillRect(x1, y, x2, y2).reset(); - if (y !== y2) g.fillRect(x1, y2, x2, y2); - }); - - // clear variables - log = undefined; - - // return convert output into minutes - return output.map(value => value /= 6E4); -} - -// define function to draw the analysis -function drawAnalysis(toDate) { - //var t0 = Date.now(); - - // get width - var width = g.getWidth(); - - // define variable for sleep calculation - var outputs = [0, 0]; // [estimated, true] - - // clear analysis area - g.clearRect(0, 71, width, width); - - // draw log graphs and read outputs - drawLog(110, toDate).forEach( - (value, index) => outputs[index] += value); - drawLog(144, Date(toDate.valueOf() - 432E5)).forEach( - (value, index) => outputs[index] += value); - - // draw outputs - g.reset(); // area: 0, 70, width, 105 - g.setFont("6x8").setFontAlign(-1, -1); - g.drawString("consecutive\nsleeping", 10, 70); - g.drawString("true\nsleeping", 10, 90); - g.setFont("12x20").setFontAlign(1, -1); - g.drawString(Math.floor(outputs[0] / 60) + "h " + - Math.floor(outputs[0] % 60) + "min", width - 10, 70); - g.drawString(Math.floor(outputs[1] / 60) + "h " + - Math.floor(outputs[1] % 60) + "min", width - 10, 90); - - //print("analysis processing seconds:", Math.round(Date.now() - t0) / 1000); -} - -// define draw night to function -function drawNightTo(prevDays) { - // calculate 10am of this or a previous day - var toDate = Date(); - toDate = Date(toDate.getFullYear(), toDate.getMonth(), toDate.getDate() - prevDays, breaktod); - - // get width - var width = g.getWidth(); - var center = width / 2; - - // reduce date by 1s to ensure correct headline - toDate = Date(toDate.valueOf() - 1E3); - - // clear heading area - g.clearRect(0, 24, width, 70); - - // display service states: service, loggging and powersaving - if (!sleeplog.enabled) { - // draw disabled service icon - g.setColor(1, 0, 0) - .drawImage(atob("FBSBAAH4AH/gH/+D//w/n8f5/nud7znP85z/f+/3/v8/z/P895+efGPj4Hw//8H/+Af+AB+A"), 2, 36); - } else if (!sleeplog.logfile) { - // draw disabled log icon - g.reset().drawImage(atob("EA6BAM//z/8AAAAAz//P/wAAAADP/8//AAAAAM//z/8="), 4, 40) - .setColor(1, 0, 0).fillPoly([2, 38, 4, 36, 22, 54, 20, 56]); } - // draw power saving icon - if (sleeplog.powersaving) g.setColor(0, 1, 0) - .drawImage(atob("FBSBAAAAcAD/AH/wP/4P/+H//h//4//+fv/nj/7x/88//Of/jH/4j/8I/+Af+AH+AD8AA4AA"), width - 22, 36); - - // draw headline - g.reset().setFont("12x20").setFontAlign(0, -1); - g.drawString("Night to " + require('locale').dow(toDate, 1) + "\n" + - require('locale').date(toDate, 1), center, 30); - - // show loading info - var info = "calculating data ...\nplease be patient :)"; - var y0 = center + 30; - var bounds = [center - 80, y0 - 20, center + 80, y0 + 20]; - g.clearRect.apply(g, bounds).drawRect.apply(g, bounds); - g.setFont("6x8").setFontAlign(0, 0); - g.drawString(info, center, y0); - - // calculate and draw analysis after timeout for faster feedback - if (ATID) ATID = clearTimeout(ATID); - ATID = setTimeout(drawAnalysis, 100, toDate); } -// define function to draw and setup UI -function startApp() { - readSettings(storage.readJSON("sleeplog.json", true) || {}); - drawNightTo(prevDays); - Bangle.setUI("leftright", (cb) => { - if (!cb) { - eval(storage.read("sleeplog.settings.js"))(startApp); - } else if (prevDays + cb >= -1) { - drawNightTo((prevDays += cb)); +// swipe listener for switching the displayed day +function swipeListener(h, v) { + // give feedback + Bangle.buzz(25); + // set previous or next day + prevDays += h; + if (prevDays < -1) prevDays = -1; + // redraw + draw(); +} + +// day selection +function daySelection() { + var date = Date(startDate - prevDays * 864E5).toString().split(" "); + E.showPrompt(date.slice(0, 3).join(" ") + "\n" + date[3] + "\n" + + prevDays + /*LANG*/" days before today", { + title: /*LANG*/"Select Day", + buttons: { + "<<7": 7, + "<1": 1, + "Ok": 0, + "1>": -1, + "7>>": -7 + } + }).then(function(v) { + if (v) { + prevDays += v; + if (prevDays < -1) prevDays = -1; + daySelection(); + } else { + fromMenu(); } }); } -// define day to display and analysis timeout id -var prevDays = 0; -var ATID; +// open settings menu +function openSettings() { + // disable back behaviour to prevent bouncing on return + backListener = () => {}; + // open settings menu + eval(require("Storage").read("sleeplog.settings.js"))(fromMenu); +} -// setup app -g.clear(); +// draw progress as bar, increment on undefined percentage +function drawProgress(progress) { + g.fillRect(19, 147, 136 * progress + 19, 149); +} + +// (re)draw info data +function drawInfo() { + // set active info type + var info = infoData[infoType % infoData.length]; + // draw info + g.clearRect(0, 69, 175, 105).reset() + .setFont("6x8").setFontAlign(-1, -1) + .drawString(info[0][0], 10, 70) + .drawString(info[1][0], 10, 90) + .setFont("12x20").setFontAlign(1, -1) + .drawString((info[0][1] / 60 | 0) + "h " + (info[0][1] % 60) + "min", 166, 70) + .drawString((info[1][1] / 60 | 0) + "h " + (info[1][1] % 60) + "min", 166, 90); + // free ram + info = undefined; +} + + +// draw graph for log segment +function drawGraph(log, date, pos) { + // set y position + var y = pos ? 144 : 110; + // clear area + g.reset().clearRect(0, y, width, y + 33); + // draw x axis + g.drawLine(0, y + 19, width, y + 19); + // draw x label + var stepWidth = width / 12; + var startHour = date.getHours() + (pos ? 0 : 12); + for (var x = 0; x < 12; x++) { + g.fillRect(x * stepWidth, y + 20, x * stepWidth, y + 22); + g.setFontAlign(-1, -1).setFont("6x8") + .drawString((startHour + x) % 24, x * stepWidth + 1, y + 24); + } + + // set height and color values: + // status: unknown, not worn, awake, light sleep, deep sleep, consecutive + // color: black, red, green, cyan, violet, blue + var heights = [0, 0.4, 0.6, 0.8, 1]; + var colors = [0, 63488, 2016, 2047, 31, 32799]; + + // cycle through log + log.forEach((entry, index, log) => { + // calculate positions + var x1 = Math.ceil((entry[0] - log[0][0]) / 72 * width); + var x2 = Math.floor(((log[index + 1] || [date / 6E5])[0] - log[0][0]) / 72 * width); + // calculate y2 position + var y2 = y + 18 * (1 - heights[entry[1]]); + // set color depending on status and consecutive sleep + g.setColor(colors[entry[2] === 2 ? 5 : entry[1]]); + // clear area, draw bar and top line + g.clearRect(x1, y, x2, y + 18); + g.fillRect(x1, y + 18, x2, y2).reset(); + if (y + 18 !== y2) g.fillRect(x1, y2, x2, y2); + }); +} + +// draw information in an interruptable cycle +function drawingCycle(calcDate, thisID, cycle, log) { + // reset analysis timeout ID + ATID = undefined; + + // check drawing ID to continue + if (thisID !== drawingID) return; + + // check cycle + if (!cycle) { + /* read log on first cycle */ + // set initial cycle + cycle = 1; + + // read log + log = slMod.readLog(calcDate - 864E5, calcDate); + + // draw progress + drawProgress(0.6); + } else if (cycle === 2) { + /* draw stats on second cycle */ + + // read stats and process into info data + infoData = slMod.getStats(calcDate, 0, log); + infoData = [ + [ + [ /*LANG*/"consecutive\nsleeping", infoData.consecSleep], + [ /*LANG*/"true\nsleeping", infoData.deepSleep + infoData.lightSleep] + ], + [ + [ /*LANG*/"deep\nsleep", infoData.deepSleep], + [ /*LANG*/"light\nsleep", infoData.lightSleep] + ], + [ + [ /*LANG*/"awake", infoData.awakeTime], + [ /*LANG*/"not worn", infoData.notWornTime] + ] + ]; + // draw info + drawInfo(); + + // draw progress + drawProgress(0.9); + } else if (cycle === 3) { + /* segment log on third cycle */ + // calculate segmentation date in 10min steps and index of the segmentation + var segmDate = calcDate / 6E5 - 72; + var segmIndex = log.findIndex(entry => entry[0] >= segmDate); + + // check if segmentation neccessary + if (segmIndex > 0) { + // split log + log = [log.slice(segmIndex), log.slice(0, segmIndex)]; + // add entry at segmentation point + if (log[0][0] !== segmDate) + log[0].unshift([segmDate, log[1][segmIndex - 1][1], log[1][segmIndex - 1][2]]); + } else if (segmIndex < 0) { + // set log as second log entry + log = [ + [], log + ]; + } else { + // add entry at segmentation point + if (log[0] !== segmDate) log.unshift([segmDate, 0, 0]); + // set log as first log entry + log = [log, []]; + } + + // draw progress + drawProgress(1); + } else if (cycle === 4) { + /* draw upper graph on fourth cycle */ + drawGraph(log[0], calcDate, 0); + } else if (cycle === 5) { + /* draw upper graph on fifth cycle */ + drawGraph(log[1], calcDate, 1); + } else { + /* stop cycle and set drawing finished */ + drawingID = 0; + // give feedback + Bangle.buzz(25); + } + + // initiate next cycle if defined + if (thisID === drawingID) ATID = setTimeout(drawingCycle, 10, calcDate, drawingID, ++cycle, log); +} + +// return from a menu +function fromMenu() { + // reset UI to custom mode + Bangle.setUI(customUI); + // enable back behaviour delayed to prevent bouncing + setTimeout(() => backListener = load, 500); + // redraw app + draw(); +} + +// draw app +function draw() { + // stop ongoing drawing + drawingID++; + if (ATID) ATID = clearTimeout(ATID); + + // clear app area + g.reset().clearRect(0, 24, width, width); + + // set date to calculate data for + var calcDate = new Date(startDate - prevDays * 864E5); + + // draw title + g.setFont("12x20").setFontAlign(0, -1) + .drawString( /*LANG*/"Night to " + require('locale').dow(calcDate, 1) + "\n" + + require('locale').date(calcDate, 1), 87, 28); + + // reset graphics and define image string + g.reset(); + var imgStr = ""; + // check which icon to set + if (!global.sleeplog || sleeplog.conf.enabled !== true) { + // set color and disabled service icon + g.setColor(1, 0, 0); + imgStr = "FBSBAOAAfwAP+AH3wD4+B8Hw+A+fAH/gA/wAH4AB+AA/wAf+APnwHw+D4Hx8A++AH/AA/gAH"; + } else if (sleeplog.debug) { + // set debugging icon + imgStr = typeof sleeplog.debug === "object" ? + "FBSBAB/4AQDAF+4BfvAX74F+CBf+gX/oFJKBf+gUkoF/6BSSgX/oFJ6Bf+gX/oF/6BAAgf/4" : // file + "FBSBAP//+f/V///4AAGAABkAAZgAGcABjgAYcAGDgBhwAY4AGcABmH+ZB/mAABgAAYAAH///"; // console + } + // draw service and settings icon + if (imgStr) g.drawImage(atob(imgStr), 2, 36); + g.reset().drawImage(atob("FBSBAAAeAAPgAHwAB4AA8AAPAwDwcA+PAP/wH/4D/8B/8A/gAfwAP4AH8AD+AA/AAPgABwAA"), width - 22, 36); + + // show loading info with progresss bar + g.reset().drawRect(7, 117, width - 8, 157) + .setFont("6x8").setFontAlign(0, 0) + .drawString( /*LANG*/ "calculating data ...\nplease be patient :)", 87, 133) + .drawRect(17, 145, 157, 151); + + // draw first progress + drawProgress(0.1); + + // initiate drawing cycle + ATID = setTimeout(drawingCycle, 10, calcDate, drawingID, 0); +} + +// define sleeplog module +var slMod = require("sleeplog"); + +// read app timeout from settings +var appTimeout = (require("Storage").readJSON("sleeplog.json", true) || {}).appTimeout; + +// set listener for back button +var backListener = load; +// define custom UI mode +var customUI = { + mode: "custom", + back: backListener, + touch: touchListener, + swipe: swipeListener +}; + +// define start values +var startDate = slMod.getLastBreak(); // date to start from +var prevDays = 0; // number of previous days to display +var infoType = 0; // type of info data to display +var infoData; // storage for info data +var ATID; // analysis timeout ID +var drawingID = 0; // drawing ID for ongoing process +// get screen width and center (zero based) +var width = g.getWidth() - 1; +var center = width / 2 - 1; + +// set areas and actions array +var aaa = [ + // day selection + { + x0: 26, + x1: width - 26, + y0: 24, + y1: 68, + interrupt: true, + funct: () => daySelection() + }, + // open settings + { + x0: width - 26, + x1: width, + y0: 24, + y1: 68, + interrupt: true, + funct: () => openSettings() + }, + // change info type + { + x0: 0, + x1: width, + y0: 69, + y1: 105, + funct: () => { + // change info type + infoType++; + // redraw info + drawInfo(); + } + } +]; + +// clear and reset screen +g.clear(true); + +// load and draw widgets Bangle.loadWidgets(); Bangle.drawWidgets(); -// start app -startApp(); +// set UI in custom mode +Bangle.setUI(customUI); + +// set app timeout if defined +if (appTimeout) Bangle.setOptions({ + lockTimeout: appTimeout, + backlightTimeout: appTimeout +}); + +// draw app +draw(); diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index f989e2835..a86c3f554 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -1,169 +1,311 @@ -// Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS): -// 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. -// https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en +// sleeplog.status values: +// undefined = service stopped, 0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep +// sleeplog.consecutive values: +// undefined = service stopped, 0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep -// sleeplog.status values: undefined = service stopped, 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping - -// load settings into global object -global.sleeplog = Object.assign({ - enabled: true, // en-/disable completely - logfile: "sleeplog.log", // logfile - powersaving: false, // disables ESS and uses build in movement detection - winwidth: 13, // 13 values, read with 12.5Hz = every 1.04s - nomothresh: 0.012, // values lower than 0.008 getting triggert by noise - sleepthresh: 577, // 577 times no movement * 1.04s window width > 10min - maxmove: 100, // movement threshold on power saving mode - tempthresh: 27, // every temperature above ist registered as worn -}, require("Storage").readJSON("sleeplog.json", true) || {}); - -// delete app settings -["breaktod", "maxawake", "minconsec"].forEach(property => delete sleeplog[property]); - -// check if service enabled -if (sleeplog.enabled) { - - // add always used values and functions to global object - sleeplog = Object.assign(sleeplog, { - // set cached values - resting: undefined, - status: undefined, - - // define function to handle stopping the service, it will be restarted on reload if enabled - stopHandler: function() { - // remove all listeners - Bangle.removeListener('accel', sleeplog.accel); - Bangle.removeListener('health', sleeplog.health); - // write log with undefined sleeping status - require("sleeplog").writeLog(0, [Math.floor(Date.now()), 0]); - // reset cached values if sleeplog is defined - if (global.sleeplog) { - sleeplog.resting = undefined; - sleeplog.status = undefined; - // reset cached ESS calculation values - if (!sleeplog.powersaving) { - sleeplog.ess_values = []; - sleeplog.nomocount = 0; - sleeplog.firstnomodate = undefined; - } - } - }, - - // define function to remove the kill listener and stop the service - // https://github.com/espruino/BangleApps/issues/1445 - stop: function() { - E.removeListener('kill', sleeplog.stopHandler); - sleeplog.stopHandler(); - }, +// create global object with settings +global.sleeplog = { + conf: Object.assign({ + // main settings + enabled: true, // en-/disable completely + // threshold settings + maxAwake: 36E5, // [ms] maximal awake time to count for consecutive sleep + minConsec: 18E5, // [ms] minimal time to count for consecutive sleep + deepTh: 100, // threshold for deep sleep + lightTh: 200, // threshold for light sleep + }, require("Storage").readJSON("sleeplog.json", true) || {}) +}; +// check if service is enabled +if (sleeplog.conf.enabled) { + // assign functions to global object + global.sleeplog = Object.assign({ // define function to initialy start or restart the service start: function() { - // add kill listener - E.on('kill', sleeplog.stopHandler); - // add health listener if defined and - if (sleeplog.health) Bangle.on('health', sleeplog.health); - // add acceleration listener if defined and set status to unknown - if (sleeplog.accel) Bangle.on('accel', sleeplog.accel); - // read log since 5min ago and restore status to last known state or unknown - sleeplog.status = (require("sleeplog").readLog(0, Date.now() - 3E5)[1] || [0, 0])[1]; - // update resting according to status - sleeplog.resting = sleeplog.status % 2; - // write restored status to log - require("sleeplog").writeLog(0, [Math.floor(Date.now()), sleeplog.status]); + // add kill and health listener + E.on('kill', sleeplog.saveStatus); + Bangle.on('health', sleeplog.health); + + // restore saved status + this.restoreStatus(); + }, + + // define function to stop the service, it will be restarted on reload if enabled + stop: function() { + // remove all listeners + Bangle.removeListener('health', sleeplog.health); + E.removeListener('kill', sleeplog.saveStatus); + + // save active values + this.saveStatus(); + // reset active values + this.status = undefined; + this.consecutive = undefined; + }, + + // define function to restore active values on a restart or reload + restoreStatus: function() { + // define restore objects with default values + var restore = { + status: 0, + consecutive: 0, + info: {} + }; + + // open log file + var file = require("Storage").open("sleeplog.log", "r"); + // read last 55 chars from log + file.read(file.getLength() - 52); + file = file.read(52); + + // check if the log contains data + if (file) { + // remove unneeded data + file = file.trim().split("\n").reverse().filter((e, i) => i < 2); + // convert file into accessable array + file = file.map(e => e.split(",").map(e => parseInt(e))); + + // add default data if no previous status is available + if (file.length < 2 || file[1].length !== 3) file.push([0, 0, 0]); + + // check if data to restore has been saved + if (file[0].length > 3) { + // read data into restore object + restore = { + status: file[1][1], + consecutive: file[1][2], + info: { + lastChange: file[1][0] * 6E5, + lastCheck: file[0][1] * 6E5, + awakeSince: file[0][2] * 6E5, + asleepSince: file[0][3] * 6E5 + } + }; + + // add debug if set + if (file[0].length === 6) + restore = Object.assign(restore, { + debug: file[0][4] ? { + writeUntil: file[0][4] * 6E5, + fileid: file[0][5] + } : true + }); + + // calculate timestamp in 10min steps, corrected to 10min ago + var timestamp = (Date.now() / 6E5 | 0) - 1; + + // check if restored status not unknown and lastCheck was 20min before timestamp + if (restore.status && restore.info.lastCheck + 12E5 < timestamp) { + // set status and consecutive to unknown + restore.status = 0; + restore.consecutive = 0; + restore.info.lastChange = restore.info.lastCheck + 6E5; + restore.info.lastCheck = timestamp; + // write undefined status 10min after restored lastCheck + this.appendStatus(restore.info.lastChange, 0, 0); + } else { + // set saveUpToDate + restore.info.saveUpToDate = true; + } + } + } + + // write restored values into global object + global.sleeplog = Object.assign(this, restore); + }, + + // define function to save active values on a stop or kill event + // - called by event listener: "this"-reference points to global + saveStatus: function(force) { + // check if global variable accessable + if (!global.sleeplog) return new Error("sleeplog: Can't save status, global object missing!"); + + // check saveUpToDate is not set or forced + if (!sleeplog.info.saveUpToDate || force) { + // save status, consecutive status and info timestamps to restore on reload + var save = [sleeplog.info.lastCheck, sleeplog.info.awakeSince, sleeplog.info.asleepSince]; + // add debuging status if active + if (sleeplog.debug) save.push(sleeplog.debug.writeUntil, sleeplog.debug.fileid); + + // stringify entries + save = "," + save.map((entry, index) => { + if (index < 4) entry /= 6E5; // store in 10min steps + return entry | 0; // sanitize + }).join(",") + "\n"; + + // add present status if forced + if (force) save = (sleeplog.info.lastChange / 6E5) + "," + + sleeplog.status + "," + sleeplog.consecutive + "\n" + save; + + // append saved data to StorageFile + require("Storage").open("sleeplog.log", "a").write(save); + + // clear save string to free ram + save = undefined; + } + }, + + // define health listener function + // - called by event listener: "this"-reference points to global + health: function(data) { + // check if global variable accessable + if (!global.sleeplog) return new Error("sleeplog: Can't process health event, global object missing!"); + + // check if movement is available + if (!data.movement) return; + + // add timestamp rounded to 10min, corrected to 10min ago + data.timestamp = data.timestamp || ((Date.now() / 6E5 | 0) - 1) * 6E5; + + // add preliminary status depending on charging and movement thresholds + data.status = Bangle.isCharging() ? 1 : + data.movement <= sleeplog.conf.deepTh ? 4 : + data.movement <= sleeplog.conf.lightTh ? 3 : 2; + + // check if changing to deep sleep from non sleepling + if (data.status === 4 && sleeplog.status <= 2) { + // check wearing status + sleeplog.checkIsWearing((isWearing, data) => { + // correct status + if (!isWearing) data.status = 1; + // set status + sleeplog.setStatus(data); + }, data); + } else { + // set status + sleeplog.setStatus(data); + } + }, + + // define function to check if the bangle is worn by using the hrm + checkIsWearing: function(returnFn, data) { + // create a temporary object to store data and functions + global.tmpWearingCheck = { + // define temporary hrm listener function to read the wearing status + hrmListener: hrm => tmpWearingCheck.isWearing = hrm.isWearing, + // set default wearing status + isWearing: false, + }; + + // enable HRM + Bangle.setHRMPower(true, "wearingCheck"); + // wait until HRM is initialised + setTimeout((returnFn, data) => { + // add HRM listener + Bangle.on('HRM-raw', tmpWearingCheck.hrmListener); + // wait for two cycles (HRM working on 60Hz) + setTimeout((returnFn, data) => { + // remove listener and disable HRM + Bangle.removeListener('HRM-raw', tmpWearingCheck.hrmListener); + Bangle.setHRMPower(false, "wearingCheck"); + // cache wearing status + var isWearing = tmpWearingCheck.isWearing; + // clear temporary object + delete global.tmpWearingCheck; + // call return function with status + returnFn(isWearing, data); + }, 34, returnFn, data); + }, 2500, returnFn, data); + }, + + // define function to set the status + setStatus: function(data) { + // update lastCheck + this.info.lastCheck = data.timestamp; + + // correct light sleep status to awake if + // previous status not deep sleep and not too long awake (asleepSince unset) + if (data.status === 3 && this.status !== 4 && !this.info.asleepSince) { + data.status = 2; + } + + // cache consecutive status to check for changes later on + data.consecutive = this.consecutive; + + // set disabled move log status + var moveLogStatus = false; + + // check if changing to deep sleep from non sleepling + if (data.status === 4 && this.status <= 2) { + // set asleepSince if undefined + this.info.asleepSince = this.info.asleepSince || data.timestamp; + // reset consecutive status + data.consecutive = 0; + // check if changing to awake + } else if (data.status === 2 && this.status > 2) { + // set awakeSince if undefined + this.info.awakeSince = this.info.awakeSince || data.timestamp; + // reset consecutive status + data.consecutive = 0; + } + // check if consecutive unknown + if (!this.consecutive) { + // check if long enough asleep or too long awake + if (data.status === 4 && this.info.asleepSince && + this.info.asleepSince + this.conf.minConsec <= data.timestamp) { + // set consecutive sleep + data.consecutive = 2; + // reset awakeSince + this.info.awakeSince = 0; + // enabled move log status + moveLogStatus = true; + } else if (data.status <= 2 && this.info.awakeSince && + this.info.awakeSince + this.conf.maxAwake <= data.timestamp) { + // set non consecutive sleep + data.consecutive = 1; + // reset asleepSince + this.info.asleepSince = 0; + } + } + + // check if the status has changed + if (data.status !== this.status || data.consecutive !== this.consecutive) { + // append status + this.appendStatus(data.timestamp, data.status, data.consecutive); + + // set new states and update lastChange + this.status = data.status; + this.consecutive = data.consecutive; + this.info.lastChange = data.timestamp; + // reset saveUpToDate status + delete this.info.saveUpToDate; + } + + // call debugging function if set + if (this.debug) require("sleeplog").debug(data); + + // call move log function if set + if (moveLogStatus) require("sleeplog").moveLog(); + }, + + // define function to append the status to the StorageFile log + appendStatus: function(timestamp, status, consecutive) { + // exit on missing timestamp + if (!timestamp) return; + // reduce timestamp to 10min step + timestamp = timestamp / 6E5 | 0; + // append to StorageFile + require("Storage").open("sleeplog.log", "a").write( + [timestamp, status || 0, consecutive || 0].join(",") + "\n" + ); + }, + + // define function to access stats of the last night + getStats: function() { + // check if stats cache is not defined or older than 24h + if (this.statsCache === undefined || this.statsCache.calculatedAt + 864E5 < Date.now()) { + // read stats of the last night into cache and remove module from cache + this.statsCache = require("sleeplog").getStats(); + Modules.removeCached("sleeplog"); + } + // return stats cache + return this.statsCache; } - }); - - // check for power saving mode - if (sleeplog.powersaving) { - // power saving mode using build in movement detection - // delete unused settings - ["winwidth", "nomothresh", "sleepthresh"].forEach(property => delete sleeplog[property]); - // add cached values and functions to global object - sleeplog = Object.assign(sleeplog, { - // define health listener function - health: function(data) { - // set global object and check for existence - var gObj = global.sleeplog; - if (!gObj) return; - - // calculate timestamp for this measurement - var timestamp = Math.floor(Date.now() - 6E5); - - // check for non-movement according to the threshold - if (data.movement <= gObj.maxmove) { - // check resting state - if (gObj.resting !== true) { - // change resting state - gObj.resting = true; - // set status to sleeping or worn - gObj.status = E.getTemperature() > gObj.tempthresh ? 3 : 1; - // write status to log, - require("sleeplog").writeLog(0, [timestamp, gObj.status, E.getTemperature()]); - } - } else { - // check resting state - if (gObj.resting !== false) { - // change resting state, set status and write status to log - gObj.resting = false; - gObj.status = 2; - require("sleeplog").writeLog(0, [timestamp, 2]); - } - } - } - }); - } else { - // full ESS calculation - // add cached values and functions to global object - sleeplog = Object.assign(sleeplog, { - // set cached values - ess_values: [], - nomocount: 0, - firstnomodate: undefined, - - // define acceleration listener function - accel: function(xyz) { - // save acceleration magnitude and start calculation on enough saved data - if (global.sleeplog && sleeplog.ess_values.push(xyz.mag) >= sleeplog.winwidth) sleeplog.calc(); - }, - - // define calculator function - calc: function() { - // exit on wrong this - if (this.enabled === undefined) return; - // calculate standard deviation over - var mean = this.ess_values.reduce((prev, cur) => cur + prev) / this.winwidth; - var stddev = Math.sqrt(this.ess_values.map(val => Math.pow(val - mean, 2)).reduce((prev, cur) => prev + cur) / this.winwidth); - // reset saved acceleration data - this.ess_values = []; - - // check for non-movement according to the threshold - if (stddev < this.nomothresh) { - // increment non-movement sections count, set date of first non-movement - if (++this.nomocount == 1) this.firstnomodate = Math.floor(Date.now()); - // check resting state and non-movement count against threshold - if (this.resting !== true && this.nomocount >= this.sleepthresh) { - // change resting state - this.resting = true; - // set status to sleeping or worn - this.status = E.getTemperature() > this.tempthresh ? 3 : 1; - // write status to log, with first no movement timestamp - require("sleeplog").writeLog(0, [this.firstnomodate, this.status, E.getTemperature()]); - } - } else { - // reset non-movement sections count - this.nomocount = 0; - // check resting state - if (this.resting !== false) { - // change resting state and set status - this.resting = false; - this.status = 2; - // write status to log - require("sleeplog").writeLog(0, [Math.floor(Date.now()), 2]); - } - } - } - }); - } + }, sleeplog); // initial starting global.sleeplog.start(); +} else { + // clear global object from ram + delete global.sleeplog; } diff --git a/apps/sleeplog/lib.js b/apps/sleeplog/lib.js index 752139e27..1919e7483 100644 --- a/apps/sleeplog/lib.js +++ b/apps/sleeplog/lib.js @@ -1,199 +1,465 @@ +// define accessable functions exports = { // define en-/disable function, restarts the service to make changes take effect - setEnabled: function(enable, logfile, powersaving) { - // check if sleeplog is available - if (typeof global.sleeplog !== "object") return; - - // set default logfile - if ((typeof logfile !== "string" || !logfile.endsWith(".log")) && - logfile !== false) logfile = "sleeplog.log"; - + setEnabled: function(enable) { // stop if enabled - if (global.sleeplog.enabled) global.sleeplog.stop(); + if (global.sleeplog && sleeplog.enabled) sleeplog.stop(); - // define storage and filename - var storage = require("Storage"); - var filename = "sleeplog.json"; + // define settings filename + var settings = "sleeplog.json"; // change enabled value in settings - storage.writeJSON(filename, Object.assign(storage.readJSON(filename, true) || {}, { - enabled: enable, - logfile: logfile, - powersaving: powersaving || false - })); + require("Storage").writeJSON(settings, Object.assign( + require("Storage").readJSON(settings, true) || {}, { + enabled: enable + } + )); // force changes to take effect by executing the boot script - eval(storage.read("sleeplog.boot.js")); + eval(require("Storage").read("sleeplog.boot.js")); - // clear variables - storage = undefined; - filename = undefined; return true; }, - // define read log function - // sorting: latest first, format: - // [[number, int, float, string], [...], ... ] - // - number // timestamp in ms - // - int // status: 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping - // - float // internal temperature - // - string // additional information - readLog: function(logfile, since, until) { - // check/set logfile - if (typeof logfile !== "string" || !logfile.endsWith(".log")) { - logfile = (global.sleeplog || {}).logfile || "sleeplog.log"; + // define read log function, returns log array + // sorting: ascending (latest first), format: + // [[number, int, int], [...], ... ] + // - number // timestamp in 10min + // - int // status: 0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep + // - int // consecutive: 0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep + readLog: function(since, until) { + // set now and check if now is before since + var now = Date.now(); + if (now < since) return []; + + // set defaults and convert since, until and now to 10min steps + since = Math.floor((since || 0) / 6E5); + until = Math.ceil((until || now) / 6E5); + now = Math.ceil(now / 6E5); + + // define output log + var log = []; + + // open StorageFile + var file = require("Storage").open("sleeplog.log", "r"); + // cache StorageFile size + var storageFileSize = file.getLength(); + // check if a Storage File needs to be read + if (storageFileSize) { + // define previous line cache + var prevLine; + // loop through StorageFile entries + while (true) { + // cache new line + var line = file.readLine(); + // exit loop if all lines are read + if (!line) break; + // skip lines starting with "," + if (line.startsWith(",")) continue; + // parse line + line = line.trim().split(",").map(e => parseInt(e)); + // exit loop if new line timestamp is not before until + if (line[0] >= until) break; + // check if new line timestamp is 24h before since or not after since + if (line[0] + 144 < since) { + // skip roughly the next 10 lines + file.read(118); + file.readLine(); + } else if (line[0] <= since) { + // cache line for next cycle + prevLine = line; + } else { + // add previous line if it was cached + if (prevLine) log.push(prevLine); + // add new line at the end of log + log.push(line); + // clear previous line cache + prevLine = undefined; + } + } + // add previous line if it was cached + if (prevLine) log.push(prevLine); + // set unknown consecutive statuses + log = log.reverse().map((entry, index) => { + if (entry[2] === 0) entry[2] = (log[index - 1] || [])[2] || 0; + return entry; + }).reverse(); + // remove duplicates + log = log.filter((entry, index) => + !(index > 0 && entry[1] === log[index - 1][1] && entry[2] === log[index - 1][2]) + ); } - // check if since is in the future - if (since > Date()) return []; + // check if log empty or first entry is after since + if (!log[0] || log[0][0] > since) { + // look for all needed storage files + var files = require("Storage").list(/^sleeplog_\d\d\d\d\.log$/, { + sf: false + }); - // read logfile - var log = require("Storage").read(logfile); - // return empty log - if (!log) return []; - // decode data if needed - if (log[0] !== "[") log = atob(log); - // do a simple check before parsing - if (!log.startsWith("[[") || !log.endsWith("]]")) return []; - log = JSON.parse(log) || []; + // check if any file available + if (files.length) { + // generate start and end times in 10min steps + files = files.map(file => { + var start = this.fnToMs(parseInt(file.substr(9, 4))) / 6E5; + return { + name: file, + start: start, + end: start + 2016 + }; + }).sort((a, b) => b.start - a.start); - // check if filtering is needed - if (since || until) { - // search for latest entry befor since - if (since) since = (log.find(element => element[0] <= since) || [0])[0]; - // filter selected time period - log = log.filter(element => (element[0] >= since) && (element[0] <= (until || 1E14))); + // read all neccessary files + var filesLog = []; + files.some(file => { + // exit loop if since after end + if (since >= file.end) return true; + // read file if until after start and since before end + if (until > file.start || since < file.end) { + var thisLog = require("Storage").readJSON(file.name, 1) || []; + if (thisLog.length) filesLog = thisLog.concat(filesLog); + } + }); + // free ram + files = undefined; + + // check if log from files is available + if (filesLog.length) { + // remove unwanted entries + filesLog = filesLog.filter((entry, index, filesLog) => ( + (filesLog[index + 1] || [now])[0] >= since && entry[0] <= until + )); + // add to log as previous entries + log = filesLog.concat(log); + } + // free ram + filesLog = undefined; + } } - // output log + // define last index + var lastIndex = log.length - 1; + // set timestamp of first entry to since if first entry before since + if (log[0] && log[0][0] < since) log[0][0] = since; + // add timestamp at now with unknown status if until after now + if (until > now) log.push([now, 0, 0]); + return log; }, - // define write log function, append or replace log depending on input - // append input if array length >1 and element[0] >9E11 - // replace log with input if at least one entry like above is inside another array - writeLog: function(logfile, input) { - // check/set logfile - if (typeof logfile !== "string" || !logfile.endsWith(".log")) { - if (!global.sleeplog || sleeplog.logfile === false) return; - logfile = sleeplog.logfile || "sleeplog.log"; + // define move log function, move StorageFile content into files seperated by fortnights + moveLog: function(force) { + /** convert old logfile (< v0.10) if present **/ + if (require("Storage").list("sleeplog.log", { + sf: false + }).length) { + convertOldLog(); } + /** may be removed in later versions **/ - // check if input is an array - if (typeof input !== "object" || typeof input.length !== "number") return; + // first day of this fortnight period + var thisFirstDay = this.fnToMs(this.msToFn(Date.now())); - // check for entry plausibility - if (input.length > 1 && input[0] * 1 > 9E11) { - // read log - var log = this.readLog(logfile); + // read timestamp of the first StorageFile entry + var firstDay = (require("Storage").open("sleeplog.log", "r").read(47) || "").match(/\n\d*/); + // calculate the first day of the fortnight period + if (firstDay) firstDay = this.fnToMs(this.msToFn(parseInt(firstDay[0].trim()) * 6E5)); - // remove last state if it was unknown and less then 5min ago - if (log.length > 0 && log[0][1] === 0 && - Math.floor(Date.now()) - log[0][0] < 3E5) log.shift(); + // check if moving is neccessary or forced + if (force || firstDay && firstDay < thisFirstDay) { + // read log for each fortnight period + while (firstDay) { + // calculate last day + var lastDay = firstDay + 12096E5; + // read log of the fortnight period + var log = require("sleeplog").readLog(firstDay, lastDay); - // add entry at the first position if it has changed - if (log.length === 0 || input.some((e, index) => index > 0 && input[index] !== log[0][index])) log.unshift(input); + // check if before this fortnight period + if (firstDay < thisFirstDay) { + // write log in seperate file + require("Storage").writeJSON("sleeplog_" + this.msToFn(firstDay) + ".log", log); + // set last day as first + firstDay = lastDay; + } else { + // rewrite StorageFile + require("Storage").open("sleeplog.log", "w").write(log.map(e => e.join(",")).join("\n")); + // clear first day to exit loop + firstDay = undefined; + } - // map log as input - input = log; - } - - // check and if neccessary reduce logsize to prevent low mem - if (input.length > 750) input = input.slice(-750); - - // simple check for log plausibility - if (input[0].length > 1 && input[0][0] * 1 > 9E11) { - // write log to storage - require("Storage").write(logfile, btoa(JSON.stringify(input))); - return true; - } - }, - - // define log to humanreadable string function - // sorting: latest last, format: - // "{substring of ISO date} - {status} for {duration}min\n..." - getReadableLog: function(printLog, since, until, logfile) { - // read log and check - var log = this.readLog(logfile, since, until); - if (!log.length) return; - // reverse array to set last timestamp to the end - log.reverse(); - - // define status description and log string - var statusText = ["unknown ", "not worn", "awake ", "sleeping"]; - var logString = []; - - // rewrite each entry - log.forEach((element, index) => { - logString[index] = "" + - Date(element[0] - Date().getTimezoneOffset() * 6E4).toISOString().substr(0, 19).replace("T", " ") + " - " + - statusText[element[1]] + - (index === log.length - 1 ? - element.length < 3 ? "" : " ".repeat(12) : - " for " + ("" + Math.round((log[index + 1][0] - element[0]) / 60000)).padStart(4) + "min" - ) + - (element[2] ? " | Temp: " + ("" + element[2]).padEnd(5) + "°C" : "") + - (element[3] ? " | " + element[3] : ""); - }); - logString = logString.join("\n"); - - // if set print and return string - if (printLog) { - print(logString); - print("- first", Date(log[0][0])); - print("- last", Date(log[log.length - 1][0])); - var period = log[log.length - 1][0] - log[0][0]; - print("- period= " + Math.floor(period / 864E5) + "d " + Math.floor(period % 864E5 / 36E5) + "h " + Math.floor(period % 36E5 / 6E4) + "min"); - } - return logString; - }, - - // define function to eliminate some errors inside the log - restoreLog: function(logfile) { - // read log and check - var log = this.readLog(logfile); - if (!log.length) return; - - // define output variable to show number of changes - var output = log.length; - - // remove non decremental entries - log = log.filter((element, index) => log[index][0] >= (log[index + 1] || [0])[0]); - - // write log - this.writeLog(logfile, log); - - // return difference in length - return output - log.length; - }, - - // define function to reinterpret worn status based on given temperature threshold - reinterpretTemp: function(logfile, tempthresh) { - // read log and check - var log = this.readLog(logfile); - if (!log.length) return; - - // set default tempthresh - tempthresh = tempthresh || (global.sleeplog ? sleeplog.tempthresh : 27); - - // define output variable to show number of changes - var output = 0; - - // remove non decremental entries - log = log.map(element => { - if (element[2]) { - var tmp = element[1]; - element[1] = element[2] > tempthresh ? 3 : 1; - if (tmp !== element[1]) output++; + // free ram + log = undefined; } - return element; + } + }, + + // define function to return stats from the last date [ms] for a specific duration [ms] or for the complete log + getStats: function(until, duration, log) { + // define stats variable + var stats = { + calculatedAt: // [date] timestamp of the calculation + Math.round(Date.now()), + deepSleep: 0, // [min] deep sleep duration + lightSleep: 0, // [min] light sleep duration + awakeSleep: 0, // [min] awake duration inside consecutive sleep + consecSleep: 0, // [min] consecutive sleep duration + awakeTime: 0, // [min] awake duration outside consecutive sleep + notWornTime: 0, // [min] duration of not worn status + unknownTime: 0, // [min] duration of unknown status + logDuration: 0, // [min] duration of all entries taken into account + firstDate: undefined, // [date] first entry taken into account + lastDate: undefined // [date] last entry taken into account + }; + + // set default inputs + until = until || stats.calculatedAt; + if (!duration) duration = 864E5; + + // read log for the specified duration or complete log if not handed over + if (!log) log = this.readLog(duration ? until - duration : 0, until); + + // check if log not empty or corrupted + if (log && log.length && log[0] && log[0].length === 3) { + // calculate and set first log date from 10min steps + stats.firstDate = log[0][0] * 6E5; + stats.lastDate = log[log.length - 1][0] * 6E5; + + // cycle through log to calculate sums til end or duration is exceeded + log.forEach((entry, index, log) => { + // calculate duration of this entry from 10min steps to minutes + var duration = ((log[index + 1] || [until / 6E5 | 0])[0] - entry[0]) * 10; + + // check if duration greater 0 + if (duration) { + // calculate sums + if (entry[1] === 4) stats.deepSleep += duration; + else if (entry[1] === 3) stats.lightSleep += duration; + else if (entry[1] === 2) { + if (entry[2] === 2) stats.awakeSleep += duration; + else if (entry[2] === 1) stats.awakeTime += duration; + } + if (entry[2] === 2) stats.consecSleep += duration; + if (entry[1] === 1) stats.notWornTime += duration; + if (entry[1] === 0) stats.unknownTime += duration; + stats.logDuration += duration; + } + }); + } + + // free ram + log = undefined; + + // return stats of the last day + return stats; + }, + + // define function to return last break time of day from date or now (default: 12 o'clock) + getLastBreak: function(date, ToD) { + // set default date or correct date type if needed + if (!date || !date.getDay) date = date ? new Date(date) : new Date(); + // set default ToD as set in sleeplog.conf or settings if available + if (ToD === undefined) ToD = (global.sleeplog && sleeplog.conf ? sleeplog.conf.breakToD : + (require("Storage").readJSON("sleeplog.json", true) || {}).breakToD) || 12; + // calculate last break time and return + return new Date(date.getFullYear(), date.getMonth(), date.getDate(), ToD); + }, + + // define functions to convert ms to the number of fortnights since the first Sunday at noon: 1970-01-04T12:00 + fnToMs: function(no) { + return (no + 0.25) * 12096E5; + }, + msToFn: function(ms) { + return (ms / 12096E5 - 0.25) | 0; + }, + + // define set debug function, options: + // enable as boolean, start/stop debugging + // duration in hours, generate csv log if set, max: 96h + setDebug: function(enable, duration) { + // check if global variable accessable + if (!global.sleeplog) return new Error("sleeplog: Can't set debugging, global object missing!"); + + // check if nothing has to be changed + if (!duration && + (enable && sleeplog.debug === true) || + (!enable && !sleeplog.debug)) return; + + // check if en- or disable debugging + if (enable) { + // define debug object + sleeplog.debug = {}; + + // check if a file should be generated + if (typeof duration === "number") { + // check duration boundaries, 0 => 8 + duration = duration > 96 ? 96 : duration || 12; + // calculate and set writeUntil in 10min steps + sleeplog.debug.writeUntil = ((Date.now() / 6E5 | 0) + duration * 6) * 6E5; + // set fileid to "{hours since 1970}" + sleeplog.debug.fileid = Date.now() / 36E5 | 0; + // write csv header on empty file + var file = require("Storage").open("sleeplog_" + sleeplog.debug.fileid + ".csv", "a"); + if (!file.getLength()) file.write( + "timestamp,movement,status,consecutive,asleepSince,awakeSince,bpm,bpmConfidence\n" + ); + // free ram + file = undefined; + } else { + // set debug as active + sleeplog.debug = true; + } + } else { + // disable debugging + delete sleeplog.debug; + } + + // save status forced + sleeplog.saveStatus(true); + }, + + // define debugging function, called after logging if debug is set + debug: function(data) { + // check if global variable accessable and debug active + if (!global.sleeplog || !sleeplog.debug) return; + + // set functions to convert timestamps + function localTime(timestamp) { + return timestamp ? Date(timestamp).toString().split(" ")[4].substr(0, 5) : "- - -"; + } + function officeTime(timestamp) { + // days since 30.12.1899 + return timestamp / 864E5 + 25569; + } + + // generate console output + var console = "sleeplog: " + + localTime(data.timestamp) + " > " + + "movement: " + ("" + data.movement).padStart(4) + ", " + + "unknown ,non consec.,consecutive".split(",")[sleeplog.consecutive] + " " + + "unknown,not worn,awake,light sleep,deep sleep".split(",")[data.status].padEnd(12) + ", " + + "asleep since: " + localTime(sleeplog.info.asleepSince) + ", " + + "awake since: " + localTime(sleeplog.info.awakeSince); + // add bpm if set + if (data.bpm) console += ", " + + "bpm: " + ("" + data.bpm).padStart(3) + ", " + + "confidence: " + data.bpmConfidence; + // output to console + print(console); + + // check if debug is set as object with a file id and it is not past writeUntil + if (typeof sleeplog.debug === "object" && sleeplog.debug.fileid && + Date.now() < sleeplog.debug.writeUntil) { + // generate next csv line + var csv = [ + officeTime(data.timestamp), + data.movement, + data.status, + sleeplog.consecutive, + sleeplog.info.asleepSince ? officeTime(sleeplog.info.asleepSince) : "", + sleeplog.info.awakeSince ? officeTime(sleeplog.info.awakeSince) : "", + data.bpm || "", + data.bpmConfidence || "" + ].join(","); + // write next line to log if set + require("Storage").open("sleeplog_" + sleeplog.debug.fileid + ".csv", "a").write(csv + "\n"); + } else { + // clear file setting in debug + sleeplog.debug = true; + } + + }, + + // print log as humanreadable output similar to debug output + printLog: function(since, until) { + // set default until + until = until || Date.now(); + // print each entry inside log + this.readLog(since, until).forEach((entry, index, log) => { + // calculate duration of this entry from 10min steps to minutes + var duration = ((log[index + 1] || [until / 6E5 | 0])[0] - entry[0]) * 10; + // print this entry + print((index + ")").padStart(4) + " " + + Date(entry[0] * 6E5).toString().substr(0, 21) + " > " + + "unknown ,non consec.,consecutive".split(",")[entry[2]] + " " + + "unknown,not worn,awake,light sleep,deep sleep".split(",")[entry[1]].padEnd(12) + + "for" + (duration + "min").padStart(8)); }); + }, - // write log - this.writeLog(logfile, log); + /** convert old (< v0.10) to new logfile data **/ + convertOldLog: function() { + // read old logfile + var oldLog = require("Storage").read("sleeplog.log") || ""; + // decode data if needed + if (!oldLog.startsWith("[")) oldLog = atob(oldLog); + // delete old logfile and return if it is empty or corrupted + if (!oldLog.startsWith("[[") || !oldLog.endsWith("]]")) { + require("Storage").erase("sleeplog.log"); + return; + } - // return output - return output; + // transform into StorageFile and clear oldLog to have more free ram accessable + require("Storage").open("sleeplog_old.log", "w").write(JSON.parse(oldLog).reverse().join("\n")); + oldLog = undefined; + + // calculate fortnight from now + var fnOfNow = this.msToFn(Date.now()); + + // open StorageFile with old log data + var file = require("Storage").open("sleeplog_old.log", "r"); + // define active fortnight and file cache + var activeFn = true; + var fileCache = []; + // loop through StorageFile entries + while (activeFn) { + // define fortnight for this entry + var thisFn = false; + // cache new line + var line = file.readLine(); + // check if line is filled + if (line) { + // parse line + line = line.substr(0, 15).split(",").map(e => parseInt(e)); + // calculate fortnight for this entry + thisFn = this.msToFn(line[0]); + // convert timestamp into 10min steps + line[0] = line[0] / 6E5 | 0; + // set consecutive to unknown + line.push(0); + } + // check if active fortnight and file cache is set, fortnight has changed and + // active fortnight is not fortnight from now + if (activeFn && fileCache.length && activeFn !== thisFn && activeFn !== fnOfNow) { + // write file cache into new file according to fortnight + require("Storage").writeJSON("sleeplog_" + activeFn + ".log", fileCache); + // clear file cache + fileCache = []; + } + // add line to file cache if it is filled + if (line) fileCache.push(line); + // set active fortnight + activeFn = thisFn; + } + // check if entries are leftover + if (fileCache.length) { + // format fileCache entries into a string + fileCache = fileCache.map(e => e.join(",")).join("\n"); + // read complete new log StorageFile as string + file = require("Storage").open("sleeplog.log", "r"); + var newLogString = file.read(file.getLength()); + // add entries at the beginning of the new log string + newLogString = fileCache + "\n" + newLogString; + // rewrite new log StorageFile + require("Storage").open("sleeplog.log", "w").write(newLogString); + } + + // free ram + file = undefined; + fileCache = undefined; + + // clean up old files + require("Storage").erase("sleeplog.log"); + require("Storage").open("sleeplog_old.log", "w").erase(); } - + /** may be removed in later versions **/ }; diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index c4dbe8631..e4014c235 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -2,27 +2,27 @@ "id":"sleeplog", "name":"Sleep Log", "shortName": "SleepLog", - "version": "0.06", - "description": "Log and view your sleeping habits. This app derived from SleepPhaseAlarm and uses also the principe of Estimation of Stationary Sleep-segments (ESS). It also provides a power saving mode using the built in movement calculation.", + "version": "0.10beta01", + "description": "Log and view your sleeping habits. This app is using the built in movement calculation.", "icon": "app.png", "type": "app", "tags": "tool,boot", "supports": ["BANGLEJS2"], "readme": "README.md", + "allow_emulator": true, "storage": [ {"name": "sleeplog.app.js", "url": "app.js"}, - {"name": "sleeplog.img", "url": "app-icon.js", "evaluate":true}, + {"name": "sleeplog.img", "url": "app-icon.js", "evaluate": true}, {"name": "sleeplog.boot.js", "url": "boot.js"}, {"name": "sleeplog", "url": "lib.js"}, {"name": "sleeplog.settings.js", "url": "settings.js"} ], "data": [ - {"name": "sleeplog.json"}, - {"name": "sleeplog.log"} + {"name": "sleeplog.json"} ], "screenshots": [ {"url": "screenshot1.png"}, {"url": "screenshot2.png"}, {"url": "screenshot3.png"} - ] + ] } diff --git a/apps/sleeplog/settings.js b/apps/sleeplog/settings.js index 11c7c0adb..9d3717cc5 100644 --- a/apps/sleeplog/settings.js +++ b/apps/sleeplog/settings.js @@ -1,144 +1,230 @@ (function(back) { + // define settings filename var filename = "sleeplog.json"; + // define logging prompt display status + var thresholdsPrompt = true; - // set storage and load settings - var storage = require("Storage"); - var settings = Object.assign({ - breaktod: 10, // time of day when to start/end graphs - maxawake: 36E5, // 60min in ms - minconsec: 18E5, // 30min in ms - tempthresh: 27, // every temperature above ist registered as worn - powersaving: false, // disables ESS and uses build in movement detection - maxmove: 100, // movement threshold on power saving mode - nomothresh: 0.012, // values lower than 0.008 getting triggert by noise - sleepthresh: 577, // 577 times no movement * 1.04s window width > 10min - winwidth: 13, // 13 values, read with 12.5Hz = every 1.04s - enabled: true, // en-/disable completely - logfile: "sleeplog.log", // logfile - }, storage.readJSON(filename, true) || {}); + // define default vaules + var defaults = { + // main settings + enabled: true, // en-/disable completely + // threshold settings + maxAwake: 36E5, // [ms] maximal awake time to count for consecutive sleep + minConsec: 18E5, // [ms] minimal time to count for consecutive sleep + deepTh: 100, // threshold for deep sleep + lightTh: 200, // threshold for light sleep + // app settings + breakToD: 12, // [h] time of day when to start/end graphs + appTimeout: 6E4 // lock and backlight timeouts for the app + }; - // write change to global.sleeplog and storage - function writeSetting(key, value) { - // change key in global.sleeplog - if (typeof global.sleeplog === "object") global.sleeplog[key] = value; - // reread settings to only change key - settings = Object.assign(settings, storage.readJSON(filename, true) || {}); - // change the value of key - settings[key] = value; - // write to storage - storage.writeJSON(filename, settings); + // assign loaded settings to default values + var settings = Object.assign(defaults, require("Storage").readJSON(filename, true) || {}); + + // write change to storage + function writeSetting() { + require("Storage").writeJSON(filename, settings); } - // define function to change values that need a restart of the service - function changeRestart() { - require("sleeplog").setEnabled(settings.enabled, settings.logfile, settings.powersaving); + // show menu to change thresholds + function showThresholds() { + // setup logging menu + var menu; + var thresholdsMenu = { + "": { + title: /*LANG*/"Thresholds" + }, + /*LANG*/"< Back": () => showMain(2), + /*LANG*/"Max Awake": { + value: settings.maxAwake / 6E4, + step: 10, + min: 10, + max: 120, + wrap: true, + noList: true, + format: v => v + /*LANG*/"min", + onchange: v => { + settings.maxAwake = v * 6E4; + writeSetting(); + } + }, + /*LANG*/"Min Consecutive": { + value: settings.minConsec / 6E4, + step: 10, + min: 10, + max: 120, + wrap: true, + noList: true, + format: v => v + /*LANG*/"min", + onchange: v => { + settings.minConsec = v * 6E4; + writeSetting(); + } + }, + /*LANG*/"Deep Sleep": { + value: settings.deepTh, + step: 1, + min: 30, + max: 200, + wrap: true, + noList: true, + onchange: v => { + settings.deepTh = v; + writeSetting(); + } + }, + /*LANG*/"Light Sleep": { + value: settings.lightTh, + step: 10, + min: 100, + max: 400, + wrap: true, + noList: true, + onchange: v => { + settings.lightTh = v; + writeSetting(); + } + }, + /*LANG*/"Reset to Default": () => { + settings.maxAwake = defaults.maxAwake; + settings.minConsec = defaults.minConsec; + settings.deepTh = defaults.deepTh; + settings.lightTh = defaults.lightTh; + writeSetting(); + showThresholds(); + } + }; + + // display info/warning prompt or menu + if (thresholdsPrompt) { + thresholdsPrompt = false; + E.showPrompt("Changes take affect from now on, not retrospective", { + title: /*LANG*/"Thresholds", + buttons: { + /*LANG*/"Ok": 0 + } + }).then(() => menu = E.showMenu(thresholdsMenu)); + } else { + menu = E.showMenu(thresholdsMenu); + } } - // calculate sleepthresh factor - var stFactor = settings.winwidth / 12.5 / 60; + // show menu or promt to change debugging + function showDebug() { + // check if sleeplog is available + if (global.sleeplog) { + // get debug status, file and duration + var enabled = !!sleeplog.debug; + var file = typeof sleeplog.debug === "object"; + var duration = 0; + // setup debugging menu + var debugMenu = { + "": { + title: /*LANG*/"Debugging" + }, + /*LANG*/"< Back": () => { + // check if some value has changed + if (enabled !== !!sleeplog.debug || file !== (typeof sleeplog.debug === "object") || duration) + require("sleeplog").setDebug(enabled, file ? duration || 12 : undefined); + // redraw main menu + showMain(7); + }, + /*LANG*/"Display log": () => { + // choose log... + E.showPrompt( /*LANG*/"Function\nunder\nconstruction.", { + title: /*LANG*/"Debug log", + buttons: { + /*LANG*/"Back": 0 + } + }).then(() => menu = E.showMenu(debugMenu)); + }, + /*LANG*/"Debug": { + value: enabled, + onchange: v => enabled = v + }, + /*LANG*/"File": { + value: file, + onchange: v => file = v + }, + /*LANG*/"Duration": { + value: file ? (sleeplog.debug.writeUntil - Date.now()) / 36E5 | 0 : 12, + min: 1, + max: 96, + wrap: true, + format: v => v + /*LANG*/ "h", + onchange: v => duration = v + }, + /*LANG*/"Cancel": () => showMain(7), + }; + // show menu + var menu = E.showMenu(debugMenu); + } else { + // show error prompt + E.showPrompt("Sleeplog" + /*LANG*/"not enabled!", { + title: /*LANG*/"Debugging", + buttons: { + /*LANG*/"Back": 7 + } + }).then(showMain); + } + } // show main menu function showMain(selected) { + // set debug image + var debugImg = !global.sleeplog ? + "FBSBAOAAfwAP+AH3wD4+B8Hw+A+fAH/gA/wAH4AB+AA/wAf+APnwHw+D4Hx8A++AH/AA/gAH" : // X + typeof sleeplog.debug === "object" ? + "FBSBAB/4AQDAF+4BfvAX74F+CBf+gX/oFJKBf+gUkoF/6BSSgX/oFJ6Bf+gX/oF/6BAAgf/4" : // file + sleeplog.debug ? + "FBSBAP//+f/V///4AAGAABkAAZgAGcABjgAYcAGDgBhwAY4AGcABmH+ZB/mAABgAAYAAH///" : // console + 0; // off + debugImg = debugImg ? "\0" + atob(debugImg) : false; + // set menu var mainMenu = { "": { title: "Sleep Log", selected: selected }, - "Exit": () => load(), - "< Back": () => back(), - "Break Tod": { - value: settings.breaktod, + /*LANG*/"< Back": () => back(), + /*LANG*/"Thresholds": () => showThresholds(), + /*LANG*/"Break ToD": { + value: settings.breakToD, step: 1, min: 0, max: 23, wrap: true, - onchange: v => writeSetting("breaktod", v), - }, - "Max Awake": { - value: settings.maxawake / 6E4, - step: 5, - min: 15, - max: 120, - wrap: true, - format: v => v + "min", - onchange: v => writeSetting("maxawake", v * 6E4), - }, - "Min Consec": { - value: settings.minconsec / 6E4, - step: 5, - min: 15, - max: 120, - wrap: true, - format: v => v + "min", - onchange: v => writeSetting("minconsec", v * 6E4), - }, - "Temp Thresh": { - value: settings.tempthresh, - step: 0.5, - min: 20, - max: 40, - wrap: true, - format: v => v + "°C", - onchange: v => writeSetting("tempthresh", v), - }, - "Power Saving": { - value: settings.powersaving, - format: v => v ? "on" : "off", - onchange: function(v) { - settings.powersaving = v; - changeRestart(); - // redraw menu with changed entries subsequent to onchange - // https://github.com/espruino/Espruino/issues/2149 - setTimeout(showMain, 1, 6); + noList: true, + format: v => v + ":00", + onchange: v => { + settings.breakToD = v; + writeSetting(); } }, - "Max Move": { - value: settings.maxmove, - step: 1, - min: 50, - max: 200, + /*LANG*/"App Timeout": { + value: settings.appTimeout / 1E3, + step: 10, + min: 10, + max: 120, wrap: true, - onchange: v => writeSetting("maxmove", v), + noList: true, + onchange: v => { + settings.appTimeout = v * 1E3; + writeSetting(); + } }, - "NoMo Thresh": { - value: settings.nomothresh, - step: 0.001, - min: 0.006, - max: 0.02, - wrap: true, - format: v => ("" + v).padEnd(5, "0"), - onchange: v => writeSetting("nomothresh", v), - }, - "Min Duration": { - value: Math.floor(settings.sleepthresh * stFactor), - step: 1, - min: 5, - max: 15, - wrap: true, - format: v => v + "min", - onchange: v => writeSetting("sleepthresh", Math.ceil(v / stFactor)), - }, - "Enabled": { + /*LANG*/"Enabled": { value: settings.enabled, - format: v => v ? "on" : "off", - onchange: function(v) { + onchange: v => { settings.enabled = v; - changeRestart(); + require("sleeplog").setEnabled(v); } }, - "Logfile ": { - value: settings.logfile === "sleeplog.log" ? true : (settings.logfile || "").endsWith(".log") ? "custom" : false, - format: v => v === true ? "default" : v ? "custom" : "off", - onchange: function(v) { - if (v !== "custom") { - settings.logfile = v ? "sleeplog.log" : false; - changeRestart(); - } - } + /*LANG*/"Debugging": { + value: debugImg, + onchange: () => setTimeout(showDebug, 10) } }; - // check power saving mode to delete unused entries - (settings.powersaving ? ["NoMo Thresh", "Min Duration"] : ["Max Move"]).forEach(property => delete mainMenu[property]); var menu = E.showMenu(mainMenu); } From a40a0e3477adb65869c58c09bd9e542c656b9c3d Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 17:21:23 +0200 Subject: [PATCH 007/821] [sleeplog] Update to do list --- apps/sleeplog/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 753c63133..f63beb67e 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -13,8 +13,9 @@ It is using the built in movement calculation to decide your sleeping state. To +++ To do list: +++ * Change README.md to represent new app version. +* Update screenshots. * Add cutom interface.html to download logged data. -* Test emulator functionallity. +* Test emulator functionallity. Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: "data": [ @@ -25,6 +26,8 @@ Temporarily removed logfiles from metadata.json to prevent removal on un-/reinst ```` --- +--- + #### Operating Principle * __ESS calculation__ From 9c2df313796f52aabd36b8ee73124adb07096ad3 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 17:29:33 +0200 Subject: [PATCH 008/821] [sleeplog] Remove emulator tag Emulation impractical because of missing libraries and data. --- apps/sleeplog/README.md | 1 - apps/sleeplog/metadata.json | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index f63beb67e..3f8c023ab 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -15,7 +15,6 @@ It is using the built in movement calculation to decide your sleeping state. To * Change README.md to represent new app version. * Update screenshots. * Add cutom interface.html to download logged data. -* Test emulator functionallity. Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: "data": [ diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index e4014c235..7ed0da1ce 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -9,7 +9,6 @@ "tags": "tool,boot", "supports": ["BANGLEJS2"], "readme": "README.md", - "allow_emulator": true, "storage": [ {"name": "sleeplog.app.js", "url": "app.js"}, {"name": "sleeplog.img", "url": "app-icon.js", "evaluate": true}, From 34ae1cdcf1f412459617b2cd22d82b4c4565f0b2 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 17:42:10 +0200 Subject: [PATCH 009/821] [sleeplog] Correct README.md --- apps/sleeplog/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 3f8c023ab..76a95e74d 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -1,8 +1,10 @@ # Sleep Log -This app logs and displays the four following states: -_unknown, not worn, awake, sleeping_ -It is using the built in movement calculation to decide your sleeping state. To detect if you are wearing the watch the internal heartrate sensor is used, except while charging. In this case it is always assumed that you are not weraing the watch. +This app logs and displays the following states: +- sleepling status: _unknown, not worn, awake, light sleep, deep sleep_ +- consecutive sleep status: _unknown, not consecutive, consecutive_ + +It is using the built in movement calculation to decide your sleeping state. While charging it is assumed that you are not wearing the watch and if the status changes to _deep sleep_ the internal heartrate sensor is used to detect if you are wearing the watch. ````diff -+- -+- @@ -25,6 +27,7 @@ Temporarily removed logfiles from metadata.json to prevent removal on un-/reinst ```` --- + --- From ae8ce7f1fd71d280878068ef7df7d0d88e93b675 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 18:22:11 +0200 Subject: [PATCH 010/821] [sleeplog] Change default app timeout behavior --- apps/sleeplog/settings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/sleeplog/settings.js b/apps/sleeplog/settings.js index 9d3717cc5..2d928ec4e 100644 --- a/apps/sleeplog/settings.js +++ b/apps/sleeplog/settings.js @@ -15,7 +15,7 @@ lightTh: 200, // threshold for light sleep // app settings breakToD: 12, // [h] time of day when to start/end graphs - appTimeout: 6E4 // lock and backlight timeouts for the app + appTimeout: 0 // lock and backlight timeouts for the app }; // assign loaded settings to default values @@ -204,10 +204,11 @@ /*LANG*/"App Timeout": { value: settings.appTimeout / 1E3, step: 10, - min: 10, + min: 0, max: 120, wrap: true, noList: true, + format: v => v ? v + "s" : "-", onchange: v => { settings.appTimeout = v * 1E3; writeSetting(); From 5c45f491d0791fb0b7cb68d177a34f760a989e40 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 18:28:05 +0200 Subject: [PATCH 011/821] [sleeplog] Add todo entry + rename options --- apps/sleeplog/README.md | 1 + apps/sleeplog/settings.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 76a95e74d..4c8e32288 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -16,6 +16,7 @@ It is using the built in movement calculation to decide your sleeping state. Whi +++ To do list: +++ * Change README.md to represent new app version. * Update screenshots. +* Add display log functionality. * Add cutom interface.html to download logged data. Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: diff --git a/apps/sleeplog/settings.js b/apps/sleeplog/settings.js index 2d928ec4e..597c1f2d6 100644 --- a/apps/sleeplog/settings.js +++ b/apps/sleeplog/settings.js @@ -138,11 +138,11 @@ } }).then(() => menu = E.showMenu(debugMenu)); }, - /*LANG*/"Debug": { + /*LANG*/"Enable": { value: enabled, onchange: v => enabled = v }, - /*LANG*/"File": { + /*LANG*/"write File": { value: file, onchange: v => file = v }, From c832017df4b195b1f92641e5a0c4bcf61bc3a68c Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:02:46 +0200 Subject: [PATCH 012/821] [sleeplog] Update README.md --- apps/sleeplog/README.md | 230 +++++++++++----------------------------- 1 file changed, 60 insertions(+), 170 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 4c8e32288..5528de3aa 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -7,197 +7,87 @@ This app logs and displays the following states: It is using the built in movement calculation to decide your sleeping state. While charging it is assumed that you are not wearing the watch and if the status changes to _deep sleep_ the internal heartrate sensor is used to detect if you are wearing the watch. ````diff --+- -+- --+- !!! BETA Version !!! -+- --+- -+- +-+- -+- +-+- BETA Version -+- +-+- -+- +```` -- The following readme text is not matching this version! +--- +### Introduction +--- +I am proud to present the new sleeplog app: version 0.10 🎉 ✨ 🎊 -+++ To do list: +++ -* Change README.md to represent new app version. -* Update screenshots. -* Add display log functionality. -* Add cutom interface.html to download logged data. +Sorry that it took so long but hopefully most of the early bugs are sorted out and the app should be ready to be use and get tested! + +I would love to hear about your impressions and like to know your choice of thresholds, to set the default values as optimized as possible. + +The last piece of work is to rewrite the README.md to show how to use it and show the restrictions and possibilities. +But here are some explanations how to use the app and settings: +- __On the app screen:__ + - swipe left & right to change the displayed day + - touch on the "title" (e.g. `Night to Fri 20/05/2022`) to enter a day selection prompt + - touch on the info area (by default displaying consecutive and true sleeping) to change the displayed information + - touch on the wrench (upper right corner) to enter the settings + - exit the app with the UI back button widget on the upper left corner +- __Inside the settings:__ + - the threshold values are accessible through a submenu + - app timeout lets you specify a separate `lockTimeout` and `backlightTimeout` only for the sleeplog app + - debug settings are available in a submenu down at the end: + - display log is not implemented yet + - options `Enable` and `write File` should be self explaining + - the `Duration` specifies how long data should be written into the .csv file + - the .csv file loggs the following data (timestamps are in days since 30.12.1899 as used by office software): + _timestamp, movement, status, consecutive, asleepSince, awakeSince, bpm, bpmConfidence_ + - __Timestamps and files:__ + 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as UNIX timestamps: + seconds since 1970-01-01 00:00 UTC + 2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: + 10 minutes since 1970-01-01 00:00 UTC (`i. / (10 * 60 * 1000)`) + 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: + hours since 1970-01-01 00:00 UTC (`i. / (60 * 60 * 1000)`) + 4. logged timestamps inside the debug .csv file are formatted for office calculation software: + days since 1899-12-30 00:00 UTC (`i. / (24 * 60 * 60 * 1000) + 25569`) + 5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: + fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX timestamp)` and `require("sleeplog").fnToMs(fortnight)`) + - __Logfiles from before 0.10:__ + timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` + - __View logged data:__ + if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data + since & until in UNIX timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h + +--- Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: - "data": [ - {"name": "sleeplog.log", "storageFile": true}, - {"wildcard": "sleeplog_????.log"}, - {"wildcard": "sleeplog_??????.csv"} - ], - +``` +"data": [ + {"name": "sleeplog.log", "storageFile": true}, + {"wildcard": "sleeplog_????.log"}, + {"wildcard": "sleeplog_??????.csv"} +], ```` ---- - ---- - - -#### Operating Principle -* __ESS calculation__ - The accelerometer polls values with 12.5Hz. On each poll the magnitude value is saved. When 13 values are collected, every 1.04 seconds, the standard deviation over this values is calculated. - Is the calculated standard deviation lower than the "no movement" threshold (__NoMo Thresh__) a "no movement" counter is incremented. Each time the "no movement" threshold is reached the "no movement" counter will be reset. The first time no movement is detected the actual timestamp is cached (in _sleeplog.firstnomodate_) for logging. - When the "no movement" counter reaches the sleep threshold the watch is considered as resting. (The sleep threshold is calculated from the __Min Duration__ setting, Example: _sleep threshold = Min Duration * 60 / calculation interval => 10min * 60s/min / 1.04s ~= 576,9 rounded up to 577_) -* __Power Saving Mode__ - On power saving mode the movement value of bangle's build in health event is checked against the maximal movement threshold (__Max Move__). The event is only triggered every 10 minutes which decreases the battery impact but also reduces accurracy. -* ___Sleeping___ __or__ ___Not Worn___ - To check if a resting watch indicates a sleeping status, the internal temperature must be greater than the temperature threshold (__Temp Thresh__). Otherwise the watch is considered as not worn. -* __True Sleep__ - The true sleep value is a simple addition of all registert sleeping periods. -* __Consecutive Sleep__ - In addition the consecutive sleep value tries to predict the complete time you were asleep, even the light sleeping phases with registered movements. All periods after a sleeping period will be summarized til the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consec__) it is not considered, otherwise it will be added to the consecutive sleep value. -* __Logging__ - To minimize the log size only a changed state is logged. The logged timestamp is matching the beginning of its measurement period. - When not on power saving mode a movement is detected nearly instantaneous and the detection of a no movement period is delayed by the minimal no movement duration. To match the beginning of the measurement period a cached timestamp (_sleeplog.firstnomodate_) is logged. - On power saving mode the measurement period is fixed to 10 minutes and all logged timestamps are also set back 10 minutes. - To prevent a LOW_MEMORY,MEMORY error the log size is limited to 750 entries, older entries will be overwritten. - ---- -### Control ---- -* __Swipe__ - Swipe left/right to display the previous/following day. -* __Touch__ / __BTN__ - Touch the screen to open the settings menu to exit or change settings. - ---- -### Settings ---- -* __Break Tod__ | break at time of day - _0_ / _1_ / _..._ / __10__ / _..._ / _12_ - Change time of day on wich the lower graph starts and the upper graph ends. -* __Max Awake__ | maximal awake duration - _15min_ / _20min_ / _..._ / __60min__ / _..._ / _120min_ - Adjust the maximal awake duration upon the exceeding of which aborts the consecutive sleep period. -* __Min Consec__ | minimal consecutive sleep duration - _15min_ / _20min_ / _..._ / __30min__ / _..._ / _120min_ - Adjust the minimal consecutive sleep duration that will be considered for the consecutive sleep value. -* __Temp Thresh__ | temperature threshold - _20°C_ / _20.5°C_ / _..._ / __25°C__ / _..._ / _40°C_ - The internal temperature must be greater than this threshold to log _sleeping_, otherwise it is _not worn_. -* __Power Saving__ - _on_ / __off__ - En-/Disable power saving mode. _Saves battery, but might decrease accurracy._ - In app icon showing that power saving mode is enabled: ![](powersaving.png) -* __Max Move__ | maximal movement threshold - (only available when on power saving mode) - _50_ / _51_ / _..._ / __100__ / _..._ / _200_ - On power saving mode the watch is considered resting if this threshold is lower or equal to the movement value of bangle's health event. -* __NoMo Thresh__ | no movement threshold - (only available when not on power saving mode) - _0.006_ / _0.007_ / _..._ / __0.012__ / _..._ / _0.020_ - The standard deviation over the measured values needs to be lower then this threshold to count as not moving. - The defaut threshold value worked best for my watch. A threshold value below 0.008 may get triggert by noise. -* __Min Duration__ | minimal no movement duration - (only available when not on power saving mode) - _5min_ / _6min_ / _..._ / __10min__ / _..._ / _15min_ - If no movement is detected for this duration, the watch is considered as resting. -* __Enabled__ - __on__ / _off_ - En-/Disable the service (all background activities). _Saves the most battery, but might make this app useless._ - In app icon showing that the service is disabled: ![](disabled.png) -* __Logfile__ - __default__ / _off_ - En-/Disable logging by setting the logfile to _sleeplog.log_ / _undefined_. - If the logfile has been customized it is displayed with _custom_. - In app icon showing that logging is disabled: ![](nolog.png) - ---- -### Global Object and Module Functions ---- -For easy access from the console or other apps the following parameters, values and functions are noteworthy: -``` ->global.sleeplog -={ - enabled: true, // bool / service status indicator - logfile: "sleeplog.log", // string / used logfile - resting: false, // bool / indicates if the watch is resting - status: 2, // int / actual status: - / undefined = service stopped, 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping - firstnomodate: 1644435877595, // number / Date.now() from first recognised no movement, not available in power saving mode - stop: function () { ... }, // funct / stops the service until the next load() - start: function () { ... }, // funct / restarts the service - ... - } - ->require("sleeplog") -={ - setEnabled: function (enable, logfile, powersaving) { ... }, - // restarts the service with changed settings - // * enable / bool / new service status - // * logfile / bool or string - // - true = enables logging to "sleeplog.log" - // - "some_file.log" = enables logging to "some_file.log" - // - false = disables logging - // * (powersaving) / bool / new power saving status, default: false - // returns: true or undefined - // - true = service restart executed - // - undefined = no global.sleeplog found - readLog: function (logfile, since, until) { ... }, - // read the raw log data for a specific time period - // * logfile / string / on no string uses logfile from global object or "sleeplog.log" - // * (since) / Date or number / startpoint of period, default: 0 - // * (until) / Date or number / endpoint of period, default: 1E14 - // returns: array - // * [[number, int, string], [...], ... ] / sorting: latest first - // - number // timestamp in ms - // - int // status: 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping - // - string // additional information - // * [] = no data available or global.sleeplog not found - writeLog: function (logfile, input) { ... }, - // append or replace log depending on input - // * logfile / string / on no string uses logfile from global object or default - // * input / array - // - append input if array length >1 and element[0] >9E11 - // - replace log with input if at least one entry like above is inside another array - // returns: true or undefined - // - true = changest written to storage - // - undefined = wrong input - getReadableLog: function (printLog, since, until, logfile) { ... } - // read the log data as humanreadable string for a specific time period - // * (printLog) / bool / direct print output with additional information, default: false - // * (since) / Date or number / see readLog(..) - // * (until) / Date or number / see readLog(..) - // * (logfile) / string / see readLog(..) - // returns: string - // * "{substring of ISO date} - {status} for {duration}min\n...", sorting: latest last - // * undefined = no data available or global.sleeplog found - restoreLog: function (logfile) { ... } - // eliminate some errors inside a specific logfile - // * (logfile) / string / see readLog(..) - // returns: int / number of changes that were made - reinterpretTemp: function (logfile, tempthresh) { ... } - // reinterpret worn status based on given temperature threshold - // * (logfile) / string / see readLog(..) - // * (tempthresh) / float / new temperature threshold, on default uses tempthresh from global object or 27 - // returns: int / number of changes that were made - } -``` --- ### Worth Mentioning --- #### To do list +* Edit/complete README.md. +* Update screenshots. +* Add display debugging log functionality. +* Add cutom interface.html to view, down- and upload logged data via App Loader. * Send the logged information to Gadgetbridge. _(For now I have no idea how to achieve this, help is appreciated.)_ -* View, down- and upload log functions via App Loader. -* Calculate and display overall sleep statistics. -* Option to automatically change power saving mode depending on time of day. #### Requests, Bugs and Feedback -Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) or send me a [mail](mailto:banglejs@storm64.de). +Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)). #### Creator Storm64 ([Mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64)) #### Contributors -nxdefiant ([github](https://github.com/nxdefiant)) +myxor ([github](https://github.com/myxor)) #### Attributions -* ESS calculation based on nxdefiant interpretation of the following publication by: - 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](https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en), - ICHI 2014, Verona, Italy, IEEE Press, 2014. -* Icons used in this app are from [https://icons8.com](https://icons8.com). +Icons used in this app are from [https://icons8.com](https://icons8.com). #### License [MIT License](LICENSE) From 6331f936f6e8f65da92d9a59ead951180b601374 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:05:05 +0200 Subject: [PATCH 013/821] [sleeplog] Update README.md --- apps/sleeplog/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 5528de3aa..93543b270 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -73,9 +73,9 @@ Temporarily removed logfiles from metadata.json to prevent removal on un-/reinst * Edit/complete README.md. * Update screenshots. * Add display debugging log functionality. -* Add cutom interface.html to view, down- and upload logged data via App Loader. +* Add custom interface.html to view, down- and upload logged data via App Loader. * Send the logged information to Gadgetbridge. - _(For now I have no idea how to achieve this, help is appreciated.)_ + ___(For now I have no idea how to achieve this, help is appreciated.)___ #### Requests, Bugs and Feedback Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)). @@ -87,7 +87,7 @@ Storm64 ([Mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64 myxor ([github](https://github.com/myxor)) #### Attributions -Icons used in this app are from [https://icons8.com](https://icons8.com). +The app icon is downloaded from [https://icons8.com](https://icons8.com). #### License [MIT License](LICENSE) From d13c8fb241d5a882a694dad5354b5a79c67a61d5 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:09:42 +0200 Subject: [PATCH 014/821] [sleeplog] Update README.md --- apps/sleeplog/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 93543b270..1f8774de1 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -42,13 +42,13 @@ But here are some explanations how to use the app and settings: 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as UNIX timestamps: seconds since 1970-01-01 00:00 UTC 2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: - 10 minutes since 1970-01-01 00:00 UTC (`i. / (10 * 60 * 1000)`) + 10 minutes since 1970-01-01 00:00 UTC (`UNIX / (10 * 60 * 1000)`) 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: - hours since 1970-01-01 00:00 UTC (`i. / (60 * 60 * 1000)`) + hours since 1970-01-01 00:00 UTC (`UNIX / (60 * 60 * 1000)`) 4. logged timestamps inside the debug .csv file are formatted for office calculation software: - days since 1899-12-30 00:00 UTC (`i. / (24 * 60 * 60 * 1000) + 25569`) + days since 1899-12-30 00:00 UTC (`UNIX / (24 * 60 * 60 * 1000) + 25569`) 5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: - fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX timestamp)` and `require("sleeplog").fnToMs(fortnight)`) + fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX)` and `require("sleeplog").fnToMs(fortnight)`) - __Logfiles from before 0.10:__ timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` - __View logged data:__ From cdff3173ba8c77c0043e03857b1b1093d5a3a3bd Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:14:05 +0200 Subject: [PATCH 015/821] [sleeplog] Replace old icons --- .gitignore | 3 +++ apps/sleeplog/conf_20x20.png | Bin 0 -> 4739 bytes apps/sleeplog/debug_20x20.png | Bin 0 -> 642 bytes apps/sleeplog/disabled.png | Bin 9751 -> 0 bytes apps/sleeplog/file_20x20.png | Bin 0 -> 635 bytes apps/sleeplog/nolog.png | Bin 10229 -> 0 bytes apps/sleeplog/off_20x20.png | Bin 0 -> 650 bytes apps/sleeplog/powersaving.png | Bin 8670 -> 0 bytes 8 files changed, 3 insertions(+) create mode 100644 apps/sleeplog/conf_20x20.png create mode 100644 apps/sleeplog/debug_20x20.png delete mode 100644 apps/sleeplog/disabled.png create mode 100644 apps/sleeplog/file_20x20.png delete mode 100644 apps/sleeplog/nolog.png create mode 100644 apps/sleeplog/off_20x20.png delete mode 100644 apps/sleeplog/powersaving.png diff --git a/.gitignore b/.gitignore index 231851dd6..f5801f54a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ tests/Layout/testresult.bmp apps.local.json _site .jekyll-cache +.owncloudsync.log +Desktop.ini +.sync_*.db* diff --git a/apps/sleeplog/conf_20x20.png b/apps/sleeplog/conf_20x20.png new file mode 100644 index 0000000000000000000000000000000000000000..4de9e9184f2ac7f33b2d9803ae72a6bda4b257e7 GIT binary patch literal 4739 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 literal 0 HcmV?d00001 diff --git a/apps/sleeplog/disabled.png b/apps/sleeplog/disabled.png deleted file mode 100644 index 8e7d9fb2c0786fc9415c47c25eefac864e509403..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9751 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 diff --git a/apps/sleeplog/file_20x20.png b/apps/sleeplog/file_20x20.png new file mode 100644 index 0000000000000000000000000000000000000000..e38399bc61e5ab6cd879de5013f446c81af58021 GIT binary patch literal 635 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 kc6^n+a literal 0 HcmV?d00001 diff --git a/apps/sleeplog/powersaving.png b/apps/sleeplog/powersaving.png deleted file mode 100644 index ea487b48c1277fd748fca990ed7b1b53ab9a8f63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8670 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 From 5e7d8c14b4ca6a420684d249bc26011699bd2afa Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:17:48 +0200 Subject: [PATCH 016/821] [sleeplog] Update README.md --- apps/sleeplog/README.md | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 1f8774de1..ea6aacb8a 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -23,12 +23,14 @@ I would love to hear about your impressions and like to know your choice of thre The last piece of work is to rewrite the README.md to show how to use it and show the restrictions and possibilities. But here are some explanations how to use the app and settings: + - __On the app screen:__ - swipe left & right to change the displayed day - touch on the "title" (e.g. `Night to Fri 20/05/2022`) to enter a day selection prompt - touch on the info area (by default displaying consecutive and true sleeping) to change the displayed information - touch on the wrench (upper right corner) to enter the settings - exit the app with the UI back button widget on the upper left corner + - __Inside the settings:__ - the threshold values are accessible through a submenu - app timeout lets you specify a separate `lockTimeout` and `backlightTimeout` only for the sleeplog app @@ -38,22 +40,23 @@ But here are some explanations how to use the app and settings: - the `Duration` specifies how long data should be written into the .csv file - the .csv file loggs the following data (timestamps are in days since 30.12.1899 as used by office software): _timestamp, movement, status, consecutive, asleepSince, awakeSince, bpm, bpmConfidence_ - - __Timestamps and files:__ - 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as UNIX timestamps: - seconds since 1970-01-01 00:00 UTC - 2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: - 10 minutes since 1970-01-01 00:00 UTC (`UNIX / (10 * 60 * 1000)`) - 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: - hours since 1970-01-01 00:00 UTC (`UNIX / (60 * 60 * 1000)`) - 4. logged timestamps inside the debug .csv file are formatted for office calculation software: - days since 1899-12-30 00:00 UTC (`UNIX / (24 * 60 * 60 * 1000) + 25569`) - 5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: - fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX)` and `require("sleeplog").fnToMs(fortnight)`) - - __Logfiles from before 0.10:__ - timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` - - __View logged data:__ - if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data - since & until in UNIX timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h + +- __Timestamps and files:__ + 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as UNIX timestamps: + seconds since 1970-01-01 00:00 UTC + 2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: + 10 minutes since 1970-01-01 00:00 UTC (`UNIX / (10 * 60 * 1000)`) + 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: + hours since 1970-01-01 00:00 UTC (`UNIX / (60 * 60 * 1000)`) + 4. logged timestamps inside the debug .csv file are formatted for office calculation software: + days since 1899-12-30 00:00 UTC (`UNIX / (24 * 60 * 60 * 1000) + 25569`) + 5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: + fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX)` and `require("sleeplog").fnToMs(fortnight)`) +- __Logfiles from before 0.10:__ + timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` +- __View logged data:__ + if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data + since & until in UNIX timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h --- From a3efd32981612dbd4fa9ba58c2a6090d3e90dbf5 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:20:13 +0200 Subject: [PATCH 017/821] [sleeplog] Update README.md --- apps/sleeplog/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index ea6aacb8a..35e57d714 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -52,8 +52,10 @@ But here are some explanations how to use the app and settings: days since 1899-12-30 00:00 UTC (`UNIX / (24 * 60 * 60 * 1000) + 25569`) 5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX)` and `require("sleeplog").fnToMs(fortnight)`) + - __Logfiles from before 0.10:__ timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` + - __View logged data:__ if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data since & until in UNIX timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h From fd5ed2b00a0a74ecacd13de5deebfe1d7f115a49 Mon Sep 17 00:00:00 2001 From: storm64 Date: Fri, 20 May 2022 20:25:19 +0200 Subject: [PATCH 018/821] [sleeplog] Update README.md --- apps/sleeplog/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 35e57d714..f083bc3e4 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -56,7 +56,7 @@ But here are some explanations how to use the app and settings: - __Logfiles from before 0.10:__ timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` -- __View logged data:__ +- __View logged data:__ if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data since & until in UNIX timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h From 79938dcab450e659e30342a34452fcf42d982c1e Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 23 May 2022 16:20:23 +0200 Subject: [PATCH 019/821] [sleeplog] Update README.md Correct timestamp information --- apps/sleeplog/README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index f083bc3e4..9f0e6a44f 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -38,27 +38,27 @@ But here are some explanations how to use the app and settings: - display log is not implemented yet - options `Enable` and `write File` should be self explaining - the `Duration` specifies how long data should be written into the .csv file - - the .csv file loggs the following data (timestamps are in days since 30.12.1899 as used by office software): + - the .csv file loggs the following data (timestamps are in days since 1900-01-01 00:00 UTC as used by office software): _timestamp, movement, status, consecutive, asleepSince, awakeSince, bpm, bpmConfidence_ - __Timestamps and files:__ - 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as UNIX timestamps: + 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: seconds since 1970-01-01 00:00 UTC 2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: - 10 minutes since 1970-01-01 00:00 UTC (`UNIX / (10 * 60 * 1000)`) + 10 minutes since 1970-01-01 00:00 UTC (`Bangle / (10 * 60 * 1000)`) 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: - hours since 1970-01-01 00:00 UTC (`UNIX / (60 * 60 * 1000)`) + hours since 1970-01-01 00:00 UTC (`Bangle / (60 * 60 * 1000)`) 4. logged timestamps inside the debug .csv file are formatted for office calculation software: - days since 1899-12-30 00:00 UTC (`UNIX / (24 * 60 * 60 * 1000) + 25569`) + days since 1900-01-01 00:00 UTC (`Bangle / (24 * 60 * 60 * 1000) + 25569`) 5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: - fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(UNIX)` and `require("sleeplog").fnToMs(fortnight)`) + fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(Bangle)` and `require("sleeplog").fnToMs(fortnight)`) - __Logfiles from before 0.10:__ timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` - __View logged data:__ if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data - since & until in UNIX timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h + since & until in Bangle timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h --- @@ -80,7 +80,6 @@ Temporarily removed logfiles from metadata.json to prevent removal on un-/reinst * Add display debugging log functionality. * Add custom interface.html to view, down- and upload logged data via App Loader. * Send the logged information to Gadgetbridge. - ___(For now I have no idea how to achieve this, help is appreciated.)___ #### Requests, Bugs and Feedback Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)). From f1790695eaff4391954da8d3fffc1cee2e7898f9 Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 24 May 2022 10:14:10 +0200 Subject: [PATCH 020/821] [sleeplog] Add interface.html --- apps/sleeplog/interface.html | 118 +++++++++++++++++++++++++++++++++++ apps/sleeplog/metadata.json | 1 + 2 files changed, 119 insertions(+) create mode 100644 apps/sleeplog/interface.html diff --git a/apps/sleeplog/interface.html b/apps/sleeplog/interface.html new file mode 100644 index 000000000..98f5a8856 --- /dev/null +++ b/apps/sleeplog/interface.html @@ -0,0 +1,118 @@ + + + + + + +
+ + + + + diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index 7ed0da1ce..909aee24e 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -9,6 +9,7 @@ "tags": "tool,boot", "supports": ["BANGLEJS2"], "readme": "README.md", + "interface": "interface.html", "storage": [ {"name": "sleeplog.app.js", "url": "app.js"}, {"name": "sleeplog.img", "url": "app-icon.js", "evaluate": true}, From 974194898e588869ebef1f1c51b7f98139283166 Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 24 May 2022 10:34:46 +0200 Subject: [PATCH 021/821] [sleeplog] Fix typos in interface.html --- apps/sleeplog/interface.html | 39 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/sleeplog/interface.html b/apps/sleeplog/interface.html index 98f5a8856..9b38ac27e 100644 --- a/apps/sleeplog/interface.html +++ b/apps/sleeplog/interface.html @@ -39,19 +39,19 @@ function getFnList() { Util.showModal("Loading..."); domTable.innerHTML = ""; Puck.eval(`require("Storage").list(/^sleeplog_\\d{4}\.log$/)`, files => { - function fnToMs(no) { return (no + 0.25) * 12096E5; } // add this fortnight files.push("" + Math.floor(Date.now() / 12096E5 - 0.25)); files = files.map(file => { - var ret { + var ret = { filename: file, fortnigt: file.match(/\\d{4})/)[0], // 1234 }; - ret.date: (ret.fortnigt + 0.25) * 12096E5; - ret.str: new Date(ret.date).toLocaleDateString(undefined) + " - " + new Date(ret.date + 12096E5).toLocaleDateString(undefined); + ret.date = (ret.fortnigt + 0.25) * 12096E5; + ret.str = new Date(ret.date).toLocaleDateString(undefined) + " - " + new Date(ret.date + 12096E5).toLocaleDateString(undefined); return ret; - }) - var html = ` + }); + var html = ` +
@@ -68,25 +68,25 @@ function getFnList() { `; files.forEach(file => { html += ` - - - + + - `; + + `; }); html += ` - +
Fortnight
${file.fortnigt} - ${file.str} - `; +
${file.fortnigt} + ${file.str} + `; if (file.filename.endsWith(".log")) html += ` - `; + `; html += ` -
`; domTable.innerHTML = html; Util.hideModal(); var buttons = domTable.querySelectorAll("button"); - for (var i=0;i { + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener("click", event => { var button = event.currentTarget; var task = button.getAttribute("task"); if (!task) return; @@ -99,20 +99,19 @@ function getFnList() { var filename = button.getAttribute("filename"); if (!filename) return; Util.showModal("Deleting..."); - Util.eraseStorage(filename,()=>{ + Util.eraseStorage(filename, () => { Util.hideModal(); getTrackList(); }); } }); } - }) + }); } function onInit() { getFnList(); } - From 7d4a61810e6ae4aa4a9b725566d3ccacd118ec37 Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 24 May 2022 10:38:19 +0200 Subject: [PATCH 022/821] [sleeplog] Fixed typos in interface.html (2) --- apps/sleeplog/interface.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/interface.html b/apps/sleeplog/interface.html index 9b38ac27e..0c19e2f18 100644 --- a/apps/sleeplog/interface.html +++ b/apps/sleeplog/interface.html @@ -23,7 +23,7 @@ function saveCSV(logData, title) { entry[1], entry[2] ].join(",")+"\n"; - } + }); Util.saveCSV(title, csv); } From 7d09a1229f575d3ba451eb8ed7a112d44e5d5d7b Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 24 May 2022 10:44:09 +0200 Subject: [PATCH 023/821] [sleeplog] Fixed typos in interface.html (3) --- apps/sleeplog/interface.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/interface.html b/apps/sleeplog/interface.html index 0c19e2f18..e2122ef74 100644 --- a/apps/sleeplog/interface.html +++ b/apps/sleeplog/interface.html @@ -44,7 +44,7 @@ function getFnList() { files = files.map(file => { var ret = { filename: file, - fortnigt: file.match(/\\d{4})/)[0], // 1234 + fortnigt: file.match(/\\d{4}/)[0], // 1234 }; ret.date = (ret.fortnigt + 0.25) * 12096E5; ret.str = new Date(ret.date).toLocaleDateString(undefined) + " - " + new Date(ret.date + 12096E5).toLocaleDateString(undefined); From f5a9f470545d28088ca5c6694bf62d058b04909c Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 24 May 2022 10:52:30 +0200 Subject: [PATCH 024/821] [sleeplog] Fixed usage of RegEx in interface.html --- apps/sleeplog/interface.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sleeplog/interface.html b/apps/sleeplog/interface.html index e2122ef74..ae59f7c06 100644 --- a/apps/sleeplog/interface.html +++ b/apps/sleeplog/interface.html @@ -38,13 +38,13 @@ function readLog(date, callback) { function getFnList() { Util.showModal("Loading..."); domTable.innerHTML = ""; - Puck.eval(`require("Storage").list(/^sleeplog_\\d{4}\.log$/)`, files => { + Puck.eval(`require("Storage").list(/^sleeplog_\\d\\d\\d\\d\\.log$/)`, files => { // add this fortnight files.push("" + Math.floor(Date.now() / 12096E5 - 0.25)); files = files.map(file => { var ret = { filename: file, - fortnigt: file.match(/\\d{4}/)[0], // 1234 + fortnigt: file.match(/\d{4}/)[0], // 1234 }; ret.date = (ret.fortnigt + 0.25) * 12096E5; ret.str = new Date(ret.date).toLocaleDateString(undefined) + " - " + new Date(ret.date + 12096E5).toLocaleDateString(undefined); From dd4cb316dc8490a2e77b480e259babd23f545613 Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 24 May 2022 12:28:17 +0200 Subject: [PATCH 025/821] [sleeplog] Redesigned interface.html --- apps/sleeplog/interface.html | 127 +++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/apps/sleeplog/interface.html b/apps/sleeplog/interface.html index ae59f7c06..97c0a11d0 100644 --- a/apps/sleeplog/interface.html +++ b/apps/sleeplog/interface.html @@ -10,22 +10,32 @@ - + \ No newline at end of file From 76629a9d78fbcca56b95276dbb7170859ae748f3 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 26 May 2022 10:41:56 +0200 Subject: [PATCH 038/821] [sleeplog] Add "View log" in debug + send via BT Add "View log" function for debugging log Send data for gadgetbridge via BT --- apps/sleeplog/ChangeLog | 2 + apps/sleeplog/README.md | 5 +- apps/sleeplog/app.js | 2 +- apps/sleeplog/boot.js | 8 + apps/sleeplog/metadata.json | 2 +- apps/sleeplog/settings.js | 320 +++++++++++++++++++++++++++++------- 6 files changed, 274 insertions(+), 65 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index 890d54dce..1adc4a4dc 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -5,3 +5,5 @@ 0.05: Fix LOW_MEMORY,MEMORY error on to big log size 0.06: Reduced log size further to 750 entries 0.10: Complete rework off this app! + - beta01: Add interface.html to view your saved log data + - beta02: Add "View log" function for debugging log + send data for gadgetbridge \ No newline at end of file diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 9f0e6a44f..13f95070c 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -77,9 +77,8 @@ Temporarily removed logfiles from metadata.json to prevent removal on un-/reinst #### To do list * Edit/complete README.md. * Update screenshots. -* Add display debugging log functionality. -* Add custom interface.html to view, down- and upload logged data via App Loader. -* Send the logged information to Gadgetbridge. +* Add more functionallities to interface.html. +* Enable recieving data on the Gadgetbridge side + testing. #### Requests, Bugs and Feedback Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)). diff --git a/apps/sleeplog/app.js b/apps/sleeplog/app.js index 097897d35..c75bf1e4c 100644 --- a/apps/sleeplog/app.js +++ b/apps/sleeplog/app.js @@ -107,7 +107,7 @@ function drawGraph(log, date, pos) { // set height and color values: // status: unknown, not worn, awake, light sleep, deep sleep, consecutive - // color: black, red, green, cyan, violet, blue + // color: black, red, green, cyan, blue, violet var heights = [0, 0.4, 0.6, 0.8, 1]; var colors = [0, 63488, 2016, 2047, 31, 32799]; diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index a86c3f554..1b303f282 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -270,6 +270,14 @@ if (sleeplog.conf.enabled) { // reset saveUpToDate status delete this.info.saveUpToDate; } + + // send status to gadgetbridge + var gb_kinds = "unknown,not_worn,activity,light_sleep,deep_sleep"; + Bluetooth.println(JSON.stringify({ + t: "act", + act: gb_kinds.split(",")[data.status], + ts: data.timestamp + })); // call debugging function if set if (this.debug) require("sleeplog").debug(data); diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index 909aee24e..8eb9c5e5c 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -2,7 +2,7 @@ "id":"sleeplog", "name":"Sleep Log", "shortName": "SleepLog", - "version": "0.10beta01", + "version": "0.10beta02", "description": "Log and view your sleeping habits. This app is using the built in movement calculation.", "icon": "app.png", "type": "app", diff --git a/apps/sleeplog/settings.js b/apps/sleeplog/settings.js index 597c1f2d6..5544c302e 100644 --- a/apps/sleeplog/settings.js +++ b/apps/sleeplog/settings.js @@ -26,6 +26,266 @@ require("Storage").writeJSON(filename, settings); } + // plot a debug file + function plotDebug(filename) { + // handle swipe events + function swipeHandler(x, y) { + if (x) { + start -= x; + if (start < 0 || maxStart && start > maxStart) { + start = start < 0 ? 0 : maxStart; + } else { + drawGraph(); + } + } else { + minMove += y * 10; + if (minMove < 0 || minMove > 300) { + minMove = minMove < 0 ? 0 : 300; + } else { + drawGraph(); + } + } + } + // handle touch events + function touchHandler() { + invert = !invert; + drawGraph(); + } + + // read required entries + function readEntries(count) { + // extract usabble data from line + function extract(line) { + if (!line) return; + line = line.trim().split(","); + return [Math.round((parseFloat(line[0]) - 25569) * 144), parseInt(line[1])]; + } + + // open debug file + var file = require("Storage").open(filename, "r"); + // skip title + file.readLine(); + // skip past entries + for (var i = 0; i < start * count; i++) { file.readLine(); } + // define data with first entry + var data = [extract(file.readLine())]; + // get start time in 10min steps + var start10min = data[0][0]; + // read first required entry + var line = extract(file.readLine()); + + // read next count entries from file + while (data.length < count) { + // check if line is immediately after the last entry + if (line[0] === start10min + data.length) { + // add line to data + data.push(line); + // read new line + line = extract(file.readLine()); + // stop if no more data available + if (!line) break; + } else { + // add line with unknown movement + data.push([start10min + data.length, 0]); + } + } + + // free ram + file = undefined + // set this start as max, if less entries than expected + if (data.length < count) maxStart = start; + return data; + } + + // draw graph at starting point + function drawGraph() { + // set correct or inverted drawing + function rect(fill, x0, y0, x1, y1) { + if (fill ^ invert) { + g.fillRect(x0, y0, x1, y1); + } else { + g.clearRect(x0, y0, x1, y1); + } + } + + // set witdh + var width = g.getWidth(); + // calculate entries to display (+ set width zero based) + var count = (width--) / 4; + // read required entries + var data = readEntries(count); + + // clear app area + g.reset().clearRect(0, width - 13, width, width); + rect(false, 0, 24, width, width - 14); + // draw x axis + g.drawLine(0, width - 13, width, width - 13); + // draw x label + data.forEach((e, i) => { + var startTime = new Date(e[0] * 6E5); + if (startTime.getMinutes() === 0) { + g.fillRect(4 * i, width - 12, 4 * i, width - 9); + g.setFontAlign(-1, -1).setFont("6x8") + .drawString(startTime.getHours(), 4 * i + 1, width - 8); + } else if (startTime.getMinutes() === 30) { + g.fillRect(4 * i, width - 12, 4 * i, width - 11); + } + }); + + // calculate max height + var height = width - 38; + // cycle through entries + data.forEach((e, i) => { + // check if movement available + if (e[1]) { + // set color depending on recognised status + var color = e[1] < deepTh ? 31 : e[1] < lightTh ? 2047 : 2016; + // correct according to min movement + e[1] -= minMove; + // keep movement in bounderies + e[1] = e[1] < 0 ? 0 : e[1] > height ? height : e[1]; + // draw line and rectangle + g.reset(); + rect(true, 4 * i, width - 14, 4 * i, width - 14 - e[1]); + g.setColor(color).fillRect(4 * i + 1, width - 14, 4 * i + 3, width - 14 - e[1]); + } else { + // draw error in red + g.setColor(63488).fillRect(4 * i, width - 14, 4 * i, width - 14 - height); + } + }); + // draw threshold lines + [deepTh, lightTh].forEach(th => { + th -= minMove; + if (th > 0 && th < height) { + // draw line + g.reset(); + rect(true, 0, width - 14 - th, width, width - 14 - th); + // draw value above or below line + var yAlign = th < height / 2 ? -1 : 1; + if (invert) g.setColor(1); + g.setFontAlign(1, yAlign).setFont("6x8") + .drawString(th + minMove, width - 2, width - 13 - th + 10 * yAlign); + } + }); + + // free ram + data = undefined; + } + + // get thresholds + var deepTh = global.sleeplog ? sleeplog.conf.deepTh : defaults.deepTh; + var lightTh = global.sleeplog ? sleeplog.conf.lightTh : defaults.lightTh; + // set lowest movement displayed + var minMove = deepTh - 20; + // set start point + var start = 0; + // define max start point value + var maxStart = 0; + // define inverted color status + var invert = false; + + // setup UI + Bangle.setUI({ + mode: "custom", + back: selectDebug, + touch: touchHandler, + swipe: swipeHandler + }); + + // first draw + drawGraph(start); + } + + // select a debug logfile + function selectDebug() { + // load debug files + var files = require("Storage").list(/^sleeplog_\d\d\d\d\d\d\.csv$/, {sf:true}); + + // check if no files found + if (!files.length) { + // show prompt + E.showPrompt( /*LANG*/"No debug files found.", { + title: /*LANG*/"Debug log", + buttons: { + /*LANG*/"Back": 0 + } + }).then(showDebug); + } else { + // prepare scroller + const H = 40; + var menuIcon = "\0\f\f\x81\0\xFF\xFF\xFF\0\0\0\0\x0F\xFF\xFF\xF0\0\0\0\0\xFF\xFF\xFF"; + // show scroller + E.showScroller({ + h: H, c: files.length, + back: showDebug, + scrollMin : -24, scroll : -24, // title is 24px, rendered at -1 + draw : (idx, r) => { + if (idx < 0) { + return g.setFont("12x20").setFontAlign(-1,0).drawString(menuIcon + " Select file", r.x + 12, r.y + H - 12); + } else { + g.setColor(g.theme.bg2).fillRect({x: r.x + 4, y: r.y + 2, w: r.w - 8, h: r.h - 4, r: 5}); + var name = new Date(parseInt(files[idx].match(/\d\d\d\d\d\d/)[0]) * 36E5); + name = name.toString().slice(0, -12).split(" ").filter((e, i) => i !== 3).join(" "); + g.setColor(g.theme.fg2).setFont("12x20").setFontAlign(-1, 0).drawString(name, r.x + 12, r.y + H / 2); + } + }, + select: (idx) => plotDebug(files[idx]) + }); + } + } + + // show menu or promt to change debugging + function showDebug() { + // check if sleeplog is available + if (global.sleeplog) { + // get debug status, file and duration + var enabled = !!sleeplog.debug; + var file = typeof sleeplog.debug === "object"; + var duration = 0; + // setup debugging menu + var debugMenu = { + "": { + title: /*LANG*/"Debugging" + }, + /*LANG*/"< Back": () => { + // check if some value has changed + if (enabled !== !!sleeplog.debug || file !== (typeof sleeplog.debug === "object") || duration) + require("sleeplog").setDebug(enabled, file ? duration || 12 : undefined); + // redraw main menu + showMain(7); + }, + /*LANG*/"View log": () => selectDebug(), + /*LANG*/"Enable": { + value: enabled, + onchange: v => enabled = v + }, + /*LANG*/"write File": { + value: file, + onchange: v => file = v + }, + /*LANG*/"Duration": { + value: file ? (sleeplog.debug.writeUntil - Date.now()) / 36E5 | 0 : 12, + min: 1, + max: 96, + wrap: true, + format: v => v + /*LANG*/ "h", + onchange: v => duration = v + }, + /*LANG*/"Cancel": () => showMain(7), + }; + // show menu + var menu = E.showMenu(debugMenu); + } else { + // show error prompt + E.showPrompt("Sleeplog" + /*LANG*/"not enabled!", { + title: /*LANG*/"Debugging", + buttons: { + /*LANG*/"Back": 7 + } + }).then(showMain); + } + } + // show menu to change thresholds function showThresholds() { // setup logging menu @@ -109,66 +369,6 @@ } } - // show menu or promt to change debugging - function showDebug() { - // check if sleeplog is available - if (global.sleeplog) { - // get debug status, file and duration - var enabled = !!sleeplog.debug; - var file = typeof sleeplog.debug === "object"; - var duration = 0; - // setup debugging menu - var debugMenu = { - "": { - title: /*LANG*/"Debugging" - }, - /*LANG*/"< Back": () => { - // check if some value has changed - if (enabled !== !!sleeplog.debug || file !== (typeof sleeplog.debug === "object") || duration) - require("sleeplog").setDebug(enabled, file ? duration || 12 : undefined); - // redraw main menu - showMain(7); - }, - /*LANG*/"Display log": () => { - // choose log... - E.showPrompt( /*LANG*/"Function\nunder\nconstruction.", { - title: /*LANG*/"Debug log", - buttons: { - /*LANG*/"Back": 0 - } - }).then(() => menu = E.showMenu(debugMenu)); - }, - /*LANG*/"Enable": { - value: enabled, - onchange: v => enabled = v - }, - /*LANG*/"write File": { - value: file, - onchange: v => file = v - }, - /*LANG*/"Duration": { - value: file ? (sleeplog.debug.writeUntil - Date.now()) / 36E5 | 0 : 12, - min: 1, - max: 96, - wrap: true, - format: v => v + /*LANG*/ "h", - onchange: v => duration = v - }, - /*LANG*/"Cancel": () => showMain(7), - }; - // show menu - var menu = E.showMenu(debugMenu); - } else { - // show error prompt - E.showPrompt("Sleeplog" + /*LANG*/"not enabled!", { - title: /*LANG*/"Debugging", - buttons: { - /*LANG*/"Back": 7 - } - }).then(showMain); - } - } - // show main menu function showMain(selected) { // set debug image From aef6f638cb15f8269edd45bafd732aa9db822b63 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 26 May 2022 11:27:36 +0200 Subject: [PATCH 039/821] [sleeplog] Change caching for global getStats --- apps/sleeplog/ChangeLog | 4 ++- apps/sleeplog/README.md | 62 +++++++++++++++++++++++++++++++------ apps/sleeplog/boot.js | 28 +++++++++++------ apps/sleeplog/metadata.json | 2 +- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index 1adc4a4dc..e0743315b 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -6,4 +6,6 @@ 0.06: Reduced log size further to 750 entries 0.10: Complete rework off this app! - beta01: Add interface.html to view your saved log data - - beta02: Add "View log" function for debugging log + send data for gadgetbridge \ No newline at end of file + - beta02: Add "View log" function for debugging log + send data for gadgetbridge + - beta03: Change caching for global getStats + \ No newline at end of file diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 13f95070c..e7adc6a30 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -12,6 +12,7 @@ It is using the built in movement calculation to decide your sleeping state. Whi -+- -+- ```` + --- ### Introduction --- @@ -60,16 +61,47 @@ But here are some explanations how to use the app and settings: if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data since & until in Bangle timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h ---- -Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: -``` -"data": [ - {"name": "sleeplog.log", "storageFile": true}, - {"wildcard": "sleeplog_????.log"}, - {"wildcard": "sleeplog_??????.csv"} -], -```` +--- +### Access statistics +--- +* Last Asleep Time [Date]: + `Date(sleeplog.awakeSince)` +* Last Awake Duration [ms]: + `Date() - sleeplog.awakeSince` +* Last Statistics [object]: + ``` + // get stats of the last night (period as displayed inside the app) + // as this might be the mostly used function the data is cached inside the global object + sleeplog.getStats(); + + // get stats of the last 24h + require("sleeplog").getStats(0, 24*60*60*1000); + // same as + require("sleeplog").getStats(Date.now(), 24*60*60*1000); + // output as object, timestamps as UNIX timestamp, durations in minutes + ={ calculatedAt: 1653123553810, deepSleep: 250, lightSleep: 150, awakeSleep: 10, + consecSleep: 320, awakeTime: 1030, notWornTime: 0, unknownTime: 0, logDuration: 1440, + firstDate: 1653036600000, lastDate: 1653111600000 } + + // to get the start of a period defined by "Break TOD" of any date + var startOfBreak = require("sleeplog").getLastBreak(); + // same as + var startOfBreak = require("sleeplog").getLastBreak(Date.now()); + // output as date + =Date: Sat May 21 2022 12:00:00 GMT+0200 + + // get stats of this period as displayed inside the app + require("sleeplog").getStats(require("sleeplog").getLastBreak(), 24*60*60*1000); + // or any other day + require("sleeplog").getStats(require("sleeplog").getLastBreak(Date(2022,4,10)), 24*60*60*1000); + ``` +* Total Statistics [object]: + ``` + // use with caution, may take a long time ! + require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); + ``` + --- ### Worth Mentioning @@ -94,3 +126,15 @@ The app icon is downloaded from [https://icons8.com](https://icons8.com). #### License [MIT License](LICENSE) + +--- + + +Temporarily removed logfiles from metadata.json to prevent removal on un-/reinstall: +``` +"data": [ + {"name": "sleeplog.log", "storageFile": true}, + {"wildcard": "sleeplog_????.log"}, + {"wildcard": "sleeplog_??????.csv"} +], +```` \ No newline at end of file diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 1b303f282..e2b7f45d9 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -222,9 +222,6 @@ if (sleeplog.conf.enabled) { // cache consecutive status to check for changes later on data.consecutive = this.consecutive; - // set disabled move log status - var moveLogStatus = false; - // check if changing to deep sleep from non sleepling if (data.status === 4 && this.status <= 2) { // set asleepSince if undefined @@ -247,8 +244,6 @@ if (sleeplog.conf.enabled) { data.consecutive = 2; // reset awakeSince this.info.awakeSince = 0; - // enabled move log status - moveLogStatus = true; } else if (data.status <= 2 && this.info.awakeSince && this.info.awakeSince + this.conf.maxAwake <= data.timestamp) { // set non consecutive sleep @@ -258,6 +253,9 @@ if (sleeplog.conf.enabled) { } } + // cache change into a known consecutive state + var changeIntoConsec = data.consecutive; + // check if the status has changed if (data.status !== this.status || data.consecutive !== this.consecutive) { // append status @@ -282,8 +280,19 @@ if (sleeplog.conf.enabled) { // call debugging function if set if (this.debug) require("sleeplog").debug(data); - // call move log function if set - if (moveLogStatus) require("sleeplog").moveLog(); + // check if changed into known consecutive state + if (changeIntoConsec) { + // check if change is to consecutive sleep or not + if (changeIntoConsec === 2) { + // call move log function + require("sleeplog").moveLog(); + } else { + // update stats cache if available + if (this.statsCache) this.statsCache = require("sleeplog").getStats(); + } + // remove module from cache if not on debugging + if (!this.debug) Modules.removeCached("sleeplog"); + } }, // define function to append the status to the StorageFile log @@ -300,8 +309,9 @@ if (sleeplog.conf.enabled) { // define function to access stats of the last night getStats: function() { - // check if stats cache is not defined or older than 24h - if (this.statsCache === undefined || this.statsCache.calculatedAt + 864E5 < Date.now()) { + // check if stats cache is not defined or older than 12h + // if stats cache is set it will be updated on every change to non consecutive sleep + if (this.statsCache === undefined || this.statsCache.calculatedAt + 432E5 < Date.now()) { // read stats of the last night into cache and remove module from cache this.statsCache = require("sleeplog").getStats(); Modules.removeCached("sleeplog"); diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index 8eb9c5e5c..157f8920c 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -2,7 +2,7 @@ "id":"sleeplog", "name":"Sleep Log", "shortName": "SleepLog", - "version": "0.10beta02", + "version": "0.10beta03", "description": "Log and view your sleeping habits. This app is using the built in movement calculation.", "icon": "app.png", "type": "app", From 8b2adb2684a4c29ae952e32348912862807dba70 Mon Sep 17 00:00:00 2001 From: pikipirs Date: Mon, 6 Jun 2022 19:50:50 +0200 Subject: [PATCH 040/821] If watchface is fullscreen (ie it doesn't load widgets), messages fail to buzz. This fixes it. --- apps/messages/lib.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/messages/lib.js b/apps/messages/lib.js index 3f801e101..68fce689f 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -69,8 +69,11 @@ exports.pushMessage = function(event) { loadMessages = false; } // first, buzz - if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages){ - WIDGETS.messages.buzz(); + if (!quiet && loadMessages){ + if(global.WIDGETS && WIDGETS.messages) + WIDGETS.messages.buzz(); + else + Bangle.buzz(); if(unlockWatch != false){ Bangle.setLocked(false); Bangle.setLCDPower(1); // turn screen on From 725ce88f95d66a1d7281c10deab669e658b65e9b Mon Sep 17 00:00:00 2001 From: pikipirs Date: Mon, 13 Jun 2022 20:56:42 +0200 Subject: [PATCH 041/821] Rewritten buzzing on new message, widget buzz method rewritten to return a promise --- apps/messages/lib.js | 37 ++++++++++++++++++++++--------------- apps/messages/widget.js | 6 +++--- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/apps/messages/lib.js b/apps/messages/lib.js index 68fce689f..7d7677e85 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -68,27 +68,34 @@ exports.pushMessage = function(event) { if(quiet && quietNoAutOpn) { loadMessages = false; } - // first, buzz - if (!quiet && loadMessages){ - if(global.WIDGETS && WIDGETS.messages) - WIDGETS.messages.buzz(); - else - Bangle.buzz(); - if(unlockWatch != false){ - Bangle.setLocked(false); - Bangle.setLCDPower(1); // turn screen on - } + if (!quiet && loadMessages && unlockWatch != false){ + Bangle.setLocked(false); + Bangle.setLCDPower(1); // turn screen on + } } // after a delay load the app, to ensure we have all the messages if (exports.messageTimeout) clearTimeout(exports.messageTimeout); exports.messageTimeout = setTimeout(function() { exports.messageTimeout = undefined; - // if we're in a clock or it's important, go straight to messages app - if (loadMessages){ - return load("messages.app.js"); + var cont = function() { + // if we're in a clock or it's important, go straight to messages app + if (loadMessages){ + return load("messages.app.js"); + } + if (global.WIDGETS && WIDGETS.messages) { // show messages if widgets are loaded + WIDGETS.messages.show(); + } + }; + if (quiet) { + //Be quiet and cont()inue displaying. + cont(); + } else { + //We have to wait for buzzing to complete before cont()inuing + if(global.WIDGETS && WIDGETS.messages) + WIDGETS.messages.buzz().then(()=>cont()); + else + Bangle.buzz().then(()=>cont()); } - if (!quiet && (!global.WIDGETS || !WIDGETS.messages)) return Bangle.buzz(); // no widgets - just buzz to let someone know - WIDGETS.messages.show(); }, 500); } /// Remove all messages diff --git a/apps/messages/widget.js b/apps/messages/widget.js index ca02d4f1f..93e859446 100644 --- a/apps/messages/widget.js +++ b/apps/messages/widget.js @@ -15,7 +15,7 @@ draw:function(recall) { g.drawImage(settings.flash && (c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y-1); } if (settings.repeat===undefined) settings.repeat = 4; - if (c<120 && (Date.now()-this.l)>settings.repeat*1000) { + if (c<120 && settings.repeat>0 && (Date.now()-this.l)>settings.repeat*1000) { this.l = Date.now(); WIDGETS["messages"].buzz(); // buzz every 4 seconds } @@ -33,8 +33,8 @@ draw:function(recall) { WIDGETS["messages"].width=0; Bangle.drawWidgets(); },buzz:function() { - if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode - require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || "."); + if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return new Promise((success) => { success(); }); // never buzz during Quiet Mode + return require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || "."); },touch:function(b,c) { var w=WIDGETS["messages"]; if (!w||!w.width||c.xw.x+w.width||c.yw.y+w.iconwidth) return; From 893857570e4ba0eaae5b73751e9e48d1bd514149 Mon Sep 17 00:00:00 2001 From: pikipirs Date: Mon, 13 Jun 2022 21:07:30 +0200 Subject: [PATCH 042/821] Rewritten buzzing on new message, widget buzz method rewritten to return a promise --- apps/messages/lib.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/messages/lib.js b/apps/messages/lib.js index 7d7677e85..5e1572384 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -72,7 +72,6 @@ exports.pushMessage = function(event) { Bangle.setLocked(false); Bangle.setLCDPower(1); // turn screen on } - } // after a delay load the app, to ensure we have all the messages if (exports.messageTimeout) clearTimeout(exports.messageTimeout); exports.messageTimeout = setTimeout(function() { From cea1052b3c70d04449a60f78d770d8768da82531 Mon Sep 17 00:00:00 2001 From: pikipirs Date: Tue, 14 Jun 2022 22:47:32 +0200 Subject: [PATCH 043/821] ChangeLog version bump, message --- apps/messages/ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index 4b577e191..759d32b05 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -53,3 +53,5 @@ 0.38: Add telegram foss handling 0.39: Set default color for message icons according to theme Don't turn on the screen after unread timeout expires (#1873) +0.40: Improved buzzing implementation when receiving notifications + From ba58178ab34a55e746b2bfc541a298c8ffaf9a6e Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Sat, 25 Jun 2022 22:00:57 +0200 Subject: [PATCH 044/821] ClockFace_menu: add "powerSave" menu option --- modules/ClockFace.md | 2 +- modules/ClockFace_menu.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/ClockFace.md b/modules/ClockFace.md index b2332c805..85482213c 100644 --- a/modules/ClockFace.md +++ b/modules/ClockFace.md @@ -208,7 +208,7 @@ let menu = { /*LANG*/"< Back": back, }; require("ClockFace_menu").addSettingsFile(menu, ".settings.json", [ - "showDate", "loadWidgets" + "showDate", "loadWidgets", "powerSave", ]); E.showMenu(menu); diff --git a/modules/ClockFace_menu.js b/modules/ClockFace_menu.js index f2267d9ca..a1dd76fee 100644 --- a/modules/ClockFace_menu.js +++ b/modules/ClockFace_menu.js @@ -11,12 +11,16 @@ exports.addItems = function(menu, callback, items) { const label = { showDate:/*LANG*/"Show date", loadWidgets:/*LANG*/"Load widgets", + powerSave:/*LANG*/"Power saving", }[key]; switch(key) { + // boolean options which default to true case "showDate": case "loadWidgets": - // boolean options, which default to true if (value===undefined) value = true; + // fall through + case "powerSave": + // same for all boolean options: menu[label] = { value: !!value, onchange: v => callback(key, v), From fed49792def34ffc338ae4d4de9521682bb77865 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Sat, 25 Jun 2022 22:04:07 +0200 Subject: [PATCH 045/821] ClockFace: make `tick` a "ram" function Should allow clocks to update without waking flash storage if their `update` runs in "ram" as well. --- modules/ClockFace.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ClockFace.js b/modules/ClockFace.js index f8dc33287..4234bbf0d 100644 --- a/modules/ClockFace.js +++ b/modules/ClockFace.js @@ -49,6 +49,7 @@ function ClockFace(options) { } ClockFace.prototype.tick = function() { + "ram" const time = new Date(); const now = { d: `${time.getFullYear()}-${time.getMonth()}-${time.getDate()}`, From 91e80cc3b2ba1d40145115c4f1ca372955308d1f Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Sun, 26 Jun 2022 00:03:29 +0200 Subject: [PATCH 046/821] barclock: add powerSave option, perform second updates in "ram" --- apps/barclock/ChangeLog | 1 + apps/barclock/README.md | 3 ++- apps/barclock/clock-bar.js | 25 +++++++++++++++++++------ apps/barclock/metadata.json | 2 +- apps/barclock/settings.js | 10 +++++++--- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/apps/barclock/ChangeLog b/apps/barclock/ChangeLog index ba44ecef8..a00ae9325 100644 --- a/apps/barclock/ChangeLog +++ b/apps/barclock/ChangeLog @@ -12,3 +12,4 @@ 0.12: Add settings to hide date,widgets 0.13: Add font setting 0.14: Use ClockFace_menu.addItems +0.15: Add Power saving option \ No newline at end of file diff --git a/apps/barclock/README.md b/apps/barclock/README.md index ff66a5cbb..28572e37c 100644 --- a/apps/barclock/README.md +++ b/apps/barclock/README.md @@ -7,4 +7,5 @@ A simple digital clock showing seconds as a horizontal bar. ## Settings * `Show date`: display date at the bottom of screen -* `Font`: choose between bitmap or vector fonts \ No newline at end of file +* `Font`: choose between bitmap or vector fonts +* `Power saving`: (Bangle.js 2 only) don't draw the seconds bar while the watch is locked \ No newline at end of file diff --git a/apps/barclock/clock-bar.js b/apps/barclock/clock-bar.js index 61ce07dfb..cb58f5609 100644 --- a/apps/barclock/clock-bar.js +++ b/apps/barclock/clock-bar.js @@ -14,12 +14,10 @@ let locale = require("locale"); } function renderBar(l) { - if (!this.fraction) { - // zero-size fillRect stills draws one line of pixels, we don't want that - return; - } - const width = this.fraction*l.w; - g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1); + "ram"; + if (!this.fraction) return; // zero-size fillRect stills draws one line of pixels, we don't want that + if (this.powerSave && Bangle.isLocked()) return; + g.fillRect(l.x, l.y, l.x+this.fraction*l.w-1, l.y+l.height-1); } @@ -91,6 +89,7 @@ const ClockFace = require("ClockFace"), this.layout.update(); }, update: function(date, c) { + "ram"; if (c.m) this.layout.time.label = timeText(date); if (c.h) this.layout.ampm.label = ampmText(date); if (c.d && this.showDate) this.layout.date.label = dateText(date); @@ -102,4 +101,18 @@ const ClockFace = require("ClockFace"), this.layout.forgetLazyState(); }, }); +if (clock.powerSave) { + Bangle.on("lock", l => { + if (l) { + clock.precision = 60; + clock.tick(); + const l = clock.layout.bar; + setTimeout(() => g.clearRect(l.x, l.y, l.x+l.w-1, l.y+l.height-1), 100); + } else { + clock.precision = 1; + clock.tick(); + } + }); +} + clock.start(); diff --git a/apps/barclock/metadata.json b/apps/barclock/metadata.json index 0c227dc52..5b783dbda 100644 --- a/apps/barclock/metadata.json +++ b/apps/barclock/metadata.json @@ -1,7 +1,7 @@ { "id": "barclock", "name": "Bar Clock", - "version": "0.14", + "version": "0.15", "description": "A simple digital clock showing seconds as a bar", "icon": "clock-bar.png", "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], diff --git a/apps/barclock/settings.js b/apps/barclock/settings.js index dfe25581c..7b88b7021 100644 --- a/apps/barclock/settings.js +++ b/apps/barclock/settings.js @@ -17,10 +17,14 @@ onchange: v => save("font", v), }, }; - require("ClockFace_menu").addItems(menu, save, { + let items = { showDate: s.showDate, loadWidgets: s.loadWidgets, - }); - + }; + // Power saving for Bangle.js 1 doesn't make sense (no updates while screen is off anyway) + if (process.env.HWVERSION>1) { + items.powerSave = s.powerSave; + } + require("ClockFace_menu").addItems(menu, save, items); E.showMenu(menu); }); From dabaec97d697162f8ff637d93bd1701e5d5414f8 Mon Sep 17 00:00:00 2001 From: Marco H Date: Wed, 6 Jul 2022 14:03:35 +0200 Subject: [PATCH 047/821] Load AGPS data on app start and automatically in background --- apps/agpsdata/ChangeLog | 1 + apps/agpsdata/app.js | 120 ++++-------------------------------- apps/agpsdata/boot.js | 33 ++++++++++ apps/agpsdata/default.json | 1 + apps/agpsdata/lib.js | 64 +++++++++++++++++++ apps/agpsdata/metadata.json | 15 +++-- apps/agpsdata/settings.js | 59 ++++++++++++++++++ 7 files changed, 181 insertions(+), 112 deletions(-) create mode 100644 apps/agpsdata/boot.js create mode 100644 apps/agpsdata/default.json create mode 100644 apps/agpsdata/lib.js create mode 100644 apps/agpsdata/settings.js diff --git a/apps/agpsdata/ChangeLog b/apps/agpsdata/ChangeLog index c17eac852..ae26512de 100644 --- a/apps/agpsdata/ChangeLog +++ b/apps/agpsdata/ChangeLog @@ -1 +1,2 @@ 0.01: First, proof of concept +0.02: Load AGPS data on app start and automatically in background diff --git a/apps/agpsdata/app.js b/apps/agpsdata/app.js index 825eda273..0877a174d 100644 --- a/apps/agpsdata/app.js +++ b/apps/agpsdata/app.js @@ -1,125 +1,29 @@ -var _GB = global.GB; -var counter = 0; - -function GB(msg) { - console.log(msg); - if (msg.t == "http") { - display("Received", "(" + msg.resp.length + ") Touch to apply", () => { - display("Apply data..", ""); - setTimeout(() => { - if (setAGPS(msg.resp)) { - display("Success", "Touch for restart", httpTest); - } - else { - display("Error", "Touch for restart", httpTest); - } - }, 1); - }); - } - if (_GB) { - _GB(msg); - } -} - -function setAGPS(data) { - var js = jsFromBase64(data); - console.log(js); - try { - eval(js); - return true; - } - catch(e) { - console.log("Error:", e); - } - return false; -} - -function jsFromBase64(b64) { - var bin = atob(b64); - var chunkSize = 128; - var js = "Bangle.setGPSPower(1);\n"; // turn GPS on - var gnss_select="1"; - js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnss_select)}")\n`; // set GNSS mode - // What about: - // NAV-TIMEUTC (0x01 0x10) - // NAV-PV (0x01 0x03) - // or AGPS.zip uses AID-INI (0x0B 0x01) - - for (var i=0;i { - display("Request...", "Touch for restart", httpTest); - if (Bluetooth.println) { - console.log("On device"); - Bluetooth.println(JSON.stringify({t:"info", msg:"HTTP Request"})); - Bluetooth.println(JSON.stringify({t:"http", url:"https://www.espruino.com/agps/casic.base64"})); - } - else { - console.log("Testing on Emulator"); - setTimeout(() => { - GB({t:"http", resp:testData}); - }, 1); - } - }); -} - -var nextStep = null; - -Bangle.on("touch", () => { - if (nextStep) { - nextStep(); - } -}); - -httpTest(); - // Show launcher when middle button pressed // Load widgets Bangle.loadWidgets(); Bangle.drawWidgets(); - -/* -require("Storage").write("httptest.info",{ - "id":"httptest", - "name":"Http Test", - "src":"httptest.js", - "icon":"wristlight.img" +display("Updating data..."); +require("agpsdata").pull(function() { + display("Success", "AGPS data updated."); +},function(error) { + display("Error:", error); }); -*/ - -var testData = "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"; diff --git a/apps/agpsdata/boot.js b/apps/agpsdata/boot.js new file mode 100644 index 000000000..78caf7858 --- /dev/null +++ b/apps/agpsdata/boot.js @@ -0,0 +1,33 @@ +(function() { + let waiting = false; + let settings = require("Storage").readJSON("agpsdata.json", 1) || { + enabled: true, + refresh: 1440 + }; + + if (settings.refresh == undefined) settings.refresh = 1440; + + function successCallback(){ + waiting = false; + } + + function errorCallback(){ + waiting = false; + } + + if (settings.enabled) { + let lastUpdate = settings.lastUpdate; + if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){ + if (!waiting){ + waiting = true; + require("agpsdata").pull(successCallback, errorCallback); + } + } + setInterval(() => { + if (!waiting && NRF.getSecurityStatus().connected){ + waiting = true; + require("agpsdata").pull(successCallback, errorCallback); + } + }, settings.refresh * 1000 * 60); + } +})(); diff --git a/apps/agpsdata/default.json b/apps/agpsdata/default.json new file mode 100644 index 000000000..097593d86 --- /dev/null +++ b/apps/agpsdata/default.json @@ -0,0 +1 @@ +{"enabled":true,"refresh":1440} diff --git a/apps/agpsdata/lib.js b/apps/agpsdata/lib.js new file mode 100644 index 000000000..f4370e97e --- /dev/null +++ b/apps/agpsdata/lib.js @@ -0,0 +1,64 @@ +function setAGPS(data) { + var js = jsFromBase64(data); + try { + eval(js); + return true; + } + catch(e) { + console.log("error:", e); + } + return false; +} + +function jsFromBase64(b64) { + var bin = atob(b64); + var chunkSize = 128; + var js = "Bangle.setGPSPower(1);\n"; // turn GPS on + var gnss_select="1"; + js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnss_select)}")\n`; // set GNSS mode + // What about: + // NAV-TIMEUTC (0x01 0x10) + // NAV-PV (0x01 0x03) + // or AGPS.zip uses AID-INI (0x0B 0x01) + + for (var i=0;i { + let result = setAGPS(event.resp); + if (result) { + updateLastUpdate(); + if (successCallback) successCallback(); + } else { + console.log("error applying AGPS data"); + if (failureCallback) failureCallback("Error applying AGPS data"); + } + }).catch((e)=>{ + console.log("error", e); + if (failureCallback) failureCallback(e); + }); + } else { + console.log("error: No http method found"); + if (failureCallback) failureCallback(/*LANG*/"No http method found"); + } +}; diff --git a/apps/agpsdata/metadata.json b/apps/agpsdata/metadata.json index af51f3a10..fd0985c90 100644 --- a/apps/agpsdata/metadata.json +++ b/apps/agpsdata/metadata.json @@ -2,15 +2,22 @@ "name": "A-GPS Data", "shortName":"AGPS Data", "icon": "agpsdata.png", - "version":"0.01", - "description": "Download assisted GPS data directly to watch", - "tags": "assisted,gps,agps,http", + "version":"0.02", + "description": "Download assisted GPS (AGPS) data directly to watch", + "tags": "boot,tool,assisted,gps,agps,http", "allow_emulator":true, "supports": ["BANGLEJS2"], "readme":"README.md", "screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" }, { "url":"screenshot3.png" }, { "url":"screenshot4.png" }, { "url":"screenshot5.png" } ], "storage": [ {"name":"agpsdata.app.js","url":"app.js"}, - {"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true} + {"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true}, + {"name":"agpsdata.default.json","url":"default.json"}, + {"name":"agpsdata.boot.js","url":"boot.js"}, + {"name":"agpsdata","url":"lib.js"}, + {"name":"agpsdata.settings.js","url":"settings.js"} + ], + "data": [ + {"name": "agpsdata.json"} ] } diff --git a/apps/agpsdata/settings.js b/apps/agpsdata/settings.js new file mode 100644 index 000000000..b6c31ba8f --- /dev/null +++ b/apps/agpsdata/settings.js @@ -0,0 +1,59 @@ +(function(back) { + function writeSettings(key, value) { + var s = require('Storage').readJSON(FILE, true) || {}; + s[key] = value; + require('Storage').writeJSON(FILE, s); + readSettings(); + } + + function readSettings(){ + settings = Object.assign( + require('Storage').readJSON("agpsdata.default.json", true) || {}, + require('Storage').readJSON(FILE, true) || {} + ); + } + + var FILE="agpsdata.json"; + var settings; + readSettings(); + + function buildMainMenu(){ + var mainmenu = { + '': { 'title': 'AGPS download' }, + '< Back': back, + "Enabled": { + value: !!settings.enabled, + onchange: v => { + writeSettings("enabled", v); + } + }, + "Refresh every": { + value: settings.refresh / 60, + min: 1, + max: 48, + step: 1, + format: v=>v+"h", + onchange: v => { + writeSettings("refresh",Math.round(v * 60)); + } + }, + "Force refresh": ()=>{ + E.showMessage("Loading AGPS data"); + + require("agpsdata").pull(function() { + E.showAlert("Success").then(()=>{ + E.showMenu(buildMainMenu()); + }); + },function(error) { + E.showAlert(error,"Error").then(()=>{ + E.showMenu(buildMainMenu()); + }); + }); + } + }; + + return mainmenu; + } + + E.showMenu(buildMainMenu()); +}); From 603abf7bb7ba626cd6a62cf2fe4d2541bc786701 Mon Sep 17 00:00:00 2001 From: Marco H Date: Wed, 6 Jul 2022 14:06:38 +0200 Subject: [PATCH 048/821] Adjust max refresh interval --- apps/agpsdata/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/agpsdata/settings.js b/apps/agpsdata/settings.js index b6c31ba8f..10e70d15c 100644 --- a/apps/agpsdata/settings.js +++ b/apps/agpsdata/settings.js @@ -30,7 +30,7 @@ "Refresh every": { value: settings.refresh / 60, min: 1, - max: 48, + max: 168, step: 1, format: v=>v+"h", onchange: v => { From 5ef4d8be70208f7c1f412038874bbc8a7625515c Mon Sep 17 00:00:00 2001 From: Marco H Date: Wed, 6 Jul 2022 14:13:23 +0200 Subject: [PATCH 049/821] Update README and screenshots --- apps/agpsdata/README.md | 15 +++++++-------- apps/agpsdata/metadata.json | 2 +- apps/agpsdata/screenshot.png | Bin 959 -> 2493 bytes apps/agpsdata/screenshot2.png | Bin 613 -> 2618 bytes apps/agpsdata/screenshot3.png | Bin 695 -> 0 bytes apps/agpsdata/screenshot4.png | Bin 533 -> 0 bytes apps/agpsdata/screenshot5.png | Bin 578 -> 0 bytes 7 files changed, 8 insertions(+), 9 deletions(-) delete mode 100644 apps/agpsdata/screenshot3.png delete mode 100644 apps/agpsdata/screenshot4.png delete mode 100644 apps/agpsdata/screenshot5.png diff --git a/apps/agpsdata/README.md b/apps/agpsdata/README.md index 93cc94259..c445ae754 100644 --- a/apps/agpsdata/README.md +++ b/apps/agpsdata/README.md @@ -1,18 +1,17 @@ # A-GPS Data -Load assisted GPS data directly to the watch using the new http requests on Android GadgetBridge. +Load assisted GPS (AGPS) data directly to the watch using the new http requests on Android GadgetBridge. + +Will download AGPS data in background (if enabled in settings). Make sure: * your GadgetBridge version supports http requests * turn on internet access in GadgetBridge settings -Currently proof of concept on Bangle2 only. Will eventually add a widget for automatic download. - -![](screenshot.png) -![](screenshot2.png) -![](screenshot3.png) -![](screenshot4.png) -![](screenshot5.png) +Currently proof of concept on Bangle2 only. ## Creator [@pidajo](https://github.com/pidajo) + +## Contributor +[@myxor](https://github.com/myxor) diff --git a/apps/agpsdata/metadata.json b/apps/agpsdata/metadata.json index fd0985c90..79f551033 100644 --- a/apps/agpsdata/metadata.json +++ b/apps/agpsdata/metadata.json @@ -8,7 +8,7 @@ "allow_emulator":true, "supports": ["BANGLEJS2"], "readme":"README.md", - "screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" }, { "url":"screenshot3.png" }, { "url":"screenshot4.png" }, { "url":"screenshot5.png" } ], + "screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" } ], "storage": [ {"name":"agpsdata.app.js","url":"app.js"}, {"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true}, diff --git a/apps/agpsdata/screenshot.png b/apps/agpsdata/screenshot.png index fae53ba85ecb0923cab59c55fb1f15adefd0be17..1fcb2d8ee3760b0b6962d07bb0fe636a5427bafa 100644 GIT binary patch literal 2493 zcmc(h`#;m|AIGnak>Q@44>fw|NK9WDF)F1ZMjN9!bKis&rHmn)68aXEaOYH!(B|}D zh)oMAcMcDQoSHf05E{{jEZJ05F!5f zMt6P759Mg{i}$P-oJerTQ&AkF+EXZs(~;0gju>G`o#?*#trDYe>Sxv#gs8BW<;~nQ z_`vj`_b*`$tM;@JF1za;eKo9+J@4SFGjnk(A4%CW@nkH$R%!k~+rpWPa!q80kvAA5 z%r?H#<)-8&)w#azQZFRA)&m{lw)tCf))C75;Pwm2os1i>Y5xM18@O2g$$RJqtoMGp z5&g4cqqEf^LxbEYZkmIqFxipBk0sFtBW?m>COqEUUKYB?U* zv_lwk8bmJwkkx8fQQ1BTNv(Q6Wi__qNFvg!l&2jM$l5O;4yL4vBC}oSpWI%S*k-52 zCJ`D_Y6dDY*ja%WnNyxOg+AL@nxCRrY_v6PAtunJ+)7&TV8uu?>~+?YngN>f06ZEs z#_eFOH}80rjmG@6j*Xo>$Ffr772h7#&!H}&LC?lFr)TEvXaSD4I=IqaNTzD?_(YL< z8sQabew?-yzrFtj&tZ(4x{z8Nyr_FEWh@-(+7)1{}pYL^186bIU=Ou)0u* zr1K3Sy{mv-M2k+#P5KzQsmd!RmXzEsdwS=LM-SRM%-WvZS~$=xHGy7^o~#>KgjZcd z68AlI_hTxPi7D*TnqZXS@$$qcmQr_S$6;eSBb|*QbWiISjR~K9A`W7tnHb~z17v$5 zjKi;=ZZeOtJ|tp&Za&5#cePL69U+@bFNylyN1nNboPFEL627#>tcU+-GIistFf;e3 zPwNNPD)ZFa%A+}$UpWQnoxk#6k5#hMjV`d5Lu?fz4Vq~7{D>ujmf@FwDps<*yYg*N z57|(C9#Lzplcp}6F7qFHlX)zTpX|s9qSfOl5_z<9Wy0S#G=`!Zk~C9&uMKMsS$+ST zAGP;GJ#!Jkv-+!6yvohc$u^hgS(OZGpIhWezH}wS<6_L@OuS&=kL*0fOeClxI8l5fB+(SFo-IfahRVtyTd*^4acN4hY~;wzsYY>iUR|A(t`$4Ex(i z4REl-_=t{y8t^O58AQ4Rdh4Fx6UA`Aa#ishhiGeX$>>;C^@0JkB4eN}@Waz%*Bd5Tw->kf)rxV&VNYUPH)2vKqJwycz#p#rsD1VhtZ(L_v}! zofr^_F3CoEHR`Rn3Wk24q}A5ad3aGu&BEJOIK4#h5Bn$`jwlGxFXd5-@T%eE;+=Dh z;NfjReb4HpATl?+M>Rln@id4t|9*0Lc=5(7*@74cMY^3FH$zLF-;2b3ke6D@f2Z>1 zYnbka;nEq50g)~tIb7`g2l*I{%Iqa$HP%(?%Z5_@N*@uz8Pq?j450@W&AFS8U*>J*y`}`(W7H8 z$79~gfd4ATT{Gp^cc$Z2d^YaJ>FELAeBALk{UwoCO1bv}bbEwcb7Sg#k^j{5wk9NI zYkmC!i3v%_{AjK3$gL7miWnnIk@`h~CXQ zn#qc~lVF1x9k~_Dd~8LB>9CbJ&H@X@RPlxj{E_y@i>+%lN^E|^T6=G>bMPg*E+M9f z=O|=0sJJIKT$Qs&G%H+bJQhS7;|j{-4;zB{@7J}?nsSA+PU*e(wSljUmA9V$Vk^yu z5^qqfL2`y9YUKE~5KtVx@CFgDUq@X7Ys!OjiB-eN13R(vr}AHLJ%m!c#?g}czC?G) zm!ujKjFMCYZQ&}6G9qhM+pmMJ^gV3T!R?oqsxsaxGh!P~h@IrNig1C1Rnb2}*@1@p zhxUs4)4u!MvwO41SeK-sK$=6xOYyl;&Y*tFz#|5dlRPn~;6_L8Ffh-B{Kt=7I4JK1 z=qJ7enL;MUBB{ncg_tWHr96i$tN(TY#uWlRGq(#es4DWgg(L{+r&nqoK`uu!yv=oi zKzF@_B|`Xz+Er`3fI$DJD+n@1kMjK@TL^TQmqU(_4Kmf<_>@AR|LdZgQq%Jx(Q`x# RW9UBy?CtO;D{TD|{|C=Nkk$YI delta 949 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 delta 600 zcmdlb@|0zQL_G^L0|P_s%%8bHim^Dz-HBn{IhmJ04okYDuOkD)#(wTUiL5|APk>K| zE0F&G|345j{Aci==gk7-a~60+7BevL9R^{>9PCp6i?Ebj4b}Ac3;KzEoz9fxOYcTBIX+Q0kP74c||Yq#1TDNOZ^(mTJ}_2D)z4*}Q$iB}#OVqH diff --git a/apps/agpsdata/screenshot3.png b/apps/agpsdata/screenshot3.png deleted file mode 100644 index be152ba28a249248125a0bc7ae3564f740faf46e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 695 zcmV;o0!aOdP)Px#1ZP1_K>z@;j|==^1poj53{Xr|MF0Q*|Ns90|Nj9008gGv1ONa432;bRa{vGi z!vFvd!vV){sAK>D0yIfPK~!ko?UxC1!ypVrQBD9i$oK@E>HY6kkdOs-p(0p4$S7l)K4dKJEZ3syd9e=)>9(nB~!>p`%l2Wd33p$3<&$H1f= zH38L-PU%p3(RwH=G^6J`)KG!z)bi&3S16HN5M@=1uJQ`&h>8f z^a5;i+AQgG&MVc4lFkbj*|Q6SO1B2{!Q6UzhkzjHEwonRjUTn$t0LNbh%MIy*{M@DW$q8_NX!$44BXk6_EBG&N)9fAV$`OcH5()K@ zz3-+yTlPHi#lz~pz&>V2$QESako}V*q2S`(CWVC$o(ju%E%SkrfSiDRO7T!}}Jun(N^zIP8N@u5KmRK@bE%5ClOG1VIo4K@bE% d5cG#~?gw(2W0)8yo_hcQ002ovPDHLkV1o0#G}-_F diff --git a/apps/agpsdata/screenshot4.png b/apps/agpsdata/screenshot4.png deleted file mode 100644 index 305a166d0866fb6f7554c2249acd49cf1bd6005e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 533 zcmeAS@N?(olHy`uVBq!ia0vp^8$g(a8A!&?{Fw`+7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`211o(uw0_p$%{{u0@e+K_~-Yh^qXMsm#F#`kNVGw3Kp1&dmXwW227srr{ zdvB*F<{ehxaVzvsxUX=EQ|;})@@Od^hT|C*o0fb0y|bNaE%nFoc1(_jq;VN zDx~{`-+y-T*kzu3xTGd)>nw*)e8E2-&zFxb)Ouu-JpbgXV8im$Z=X%{&E7UOTuW-< zq@CSI^R{u8ClwxzQ3^7$^w#Wt+cY~fWz(!K*`%=P`?TygXnvHt+VO5e@aKr~V^N2V zeiuBIEsB4n=M`FG^G$W~#)vn6Jd-BvJ2L&~r`0o__RVmAlp^(c*NodY=SBQeS#~Sx zU7>aS_2rMlW_!qNA>lcBN7dF< zFMqbNEW~Z{#y0!;0YFt5U(SX{K`w^bri$?O!WP-10?S0>gTe~ HDWM4f#C71} diff --git a/apps/agpsdata/screenshot5.png b/apps/agpsdata/screenshot5.png deleted file mode 100644 index 6468a18725fb1aa2dcc4fa43debd4f828b781f19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 578 zcmeAS@N?(olHy`uVBq!ia0vp^8$g(a8A!&?{Fw`+7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`211o(uw0_p$%{{u0@e+K_~-Yh^qXMsm#F#`kNVGw3Kp1&dmXwZI77srr{ zdvB-n<{ehxar5<0$P;+Vwfp~FF{Wqg4jwG?)V@{+T|IKaJZtubOXi8%H8tVF`I{g4 zCEVY7WPWbgpUD$D(>LD{vwr(z((Om*mu-2pxa@UtjoF=a$8eLQ+P{9}AI(~MW2#w4 z)b5qJ6I7#j$wo|g+*h$n`-WjCearr9hDrDlw=T`Mm-z2=th4c>BUAkSpE=vyoZu~fDr@-blci2xm z%sI$ES@3AlWsV)()9x*24@p|IW{ugB10HGf^RH$#`WD6>34Qea+43Z>y_Pz08s_|6 z&!1mf+VJ@3wX-g%yIWIF#XtUNtY+K)bkSb<+G6J;Pxp@+r}Li%e09E-a#;4G;TgZ0eaAP? zJsmxJZ%jnm{jRxJ_WwEc_xsd@!33-rgI0M}p+Ej+Ti2uCJSPBjfKmCU9uMi5ZPA#7mRIGQXJ3;{XYIy85}S Ib4q9e06g3R=Kufz From bdf67c0360a2e3784434b4de99fb65b35bc49b44 Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 7 Jul 2022 09:51:57 +0200 Subject: [PATCH 050/821] Split settings file and data file into two --- apps/agpsdata/boot.js | 2 +- apps/agpsdata/lib.js | 7 ++++--- apps/agpsdata/metadata.json | 3 ++- apps/agpsdata/settings.js | 7 +++++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/agpsdata/boot.js b/apps/agpsdata/boot.js index 78caf7858..6415f0b52 100644 --- a/apps/agpsdata/boot.js +++ b/apps/agpsdata/boot.js @@ -1,6 +1,6 @@ (function() { let waiting = false; - let settings = require("Storage").readJSON("agpsdata.json", 1) || { + let settings = require("Storage").readJSON("agpsdata.settings.json", 1) || { enabled: true, refresh: 1440 }; diff --git a/apps/agpsdata/lib.js b/apps/agpsdata/lib.js index f4370e97e..c27ad5db6 100644 --- a/apps/agpsdata/lib.js +++ b/apps/agpsdata/lib.js @@ -36,9 +36,10 @@ function CASIC_CHECKSUM(cmd) { } function updateLastUpdate() { - let settings = require("Storage").readJSON("agpsdata.json", 1) || {}; - settings.lastUpdate = Math.round(Date.now()); - require("Storage").writeJSON("agpsdata.json", settings) + const file = "agpsdata.json"; + let data = require("Storage").readJSON(file, 1) || {}; + data.lastUpdate = Math.round(Date.now()); + require("Storage").writeJSON(file, data); } exports.pull = function(successCallback, failureCallback) { diff --git a/apps/agpsdata/metadata.json b/apps/agpsdata/metadata.json index 79f551033..558fcce69 100644 --- a/apps/agpsdata/metadata.json +++ b/apps/agpsdata/metadata.json @@ -18,6 +18,7 @@ {"name":"agpsdata.settings.js","url":"settings.js"} ], "data": [ - {"name": "agpsdata.json"} + {"name": "agpsdata.json"}, + {"name": "agpsdata.settings.json"} ] } diff --git a/apps/agpsdata/settings.js b/apps/agpsdata/settings.js index 10e70d15c..9d754e82b 100644 --- a/apps/agpsdata/settings.js +++ b/apps/agpsdata/settings.js @@ -1,6 +1,9 @@ (function(back) { function writeSettings(key, value) { - var s = require('Storage').readJSON(FILE, true) || {}; + var s = Object.assign( + require('Storage').readJSON("agpsdata.default.json", true) || {}, + require('Storage').readJSON(FILE, true) || {} + ); s[key] = value; require('Storage').writeJSON(FILE, s); readSettings(); @@ -13,7 +16,7 @@ ); } - var FILE="agpsdata.json"; + var FILE="agpsdata.settings.json"; var settings; readSettings(); From 1c2ca29f3f9fa2e40cf05953a66e28cebc074c95 Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 7 Jul 2022 14:20:37 +0200 Subject: [PATCH 051/821] Allow retry if download via app fails --- apps/agpsdata/app.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/agpsdata/app.js b/apps/agpsdata/app.js index 0877a174d..080efebc0 100644 --- a/apps/agpsdata/app.js +++ b/apps/agpsdata/app.js @@ -3,7 +3,7 @@ function display(text1, text2) { g.clear(); var img = require("Storage").read("agpsdata.img"); if (img) { - g.drawImage(img, g.getWidth() - 48, g.getHeight()-48-24); + g.drawImage(img, g.getWidth() - 48, g.getHeight() - 48 - 24); } g.setFont("Vector", 20); g.setFontAlign(0, 1); @@ -21,9 +21,16 @@ function display(text1, text2) { Bangle.loadWidgets(); Bangle.drawWidgets(); -display("Updating data..."); -require("agpsdata").pull(function() { - display("Success", "AGPS data updated."); -},function(error) { - display("Error:", error); -}); +function start() { + display("Updating data..."); + require("agpsdata") + .pull(function() { display("Success", "AGPS data updated."); }, + function(error) { + display("Error:" + error, "touch to retry"); + Bangle.on("touch", () => { + start(); + }); + }); +} + +start(); From cf1f2119ccc17d8c523491d3ff01021f05dcbf6c Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 7 Jul 2022 21:24:47 +0200 Subject: [PATCH 052/821] Improve app handling Unify writing --- apps/agpsdata/README.md | 6 +++--- apps/agpsdata/app.js | 23 +++++++++++------------ apps/agpsdata/metadata.json | 4 ++-- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/apps/agpsdata/README.md b/apps/agpsdata/README.md index c445ae754..9d5672434 100644 --- a/apps/agpsdata/README.md +++ b/apps/agpsdata/README.md @@ -1,14 +1,14 @@ # A-GPS Data -Load assisted GPS (AGPS) data directly to the watch using the new http requests on Android GadgetBridge. +Load assisted GPS (A-GPS) data directly to your Bangle.js using the new http requests on Android GadgetBridge. -Will download AGPS data in background (if enabled in settings). +Will download A-GPS data in background (if enabled in settings). Make sure: * your GadgetBridge version supports http requests * turn on internet access in GadgetBridge settings -Currently proof of concept on Bangle2 only. +Currently proof of concept on Bangle.js 2 only. ## Creator [@pidajo](https://github.com/pidajo) diff --git a/apps/agpsdata/app.js b/apps/agpsdata/app.js index 080efebc0..b3b070539 100644 --- a/apps/agpsdata/app.js +++ b/apps/agpsdata/app.js @@ -5,11 +5,11 @@ function display(text1, text2) { if (img) { g.drawImage(img, g.getWidth() - 48, g.getHeight() - 48 - 24); } - g.setFont("Vector", 20); + g.setFont("Vector", 18); g.setFontAlign(0, 1); g.drawString(text1, g.getWidth() / 2, g.getHeight() / 3 + 24); if (text2 != undefined) { - g.setFont("Vector", 15); + g.setFont("Vector", 12); g.setFontAlign(-1, -1); g.drawString(text2, 5, g.getHeight() / 3 + 29); } @@ -22,15 +22,14 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); function start() { - display("Updating data..."); - require("agpsdata") - .pull(function() { display("Success", "AGPS data updated."); }, - function(error) { - display("Error:" + error, "touch to retry"); - Bangle.on("touch", () => { - start(); - }); - }); + display("Updating A-GPS..."); + require("agpsdata").pull(function() { + display("A-GPS updated.", "touch to close"); + Bangle.on("touch", () => { load(); }); + }, + function(error) { + display("Error:" + error, "touch to retry"); + Bangle.on("touch", () => { start(); }); + }); } - start(); diff --git a/apps/agpsdata/metadata.json b/apps/agpsdata/metadata.json index 558fcce69..88f9af568 100644 --- a/apps/agpsdata/metadata.json +++ b/apps/agpsdata/metadata.json @@ -1,9 +1,9 @@ { "id": "agpsdata", "name": "A-GPS Data", - "shortName":"AGPS Data", + "shortName":"A-GPS Data", "icon": "agpsdata.png", "version":"0.02", - "description": "Download assisted GPS (AGPS) data directly to watch", + "description": "Download assisted GPS (A-GPS) data directly to your Bangle.js", "tags": "boot,tool,assisted,gps,agps,http", "allow_emulator":true, "supports": ["BANGLEJS2"], From 894285aff5b085cd9b1596c2ad5fedf6eb60ee0d Mon Sep 17 00:00:00 2001 From: xxDUxx <96152564+xxDUxx@users.noreply.github.com> Date: Sun, 10 Jul 2022 11:13:58 +0200 Subject: [PATCH 053/821] Create Readme.md --- apps/tinyVario/Readme.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 apps/tinyVario/Readme.md diff --git a/apps/tinyVario/Readme.md b/apps/tinyVario/Readme.md new file mode 100644 index 000000000..e23f3af66 --- /dev/null +++ b/apps/tinyVario/Readme.md @@ -0,0 +1,17 @@ +# Turn your Bangle.js2 into a flight computer! + +## This is a work in progress. Working features so far: +- Altimeter +- Variometer +- Average rate of climb +- Ground speed +- Flying time with automatic take-off detection + +## Planned features: +- Settings page to adjust QNH, change units +- final glide computer +- waypoint navigation +- flight log (possibly IGC file export) + +Contact me for feedback and suggestions! +tinyVario@dumke.org From 8468031925b48d01c6ee4978530729073cd629eb Mon Sep 17 00:00:00 2001 From: xxDUxx <96152564+xxDUxx@users.noreply.github.com> Date: Sun, 10 Jul 2022 11:18:28 +0200 Subject: [PATCH 054/821] Update Readme.md --- apps/tinyVario/Readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/tinyVario/Readme.md b/apps/tinyVario/Readme.md index e23f3af66..c375585c2 100644 --- a/apps/tinyVario/Readme.md +++ b/apps/tinyVario/Readme.md @@ -1,5 +1,7 @@ # Turn your Bangle.js2 into a flight computer! +All settings can be accessed by touching the corresponding data fields. I made all interface objects as big as possible to make it easier to use with gloves. + ## This is a work in progress. Working features so far: - Altimeter - Variometer @@ -7,8 +9,9 @@ - Ground speed - Flying time with automatic take-off detection + ## Planned features: -- Settings page to adjust QNH, change units +- glide slope display - final glide computer - waypoint navigation - flight log (possibly IGC file export) From d537fc5d73c49c9ecc4c6bde40ef9498651d4506 Mon Sep 17 00:00:00 2001 From: Marco H Date: Mon, 11 Jul 2022 13:39:14 +0200 Subject: [PATCH 055/821] Add setting for GNSS type --- apps/agpsdata/README.md | 2 + apps/agpsdata/app.js | 39 ++++++++---- apps/agpsdata/default.json | 2 +- apps/agpsdata/lib.js | 16 ++++- apps/agpsdata/settings.js | 119 ++++++++++++++++++++----------------- 5 files changed, 109 insertions(+), 69 deletions(-) diff --git a/apps/agpsdata/README.md b/apps/agpsdata/README.md index 9d5672434..57bb055a1 100644 --- a/apps/agpsdata/README.md +++ b/apps/agpsdata/README.md @@ -4,6 +4,8 @@ Load assisted GPS (A-GPS) data directly to your Bangle.js using the new http req Will download A-GPS data in background (if enabled in settings). +The GNSS type can be configured in the settings. + Make sure: * your GadgetBridge version supports http requests * turn on internet access in GadgetBridge settings diff --git a/apps/agpsdata/app.js b/apps/agpsdata/app.js index b3b070539..647723bb4 100644 --- a/apps/agpsdata/app.js +++ b/apps/agpsdata/app.js @@ -21,15 +21,34 @@ function display(text1, text2) { Bangle.loadWidgets(); Bangle.drawWidgets(); +let waiting = false; + function start() { - display("Updating A-GPS..."); - require("agpsdata").pull(function() { - display("A-GPS updated.", "touch to close"); - Bangle.on("touch", () => { load(); }); - }, - function(error) { - display("Error:" + error, "touch to retry"); - Bangle.on("touch", () => { start(); }); - }); + g.reset(); + g.clear(); + waiting = false; + display("Retry?", "touch to retry"); + Bangle.on("touch", () => { updateAgps(); }); } -start(); + +function updateAgps() { + g.reset(); + g.clear(); + if (!waiting) { + waiting = true; + display("Updating A-GPS..."); + require("agpsdata").pull(function() { + waiting = false; + display("A-GPS updated.", "touch to close"); + Bangle.on("touch", () => { load(); }); + }, + function(error) { + waiting = false; + E.showAlert(error, "Error") + .then(() => { start(); }); + }); + } else { + display("Waiting..."); + } +} +updateAgps(); diff --git a/apps/agpsdata/default.json b/apps/agpsdata/default.json index 097593d86..0b6e0cecf 100644 --- a/apps/agpsdata/default.json +++ b/apps/agpsdata/default.json @@ -1 +1 @@ -{"enabled":true,"refresh":1440} +{"enabled":true,"refresh":1440,"gnsstype":1} diff --git a/apps/agpsdata/lib.js b/apps/agpsdata/lib.js index c27ad5db6..7d9758c0a 100644 --- a/apps/agpsdata/lib.js +++ b/apps/agpsdata/lib.js @@ -1,3 +1,13 @@ +function readSettings() { + settings = Object.assign( + require('Storage').readJSON("agpsdata.default.json", true) || {}, + require('Storage').readJSON(FILE, true) || {}); +} + +var FILE = "agpsdata.settings.json"; +var settings; +readSettings(); + function setAGPS(data) { var js = jsFromBase64(data); try { @@ -14,8 +24,8 @@ function jsFromBase64(b64) { var bin = atob(b64); var chunkSize = 128; var js = "Bangle.setGPSPower(1);\n"; // turn GPS on - var gnss_select="1"; - js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnss_select)}")\n`; // set GNSS mode + var gnsstype = settings.gnsstype || 1; // default GPS + js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnsstype)}")\n`; // set GNSS mode // What about: // NAV-TIMEUTC (0x01 0x10) // NAV-PV (0x01 0x03) @@ -60,6 +70,6 @@ exports.pull = function(successCallback, failureCallback) { }); } else { console.log("error: No http method found"); - if (failureCallback) failureCallback(/*LANG*/"No http method found"); + if (failureCallback) failureCallback(/*LANG*/"No http method"); } }; diff --git a/apps/agpsdata/settings.js b/apps/agpsdata/settings.js index 9d754e82b..80a2f3956 100644 --- a/apps/agpsdata/settings.js +++ b/apps/agpsdata/settings.js @@ -1,62 +1,71 @@ (function(back) { - function writeSettings(key, value) { - var s = Object.assign( - require('Storage').readJSON("agpsdata.default.json", true) || {}, - require('Storage').readJSON(FILE, true) || {} - ); - s[key] = value; - require('Storage').writeJSON(FILE, s); - readSettings(); - } - - function readSettings(){ - settings = Object.assign( - require('Storage').readJSON("agpsdata.default.json", true) || {}, - require('Storage').readJSON(FILE, true) || {} - ); - } - - var FILE="agpsdata.settings.json"; - var settings; +function writeSettings(key, value) { + var s = Object.assign( + require('Storage').readJSON(settingsDefaultFile, true) || {}, + require('Storage').readJSON(settingsFile, true) || {}); + s[key] = value; + require('Storage').writeJSON(settingsFile, s); readSettings(); +} - function buildMainMenu(){ - var mainmenu = { - '': { 'title': 'AGPS download' }, - '< Back': back, - "Enabled": { - value: !!settings.enabled, - onchange: v => { - writeSettings("enabled", v); - } - }, - "Refresh every": { - value: settings.refresh / 60, - min: 1, - max: 168, - step: 1, - format: v=>v+"h", - onchange: v => { - writeSettings("refresh",Math.round(v * 60)); - } - }, - "Force refresh": ()=>{ - E.showMessage("Loading AGPS data"); - - require("agpsdata").pull(function() { - E.showAlert("Success").then(()=>{ - E.showMenu(buildMainMenu()); +function readSettings() { + settings = Object.assign( + require('Storage').readJSON(settingsDefaultFile, true) || {}, + require('Storage').readJSON(settingsFile, true) || {}); +} + +var settingsFile = "agpsdata.settings.json"; +var settingsDefaultFile = "agpsdata.default.json"; + +var settings; +readSettings(); + +const gnsstypes = [ + "", "GPS", "BDS", "GPS+BDS", "GLONASS", "GPS+GLONASS", "BDS+GLONASS", + "GPS+BDS+GLON." +]; + +function buildMainMenu() { + var mainmenu = { + '' : {'title' : 'AGPS download'}, + '< Back' : back, + "Enabled" : { + value : !!settings.enabled, + onchange : v => { writeSettings("enabled", v); } + }, + "Refresh every" : { + value : settings.refresh / 60, + min : 1, + max : 168, + step : 1, + format : v => v + "h", + onchange : v => { writeSettings("refresh", Math.round(v * 60)); } + }, + "GNSS type" : { + value : settings.gnsstype, + min : 1, + max : 7, + step : 1, + format : v => gnsstypes[v], + onchange : x => writeSettings('gnsstype', x) + }, + "Force refresh" : () => { + E.showMessage("Loading A-GPS data"); + require("agpsdata") + .pull( + function() { + E.showAlert("Success").then( + () => { E.showMenu(buildMainMenu()); }); + }, + function(error) { + E.showAlert(error, "Error") + .then(() => { E.showMenu(buildMainMenu()); }); }); - },function(error) { - E.showAlert(error,"Error").then(()=>{ - E.showMenu(buildMainMenu()); - }); - }); - } - }; + } + }; - return mainmenu; - } + return mainmenu; +} - E.showMenu(buildMainMenu()); +E.showMenu(buildMainMenu()); }); From 2f940d901e6425693b184b420000bb6f9624a8f7 Mon Sep 17 00:00:00 2001 From: deirdreobyrne Date: Mon, 11 Jul 2022 14:37:09 +0100 Subject: [PATCH 056/821] Checks firmware; E.setDST(...) moved to boot.js --- apps/widdst/ChangeLog | 1 + apps/widdst/boot.js | 15 +++++++++++++++ apps/widdst/metadata.json | 3 ++- apps/widdst/widget.js | 30 ++++++++++-------------------- 4 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 apps/widdst/boot.js diff --git a/apps/widdst/ChangeLog b/apps/widdst/ChangeLog index ec66c5568..e350137ee 100644 --- a/apps/widdst/ChangeLog +++ b/apps/widdst/ChangeLog @@ -1 +1,2 @@ 0.01: Initial version +0.02: Checks for correct firmware; E.setDST(...) moved to boot.js diff --git a/apps/widdst/boot.js b/apps/widdst/boot.js new file mode 100644 index 000000000..b0a844532 --- /dev/null +++ b/apps/widdst/boot.js @@ -0,0 +1,15 @@ +(() => { + + if (E.setDST) { + var dstSettings = require('Storage').readJSON('widdst.json',1)||{}; + if (dstSettings.has_dst) { + E.setDST(60*dstSettings.dst_size, 60*dstSettings.tz, dstSettings.dst_start.dow_number, dstSettings.dst_start.dow, + dstSettings.dst_start.month, dstSettings.dst_start.day_offset, 60*dstSettings.dst_start.at, + dstSettings.dst_end.dow_number, dstSettings.dst_end.dow, dstSettings.dst_end.month, dstSettings.dst_end.day_offset, + 60*dstSettings.dst_end.at); + } else { + E.setDST(0,0,0,0,0,0,0,0,0,0,0,0); + } + } + +})() diff --git a/apps/widdst/metadata.json b/apps/widdst/metadata.json index 16cc6c94f..f280c96eb 100644 --- a/apps/widdst/metadata.json +++ b/apps/widdst/metadata.json @@ -1,6 +1,6 @@ { "id": "widdst", "name": "Daylight Saving", - "version":"0.01", + "version":"0.02", "description": "Widget to set daylight saving rules. Requires Espruino 2v14.49 or later - see the instructions below for more information.", "icon": "icon.png", "type": "widget", @@ -9,6 +9,7 @@ "readme": "README.md", "storage": [ {"name":"widdst.wid.js","url":"widget.js"}, + {"name":"widdst.boot.js","url":"boot.js"}, {"name":"widdst.settings.js","url":"settings.js"} ], "data": [{"name":"widdst.json"}] diff --git a/apps/widdst/widget.js b/apps/widdst/widget.js index f9a5b7f81..f690cc68f 100644 --- a/apps/widdst/widget.js +++ b/apps/widdst/widget.js @@ -40,26 +40,16 @@ clear(); } } - - function setDst() { - var dstSettings = require('Storage').readJSON('widdst.json',1)||{}; - if (dstSettings.has_dst) { - E.setDST(60*dstSettings.dst_size, 60*dstSettings.tz, dstSettings.dst_start.dow_number, dstSettings.dst_start.dow, - dstSettings.dst_start.month, dstSettings.dst_start.day_offset, 60*dstSettings.dst_start.at, - dstSettings.dst_end.dow_number, dstSettings.dst_end.dow, dstSettings.dst_end.month, dstSettings.dst_end.day_offset, - 60*dstSettings.dst_end.at); - } else { - E.setDST(0,0,0,0,0,0,0,0,0,0,0,0); - } + + // Register ourselves + if (E.setDST) { + WIDGETS["widdst"] = { + area: "tl", + width: 0, + draw: draw + }; + } else { + E.showAlert("Firmware update needed to support Daylight Saving Time"); } - setDst(); - - // Register ourselves - WIDGETS["widdst"] = { - area: "tl", - width: 0, - draw: draw - }; - })() From 7090817fa041ab92ad78965809ba93aae24ab78a Mon Sep 17 00:00:00 2001 From: Hank Date: Tue, 12 Jul 2022 07:12:54 +0200 Subject: [PATCH 057/821] Taking care of the readme files --- apps/drinkcounter/README.md | 5 ++++- apps/hworldclock/README.md | 10 +++++----- apps/widbt_notify/metadata.json | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/drinkcounter/README.md b/apps/drinkcounter/README.md index f1700531b..5638ee066 100644 --- a/apps/drinkcounter/README.md +++ b/apps/drinkcounter/README.md @@ -1,11 +1,14 @@ # Drink Counter -Development still in progress. Counts drinks you had for science. Calculates BAC. +Counts drinks you had for science. Calculates BAC. ## Usage Swipe left/right to select drink. Swipe up/down to add/remove drinks. +## Important notes + +No warranty whatsoever. Use at your own risk. Calculations might be wrong. Do not drink and drive - even if BAC is low. ## Creator diff --git a/apps/hworldclock/README.md b/apps/hworldclock/README.md index 170a2aa8b..25cc368ca 100644 --- a/apps/hworldclock/README.md +++ b/apps/hworldclock/README.md @@ -1,16 +1,16 @@ # Hanks World Clock - See the time in four locations -In addition to the main clock and date in your current location, you can add up to three other locations. Great for travel or remote working. -Additionally we show the sunset/sunrise and seconds for the current location and the day name is shown in your locale. +In addition to the main clock and date in your current location, you can add up to three other locations. Great for travel or remote working. +Additionally we show the sunset/sunrise and seconds for the current location and the day name is shown in your locale. If watch is locked, seconds get refreshed every 10 seconds. ![](hworldclock.png) ## Usage -Location for sun set / rise set with mylocation app. +Location for sun set / rise set with mylocation app. -Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India). +Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India). The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the app store and update. Currently the clock only supports 24 hour time format for the additional time zones. @@ -23,5 +23,5 @@ Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if y Created by Hank. -Based on the great work of "World Clock - 4 time zones". Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock). +Based on the great work of "World Clock - 4 time zones". Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock). And Sun Clock [Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock) \ No newline at end of file diff --git a/apps/widbt_notify/metadata.json b/apps/widbt_notify/metadata.json index 566a51b57..6def70c64 100644 --- a/apps/widbt_notify/metadata.json +++ b/apps/widbt_notify/metadata.json @@ -2,7 +2,7 @@ "id": "widbt_notify", "name": "Bluetooth Widget with Notification", "version": "0.15", - "description": "Show the current Bluetooth connection status in the top right of the clock and vibrate when disconnected.", + "description": "Show the current Bluetooth connection status in the top right of the watch. Optional buzz and/or and hide if disconnected", "icon": "widget.png", "type": "widget", "tags": "widget,bluetooth", From 3207c73b4f5acd866d2ccc50686790ac5715f162 Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 12 Jul 2022 09:25:27 +0200 Subject: [PATCH 058/821] Alternative marker icon (configurable via settings) --- apps/widgps/ChangeLog | 1 + apps/widgps/README.md | 10 +++++++--- apps/widgps/default.json | 1 + apps/widgps/metadata.json | 11 ++++++++--- apps/widgps/settings.js | 28 ++++++++++++++++++++++++++ apps/widgps/widget.js | 41 +++++++++++++++++++++++++++------------ 6 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 apps/widgps/default.json create mode 100644 apps/widgps/settings.js diff --git a/apps/widgps/ChangeLog b/apps/widgps/ChangeLog index 0eb9e5692..d4a569574 100644 --- a/apps/widgps/ChangeLog +++ b/apps/widgps/ChangeLog @@ -4,3 +4,4 @@ 0.04: Show GPS fix status 0.05: Don't poll for GPS status, override setGPSPower handler (fix #1456) 0.06: Periodically update so the always on display does show current GPS fix +0.07: Alternative marker icon (configurable via settings) diff --git a/apps/widgps/README.md b/apps/widgps/README.md index 37e088bcf..e9916fe80 100644 --- a/apps/widgps/README.md +++ b/apps/widgps/README.md @@ -6,12 +6,16 @@ The GPS can quickly run the battery down if it is on all the time so it is useful to know if it has been switched on or not. - Uses Bangle.isGPSOn() -- Shows in grey when the GPS is off -- Shows in amber when the GPS is on but has no fix -- Shows in green when the GPS is on and has a fix +There are two icons which can be used to visualize the GPS/GNSS status: +1. A cross colored depending on the GPS/GNSS status +2. Different place markers depending on GPS/GNSS status + + Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/) Extended by Marco ([myxor](https://github.com/myxor)) + +Place marker icons from [icons8.com](https://icons8.com/icon/set/maps/material-outlined). diff --git a/apps/widgps/default.json b/apps/widgps/default.json new file mode 100644 index 000000000..d1ab3f797 --- /dev/null +++ b/apps/widgps/default.json @@ -0,0 +1 @@ +{"crossIcon": "true"} diff --git a/apps/widgps/metadata.json b/apps/widgps/metadata.json index b135c77bd..144dd6cc6 100644 --- a/apps/widgps/metadata.json +++ b/apps/widgps/metadata.json @@ -1,14 +1,19 @@ { "id": "widgps", "name": "GPS Widget", - "version": "0.06", - "description": "Tiny widget to show the power and fix status of the GPS", + "version": "0.07", + "description": "Tiny widget to show the power and fix status of the GPS/GNSS", "icon": "widget.png", "type": "widget", "tags": "widget,gps", "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "storage": [ - {"name":"widgps.wid.js","url":"widget.js"} + {"name":"widgps.wid.js","url":"widget.js"}, + {"name":"widgps.default.json","url":"default.json"}, + {"name":"widgps.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"widgps.json"} ] } diff --git a/apps/widgps/settings.js b/apps/widgps/settings.js new file mode 100644 index 000000000..4cd9e0b83 --- /dev/null +++ b/apps/widgps/settings.js @@ -0,0 +1,28 @@ +(function(back) { +function writeSettings(key, value) { + var s = require('Storage').readJSON(FILE, true) || {}; + s[key] = value; + require('Storage').writeJSON(FILE, s); + readSettings(); +} + +function readSettings() { + settings = Object.assign( + require('Storage').readJSON("widgps.default.json", true) || {}, + require('Storage').readJSON(FILE, true) || {}); +} + +var FILE = "widgps.json"; +var settings; +readSettings(); + +var mainmenu = { + '' : {'title' : 'GPS widget'}, + '< Back' : back, + "Cross icon" : { + value : !!settings.crossIcon , + onchange : v => { writeSettings("crossIcon", v); } + }, +}; +E.showMenu(mainmenu); +}); diff --git a/apps/widgps/widget.js b/apps/widgps/widget.js index 206096013..0c9753bce 100644 --- a/apps/widgps/widget.js +++ b/apps/widgps/widget.js @@ -1,4 +1,8 @@ (function(){ + let settings = require("Storage").readJSON("widgps.json", 1) || { + crossIcon: true + }; + var interval; // override setGPSPower so we know if GPS is on or off @@ -7,20 +11,10 @@ var isGPSon = oldSetGPSPower(on,id); WIDGETS.gps.draw(); return isGPSon; - } + }; WIDGETS.gps={area:"tr",width:24,draw:function() { g.reset(); - if (Bangle.isGPSOn()) { - const gpsObject = Bangle.getGPSFix(); - if (gpsObject && gpsObject.fix > 0) { - g.setColor("#0F0"); // on and has fix = green - } else { - g.setColor("#FD0"); // on but no fix = amber - } - } else { - g.setColor("#888"); // off = grey - } // check if we need to update the widget periodically if (Bangle.isGPSOn() && interval === undefined) { @@ -31,6 +25,29 @@ clearInterval(interval); interval = undefined; } - g.drawImage(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), this.x, 2+this.y); + if (settings.crossIcon) { + if (Bangle.isGPSOn()) { + const gpsObject = Bangle.getGPSFix(); + if (gpsObject && gpsObject.fix > 0) { + g.setColor("#0F0"); // on and has fix = green + } else { + g.setColor("#FD0"); // on but no fix = amber + } + } else { + g.setColor("#888"); // off = grey + } + g.drawImage(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), this.x, 2+this.y); + } else { // marker icons + if (Bangle.isGPSOn()) { + const gpsObject = Bangle.getGPSFix(); + if (gpsObject && gpsObject.fix > 0) { + g.drawImage(atob("GBjBAP//AAAAAAAAAAAAfgAA/wABw4ADAMAHAMAGPGAGPGAGPGAGPGADAMADAMADgcABgYABw4AAwwAAZgAAfgAAPAAAGAAAAAAAAAAAAAA="), this.x, 2+this.y); // on and has fix + } else { + g.drawImage(atob("GBjBAP//AAAAAAAAAAAIfgAN/4APgeAPAHAPgDAAPBgAfhgA5wwAwwwAwwwwwwAwZgAwfgAYPAAYGAAMAfAOAPAHgfAB/7AAfhAAAAAAAAA="), this.x, 2+this.y); // on but no fix + } + } else { + g.drawImage(atob("GBjBAP//AAAAAAAAAAAAfgAY/wAcQ4AOAMAHAMAHjGAHxGAG4GAGcGADOMADHEADjgABhwABw4AAw8AAZuAAfnAAPDgAGBgAAAAAAAAAAAA="), this.x, 2+this.y); // off + } + } }}; })(); From f532bcb5be616058b056119417841a94e86316ac Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 12 Jul 2022 10:13:58 +0200 Subject: [PATCH 059/821] Use white icons when dark theme is enabled --- apps/widgps/widget.js | 87 +++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/apps/widgps/widget.js b/apps/widgps/widget.js index 0c9753bce..085e4f885 100644 --- a/apps/widgps/widget.js +++ b/apps/widgps/widget.js @@ -1,32 +1,34 @@ -(function(){ - let settings = require("Storage").readJSON("widgps.json", 1) || { - crossIcon: true - }; +(function() { +let settings = + require("Storage").readJSON("widgps.json", 1) || {crossIcon : true}; - var interval; +var interval; - // override setGPSPower so we know if GPS is on or off - var oldSetGPSPower = Bangle.setGPSPower; - Bangle.setGPSPower = function(on,id) { - var isGPSon = oldSetGPSPower(on,id); - WIDGETS.gps.draw(); - return isGPSon; - }; +// override setGPSPower so we know if GPS is on or off +var oldSetGPSPower = Bangle.setGPSPower; +Bangle.setGPSPower = function(on, id) { + var isGPSon = oldSetGPSPower(on, id); + WIDGETS.gps.draw(); + return isGPSon; +}; - WIDGETS.gps={area:"tr",width:24,draw:function() { +WIDGETS.gps = { + area : "tr", + width : 24, + draw : function() { g.reset(); // check if we need to update the widget periodically if (Bangle.isGPSOn() && interval === undefined) { - interval = setInterval(function() { - WIDGETS.gps.draw(WIDGETS.gps); - }, 10*1000); // update every 10 seconds to show gps fix/no fix + interval = setInterval( + function() { WIDGETS.gps.draw(WIDGETS.gps); }, + 10 * 1000); // update every 10 seconds to show gps fix/no fix } else if (!Bangle.isGPSOn() && interval !== undefined) { clearInterval(interval); interval = undefined; } if (settings.crossIcon) { - if (Bangle.isGPSOn()) { + if (Bangle.isGPSOn()) { const gpsObject = Bangle.getGPSFix(); if (gpsObject && gpsObject.fix > 0) { g.setColor("#0F0"); // on and has fix = green @@ -36,18 +38,57 @@ } else { g.setColor("#888"); // off = grey } - g.drawImage(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), this.x, 2+this.y); + g.drawImage( + atob( + "GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), + this.x, 2 + this.y); } else { // marker icons - if (Bangle.isGPSOn()) { + const darkTheme = g.theme.dark; + if (Bangle.isGPSOn()) { const gpsObject = Bangle.getGPSFix(); if (gpsObject && gpsObject.fix > 0) { - g.drawImage(atob("GBjBAP//AAAAAAAAAAAAfgAA/wABw4ADAMAHAMAGPGAGPGAGPGAGPGADAMADAMADgcABgYABw4AAwwAAZgAAfgAAPAAAGAAAAAAAAAAAAAA="), this.x, 2+this.y); // on and has fix + // on and has fix + if (!darkTheme) { + g.drawImage( + atob( + "GBjBAP//AAAAAAAAAAAAfgAA/wABw4ADAMAHAMAGPGAGPGAGPGAGPGADAMADAMADgcABgYABw4AAwwAAZgAAfgAAPAAAGAAAAAAAAAAAAAA="), + this.x, 2 + this.y); + } else { + g.drawImage( + atob( + "GBjBAP////8AAAAAAAAAfgAA/wABw4ADAMAHAMAGPGAGPGAGPGAGPGADAMADAMADgcABgYABw4AAwwAAZgAAfgAAPAAAGAAAAAAAAAAAAAA="), + this.x, 2 + this.y); + } + } else { - g.drawImage(atob("GBjBAP//AAAAAAAAAAAIfgAN/4APgeAPAHAPgDAAPBgAfhgA5wwAwwwAwwwwwwAwZgAwfgAYPAAYGAAMAfAOAPAHgfAB/7AAfhAAAAAAAAA="), this.x, 2+this.y); // on but no fix + // GNSS on but no fix + if (!darkTheme) { + g.drawImage( + atob( + "GBjBAP//AAAAAAAAAAAIfgAN/4APgeAPAHAPgDAAPBgAfhgA5wwAwwwAwwwwwwAwZgAwfgAYPAAYGAAMAfAOAPAHgfAB/7AAfhAAAAAAAAA="), + this.x, 2 + this.y); + } else { + g.drawImage( + atob( + "GBjBAP////8AAAAAAAAIfgAN/4APgeAPAHAPgDAAPBgAfhgA5wwAwwwAwwwwwwAwZgAwfgAYPAAYGAAMAfAOAPAHgfAB/7AAfhAAAAAAAAA="), + this.x, 2 + this.y); + } } } else { - g.drawImage(atob("GBjBAP//AAAAAAAAAAAAfgAY/wAcQ4AOAMAHAMAHjGAHxGAG4GAGcGADOMADHEADjgABhwABw4AAw8AAZuAAfnAAPDgAGBgAAAAAAAAAAAA="), this.x, 2+this.y); // off + // GNSS off + if (!darkTheme) { + g.drawImage( + atob( + "GBjBAP//AAAAAAAAAAAAfgAY/wAcQ4AOAMAHAMAHjGAHxGAG4GAGcGADOMADHEADjgABhwABw4AAw8AAZuAAfnAAPDgAGBgAAAAAAAAAAAA="), + this.x, 2 + this.y); + } else { + g.drawImage( + atob( + "GBjBAP////8AAAAAAAAAfgAY/wAcQ4AOAMAHAMAHjGAHxGAG4GAGcGADOMADHEADjgABhwABw4AAw8AAZuAAfnAAPDgAGBgAAAAAAAAAAAA="), + this.x, 2 + this.y); + } } } - }}; + } +}; })(); From 8fc24cb63c145c91a0dfed296915abe3a8c0a87f Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 12 Jul 2022 10:40:02 +0200 Subject: [PATCH 060/821] Update README.md --- apps/widgps/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/widgps/README.md b/apps/widgps/README.md index e9916fe80..81a24100b 100644 --- a/apps/widgps/README.md +++ b/apps/widgps/README.md @@ -9,6 +9,9 @@ it is useful to know if it has been switched on or not. There are two icons which can be used to visualize the GPS/GNSS status: 1. A cross colored depending on the GPS/GNSS status + - Shows in grey when the GPS is off + - Shows in amber when the GPS is on but has no fix + - Shows in green when the GPS is on and has a fix 2. Different place markers depending on GPS/GNSS status From 67886838f8df9291401452678a8b190d29b7d24e Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 12 Jul 2022 11:25:39 +0200 Subject: [PATCH 061/821] Use 1-bit icons --- apps/widgps/widget.js | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/apps/widgps/widget.js b/apps/widgps/widget.js index 085e4f885..fd9b44484 100644 --- a/apps/widgps/widget.js +++ b/apps/widgps/widget.js @@ -43,50 +43,25 @@ WIDGETS.gps = { "GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), this.x, 2 + this.y); } else { // marker icons - const darkTheme = g.theme.dark; if (Bangle.isGPSOn()) { const gpsObject = Bangle.getGPSFix(); if (gpsObject && gpsObject.fix > 0) { // on and has fix - if (!darkTheme) { - g.drawImage( - atob( - "GBjBAP//AAAAAAAAAAAAfgAA/wABw4ADAMAHAMAGPGAGPGAGPGAGPGADAMADAMADgcABgYABw4AAwwAAZgAAfgAAPAAAGAAAAAAAAAAAAAA="), + g.drawImage( + atob("GBiBAAAAAAAAAAB+AAD/AAHDgAMAwAcAwAY8YAY8YAY8YAY8YAMAwAMAwAOBwAGBgAHDgADDAABmAAB+AAA8AAAYAAAAAAAAAAAAAA=="), this.x, 2 + this.y); - } else { - g.drawImage( - atob( - "GBjBAP////8AAAAAAAAAfgAA/wABw4ADAMAHAMAGPGAGPGAGPGAGPGADAMADAMADgcABgYABw4AAwwAAZgAAfgAAPAAAGAAAAAAAAAAAAAA="), - this.x, 2 + this.y); - } } else { // GNSS on but no fix - if (!darkTheme) { - g.drawImage( - atob( - "GBjBAP//AAAAAAAAAAAIfgAN/4APgeAPAHAPgDAAPBgAfhgA5wwAwwwAwwwwwwAwZgAwfgAYPAAYGAAMAfAOAPAHgfAB/7AAfhAAAAAAAAA="), + g.drawImage( + atob("GBiBAAAAAAAAAAh+AA3/gA+B4A8AcA+AMAA8GAB+GADnDADDDADDDDDDADBmADB+ABg8ABgYAAwB8A4A8AeB8AH/sAB+EAAAAAAAAA=="), this.x, 2 + this.y); - } else { - g.drawImage( - atob( - "GBjBAP////8AAAAAAAAIfgAN/4APgeAPAHAPgDAAPBgAfhgA5wwAwwwAwwwwwwAwZgAwfgAYPAAYGAAMAfAOAPAHgfAB/7AAfhAAAAAAAAA="), - this.x, 2 + this.y); - } } } else { // GNSS off - if (!darkTheme) { - g.drawImage( - atob( - "GBjBAP//AAAAAAAAAAAAfgAY/wAcQ4AOAMAHAMAHjGAHxGAG4GAGcGADOMADHEADjgABhwABw4AAw8AAZuAAfnAAPDgAGBgAAAAAAAAAAAA="), + g.drawImage( + atob("GBiBAAAAAAAAAAB+ABj/ABxDgA4AwAcAwAeMYAfEYAbgYAZwYAM4wAMcQAOOAAGHAAHDgADDwABm4AB+cAA8OAAYGAAAAAAAAAAAAA=="), this.x, 2 + this.y); - } else { - g.drawImage( - atob( - "GBjBAP////8AAAAAAAAAfgAY/wAcQ4AOAMAHAMAHjGAHxGAG4GAGcGADOMADHEADjgABhwABw4AAw8AAZuAAfnAAPDgAGBgAAAAAAAAAAAA="), - this.x, 2 + this.y); - } } } } From efdacb23c0630429f982b2149aa37caae4b4d98c Mon Sep 17 00:00:00 2001 From: Hank Date: Tue, 12 Jul 2022 12:48:30 +0200 Subject: [PATCH 062/821] Adopted for BJS1 --- apps/drinkcounter/ChangeLog | 3 +- apps/drinkcounter/app.js | 147 ++++++++++++++++++++------------ apps/drinkcounter/metadata.json | 4 +- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/apps/drinkcounter/ChangeLog b/apps/drinkcounter/ChangeLog index 69faa7904..d8d174c4c 100644 --- a/apps/drinkcounter/ChangeLog +++ b/apps/drinkcounter/ChangeLog @@ -1,3 +1,4 @@ 0.10: Initial release - still work in progress 0.15: Added settings and calculations -0.20: Added status saving \ No newline at end of file +0.20: Added status saving +0.25: Adopted for Bangle.js 1 - kind of \ No newline at end of file diff --git a/apps/drinkcounter/app.js b/apps/drinkcounter/app.js index 47a4feb13..3e993b898 100644 --- a/apps/drinkcounter/app.js +++ b/apps/drinkcounter/app.js @@ -3,6 +3,7 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); require("Font8x16").add(Graphics); +const BANGLEJS2 = process.env.HWVERSION == 2; const SETTINGSFILE = "drinkcounter.json"; setting = require("Storage").readJSON("setting.json",1); E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ @@ -154,7 +155,9 @@ function updateDrinks(){ } g.setBgColor(g.theme.bg).setColor(g.theme.fg); - g.drawImage(icoReset,145,145); + if (BANGLEJS2) { + g.drawImage(icoReset,145,145); + } drinkStatus.firstDrinkTime = firstDrinkTime; settings_file = require("Storage").open("drinkcounter.status.json", "w"); @@ -184,6 +187,10 @@ function addDrink(){ function removeDrink(){ if (drinks[activeDrink] > 0) drinks[activeDrink] = drinks[activeDrink] - 1; updateDrinks(); + + if ((!BANGLEJS2) && (drinks[0] == 0) && (drinks[1] == 0) && (drinks[2] == 0)) { + resetDrinksFn() + } } function previousDrink(){ @@ -203,61 +210,95 @@ function showDrinks() { g.drawImage(icoShot,80,100); } -function initDragEvents() { - Bangle.on("drag", e => { - if (!drag) { // start dragging - drag = {x: e.x, y: e.y}; - } else if (!e.b) { // released - const dx = e.x-drag.x, dy = e.y-drag.y; - drag = null; - if (Math.abs(dx)>Math.abs(dy)+10) { - // horizontal - if (dx < dy) { - //console.log("left " + dx + " " + dy); - previousDrink(); - } else { - //console.log("right " + dx + " " + dy); - nextDrink(); - } - } else if (Math.abs(dy)>Math.abs(dx)+10) { - // vertical - if (dx < dy) { - //console.log("down " + dx + " " + dy); - removeDrink(); - } else { - //console.log("up " + dx + " " + dy); - addDrink(); - } - } else { - //console.log("tap " + e.x + " " + e.y); - if (e.x > 145 && e.y > 145) { - g.clearRect(0,34,176,176); //Clear - resetDrinks = E.showPrompt("Reset drinks?", { - title: "Confirm", - buttons: { Yes: true, No: false }, - }); - resetDrinks.then((confirm) => { - if (confirm) { - for (let i = 0; i <= maxDrinks; i++) { - drinks[i] = 0; - } - //console.log("reset to default"); - } - //console.log("reset " + confirm); - firstDrinkTime = null; - showDrinks(); - updateDrinks(); - updateTime(); - updateFirstDrinkTime(); - }); - } - } - } -}); +function resetDrinksFn() { + g.clearRect(0,34,176,176); //Clear + resetDrinks = E.showPrompt("Reset drinks?", { + title: "Confirm", + buttons: { Yes: true, No: false }, + }); + resetDrinks.then((confirm) => { + if (confirm) { + for (let i = 0; i <= maxDrinks; i++) { + drinks[i] = 0; + } + //console.log("reset to default"); + } + //console.log("reset " + confirm); + firstDrinkTime = null; + showDrinks(); + updateDrinks(); + updateTime(); + updateFirstDrinkTime(); + }); } -loadMySettings(); +function bjsGetButtonState() { + let btn1 = BTN1.read(); + let btn2 = BTN2.read(); + let btn3 = BTN3.read(); + let btn4 = BTN4.read(); + let btn5 = BTN5.read(); + if (BTN1.read()) { + addDrink(); + } + if (BTN2.read()) { + resetDrinksFn(); + } + if (BTN3.read()) { + removeDrink(); + } + if (BTN4.read()) { + previousDrink(); + } + if (BTN5.read()) { + nextDrink(); + } +} + +function initDragEvents() { + Bangle.on("drag", e => { + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + if (Math.abs(dx)>Math.abs(dy)+10) { + // horizontal + if (dx < dy) { + //console.log("left " + dx + " " + dy); + previousDrink(); + } else { + //console.log("right " + dx + " " + dy); + nextDrink(); + } + } else if (Math.abs(dy)>Math.abs(dx)+10) { + // vertical + if (dx < dy) { + //console.log("down " + dx + " " + dy); + removeDrink(); + } else { + //console.log("up " + dx + " " + dy); + addDrink(); + } + } else { + //console.log("tap " + e.x + " " + e.y); + if (e.x > 145 && e.y > 145) { + resetDrinksFn(); + } + } + } + } +); +} + + + +if (!BANGLEJS2) { + setInterval(bjsGetButtonState, 100); // 10 Hz + } + +loadMySettings(); showDrinks(); diff --git a/apps/drinkcounter/metadata.json b/apps/drinkcounter/metadata.json index 3d1a167f0..2b8d7fe71 100644 --- a/apps/drinkcounter/metadata.json +++ b/apps/drinkcounter/metadata.json @@ -2,14 +2,14 @@ "id": "drinkcounter", "name": "Drink Counter", "shortName": "Drink Counter", - "version": "0.20", + "version": "0.25", "description": "Counts drinks you had for science. Calculates blood alcohol content (BAC)", "allow_emulator":true, "icon": "drinkcounter.png", "type": "app", "tags": "health", "screenshots": [{"url":"screenshot_drnkcnt.png"}], - "supports": ["BANGLEJS2"], + "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", "storage": [ {"name":"drinkcounter.app.js","url":"app.js"}, From 0aa5d76cbfe836e7a558f3eaa20da3c176886136 Mon Sep 17 00:00:00 2001 From: Hank Date: Tue, 12 Jul 2022 12:51:37 +0200 Subject: [PATCH 063/821] Tabs --- apps/drinkcounter/app.js | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/drinkcounter/app.js b/apps/drinkcounter/app.js index 3e993b898..bebb50125 100644 --- a/apps/drinkcounter/app.js +++ b/apps/drinkcounter/app.js @@ -257,37 +257,37 @@ function bjsGetButtonState() { } function initDragEvents() { - Bangle.on("drag", e => { + Bangle.on("drag", e => { if (!drag) { // start dragging - drag = {x: e.x, y: e.y}; + drag = {x: e.x, y: e.y}; } else if (!e.b) { // released - const dx = e.x-drag.x, dy = e.y-drag.y; - drag = null; - if (Math.abs(dx)>Math.abs(dy)+10) { - // horizontal - if (dx < dy) { - //console.log("left " + dx + " " + dy); - previousDrink(); + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + if (Math.abs(dx)>Math.abs(dy)+10) { + // horizontal + if (dx < dy) { + //console.log("left " + dx + " " + dy); + previousDrink(); + } else { + //console.log("right " + dx + " " + dy); + nextDrink(); + } + } else if (Math.abs(dy)>Math.abs(dx)+10) { + // vertical + if (dx < dy) { + //console.log("down " + dx + " " + dy); + removeDrink(); + } else { + //console.log("up " + dx + " " + dy); + addDrink(); + } } else { - //console.log("right " + dx + " " + dy); - nextDrink(); - } - } else if (Math.abs(dy)>Math.abs(dx)+10) { - // vertical - if (dx < dy) { - //console.log("down " + dx + " " + dy); - removeDrink(); - } else { - //console.log("up " + dx + " " + dy); - addDrink(); - } - } else { - //console.log("tap " + e.x + " " + e.y); - if (e.x > 145 && e.y > 145) { - resetDrinksFn(); + //console.log("tap " + e.x + " " + e.y); + if (e.x > 145 && e.y > 145) { + resetDrinksFn(); + } } } - } } ); } From 29e2387e4a5d83d82a1009c872ae8f3e17577adf Mon Sep 17 00:00:00 2001 From: Hank Date: Tue, 12 Jul 2022 12:53:02 +0200 Subject: [PATCH 064/821] Tabs --- apps/drinkcounter/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/drinkcounter/app.js b/apps/drinkcounter/app.js index bebb50125..c908f15b4 100644 --- a/apps/drinkcounter/app.js +++ b/apps/drinkcounter/app.js @@ -257,7 +257,7 @@ function bjsGetButtonState() { } function initDragEvents() { - Bangle.on("drag", e => { + Bangle.on("drag", e => { if (!drag) { // start dragging drag = {x: e.x, y: e.y}; } else if (!e.b) { // released From b580fa2f8462e1db8c3866a59694f8faa70b395e Mon Sep 17 00:00:00 2001 From: BartS23 <10829389+BartS23@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:46:25 +0200 Subject: [PATCH 065/821] Require espruinotools as Espruino To avoid the error below: ReferenceError: Espruino is not defined at parseJS (eval at (/home/romek/Bangle/BangleApps/bin/thumbnailer.js:43:1), :95:5) at eval (eval at (/home/romek/Bangle/BangleApps/bin/thumbnailer.js:43:1), :166:37) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Promise.all (index 0) --- bin/thumbnailer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/thumbnailer.js b/bin/thumbnailer.js index b6862741a..e94ec5229 100755 --- a/bin/thumbnailer.js +++ b/bin/thumbnailer.js @@ -37,7 +37,8 @@ var SETTINGS = { var Const = { }; module = undefined; -eval(require("fs").readFileSync(__dirname + "/../core/lib/espruinotools.js").toString()); +var Espruino = require(__dirname + "/../core/lib/espruinotools.js"); +//eval(require("fs").readFileSync(__dirname + "/../core/lib/espruinotools.js").toString()); eval(require("fs").readFileSync(__dirname + "/../core/js/utils.js").toString()); eval(require("fs").readFileSync(__dirname + "/../core/js/appinfo.js").toString()); var apps = JSON.parse(require("fs").readFileSync(__dirname+"/../apps.json")); From 1a5e7794f67d0af53406c6cbc818c72af813dd03 Mon Sep 17 00:00:00 2001 From: Hank Date: Tue, 12 Jul 2022 13:51:20 +0200 Subject: [PATCH 066/821] BTN improvement --- apps/drinkcounter/app.js | 93 ++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 57 deletions(-) diff --git a/apps/drinkcounter/app.js b/apps/drinkcounter/app.js index c908f15b4..323d9fb41 100644 --- a/apps/drinkcounter/app.js +++ b/apps/drinkcounter/app.js @@ -232,72 +232,51 @@ function resetDrinksFn() { }); } -function bjsGetButtonState() { - - let btn1 = BTN1.read(); - let btn2 = BTN2.read(); - let btn3 = BTN3.read(); - let btn4 = BTN4.read(); - let btn5 = BTN5.read(); - if (BTN1.read()) { - addDrink(); - } - if (BTN2.read()) { - resetDrinksFn(); - } - if (BTN3.read()) { - removeDrink(); - } - if (BTN4.read()) { - previousDrink(); - } - if (BTN5.read()) { - nextDrink(); - } -} function initDragEvents() { - Bangle.on("drag", e => { - if (!drag) { // start dragging - drag = {x: e.x, y: e.y}; - } else if (!e.b) { // released - const dx = e.x-drag.x, dy = e.y-drag.y; - drag = null; - if (Math.abs(dx)>Math.abs(dy)+10) { - // horizontal - if (dx < dy) { - //console.log("left " + dx + " " + dy); - previousDrink(); - } else { - //console.log("right " + dx + " " + dy); - nextDrink(); - } - } else if (Math.abs(dy)>Math.abs(dx)+10) { - // vertical - if (dx < dy) { - //console.log("down " + dx + " " + dy); - removeDrink(); - } else { - //console.log("up " + dx + " " + dy); - addDrink(); - } + +if (BANGLEJS2) { + Bangle.on("drag", e => { + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + if (Math.abs(dx)>Math.abs(dy)+10) { + // horizontal + if (dx < dy) { + //console.log("left " + dx + " " + dy); + previousDrink(); } else { - //console.log("tap " + e.x + " " + e.y); - if (e.x > 145 && e.y > 145) { - resetDrinksFn(); - } + //console.log("right " + dx + " " + dy); + nextDrink(); + } + } else if (Math.abs(dy)>Math.abs(dx)+10) { + // vertical + if (dx < dy) { + //console.log("down " + dx + " " + dy); + removeDrink(); + } else { + //console.log("up " + dx + " " + dy); + addDrink(); + } + } else { + //console.log("tap " + e.x + " " + e.y); + if (e.x > 145 && e.y > 145) { + resetDrinksFn(); } } } -); + }); + } else { + setWatch(addDrink, BTN1, { repeat: true, debounce:50 }); + setWatch(removeDrink, BTN3, { repeat: true, debounce:50 }); + setWatch(previousDrink, BTN4, { repeat: true, debounce:50 }); + setWatch(nextDrink, BTN5, { repeat: true, debounce:50 }); + } } - -if (!BANGLEJS2) { - setInterval(bjsGetButtonState, 100); // 10 Hz - } - loadMySettings(); showDrinks(); From 5f4db2e78c7fff8453e1926d0cedc27c82cec4d7 Mon Sep 17 00:00:00 2001 From: BartS23 <10829389+BartS23@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:14:49 +0200 Subject: [PATCH 067/821] Don't treat "Uncaught Storage Updated!" as error --- bin/thumbnailer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/thumbnailer.js b/bin/thumbnailer.js index e94ec5229..e1d427131 100755 --- a/bin/thumbnailer.js +++ b/bin/thumbnailer.js @@ -97,7 +97,7 @@ function getThumbnail(appId, imageFn) { var firstPixel = rgba32[0]; var blankImage = rgba32.every(col=>col==firstPixel) - if (appLog.indexOf("Uncaught")>=0) + if (appLog.replace("Uncaught Storage Updated!", "").indexOf("Uncaught")>=0) erroredApps.push( { id : app.id, log : appLog } ); if (!blankImage) { From bf2613c440427e4b66d220197d7211df4c82cd14 Mon Sep 17 00:00:00 2001 From: BartS23 <10829389+BartS23@users.noreply.github.com> Date: Wed, 13 Jul 2022 21:23:33 +0200 Subject: [PATCH 068/821] Output app name, reset lcd and intervals flow app sets LCDMode to 120x120 wich will break image export (TypeError: FUNCTION_TABLE[(HEAPU8[(($0 + 52) | 0)] | (HEAPU8[(($0 + 53) | 0)] << 8) | ((HEAPU8[(($0 + 54) | 0)] << 16) | (HEAPU8[(($0 + 55) | 0)] << 24)))] is not a function) Some applications use a very short timeout, which also break the export. --- bin/thumbnailer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/thumbnailer.js b/bin/thumbnailer.js index e1d427131..278def4e4 100755 --- a/bin/thumbnailer.js +++ b/bin/thumbnailer.js @@ -79,7 +79,7 @@ function getThumbnail(appId, imageFn) { settings : SETTINGS, device : { id : DEVICEID } }).then(files => { - console.log("AppInfo returned");//, files); + console.log(`AppInfo returned for ${appId}`);//, files); flashMemory.set(factoryFlashMemory); jsTransmitString("reset()\n"); console.log("Uploading..."); @@ -89,6 +89,7 @@ function getThumbnail(appId, imageFn) { appLog = ""; jsTransmitString(command); console.log("Done."); + jsTransmitString("Bangle.setLCDMode();clearInterval();clearTimeout();\n"); jsStopIdle(); var rgba = new Uint8Array(GFX_WIDTH*GFX_HEIGHT*4); From 0902a3a5ecc82508e9942d94577fda23370e729b Mon Sep 17 00:00:00 2001 From: Hank Date: Thu, 14 Jul 2022 08:17:33 +0200 Subject: [PATCH 069/821] Adopted for BangleJS2 --- apps/counter/ChangeLog | 1 + apps/counter/counter.js | 119 +++++++++++++++++++++++++++---------- apps/counter/metadata.json | 4 +- 3 files changed, 92 insertions(+), 32 deletions(-) diff --git a/apps/counter/ChangeLog b/apps/counter/ChangeLog index f3f1c4eac..8402b3467 100644 --- a/apps/counter/ChangeLog +++ b/apps/counter/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Added decrement and touch functions 0.03: Set color - ensures widgets don't end up coloring the counter's text +0.04: Adopted for BangleJS 2 diff --git a/apps/counter/counter.js b/apps/counter/counter.js index 3e0687944..0054ada6d 100644 --- a/apps/counter/counter.js +++ b/apps/counter/counter.js @@ -1,45 +1,104 @@ var counter = 0; +const BANGLEJS2 = process.env.HWVERSION == 2; + +if (BANGLEJS2) { + var drag; + var y = 45; + var x = 5; +} else { + var y = 100; + var x = 25; +} function updateScreen() { - g.clearRect(0, 50, 250, 150); - g.setColor(0xFFFF); + if (BANGLEJS2) { + g.clearRect(0, 50, 250, 130); + } else { + g.clearRect(0, 50, 250, 150); + } + g.setBgColor(g.theme.bg).setColor(g.theme.fg); g.setFont("Vector",40).setFontAlign(0,0); g.drawString(Math.floor(counter), g.getWidth()/2, 100); - g.drawString('-', 45, 100); - g.drawString('+', 185, 100); + if (!BANGLEJS2) { + g.drawString('-', 45, 100); + g.drawString('+', 185, 100); + } } -// add a count by using BTN1 or BTN5 -setWatch(() => { - counter += 1; - updateScreen(); -}, BTN1, {repeat:true}); +if (BANGLEJS2) { + setWatch(() => { + counter = 0; + updateScreen(); + }, BTN1, {repeat:true}); + Bangle.on("drag", e => { + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + if (Math.abs(dx)>Math.abs(dy)+10) { + // horizontal + if (dx < dy) { + //console.log("left " + dx + " " + dy); + } else { + //console.log("right " + dx + " " + dy); + } + } else if (Math.abs(dy)>Math.abs(dx)+10) { + // vertical + if (dx < dy) { + //console.log("down " + dx + " " + dy); + if (counter > 0) counter -= 1; + updateScreen(); + } else { + //console.log("up " + dx + " " + dy); + counter += 1; + updateScreen(); + } + } else { + //console.log("tap " + e.x + " " + e.y); + } + } + }); + } else { -setWatch(() => { - counter += 1; - updateScreen(); -}, BTN5, {repeat:true}); + // add a count by using BTN1 or BTN5 + setWatch(() => { + counter += 1; + updateScreen(); + }, BTN1, {repeat:true}); + + setWatch(() => { + counter += 1; + updateScreen(); + }, BTN5, {repeat:true}); + + // subtract a count by using BTN3 or BTN4 + setWatch(() => { + if (counter > 0) counter -= 1; + updateScreen(); + }, BTN4, {repeat:true}); + + setWatch(() => { + if (counter > 0) counter -= 1; + updateScreen(); + }, BTN3, {repeat:true}); + + // reset by using BTN2 + setWatch(() => { + counter = 0; + updateScreen(); + }, BTN2, {repeat:true}); +} -// subtract a count by using BTN3 or BTN4 -setWatch(() => { - counter -= 1; - updateScreen(); -}, BTN4, {repeat:true}); - -setWatch(() => { - counter -= 1; - updateScreen(); -}, BTN3, {repeat:true}); - -// reset by using BTN2 -setWatch(() => { - counter = 0; - updateScreen(); -}, BTN2, {repeat:true}); g.clear(1).setFont("6x8"); -g.drawString('Tap right or BTN1 to increase\nTap left or BTN3 to decrease\nPress BTN2 to reset.', 25, 200); +g.setBgColor(g.theme.bg).setColor(g.theme.fg); +if (BANGLEJS2) { + g.drawString('Swipe up to increase\nSwipe down to decrease\nPress button to reset.', x, 100 + y); +} else { + g.drawString('Tap right or BTN1 to increase\nTap left or BTN3 to decrease\nPress BTN2 to reset.', x, 100 + y); +} Bangle.loadWidgets(); Bangle.drawWidgets(); diff --git a/apps/counter/metadata.json b/apps/counter/metadata.json index e455fda95..daba58d39 100644 --- a/apps/counter/metadata.json +++ b/apps/counter/metadata.json @@ -1,11 +1,11 @@ { "id": "counter", "name": "Counter", - "version": "0.03", + "version": "0.04", "description": "Simple counter", "icon": "counter_icon.png", "tags": "tool", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS", "BANGLEJS2"], "screenshots": [{"url":"bangle1-counter-screenshot.png"}], "allow_emulator": true, "storage": [ From b02947dc934294ef949223b5883fad0045bcd80c Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 14 Jul 2022 17:10:59 +0100 Subject: [PATCH 070/821] Allow option to include GPX entries even when there is no corresponding GPS info --- apps/recorder/interface.html | 38 +++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/apps/recorder/interface.html b/apps/recorder/interface.html index 8cf339e85..67803dc39 100644 --- a/apps/recorder/interface.html +++ b/apps/recorder/interface.html @@ -12,9 +12,22 @@ + + + + diff --git a/apps/espruinoterm/metadata.json b/apps/espruinoterm/metadata.json new file mode 100644 index 000000000..25e6183e1 --- /dev/null +++ b/apps/espruinoterm/metadata.json @@ -0,0 +1,20 @@ +{ + "id": "espruinoterm", + "name": "Espruino Terminal", + "shortName": "Espruino Term", + "version": "0.01", + "description": "Send commands to other Espruino devices via the Bluetooth UART interface, and see the result on a VT100 terminal. Customisable commands!", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "tags": "tool,bluetooth", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "interface": "interface.html", + "dependencies": {"textinput":"type"}, + "storage": [ + {"name":"espruinoterm.app.js","url":"app.js"}, + {"name":"espruinoterm.img","url":"app-icon.js","evaluate":true} + ],"data": [ + {"name":"espruinoterm.json","url":"app.json"} + ] +} diff --git a/apps/espruinoterm/screenshot.png b/apps/espruinoterm/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..cce881a378eff5c194a0871b6b94460c9feffb4c GIT binary patch literal 2223 zcmeHJc~BDi7XE=kNow7c5+m2l?WOiiEw5;qNrh=*E=YN8Lvtgy0^A^PCZ%RKg|u9* zqIOFnQ-o5BrjjzsToN-}Leq#yd=_q*&-d4xdGr2x|GzVH&NuVTnKSd9GxN=19`wiR zA&e0K0KI*Ceelb8_g}dZzFc{Nv4~}WB;&E(;LdBb0)UmL_WA5OM2S$9eJHIq)7?`y zx%8&-x0YBo9?CqIH1&n|&emHvW3)AFVrpSIo_HCq2_Auns$Op~(Tq;bmL9d0Le+9J ziBnttvp)VqZ7y;NKAk}4KKAAAeQiR9kA^bM57fHkLMTY_`s-qY5o;mWuN86_caD0U z1lOdLe%=^gk%q*04rFe1-BjBM`JvkkpZZlx+JRVNwSTqpF)WNu1FT5(!9rWn@|f8| z30k(BfzN@BT)c;M&RpH|X*7s|KBidiyxXN>sL(KcZnayYmot}v)k3M3&#gDSE+^^#0{1bAYjd{G<#R-`v%-x`AT=2Z=j7Pv7E^@$mh?Y0!We;i7fY7u5lu)p*blSs7Yhb~2@M^s{Hd7y=6u%0kVbv&wY z->w>N^t_`J`RsvP_DAcY;CavY>$>wDp zz0&8Bo7IM&n&Fx7Xl@`bbs(IS_wDMr`5*7*3nJ#<*=$wJq{C{rRF^rzEbZ)0+lt!y z>eAS@ZMxGAQM7!l*Amj`jKRb&J80FU?^G*MXo0GW^4)d(fUNvWShzAgb~K!L`$1%} z0)JKWu+D;4%317{|5f}+6D@Q<-8K<5^z^N(-u_*b+iOm>SfYe)JI~0+=x^l`G8A_? zgVtUH?cNa7;+1T_qZAV!8Aj+{6;}QNclTq@w;N_eub~19^AI2;U&|0Rgvafj{Furm7!pI%zRaK(2H&4;Mkz^&vbIyM)0FCq4Ao%Q%1| z7G3GnCN&dEP}_Op@zV(gA2uhr;wF&+$vTOSJFam{p`d=;Xe!et(sOm5zfEQ3PJv^p zogd3c8dRxdyGyTLK)t%NQyPkUKS>oUk4SgqQMCl4_v!4BnxNh6}bLW6HIr6d4ZkS=3gO_ zp{D%cWU&i%(^qLXS?h=fehzxl%Fhci&;cm8+nnX|TOi%ViSl3N{-6Ah`~NQsQLku- X*+mIcB9oRs5U_8Lzt0`-$aDV$whsFx literal 0 HcmV?d00001 diff --git a/apps/kbtouch/metadata.json b/apps/kbtouch/metadata.json index f6d6d5228..89d121d63 100644 --- a/apps/kbtouch/metadata.json +++ b/apps/kbtouch/metadata.json @@ -6,10 +6,11 @@ "type":"textinput", "tags": "keyboard", "supports" : ["BANGLEJS2"], - "screenshots": [{"url":"screenshot.png"}], + "screenshots": [{"url":"screenshot.png"}], "readme": "README.md", "storage": [ {"name":"textinput","url":"lib.js"}, {"name":"kbtouch.settings.js","url":"settings.js"} - ] + ], + "sortorder":-1 } From 7c6be92667e7f5adcf9f3209402aac9f603a3074 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 19 Jul 2022 16:10:59 +0100 Subject: [PATCH 092/821] updating web tools --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index c46b4edd2..fcdbe4c46 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit c46b4edd2052d0df37fea41f8839af8175a78ec9 +Subproject commit fcdbe4c46dd3704cfa1e9b1aa13f92228d744aad From d7e9469909e769fcf9b69081682740d09f92e77d Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 19 Jul 2022 17:13:01 +0100 Subject: [PATCH 093/821] Added automatic Espruino programmer tool --- apps/espruinoctrl/README.md | 2 +- apps/espruinoprog/ChangeLog | 1 + apps/espruinoprog/README.md | 42 +++++++++ apps/espruinoprog/app-icon.js | 1 + apps/espruinoprog/app.js | 90 ++++++++++++++++++++ apps/espruinoprog/app.png | Bin 0 -> 9645 bytes apps/espruinoprog/custom.html | 145 ++++++++++++++++++++++++++++++++ apps/espruinoprog/metadata.json | 17 ++++ 8 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 apps/espruinoprog/ChangeLog create mode 100644 apps/espruinoprog/README.md create mode 100644 apps/espruinoprog/app-icon.js create mode 100644 apps/espruinoprog/app.js create mode 100644 apps/espruinoprog/app.png create mode 100644 apps/espruinoprog/custom.html create mode 100644 apps/espruinoprog/metadata.json diff --git a/apps/espruinoctrl/README.md b/apps/espruinoctrl/README.md index a7bca662c..7b2e434e7 100644 --- a/apps/espruinoctrl/README.md +++ b/apps/espruinoctrl/README.md @@ -17,7 +17,7 @@ showing available Espruino devices is popped up. device being connected to. Use this if you want to print data - eg: `print(E.getBattery())` When done, click 'Upload'. Your changes will be saved to local storage -so they'll be remembered next time you upload from the same device.s +so they'll be remembered next time you upload from the same device. ## Usage diff --git a/apps/espruinoprog/ChangeLog b/apps/espruinoprog/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/espruinoprog/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/espruinoprog/README.md b/apps/espruinoprog/README.md new file mode 100644 index 000000000..a8f15d3e9 --- /dev/null +++ b/apps/espruinoprog/README.md @@ -0,0 +1,42 @@ +# Espruino Programmer + +Finds Bluetooth devices with a specific name (eg `Puck.js`), connects and uploads code. Great for programming many devices at once! + +**WARNING:** This will reprogram **any matching Espruino device within range** while +the app is running. Unless you are careful to remove other devices from the area or +turn them off, you could find some of your devices unexpectedly get programmed! + +## Customising + +Click on the Customise button in the app loader to set up the programmer. + +* First you need to choose the kind of devices you want to upload to. This is +the text that should match the Bluetooth advertising name. So `Puck.js` for Puck.js +devices, or `Bangle.js` for Bangles. +* Now paste in the code you want to write to the device. This is automatically +written to flash (`.bootcde`). See https://www.espruino.com/Saving#save-on-send-to-flash- +for more information. +* Now enter the code that should be sent **after** programming. This code +should make the device so it doesn't advertise on Bluetooth with the Bluetooth +name you entered for the first item. It may also help if it indicates to you that +the device is programmed properly. + * You could turn advertising off with `NRF.sleep()` + * You could change the advertising name with `NRF.setAdvertising({},{name:"Ok"});` + * On a Bangle, you could turn it off with `Bangle.off()` +* Finally scroll down and click `Upload` +* Now you can run the new `Programmer` app on the Bangle. + +## Usage + +Just run the app, and as soon as it starts it'll start scanning for +devices to upload to! + +To stop scanning, long-press the button to return to the clock. + +## Notes + +* Right now the Espruino Tools used here are unaware of the device they're writing to, +and as a result they don't use Storage and so the size of the files you can +write to the device are quite limited. You should be find with up to 4k of code. +* Currently, code is not minified before upload (so you need to supply pre-minified + code if you want that) diff --git a/apps/espruinoprog/app-icon.js b/apps/espruinoprog/app-icon.js new file mode 100644 index 000000000..532c60eea --- /dev/null +++ b/apps/espruinoprog/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4cA/4AB7wJB8/5uX+7uUgH41lSKf4AKpMkyQCCggEDAQVtCAMCCNWUx9JufSrmkCJeKqsiytICJtFkWRCJWAEaARCI5BkEoAGBymJ9eSvXkCJZ9JCLI1DyM9uQRLNYWRpRZMR5ARWAwSPCuWR9MuCJZZIgARGPouTCIcSA4OQAoMW7dt2wCEEZECCI1oCJAADrZyBAAcDuQRByOABQkKDAvbtwRBxu24AMFAAcGCIY/B7AQIhpOC3MjKIVsCJe3jYRCwiPEkARBQg227ieDAQO0CJPhCKHJCK1N0ARI28JCIjUDEY4OBzWRfAoRG3ARBygRH3oPBswRB4QjFfAYgCt9pYoJoEkmbCJONCI1ACJGSiQRE7TXDCIuQEYkmCIhpDEYSCFCIj2DCIOTrYRE6ARDAH4AHA")) diff --git a/apps/espruinoprog/app.js b/apps/espruinoprog/app.js new file mode 100644 index 000000000..b939d4007 --- /dev/null +++ b/apps/espruinoprog/app.js @@ -0,0 +1,90 @@ +var uart; // require("ble_uart") +var device; // BluetoothDevice +var uploadTimeout; // a timeout used during upload - if we disconnect, kill this +Bangle.loadWidgets(); + +var json = require("Storage").readJSON("espruinoprog.json",1); +/*var json = { // for example + namePrefix : "Puck.js ", + code : "E.setBootCode('digitalPulse(LED2,1,100);')", + post : "LED.set();NRF.sleep()", +};*/ + +if ("object" != typeof json) { + E.showAlert("JSON not found","Programmer").then(() => load()); + throw new Error("JSON not found"); + // stops execution +} + +// Set up terminal +var R = Bangle.appRect; +var termg = Graphics.createArrayBuffer(R.w, R.h, 1, {msb:true}); +termg.setFont("6x8"); +var term; + +function showTerminal() { + E.showMenu(); // clear anything that was drawn + if (term) term.print(""); // redraw terminal +} + +function scanAndConnect() { + termg.clear(); + term = require("VT100").connect(termg, { + charWidth : 6, + charHeight : 8 + }); + term.print = str => { + for (var i of str) term.char(i); + g.reset().drawImage(termg,R.x,R.y); + }; + term.print(`\r\nScanning...\r\n`); + NRF.requestDevice({ filters: [{ namePrefix: json.namePrefix }] }).then(function(dev) { + term.print(`Found ${dev.name||dev.id.substr(0,17)}\r\n`); + device = dev; + + term.print(`Connect to ${dev.name||dev.id.substr(0,17)}...\r\n`); + device.removeAllListeners(); + device.on('gattserverdisconnected', function(reason) { + if (!uart) return; + term.print(`\r\nDISCONNECTED (${reason})\r\n`); + uart = undefined; + device = undefined; + if (uploadTimeout) clearTimeout(uploadTimeout); + uploadTimeout = undefined; + setTimeout(scanAndConnect, 1000); + }); + require("ble_uart").connect(device).then(function(u) { + uart = u; + term.print("Connected...\r\n"); + uart.removeAllListeners(); + uart.on('data', function(d) { term.print(d); }); + uart.write(json.code+"\n").then(() => { + term.print("\r\nUpload Complete...\r\n"); + // main upload completed - wait a bit + uploadTimeout = setTimeout(function() { + term.print("\r\nFinal Upload...\r\n"); + // now upload the code to run after... + uart.write(json.post+"\n").then(() => { + term.print("\r\nDone.\r\n"); + // now wait and disconnect (if not already done!) + uploadTimeout = setTimeout(function() { + term.print("\r\nDisconnecting...\r\n"); + if (uart) uart.disconnect(); + }, 500); + }); + }, 1000); + }); + }); + }).catch(err => { + if (err.toString().startsWith("No device found")) { + // expected - try again + scanAndConnect(); + } else + term.print(`\r\ERROR ${err.toString()}\r\n`); + }); +} + +// now start +Bangle.drawWidgets(); +showTerminal(); +scanAndConnect(); diff --git a/apps/espruinoprog/app.png b/apps/espruinoprog/app.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b435f04bfbc91e77a1ff7b8895a48357381b12 GIT binary patch literal 9645 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 literal 0 HcmV?d00001 diff --git a/apps/espruinoprog/custom.html b/apps/espruinoprog/custom.html new file mode 100644 index 000000000..c994aaea7 --- /dev/null +++ b/apps/espruinoprog/custom.html @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + +

Upload code to devices with names starting with:

+

+

Enter your program to upload here:

+

+

Enter the code to send after upload here:

+

+

Then click  

+

Click here to reset to defaults.

+ + + + diff --git a/apps/espruinoprog/metadata.json b/apps/espruinoprog/metadata.json new file mode 100644 index 000000000..75fcf5a79 --- /dev/null +++ b/apps/espruinoprog/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "espruinoprog", + "name": "Espruino Programmer", + "shortName": "Programmer", + "version": "0.01", + "description": "Finds Bluetooth devices with a specific name (eg 'Puck.js'), connects and uploads code. Great for programming many devices at once!", + "icon": "app.png", + "tags": "tool,bluetooth", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "custom": "custom.html", + "storage": [ + {"name":"espruinoprog.app.js","url":"app.js"}, + {"name":"espruinoprog.img","url":"app-icon.js","evaluate":true}, + {"name":"espruinoprog.json"} + ] +} From 805b5026da669f534ce76b1fb684f64c2ab913e5 Mon Sep 17 00:00:00 2001 From: qucchia Date: Wed, 20 Jul 2022 15:07:21 +0200 Subject: [PATCH 094/821] Start auto-generating types --- typescript/package-lock.json | 129 ++ typescript/package.json | 3 + typescript/types/bangle.d.ts | 211 -- typescript/types/espruino.d.ts | 59 - typescript/types/globals.d.ts | 216 -- typescript/types/graphics.d.ts | 266 --- typescript/types/i2c.d.ts | 7 - typescript/types/main.d.ts | 3927 +++++++++++++++++++++++++++++++- typescript/types/serial.d.ts | 1 - typescript/types/spi.d.ts | 24 - typescript/types/storage.d.ts | 29 - 11 files changed, 4039 insertions(+), 833 deletions(-) delete mode 100644 typescript/types/bangle.d.ts delete mode 100644 typescript/types/espruino.d.ts delete mode 100644 typescript/types/globals.d.ts delete mode 100644 typescript/types/graphics.d.ts delete mode 100644 typescript/types/i2c.d.ts delete mode 100644 typescript/types/serial.d.ts delete mode 100644 typescript/types/spi.d.ts delete mode 100644 typescript/types/storage.d.ts diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 52be5f98a..8e498e884 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -7,10 +7,89 @@ "": { "name": "Bangle.ts", "version": "0.0.1", + "dependencies": { + "node-fetch": "^3.2.9" + }, "devDependencies": { "typescript": "4.5.2" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.9.tgz", + "integrity": "sha512-/2lI+DBecVvVm9tDhjziTVjo2wmTsSxSk58saUYP0P/fRJ3xxtfMDY24+CKTkfm0Dlhyn3CSXNL0SoRiCZ8Rzg==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/typescript": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", @@ -23,14 +102,64 @@ "engines": { "node": ">=4.2.0" } + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } } }, "dependencies": { + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" + }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.9.tgz", + "integrity": "sha512-/2lI+DBecVvVm9tDhjziTVjo2wmTsSxSk58saUYP0P/fRJ3xxtfMDY24+CKTkfm0Dlhyn3CSXNL0SoRiCZ8Rzg==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "typescript": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "dev": true + }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" } } } diff --git a/typescript/package.json b/typescript/package.json index 8c48ce9a7..1d56c651e 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -9,5 +9,8 @@ "scripts": { "build": "tsc", "build:types": "tsc ./types/main.d.ts" + }, + "dependencies": { + "node-fetch": "^3.2.9" } } diff --git a/typescript/types/bangle.d.ts b/typescript/types/bangle.d.ts deleted file mode 100644 index 1e805dd41..000000000 --- a/typescript/types/bangle.d.ts +++ /dev/null @@ -1,211 +0,0 @@ -type Accel = { - x: number; - y: number; - z: number; - diff: number; - td: number; - mag: number; -}; - -type Mag = { - x: number; - y: number; - z: number; - dx: number; - dy: number; - dz: number; - heading: number; -}; - -type GPS = { - lat: number; - lon: number; - alt: number; - speed: number; - course: number; - time: Date; - stallites: number; - fix: number; - hdop: number; -}; - -type HealthStatus = { - movement: number; - steps: number; - bpm: number; - bpmConfidence: number; -}; - -type BangleOptions = { - wakeOnBTN1: boolean; - wakeOnBTN2: boolean; - wakeOnBTN3: boolean; - wakeOnFaceUp: boolean; - wakeOnTouch: boolean; - wakeOnTwist: boolean; - twistThreshold: number; - twistMaxY: number; - twistTimeout: number; - gestureStartThresh: number; - gestureEndThresh: number; - gestureInactiveCount: number; - gestureMinLength: number; - powerSave: boolean; - lockTimeout: number; - lcdPowerTimeout: number; - backlightTimeout: number; - hrmPollInterval: number; -}; - -type Optional = { - [key in keyof T]?: T[key]; -}; - -type LCDMode = "direct" | "doublebuffered" | "120x120" | "80x80"; - -declare const Bangle: { - accelRd: ((register: any, length: number) => number[]) & - ((register: any) => number); - accelWr: (register: any, data: number[] | number) => void; - appRect: { - x: number; - y: number; - w: number; - h: number; - x2: number; - y2: number; - }; - beep: (time?: number, frequency?: number) => Promise; - buzz: (time?: number, strength?: number) => Promise; - compassRd: ((register: any, length: number) => number[]) & - ((register: any) => number); - compassWr: (register: any, data: number[] | number) => void; - dbg: () => any; - drawWidgets: () => void; - F_BEEPSET: boolean; - getAccel: () => Accel; - getCompass: () => Mag; - getGPSFix: () => GPS; - getHealthStatus: (range?: "current" | "last" | "day") => HealthStatus; - getLCDMode: () => LCDMode; - getLogo: () => string; - getOptions: () => BangleOptions; - getStepCount: () => number; - hrmRd: ((register: any, length: number) => number[]) & - ((register: any) => number); - ioWr: (mask: Pin, isOn: boolean) => void; - isCharging: () => boolean; - isCompassOn: () => boolean; - isGPSOn: () => boolean; - isHRMOn: () => boolean; - isLCDOn: () => boolean; - isLocked: () => boolean; - lcdWr: (register: any, data: number[] | number) => void; - loadWidgets: () => void; - off: () => void; - on: ((event: "accel", listener: (xyz: Accel) => void) => void) & - (( - event: "aiGesture", - listener: ( - gesture: string | number | undefined, - weights: number[] - ) => void - ) => void) & - ((event: "charging", listener: (charging: boolean) => void) => void) & - (( - event: "drag", - listener: (event: { - x: number; - y: number; - dx: number; - dy: number; - b: number; - }) => void - ) => void) & - ((event: "faceUp", listener: (up: boolean) => void) => void) & - ((event: "gesture", listener: (xyz: Int8Array) => void) => void) & - ((event: "GPS", listener: (gps: GPS) => void) => void) & - (( - event: "GPS-raw", - listener: (nmea: string, dataLoss: boolean) => void - ) => void) & - ((event: "health", listener: (info: HealthStatus) => void) => void) & - (( - event: "HRM", - listener: (hrm: { - bpm: number; - confidence: number; - raw: Uint8Array; - }) => void - ) => void) & - (( - event: "HRM-raw", - listener: (hrm: { - raw: number; - filt: number; - bpm: number; - confidence: number; - }) => void - ) => void) & - ((event: "lcdPower", listener: (on: boolean) => void) => void) & - ((event: "lock", listener: (on: boolean) => void) => void) & - ((event: "mag", listener: (mag: Mag) => void) => void) & - ((event: "midnight", listener: () => void) => void) & - (( - event: "pressure", - listener: (info: { - temperature: number; - pressure: number; - altitude: number; - }) => void - ) => void) & - ((event: "step", listener: (up: number) => void) => void) & - ((event: "swipe", listener: (direction: number) => void) => void) & - (( - event: "tap", - listener: (data: { - dir: string; - double: boolean; - x: number; - y: number; - z: number; - }) => void - ) => void) & - (( - event: "touch", - listener: (button: number, xy: { x: number; y: number }) => void - ) => void) & - ((event: "twist", listener: () => void) => void); - project: (latlon: { lat: number; lon: number }) => { x: number; y: number }; - resetCompass: () => void; - setCompassPower: (isOn: boolean, appID: string) => boolean; - setGPSPower: (isOn: boolean, appID: string) => boolean; - setHRMPower: (isOn: boolean, appID: string) => boolean; - setLCDBrightness: (brightness: number) => void; - setLCDMode: (mode?: LCDMode) => void; - setLCDOffset: (y: number) => void; - setLCDPower: (isOn: boolean, appID: string) => boolean; - setLCDTimeout: (timeout: number) => void; - setLocked: (isLocked: boolean) => void; - setOptions: (options: Optional) => void; - setPollInterval: (timeout: number) => void; - setStepCount: (timeout: number) => void; - setUI: ( - type?: - | "updown" - | "leftright" - | "clock" - | "clockupdown" - | { - mode: "custom"; - back?: () => void; - touch?: (n: number, e: number) => void; - swipe?: (dir: number) => void; - drag?: (e: number) => void; - btn?: (n: number) => void; - }, - callback?: (direction: number) => void - ) => void; - showLauncher: () => void; - softOff: () => void; -}; diff --git a/typescript/types/espruino.d.ts b/typescript/types/espruino.d.ts deleted file mode 100644 index c06afcc0e..000000000 --- a/typescript/types/espruino.d.ts +++ /dev/null @@ -1,59 +0,0 @@ -/*~ This file declares the Espruino utility class. - *~ Reference: https://banglejs.com/reference#E - */ - -declare const E: { - showAlert: (() => Promise) & - ((message: string, title?: string) => Promise); - showMenu: (() => undefined) & - ((menu: { - // The "" value includes menu options. - ""?: { - title?: string; - back?: () => void; - selected?: number; - fontHeight?: number; - x?: number; - y?: number; - x2?: number; - y2?: number; - cB?: number; - cF?: number; - cHB?: number; - cHF?: number; - predraw?: (gfx: GraphicsApi) => void; - preflip?: (gfx: GraphicsApi, less: boolean, more: boolean) => void; - } & { - // All the other key-value pairs are menu items. - [key: string]: - | undefined - | (() => void) - | { - value: boolean; - format?: (value: boolean) => string; - onchange?: (value: boolean) => void; - } - | { - value: number; - min?: number; - max?: number; - step?: number; - format?: (value: number) => string; - onchange?: (value: number) => void; - }; - }; - }) => { - draw: () => void; - move: () => void; - select: () => void; - }); - showPrompt: (() => Promise) & - (( - message: string, - options?: { - title?: string; - buttons?: { [key: string]: T }; - img?: string; - } - ) => Promise); -}; diff --git a/typescript/types/globals.d.ts b/typescript/types/globals.d.ts deleted file mode 100644 index 86bdb9abf..000000000 --- a/typescript/types/globals.d.ts +++ /dev/null @@ -1,216 +0,0 @@ -/*~ This file declares the Espruino globals. - *~ Reference: https://banglejs.com/reference#_global - */ - -/* Note: The following don't have to be declared as they are - * already part of regular JavaScript: - * btoa - * clearInterval - * clearTimeout - * decodeURIComponent - * encodeURIComponent - * eval - * Infinity - * isFinite - * isNaN - * NaN - * parseFloat - * parseInt - * setInterval - * setTimeout - */ - -// Pins -declare type Pin = number; -declare type PinMode = - | "analog" - | "input" - | "intupt_pullup" - | "intupt_pulldown" - | "output" - | "opendrain" - | "af_output" - | "af_opendrain"; - -declare const BTN: 24; -declare const BTN1: 24; -declare const BTN2: 22; -declare const BTN3: 23; -declare const BTN4: 11; -declare const BTN5: 16; -declare const VIBRATE: 13; - -declare function getPinMode(pin: Pin): PinMode; -declare function pinMode( - pin: Pin, - mode?: PinMode | "auto", - automatic?: boolean -): void; - -// Analog pins - -/** - * Get the analog value of the given pin. - * This is different to Arduino which only returns an integer between 0 and 1023. - * However only pins connected to an ADC will work (see the datasheet). - * **Note**: if you didn't call `pinMode` beforehand then this function will also reset pin's state to "analog". - * @param {number} pin - The pin to use. - * @returns {number} The analog Value of the Pin between 0 and 1. - * @url https://banglejs.com/reference#l__global_analogRead - */ -declare function analogRead(pin: Pin): number; - -/** - * Set the analog Value of a pin. It will be output using PWM. - * **Note**: if you didn't call pinMode beforehand then this function will also reset pin's state to "output". - * @param {number} pin - The pin to use. - * @param {number} value - A value between 0 and 1. - * @param {object} [options] - Additonal options. - * @param {number} [options.freq] - Pulse frequency in Hz, e.g. 10 - specifying a frequency will force PWM output, even if the pin has a DAC. - * @param {boolean} [options.soft] - If true software PWM is used if hardware is not available. - * @param {boolean} [options.forceSoft] - If true software PWM is used even if hardware PWM or a DAC is available. - */ -declare function analogWrite( - pin: Pin, - value: number, - options?: { freq?: number; soft?: boolean; forceSoft?: boolean } -): void; - -// Digital pins -declare const HIGH: 1; - -declare const LOW: 0; - -declare function digitalPulse(pin: Pin, value: boolean, time: number): void; - -declare function digitalRead(pin: Pin | Pin[]): number; - -declare function digitalWrite(pin: Pin, value: boolean): void; -declare function digitalWrite(pin: Pin[], value: number): void; -declare function digitalWrite( - pin: { - write: (value: boolean) => void; - }, - value: boolean -): void; - -// Other globals -declare function atob(base64Data: string): string; - -declare function btoa(binaryData: string): string; - -declare function changeInterval(id: number, time: number): void; - -declare function dump(): void; - -declare function echo(echoOn: boolean): void; - -declare function edit(funcName: string | Function): void; - -declare function getSerial(): number; - -declare function getTime(): number; - -declare const global: any; //TODO define better - -declare const I2C1: I2C; - -declare function load(file?: string): void; - -declare function peek8(address: number, count?: 1): number; -declare function peek8(address: number, count: number): Uint8Array; - -declare function peek16(address: number, count?: 1): number; -declare function peek16(address: number, count: number): Uint16Array; - -declare function peek32(address: number, count?: 1): number; -declare function peek32(address: number, count: number): Uint32Array; - -declare function poke8(address: number, value: number): void; - -declare function poke16(address: number, value: number): void; - -declare function poke32(address: number, value: number): void; - -declare function print(...args: any[]): void; - -declare const Serial1: Serial; - -declare const Bluetooth: Serial; - -declare const LoopbackA: Serial; - -declare const LoopbackB: Serial; - -declare function require(module: "heatshrink"): { - decompress: (compressedString: string) => string; -}; -declare function require(module: "Storage"): Storage; -declare type Module = "heatshrink" | "Storage"; - -declare function reset(clearFlash?: true): void; - -declare function setInterval(id: any): void; - -declare function setBusyIndicator(pin?: Pin): void; - -declare function setSleepIndicator(pin?: Pin): void; - -declare function setTime(time: number): void; - -type Data = - | number - | string - | Array - | ArrayBuffer - | { data: Data; count: number } - | { callback: () => Data }; - -declare function shiftOut( - pins: Pin | Pin[], - options: { clk: Pin; repeat?: number }, - data: Data -): void; - -declare const SPI1: SPIInstance; - -declare const Terminal: Serial; - -declare function trace(root?: number): void; - -// Watches -declare function clearWatch(id?: number): void; -declare const setWatch: (( - callback: - | ((obj: { state: boolean; time: number; lastTime: number }) => void) - | string, - pin: number, - options?: - | boolean - | number - | { - repeat?: boolean; - edge?: "rising" | "falling" | "both"; - debounce?: number; - irq?: boolean; - } -) => number) & - // If a data option is specified, the callback will also have one. - (( - callback: - | ((obj: { - state: boolean; - time: number; - lastTime: number; - data: any; // TODO: Specify data type - }) => void) - | string, - pin: number, - options?: { - data: number; - repeat?: boolean; - edge?: "rising" | "falling" | "both"; - debounce?: number; - irq?: boolean; - } - ) => number); diff --git a/typescript/types/graphics.d.ts b/typescript/types/graphics.d.ts deleted file mode 100644 index 05f24cc18..000000000 --- a/typescript/types/graphics.d.ts +++ /dev/null @@ -1,266 +0,0 @@ -/*~ This file declares the Graphics class. - *~ Reference: https://banglejs.com/reference#Graphics - */ - -type Image = { - width: number; - height: number; - buffer: ArrayBuffer | string; - bpp?: number; - transparent?: number; - palette?: Uint16Array; -}; - -type Theme = { - fg: number; - bg: number; - fg2: number; - bg2: number; - fgH: number; - bgH: number; - dark: boolean; -}; - -type Layer = { - x: number; - y: number; - image: string | Image | ArrayBuffer; - scale?: number; - rotate?: number; - center?: boolean; - repeat?: boolean; - nobounds?: boolean; -}; - -type GraphicsApi = { - asBMP: () => string | undefined; - - asImage: ((type: "object" | undefined) => object) & - ((type: "string") => string); - - asURL: () => string | undefined; - - blit: (options: { - x1: number; - y1: number; - w: number; - h: number; - x2: number; - y2: number; - setModified?: boolean; - }) => GraphicsApi; - - buffer: ArrayBuffer; - - clear: (reset?: boolean) => GraphicsApi; - - clearRect: ((x1: number, y1: number, x2: number, y2: number) => GraphicsApi) & - ((options: { - x: number; - y: number; - x2: number; - y2: number; - }) => GraphicsApi) & - ((options: { x: number; y: number; w: number; h: number }) => GraphicsApi); - - createArrayBuffer: ( - width: number, - height: number, - bpp: number, - options?: { - zigzag?: boolean; - vertical_byte?: boolean; - msb?: boolean; - interleavex?: boolean; - color_order?: "rgb" | "rbg" | "grb" | "gbr" | "brg" | "bgr"; - } - ) => GraphicsApi; - - createCallback: ( - width: number, - height: number, - bpp: number, - callback: ((x: number, y: number, colour: number) => void) & { - setPixel: (x: number, y: number, colour: number) => void; - fillRect: ( - x1: number, - y1: number, - x2: number, - y2: number, - colour: number - ) => void; - } - ) => GraphicsApi; - - createImage: (str: string) => Image; - - drawCircle: (x: number, y: number, radius: number) => GraphicsApi; - - drawCircleAA: (x: number, y: number, radius: number) => GraphicsApi; - - drawEllipse: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - drawImage: ( - image: string | Image | ArrayBuffer, - xOffset: number, - yOffset: number, - options?: { - rotate?: number; - scale?: number; - frame?: number; - } - ) => GraphicsApi; - - drawImages: ( - layers: [Layer?, Layer?, Layer?], - options?: { - x: number; - y: number; - width: number; - height: number; - } - ) => GraphicsApi; - - drawLine: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - drawLineAA: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - // TODO: Somehow define that poly must have an even number of items - drawPoly: (poly: number[], closed?: boolean) => GraphicsApi; - - drawPolyAA: (poly: number[], closed?: boolean) => GraphicsApi; - - drawRect: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - drawString: ( - str: string | number | boolean, - x: number, - y: number, - solid?: boolean - ) => GraphicsApi; - - dump: () => void; - - fillCircle: (x: number, y: number, radius: number) => GraphicsApi; - - fillEllipse: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - fillPoly: (poly: number[], closed?: boolean) => GraphicsApi; - - fillPolyAA: (poly: number[], closed?: boolean) => GraphicsApi; - - fillRect: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - flip: (all?: boolean) => GraphicsApi; - - getBgColor: () => number; - - getBPP: () => number; - - getColor: () => number; - - getFont: () => string; - - getFontHeight: () => number; - - getFonts: () => string[]; - - getHeight: () => number; - - getInstance: () => GraphicsApi | undefined; - - getModified: ( - reset?: boolean - ) => { x1: number; y1: number; x2: number; y2: number } | undefined; - - getPixel: (x: number, y: number) => number; - - getWidth: () => number; - - imageMetrics: (image: string | GraphicsApi | Image | ArrayBuffer) => - | { - width: number; - height: number; - bpp: number; - transparent: number; - } - | undefined; - - lineTo: (x: number, y: number) => GraphicsApi; - - moveTo: (x: number, y: number) => GraphicsApi; - - quadraticBezier: ( - vertices: [ - x0: number, - y0: number, - x1: number, - y1: number, - x2: number, - y2: number - ], - points?: number - ) => number[]; - - reset: () => GraphicsApi; - - scroll: (x: number, y: number) => GraphicsApi; - - setBgColor: ((color: string) => GraphicsApi) & - ((color: number) => GraphicsApi) & - ((r: number, g: number, b: number) => GraphicsApi); - - setClipRect: (x1: number, y1: number, x2: number, y2: number) => GraphicsApi; - - setColor: ((color: string) => GraphicsApi) & - ((color: number) => GraphicsApi) & - ((r: number, g: number, b: number) => GraphicsApi); - - setFont: (name?: string, size?: number) => GraphicsApi; - - setFontAlign: ( - x: -1 | 0 | 1, - y: -1 | 0 | 1, - rotation?: 0 | 1 | 2 | 3 - ) => GraphicsApi; - - setFontBitmap: () => GraphicsApi; - - setFontCustom: ( - bitmap: ArrayBuffer, - firstChar: number, - width: number | string, - height: number - ) => GraphicsApi; - - setFontVector: (size: number) => GraphicsApi; - - setPixel: ( - x: number, - y: number, - colour: number | string | undefined - ) => GraphicsApi; - - setRotation: (rotation: 0 | 1 | 2 | 3, reflect?: boolean) => GraphicsApi; - - setTheme: (theme: Theme) => GraphicsApi; - - stringMetrics: (str: string) => { width: number; height: number }; - - stringWidth: (str: string) => number; - - theme: Theme; - - toColor: ((color: string) => number) & - ((color: number) => number) & - ((r: number, g: number, b: number) => number); - - transformVertices: ( - verts: number[], - transformation: - | { x?: number; y?: number; scale?: number; rotate?: number } - | [number, number, number, number, number, number] // 2D transformation matrix - ) => number[]; - - wrapString: (str: string, maxWidth: number) => string[]; -}; diff --git a/typescript/types/i2c.d.ts b/typescript/types/i2c.d.ts deleted file mode 100644 index a45ea4bcc..000000000 --- a/typescript/types/i2c.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*~ This file declares the I2c class. - *~ Reference: https://banglejs.com/reference#I2C - */ - -declare class I2C { - constructor(); -} diff --git a/typescript/types/main.d.ts b/typescript/types/main.d.ts index a4c08b312..b299f1ea9 100644 --- a/typescript/types/main.d.ts +++ b/typescript/types/main.d.ts @@ -1,26 +1,3913 @@ -/*~ These are the type declarations for Espruino on the Bangle.JS. - *~ Reference: https://banglejs.com/reference +/* Note: This file was automatically generated. */ + +/* This library allows you to write to Neopixel/WS281x/APA10x/SK6812 LED strips + *

These use a high speed single-wire protocol which needs platform-specific + * implementation on some devices - hence this library to simplify things.

+ * @url http://www.espruino.com/Reference#l_neopixel_undefined */ +declare function neopixel(): void; -/// -/// -/// -/// -/// -/// -/// -/// +/* This library provides TV out capability on the Espruino and Espruino Pico. + * See the Television page for more information. + * @url http://www.espruino.com/Reference#l_tv_undefined + */ +declare function tv(): void; -declare const Graphics: GraphicsApi; -declare const g: GraphicsApi; +/* The NRF class is for controlling functionality of the Nordic nRF51/nRF52 chips. + * Most functionality is related to Bluetooth Low Energy, however there are also some functions related to NFC that apply to NRF52-based devices. + * @url http://www.espruino.com/Reference#NRF + */ +declare function NRF(): void; -type WidgetArea = "tl" | "tr" | "bl" | "br"; -type Widget = { - area: WidgetArea; - width: number; - draw: (this: { x: number; y: number }) => void; -}; +declare namespace NRF { + /* @url http://www.espruino.com/Reference#l_NRF_getSecurityStatus + */ + function getSecurityStatus(): any; -declare const WIDGETS: { [key: string]: Widget }; + /* Get this device's default Bluetooth MAC address. + *

For Puck.js, the last 5 characters of this (eg. ee:ff) + * are used in the device's advertised Bluetooth name.

+ * @url http://www.espruino.com/Reference#l_NRF_getAddress + */ + function getAddress(): any; -declare let exports: any; + /* @url http://www.espruino.com/Reference#l_NRF_setServices + */ + function setServices(data: any, options: any): void; + + /* @url http://www.espruino.com/Reference#l_NRF_setAdvertising + */ + function setAdvertising(data: any, options: any): void; + + /* If a device is connected to Espruino, disconnect from it. + * @url http://www.espruino.com/Reference#l_NRF_disconnect + */ + function disconnect(): void; + + /*

Disable Bluetooth advertising and disconnect from any device that + * connected to Puck.js as a peripheral (this won't affect any devices + * that Puck.js initiated connections to).

+ * This makes Puck.js undiscoverable, so it can't be connected to. + * Use NRF.wake() to wake up and make Puck.js connectable again. + * @url http://www.espruino.com/Reference#l_NRF_sleep + */ + function sleep(): void; + + /*

Enable Bluetooth advertising (this is enabled by default), which + * allows other devices to discover and connect to Puck.js.

+ * Use NRF.sleep() to disable advertising. + * @url http://www.espruino.com/Reference#l_NRF_wake + */ + function wake(): void; + + /*

Restart the Bluetooth softdevice (if there is currently a BLE connection, + * it will queue a restart to be done when the connection closes).

+ *

You shouldn't need to call this function in normal usage. However, Nordic's + * BLE softdevice has some settings that cannot be reset. For example there + * are only a certain number of unique UUIDs. Once these are all used the + * only option is to restart the softdevice to clear them all out.

+ * @url http://www.espruino.com/Reference#l_NRF_restart + */ + function restart(callback: any): void; + + /* Get the battery level in volts (the voltage that the NRF chip is running off of). + *

This is the battery level of the device itself - it has nothing to with any + * device that might be connected.

+ * @url http://www.espruino.com/Reference#l_NRF_getBattery + */ + function getBattery(): number; + + /*

This is just like NRF.setAdvertising, except instead of advertising + * the data, it returns the packet that would be advertised as an array.

+ * @url http://www.espruino.com/Reference#l_NRF_getAdvertisingData + */ + function getAdvertisingData(data: any, options: any): any; + + /* Set the BLE radio transmit power. The default TX power is 0 dBm, and + * @url http://www.espruino.com/Reference#l_NRF_setTxPower + */ + function setTxPower(power: number): void; + + /*

THIS IS DEPRECATED - please use NRF.setConnectionInterval for + * peripheral and NRF.connect(addr, options)/BluetoothRemoteGATTServer.connect(options) + * for central connections.

+ *

This sets the connection parameters - these affect the transfer speed and + * power usage when the device is connected.

+ *
    + *
  • When not low power, the connection interval is between 7.5 and 20ms
  • + *
  • When low power, the connection interval is between 500 and 1000ms
  • + *
+ *

When low power connection is enabled, transfers of data over Bluetooth + * will be very slow, however power usage while connected will be drastically + * decreased.

+ *

This will only take effect after the connection is disconnected and + * re-established.

+ * @url http://www.espruino.com/Reference#l_NRF_setLowPowerConnection + */ + function setLowPowerConnection(lowPower: boolean): void; + + /* Send a USB HID report. HID must first be enabled with NRF.setServices({}, {hid: hid_report}) + * @url http://www.espruino.com/Reference#l_NRF_sendHIDReport + */ + function sendHIDReport(data: any, callback: any): void; + + /* Check if Apple Notification Center Service (ANCS) is currently active on the BLE connection + * @url http://www.espruino.com/Reference#l_NRF_ancsIsActive + */ + function ancsIsActive(): boolean; + + /* Send an ANCS action for a specific Notification UID. Corresponds to posaction/negaction in the 'ANCS' event that was received + * @url http://www.espruino.com/Reference#l_NRF_ancsAction + */ + function ancsAction(uid: number, positive: boolean): void; + + /* Get ANCS info for a notification, eg: + * @url http://www.espruino.com/Reference#l_NRF_ancsGetNotificationInfo + */ + function ancsGetNotificationInfo(uid: number): Promise; + + /* Check if Apple Media Service (AMS) is currently active on the BLE connection + * @url http://www.espruino.com/Reference#l_NRF_amsIsActive + */ + function amsIsActive(): boolean; + + /*

Get Apple Media Service (AMS) info for the current media player. + * "playbackinfo" returns a concatenation of three comma-separated values:

+ *
    + *
  • PlaybackState: a string that represents the integer value of the playback state:
      + *
    • PlaybackStatePaused = 0
    • + *
    • PlaybackStatePlaying = 1
    • + *
    • PlaybackStateRewinding = 2
    • + *
    • PlaybackStateFastForwarding = 3
    • + *
    + *
  • + *
  • PlaybackRate: a string that represents the floating point value of the playback rate.
  • + *
  • ElapsedTime: a string that represents the floating point value of the elapsed time of the current track, in seconds
  • + *
+ * @url http://www.espruino.com/Reference#l_NRF_amsGetPlayerInfo + */ + function amsGetPlayerInfo(id: any): Promise; + + /* Get Apple Media Service (AMS) info for the currently-playing track + * @url http://www.espruino.com/Reference#l_NRF_amsGetTrackInfo + */ + function amsGetTrackInfo(id: any): Promise; + + /* Send an AMS command to an Apple Media Service device to control music playback + * Command is one of play, pause, playpause, next, prev, volup, voldown, repeat, shuffle, skipforward, skipback, like, dislike, bookmark + * @url http://www.espruino.com/Reference#l_NRF_amsCommand + */ + function amsCommand(id: any): void; + + /*

If set to true, whenever a device bonds it will be added to the + * whitelist.

+ *

When set to false, the whitelist is cleared and newly bonded + * devices will not be added to the whitelist.

+ *

Note: This is remembered between reset()s but isn't + * remembered after power-on (you'll have to add it to onInit().

+ * @url http://www.espruino.com/Reference#l_NRF_setWhitelist + */ + function setWhitelist(whitelisting: boolean): void; + + /*

When connected, Bluetooth LE devices communicate at a set interval. + * Lowering the interval (eg. more packets/second) means a lower delay when + * sending data, higher bandwidth, but also more power consumption.

+ *

By default, when connected as a peripheral Espruino automatically adjusts the + * connection interval. When connected it's as fast as possible (7.5ms) but when idle + * for over a minute it drops to 200ms. On continued activity (>1 BLE operation) the + * interval is raised to 7.5ms again.

+ * The options for interval are: + *
    + *
  • undefined / "auto" : (default) automatically adjust connection interval
  • + *
  • 100 : set min and max connection interval to the same number (between 7.5ms and 4000ms)
  • + *
  • {minInterval:20, maxInterval:100} : set min and max connection interval as a range
  • + *
+ *

This configuration is not remembered during a save() - you will have to + * re-set it via onInit.

+ *

Note: If connecting to another device (as Central), you can use + * an extra argument to NRF.connect or BluetoothRemoteGATTServer.connect + * to specify a connection interval.

+ * Note: This overwrites any changes imposed by the deprecated NRF.setLowPowerConnection + * @url http://www.espruino.com/Reference#l_NRF_setConnectionInterval + */ + function setConnectionInterval(interval: any): void; + + /* @url http://www.espruino.com/Reference#l_NRF_startBonding + */ + function startBonding(forceRepair: boolean): any; + +} + +/* The Bluetooth Serial port - used when data is sent or received over Bluetooth Smart on nRF51/nRF52 chips. + * @url http://www.espruino.com/Reference#l__global_Bluetooth + */ +declare const Bluetooth: Serial; + +declare namespace Bluetooth { + /* @url http://www.espruino.com/Reference#l_Bluetooth_setConsole + */ + function setConsole(): void; + +} + +/* Class containing utility functions for the Seeed WIO LTE board + * @url http://www.espruino.com/Reference#WioLTE + */ +declare function WioLTE(): void; + +declare namespace WioLTE { + /* Set the WIO's LED + * @url http://www.espruino.com/Reference#l_WioLTE_LED + */ + function LED(red: number, green: number, blue: number): void; + + /* Set the power of Grove connectors, except for D38 and D39 which are always on. + * @url http://www.espruino.com/Reference#l_WioLTE_setGrovePower + */ + function setGrovePower(onoff: boolean): void; + + /* Turn power to the WIO's LED on or off. + * Turning the LED on won't immediately display a color - that must be done with WioLTE.LED(r,g,b) + * @url http://www.espruino.com/Reference#l_WioLTE_setLEDPower + */ + function setLEDPower(onoff: boolean): void; + + /* @url http://www.espruino.com/Reference#l_WioLTE_D38 + */ + const D38: any; + + /* @url http://www.espruino.com/Reference#l_WioLTE_D20 + */ + const D20: any; + + /* @url http://www.espruino.com/Reference#l_WioLTE_A6 + */ + const A6: any; + + /* @url http://www.espruino.com/Reference#l_WioLTE_I2C + */ + const I2C: any; + + /* @url http://www.espruino.com/Reference#l_WioLTE_UART + */ + const UART: any; + + /* @url http://www.espruino.com/Reference#l_WioLTE_A4 + */ + const A4: any; + +} + +/* This class provides Graphics operations that can be applied to a surface. + * Use Graphics.createXXX to create a graphics object that renders in the way you want. See the Graphics page for more information. + * Note: On boards that contain an LCD, there is a built-in 'LCD' object of type Graphics. For instance to draw a line you'd type: LCD.drawLine(0,0,100,100) + * @url http://www.espruino.com/Reference#Graphics + */ +declare function Graphics(): void; + +type Graphics = { + /* Set the current font + * @url http://www.espruino.com/Reference#l_Graphics_setFont6x15 + */ + setFont6x15: (scale: number) => Graphics; + + /*

On instances of graphics that drive a display with + * an offscreen buffer, calling this function will + * copy the contents of the offscreen buffer to the + * screen.

+ *

Call this when you have drawn something to Graphics + * and you want it shown on the screen.

+ *

If a display does not have an offscreen buffer, + * it may not have a g.flip() method.

+ *

On Bangle.js 1, there are different graphics modes + * chosen with Bangle.setLCDMode(). The default mode + * is unbuffered and in this mode g.flip() does not + * affect the screen contents.

+ *

On some devices, this command will attempt to + * only update the areas of the screen that have + * changed in order to increase speed. If you have + * accessed the Graphics.buffer directly then you + * may need to use Graphics.flip(true) to force + * a full update of the screen.

+ * @url http://www.espruino.com/Reference#l_Graphics_flip + */ + flip: (all: boolean) => void; + + /* The width of this Graphics instance + * @url http://www.espruino.com/Reference#l_Graphics_getWidth + */ + getWidth: () => number; + + /* The height of this Graphics instance + * @url http://www.espruino.com/Reference#l_Graphics_getHeight + */ + getHeight: () => number; + + /* The number of bits per pixel of this Graphics instance + *

Note: Bangle.js 2 behaves a little differently here. The display + * is 3 bit, so getBPP returns 3 and asBMP/asImage/etc return 3 bit images. + * However in order to allow dithering, the colors returned by Graphics.getColor and Graphics.theme + * are actually 16 bits.

+ * @url http://www.espruino.com/Reference#l_Graphics_getBPP + */ + getBPP: () => number; + + /*

Reset the state of Graphics to the defaults (eg. Color, Font, etc) + * that would have been used when Graphics was initialised.

+ * @url http://www.espruino.com/Reference#l_Graphics_reset + */ + reset: () => Graphics; + + /* Clear the LCD with the Background Color + * @url http://www.espruino.com/Reference#l_Graphics_clear + */ + clear: (reset: boolean) => Graphics; + + /* Fill a rectangular area in the Foreground Color + *

On devices with enough memory, you can specify {x,y,x2,y2,r} as the first + * argument, which allows you to draw a rounded rectangle.

+ * @url http://www.espruino.com/Reference#l_Graphics_fillRect + */ + fillRect: (x1: any, y1: number, x2: number, y2: number) => Graphics; + + /* Fill a rectangular area in the Background Color + *

On devices with enough memory, you can specify {x,y,x2,y2,r} as the first + * argument, which allows you to draw a rounded rectangle.

+ * @url http://www.espruino.com/Reference#l_Graphics_clearRect + */ + clearRect: (x1: any, y1: number, x2: number, y2: number) => Graphics; + + /* Draw an unfilled rectangle 1px wide in the Foreground Color + * @url http://www.espruino.com/Reference#l_Graphics_drawRect + */ + drawRect: (x1: any, y1: number, x2: number, y2: number) => Graphics; + + /* Draw a filled circle in the Foreground Color + * @url http://www.espruino.com/Reference#l_Graphics_fillCircle + */ + fillCircle: (x: number, y: number, rad: number) => Graphics; + + /* Draw an unfilled circle 1px wide in the Foreground Color + * @url http://www.espruino.com/Reference#l_Graphics_drawCircle + */ + drawCircle: (x: number, y: number, rad: number) => Graphics; + + /* Draw a circle, centred at (x,y) with radius r in the current foreground color + * @url http://www.espruino.com/Reference#l_Graphics_drawCircleAA + */ + drawCircleAA: (x: number, y: number, r: number) => Graphics; + + /* Draw a filled ellipse in the Foreground Color + * @url http://www.espruino.com/Reference#l_Graphics_fillEllipse + */ + fillEllipse: (x1: number, y1: number, x2: number, y2: number) => Graphics; + + /* Draw an ellipse in the Foreground Color + * @url http://www.espruino.com/Reference#l_Graphics_drawEllipse + */ + drawEllipse: (x1: number, y1: number, x2: number, y2: number) => Graphics; + + /* Get a pixel's color + * @url http://www.espruino.com/Reference#l_Graphics_getPixel + */ + getPixel: (x: number, y: number) => number; + + /* Set a pixel's color + * @url http://www.espruino.com/Reference#l_Graphics_setPixel + */ + setPixel: (x: number, y: number, col: any) => Graphics; + + /* Set the color to use for subsequent drawing operations. + * If just r is specified as an integer, the numeric value will be written directly into a pixel. eg. On a 24 bit Graphics instance you set bright blue with either g.setColor(0,0,1) or g.setColor(0x0000FF). + * A good shortcut to ensure you get white on all platforms is to use g.setColor(-1) + * The mapping is as follows: + *
    + *
  • 32 bit: r,g,b => 0xFFrrggbb
  • + *
  • 24 bit: r,g,b => 0xrrggbb
  • + *
  • 16 bit: r,g,b => 0brrrrrggggggbbbbb (RGB565)
  • + *
  • Other bpp: r,g,b => white if r+g+b > 50%, otherwise black (use r on its own as an integer)
  • + *
+ * If you specified color_order when creating the Graphics instance, r,g and b will be swapped as you specified. + *

Note: On devices with low flash memory, r must be an integer representing the color in the current bit depth. It cannot + * be a floating point value, and g and b are ignored.

+ * @url http://www.espruino.com/Reference#l_Graphics_setColor + */ + setColor: (r: any, g: any, b: any) => Graphics; + + /* Set the background color to use for subsequent drawing operations. + * See Graphics.setColor for more information on the mapping of r, g, and b to pixel values. + *

Note: On devices with low flash memory, r must be an integer representing the color in the current bit depth. It cannot + * be a floating point value, and g and b are ignored.

+ * @url http://www.espruino.com/Reference#l_Graphics_setBgColor + */ + setBgColor: (r: any, g: any, b: any) => Graphics; + + /* Get the color to use for subsequent drawing operations + * @url http://www.espruino.com/Reference#l_Graphics_getColor + */ + getColor: () => number; + + /* Get the background color to use for subsequent drawing operations + * @url http://www.espruino.com/Reference#l_Graphics_getBgColor + */ + getBgColor: () => number; + + /*

This sets the 'clip rect' that subsequent drawing operations are clipped to + * sit between.

+ *

These values are inclusive - eg g.setClipRect(1,0,5,0) will ensure that only + * pixel rows 1,2,3,4,5 are touched on column 0.

+ *

Note: For maximum flexibility on Bangle.js 1, the values here are not range checked. For normal + * use, X and Y should be between 0 and getWidth()-1/getHeight()-1.

+ *

Note: The x/y values here are rotated, so that if Graphics.setRotation is used + * they correspond to the coordinates given to the draw functions, not to the + * physical device pixels.

+ * @url http://www.espruino.com/Reference#l_Graphics_setClipRect + */ + setClipRect: (x1: number, y1: number, x2: number, y2: number) => Graphics; + + /* Make subsequent calls to drawString use the built-in 4x6 pixel bitmapped Font + * It is recommended that you use Graphics.setFont("4x6") for more flexibility. + * @url http://www.espruino.com/Reference#l_Graphics_setFontBitmap + */ + setFontBitmap: () => Graphics; + + /* Make subsequent calls to drawString use a Vector Font of the given height. + * It is recommended that you use Graphics.setFont("Vector", size) for more flexibility. + * @url http://www.espruino.com/Reference#l_Graphics_setFontVector + */ + setFontVector: (size: number) => Graphics; + + /*

Make subsequent calls to drawString use a Custom Font of the given height. See the Fonts page for more + * information about custom fonts and how to create them.

+ * For examples of use, see the font modules. + *

Note: while you can specify the character code of the first character with firstChar, + * the newline character 13 will always be treated as a newline and not rendered.

+ * @url http://www.espruino.com/Reference#l_Graphics_setFontCustom + */ + setFontCustom: (bitmap: any, firstChar: number, width: any, height: number) => Graphics; + + /* Set the alignment for subsequent calls to drawString + * @url http://www.espruino.com/Reference#l_Graphics_setFontAlign + */ + setFontAlign: (x: number, y: number, rotation: number) => Graphics; + + /* Set the font by name. Various forms are available: + *
    + *
  • g.setFont("4x6") - standard 4x6 bitmap font
  • + *
  • g.setFont("Vector:12") - vector font 12px high
  • + *
  • g.setFont("4x6:2") - 4x6 bitmap font, doubled in size
  • + *
  • g.setFont("6x8:2x3") - 6x8 bitmap font, doubled in width, tripled in height
  • + *
+ * You can also use these forms, but they are not recommended: + *
    + *
  • g.setFont("Vector12") - vector font 12px high
  • + *
  • g.setFont("4x6",2) - 4x6 bitmap font, doubled in size
  • + *
+ * g.getFont() will return the current font as a String. + * For a list of available font names, you can use g.getFonts(). + * @url http://www.espruino.com/Reference#l_Graphics_setFont + */ + setFont: (name: any, size: number) => Graphics; + + /* Get the font by name - can be saved and used with Graphics.setFont. + *

Normally this might return something like "4x6", but if a scale + * factor is specified, a colon and then the size is reported, like "4x6:2"

+ *

Note: For custom fonts, Custom is currently + * reported instead of the font name.

+ * @url http://www.espruino.com/Reference#l_Graphics_getFont + */ + getFont: () => string; + + /* Return an array of all fonts currently in the Graphics library. + *

Note: Vector fonts are specified as Vector# where # is the font height. As there + * are effectively infinite fonts, just Vector is included in the list.

+ * @url http://www.espruino.com/Reference#l_Graphics_getFonts + */ + getFonts: () => any[]; + + /* Return the height in pixels of the current font + * @url http://www.espruino.com/Reference#l_Graphics_getFontHeight + */ + getFontHeight: () => number; + + /* Return the size in pixels of a string of text in the current font + * @url http://www.espruino.com/Reference#l_Graphics_stringWidth + */ + stringWidth: (str: any) => number; + + /* Return the width and height in pixels of a string of text in the current font + * @url http://www.espruino.com/Reference#l_Graphics_stringMetrics + */ + stringMetrics: (str: any) => any; + + /* Draw a line between x1,y1 and x2,y2 in the current foreground color + * @url http://www.espruino.com/Reference#l_Graphics_drawLine + */ + drawLine: (x1: number, y1: number, x2: number, y2: number) => Graphics; + + /* Draw a line between x1,y1 and x2,y2 in the current foreground color + * @url http://www.espruino.com/Reference#l_Graphics_drawLineAA + */ + drawLineAA: (x1: number, y1: number, x2: number, y2: number) => Graphics; + + /* Draw a line from the last position of lineTo or moveTo to this position + * @url http://www.espruino.com/Reference#l_Graphics_lineTo + */ + lineTo: (x: number, y: number) => Graphics; + + /* Move the cursor to a position - see lineTo + * @url http://www.espruino.com/Reference#l_Graphics_moveTo + */ + moveTo: (x: number, y: number) => Graphics; + + /* Draw a polyline (lines between each of the points in poly) in the current foreground color + * Note: there is a limit of 64 points (128 XY elements) for polygons + * @url http://www.espruino.com/Reference#l_Graphics_drawPoly + */ + drawPoly: (poly: any, closed: boolean) => Graphics; + + /* Draw an antialiased polyline (lines between each of the points in poly) in the current foreground color + * Note: there is a limit of 64 points (128 XY elements) for polygons + * @url http://www.espruino.com/Reference#l_Graphics_drawPolyAA + */ + drawPolyAA: (poly: any, closed: boolean) => Graphics; + + /* Set the current rotation of the graphics device. + * @url http://www.espruino.com/Reference#l_Graphics_setRotation + */ + setRotation: (rotation: number, reflect: boolean) => Graphics; + + /*

Return the width and height in pixels of an image (either Graphics, Image Object, Image String or ArrayBuffer). Returns + * undefined if image couldn't be decoded.

+ *

frames is also included is the image contains more information than you'd expect for a single bitmap. In + * this case the bitmap might be an animation with multiple frames

+ * @url http://www.espruino.com/Reference#l_Graphics_imageMetrics + */ + imageMetrics: (str: any) => any; + + /*

Return this Graphics object as an Image that can be used with Graphics.drawImage. + * Check out the Graphics reference page + * for more information on images.

+ * Will return undefined if data can't be allocated for the image. + * The image data itself will be referenced rather than copied if: + *
    + *
  • An image object was requested (not string)
  • + *
  • The Graphics instance was created with Graphics.createArrayBuffer
  • + *
  • Is 8 bpp OR the {msb:true} option was given
  • + *
  • No other format options (zigzag/etc) were given
  • + *
+ *

Otherwise data will be copied, which takes up more space and + * may be quite slow.

+ * @url http://www.espruino.com/Reference#l_Graphics_asImage + */ + asImage: (type: any) => any; + + /*

Return the area of the Graphics canvas that has been modified, and optionally clear + * the modified area to 0.

+ * For instance if g.setPixel(10,20) was called, this would return {x1:10, y1:20, x2:10, y2:20} + * @url http://www.espruino.com/Reference#l_Graphics_getModified + */ + getModified: (reset: boolean) => any; + + /*

Scroll the contents of this graphics in a certain direction. The remaining area + * is filled with the background color.

+ *

Note: This uses repeated pixel reads and writes, so will not work on platforms that + * don't support pixel reads.

+ * @url http://www.espruino.com/Reference#l_Graphics_scroll + */ + scroll: (x: number, y: number) => Graphics; + + /* Create a Windows BMP file from this Graphics instance, and return it as a String. + * @url http://www.espruino.com/Reference#l_Graphics_asBMP + */ + asBMP: () => any; + + /* Create a URL of the form data:image/bmp;base64,... that can be pasted into the browser. + * The Espruino Web IDE can detect this data on the console and render the image inline automatically. + * @url http://www.espruino.com/Reference#l_Graphics_asURL + */ + asURL: () => any; + + /* Output this image as a bitmap URL of the form data:image/bmp;base64,.... The Espruino Web IDE will detect this on the console and will render the image inline automatically. + * This is identical to console.log(g.asURL()) - it is just a convenient function for easy debugging and producing screenshots of what is currently in the Graphics instance. + *

Note: This may not work on some bit depths of Graphics instances. It will also not work for the main Graphics instance + * of Bangle.js 1 as the graphics on Bangle.js 1 are stored in write-only memory.

+ * @url http://www.espruino.com/Reference#l_Graphics_dump + */ + dump: () => void; + + /* Calculate the square area under a Bezier curve. + *

x0,y0: start point + * x1,y1: control point + * y2,y2: end point

+ * Max 10 points without start point. + * @url http://www.espruino.com/Reference#l_Graphics_quadraticBezier + */ + quadraticBezier: (arr: any, options: any) => any; + + /* Set the current font + * @url http://www.espruino.com/Reference#l_Graphics_setFont12x20 + */ + setFont12x20: (scale: number) => Graphics; + +} + +declare namespace Graphics { + /*

On devices like Pixl.js or HYSTM boards that contain a built-in display + * this will return an instance of the graphics class that can be used to + * access that display.

+ * Internally, this is stored as a member called gfx inside the 'hiddenRoot'. + * @url http://www.espruino.com/Reference#l_Graphics_getInstance + */ + function getInstance(): any; + + /* Create a Graphics object that renders to an Array Buffer. This will have a field called 'buffer' that can get used to get at the buffer itself + * @url http://www.espruino.com/Reference#l_Graphics_createArrayBuffer + */ + function createArrayBuffer(width: number, height: number, bpp: number, options: any): Graphics; + + /* Create a Graphics object that renders by calling a JavaScript callback function to draw pixels + * @url http://www.espruino.com/Reference#l_Graphics_createCallback + */ + function createCallback(width: number, height: number, bpp: number, callback: any): Graphics; + + /* Create a Graphics object that renders to SDL window (Linux-based devices only) + * @url http://www.espruino.com/Reference#l_Graphics_createSDL + */ + function createSDL(width: number, height: number, bpp: number): Graphics; + +} + +/* A simple VT100 terminal emulator. + *

When data is sent to the Terminal object, Graphics.getInstance() + * is called and if an instance of Graphics is found then characters + * are written to it.

+ * @url http://www.espruino.com/Reference#l__global_Terminal + */ +declare const Terminal: Serial; + +/* Class containing utility functions for accessing IO on the hexagonal badge + * @url http://www.espruino.com/Reference#Badge + */ +declare function Badge(): void; + +declare namespace Badge { + /* Capacitive sense - the higher the capacitance, the higher the number returned. + * Supply a corner number between 1 and 6, and an integer value will be returned that is proportional to the capacitance + * @url http://www.espruino.com/Reference#l_Badge_capSense + */ + function capSense(corner: number): number; + + /*

Return an approximate battery percentage remaining based on + * a normal CR2032 battery (2.8 - 2.2v)

+ * @url http://www.espruino.com/Reference#l_Badge_getBatteryPercentage + */ + function getBatteryPercentage(): number; + + /* Set the LCD's contrast + * @url http://www.espruino.com/Reference#l_Badge_setContrast + */ + function setContrast(c: number): void; + +} + +/* @url http://www.espruino.com/Reference#l_tensorflow_undefined + */ +declare function tensorflow(): void; + +declare namespace tensorflow { + /* @url http://www.espruino.com/Reference#l_tensorflow_create + */ + function create(arenaSize: number, model: any): TFMicroInterpreter; + +} + +/* Class containing an instance of TFMicroInterpreter + * @url http://www.espruino.com/Reference#TFMicroInterpreter + */ +declare function TFMicroInterpreter(): void; + +type TFMicroInterpreter = { + /* @url http://www.espruino.com/Reference#l_TFMicroInterpreter_getInput + */ + getInput: () => EspruinoArrayBufferView; + + /* @url http://www.espruino.com/Reference#l_TFMicroInterpreter_getOutput + */ + getOutput: () => EspruinoArrayBufferView; + + /* @url http://www.espruino.com/Reference#l_TFMicroInterpreter_invoke + */ + invoke: () => void; + +} + +/* Cryptographic functions + * Note: This library is currently only included in builds for boards where there is space. For other boards there is crypto.js which implements SHA1 in JS. + * @url http://www.espruino.com/Reference#l_crypto_undefined + */ +declare function Espruinocrypto(): void; + +declare namespace Espruinocrypto { + /* Class containing AES encryption/decryption + * @url http://www.espruino.com/Reference#l_crypto_AES + */ + const AES: any; + + /* Performs a SHA1 hash and returns the result as a 20 byte ArrayBuffer. + *

Note: On some boards (currently only Espruino Original) there + * isn't space for a fully unrolled SHA1 implementation so a slower + * all-JS implementation is used instead.

+ * @url http://www.espruino.com/Reference#l_crypto_SHA1 + */ + function SHA1(message: any): ArrayBuffer; + + /* Performs a SHA224 hash and returns the result as a 28 byte ArrayBuffer + * @url http://www.espruino.com/Reference#l_crypto_SHA224 + */ + function SHA224(message: any): ArrayBuffer; + + /* Performs a SHA256 hash and returns the result as a 32 byte ArrayBuffer + * @url http://www.espruino.com/Reference#l_crypto_SHA256 + */ + function SHA256(message: any): ArrayBuffer; + + /* Performs a SHA384 hash and returns the result as a 48 byte ArrayBuffer + * @url http://www.espruino.com/Reference#l_crypto_SHA384 + */ + function SHA384(message: any): ArrayBuffer; + + /* Performs a SHA512 hash and returns the result as a 64 byte ArrayBuffer + * @url http://www.espruino.com/Reference#l_crypto_SHA512 + */ + function SHA512(message: any): ArrayBuffer; + + /* Password-Based Key Derivation Function 2 algorithm, using SHA512 + * @url http://www.espruino.com/Reference#l_crypto_PBKDF2 + */ + function PBKDF2(passphrase: any, salt: any, options: any): ArrayBuffer; + +} + +/* Class containing AES encryption/decryption + * Note: This library is currently only included in builds for boards where there is space. For other boards there is crypto.js which implements SHA1 in JS. + * @url http://www.espruino.com/Reference#AES + */ +declare function AES(): void; + +declare namespace AES { + /* @url http://www.espruino.com/Reference#l_AES_encrypt + */ + function encrypt(passphrase: any, key: any, options: any): ArrayBuffer; + + /* @url http://www.espruino.com/Reference#l_AES_decrypt + */ + function decrypt(passphrase: any, key: any, options: any): ArrayBuffer; + +} + +/* Class containing utility functions for the Bangle.js Smart Watch + * @url http://www.espruino.com/Reference#Bangle + */ +declare function Bangle(): void; + +declare namespace Bangle { + /*

This function can be used to adjust the brightness of Bangle.js's display, and + * hence prolong its battery life.

+ *

Due to hardware design constraints, software PWM has to be used which + * means that the display may flicker slightly when Bluetooth is active + * and the display is not at full power.

+ * Power consumption + *
    + *
  • 0 = 7mA
  • + *
  • 0.1 = 12mA
  • + *
  • 0.2 = 18mA
  • + *
  • 0.5 = 28mA
  • + *
  • 0.9 = 40mA (switching overhead)
  • + *
  • 1 = 40mA
  • + *
+ * @url http://www.espruino.com/Reference#l_Bangle_setLCDBrightness + */ + function setLCDBrightness(brightness: number): void; + + /* This function can be used to change the way graphics is handled on Bangle.js. + * Available options for Bangle.setLCDMode are: + *
    + *
  • Bangle.setLCDMode() or Bangle.setLCDMode("direct") (the default) - The drawable area is 240x240 16 bit. Unbuffered, so draw calls take effect immediately. Terminal and vertical scrolling work (horizontal scrolling doesn't).
  • + *
  • Bangle.setLCDMode("doublebuffered") - The drawable area is 240x160 16 bit, terminal and scrolling will not work. g.flip() must be called for draw operations to take effect.
  • + *
  • Bangle.setLCDMode("120x120") - The drawable area is 120x120 8 bit, g.getPixel, terminal, and full scrolling work. Uses an offscreen buffer stored on Bangle.js, g.flip() must be called for draw operations to take effect.
  • + *
  • Bangle.setLCDMode("80x80") - The drawable area is 80x80 8 bit, g.getPixel, terminal, and full scrolling work. Uses an offscreen buffer stored on Bangle.js, g.flip() must be called for draw operations to take effect.
  • + *
+ * You can also call Bangle.setLCDMode() to return to normal, unbuffered "direct" mode. + * @url http://www.espruino.com/Reference#l_Bangle_setLCDMode + */ + function setLCDMode(mode: any): void; + + /* The current LCD mode. + * See Bangle.setLCDMode for examples. + * @url http://www.espruino.com/Reference#l_Bangle_getLCDMode + */ + function getLCDMode(): any; + + /*

This can be used to move the displayed memory area up or down temporarily. It's + * used for displaying notifications while keeping the main display contents + * intact.

+ * @url http://www.espruino.com/Reference#l_Bangle_setLCDOffset + */ + function setLCDOffset(y: number): void; + + /* This function can be used to turn Bangle.js's LCD power saving on or off. + * With power saving off, the display will remain in the state you set it with Bangle.setLCDPower. + * With power saving on, the display will turn on if a button is pressed, the watch is turned face up, or the screen is updated (see Bangle.setOptions for configuration). It'll turn off automatically after the given timeout. + * Note: This function also sets the Backlight and Lock timeout (the time at which the touchscreen/buttons start being ignored). To set both separately, use Bangle.setOptions + * @url http://www.espruino.com/Reference#l_Bangle_setLCDTimeout + */ + function setLCDTimeout(isOn: number): void; + + /*

Set how often the watch should poll for new acceleration/gyro data and kick the Watchdog timer. It isn't + * recommended that you make this interval much larger than 1000ms, but values up to 4000ms are allowed.

+ *

Calling this will set Bangle.setOptions({powerSave: false}) - disabling the dynamic adjustment of + * poll interval to save battery power when Bangle.js is stationary.

+ * @url http://www.espruino.com/Reference#l_Bangle_setPollInterval + */ + function setPollInterval(interval: number): void; + + /* Set internal options used for gestures, etc... + *
    + *
  • wakeOnBTN1 should the LCD turn on when BTN1 is pressed? default = true
  • + *
  • wakeOnBTN2 (Bangle.js 1) should the LCD turn on when BTN2 is pressed? default = true
  • + *
  • wakeOnBTN3 (Bangle.js 1) should the LCD turn on when BTN3 is pressed? default = true
  • + *
  • wakeOnFaceUp should the LCD turn on when the watch is turned face up? default = false
  • + *
  • wakeOnTouch should the LCD turn on when the touchscreen is pressed? default = false
  • + *
  • wakeOnTwist should the LCD turn on when the watch is twisted? default = true
  • + *
  • twistThreshold How much acceleration to register a twist of the watch strap? Can be negative for oppsite direction. default = 800
  • + *
  • twistMaxY Maximum acceleration in Y to trigger a twist (low Y means watch is facing the right way up). default = -800
  • + *
  • twistTimeout How little time (in ms) must a twist take from low->high acceleration? default = 1000
  • + *
  • gestureStartThresh how big a difference before we consider a gesture started? default = sqr(800)
  • + *
  • gestureEndThresh how small a difference before we consider a gesture ended? default = sqr(2000)
  • + *
  • gestureInactiveCount how many samples do we keep after a gesture has ended? default = 4
  • + *
  • gestureMinLength how many samples must a gesture have before we notify about it? default = 10
  • + *
  • powerSave after a minute of not being moved, Bangle.js will change the accelerometer poll interval down to 800ms (10x accelerometer samples). + * On movement it'll be raised to the default 80ms. If Bangle.setPollInterval is used this is disabled, and for it to work the poll interval + * must be either 80ms or 800ms. default = true
  • + *
  • lockTimeout how many milliseconds before the screen locks
  • + *
  • lcdPowerTimeout how many milliseconds before the screen turns off
  • + *
  • backlightTimeout how many milliseconds before the screen's backlight turns off
  • + *
  • hrmPollInterval set the requested poll interval (in milliseconds) for the heart rate monitor. On Bangle.js 2 only 10,20,40,80,160,200 ms are supported, and polling rate may not be exact. The algorithm's filtering is tuned for 20-40ms poll intervals, so higher/lower intervals may effect the reliability of the BPM reading.
  • + *
  • seaLevelPressure (Bangle.js 2) Normally 1013.25 millibars - this is used for calculating altitude with the pressure sensor
  • + *
+ * Where accelerations are used they are in internal units, where 8192 = 1g + * @url http://www.espruino.com/Reference#l_Bangle_setOptions + */ + function setOptions(options: any): void; + + /* Return the current state of options as set by Bangle.setOptions + * @url http://www.espruino.com/Reference#l_Bangle_getOptions + */ + function getOptions(): any; + + /* Also see the Bangle.lcdPower event + * @url http://www.espruino.com/Reference#l_Bangle_isLCDOn + */ + function isLCDOn(): boolean; + + /*

This function can be used to lock or unlock Bangle.js + * (eg whether buttons and touchscreen work or not)

+ * @url http://www.espruino.com/Reference#l_Bangle_setLocked + */ + function setLocked(isLocked: boolean): void; + + /* Also see the Bangle.lock event + * @url http://www.espruino.com/Reference#l_Bangle_isLocked + */ + function isLocked(): boolean; + + /* @url http://www.espruino.com/Reference#l_Bangle_isCharging + */ + function isCharging(): boolean; + + /* Writes a command directly to the ST7735 LCD controller + * @url http://www.espruino.com/Reference#l_Bangle_lcdWr + */ + function lcdWr(cmd: number, data: any): void; + + /* Is the Heart rate monitor powered? + * Set power with Bangle.setHRMPower(...); + * @url http://www.espruino.com/Reference#l_Bangle_isHRMOn + */ + function isHRMOn(): boolean; + + /* Is the GPS powered? + * Set power with Bangle.setGPSPower(...); + * @url http://www.espruino.com/Reference#l_Bangle_isGPSOn + */ + function isGPSOn(): boolean; + + /* Get the last available GPS fix info (or undefined if GPS is off). + * The fix info received is the same as you'd get from the Bangle.GPS event. + * @url http://www.espruino.com/Reference#l_Bangle_getGPSFix + */ + function getGPSFix(): any; + + /* Is the compass powered? + * Set power with Bangle.setCompassPower(...); + * @url http://www.espruino.com/Reference#l_Bangle_isCompassOn + */ + function isCompassOn(): boolean; + + /*

Resets the compass minimum/maximum values. Can be used if the compass isn't + * providing a reliable heading any more.

+ * @url http://www.espruino.com/Reference#l_Bangle_resetCompass + */ + function resetCompass(): void; + + /*

Set the power to the barometer IC. Once enbled, Bangle.pressure events + * are fired each time a new barometer reading is available.

+ * When on, the barometer draws roughly 50uA + * @url http://www.espruino.com/Reference#l_Bangle_setBarometerPower + */ + function setBarometerPower(isOn: boolean, appID: any): boolean; + + /* Is the Barometer powered? + * Set power with Bangle.setBarometerPower(...); + * @url http://www.espruino.com/Reference#l_Bangle_isBarometerOn + */ + function isBarometerOn(): boolean; + + /* Returns the current amount of steps recorded by the step counter + * @url http://www.espruino.com/Reference#l_Bangle_getStepCount + */ + function getStepCount(): number; + + /* Sets the current value of the step counter + * @url http://www.espruino.com/Reference#l_Bangle_setStepCount + */ + function setStepCount(count: number): void; + + /* Get the most recent Magnetometer/Compass reading. Data is in the same format as the Bangle.on('mag', event. + * Returns an {x,y,z,dx,dy,dz,heading} object + *
    + *
  • x/y/z raw x,y,z magnetometer readings
  • + *
  • dx/dy/dz readings based on calibration since magnetometer turned on
  • + *
  • heading in degrees based on calibrated readings (will be NaN if magnetometer hasn't been rotated around 360 degrees)
  • + *
+ *

To get this event you must turn the compass on + * with Bangle.setCompassPower(1).

+ * @url http://www.espruino.com/Reference#l_Bangle_getCompass + */ + function getCompass(): any; + + /* Get the most recent accelerometer reading. Data is in the same format as the Bangle.on('accel', event. + *
    + *
  • x is X axis (left-right) in g
  • + *
  • y is Y axis (up-down) in g
  • + *
  • z is Z axis (in-out) in g
  • + *
  • diff is difference between this and the last reading in g (calculated by comparing vectors, not magnitudes)
  • + *
  • td is the elapsed
  • + *
  • mag is the magnitude of the acceleration in g
  • + *
+ * @url http://www.espruino.com/Reference#l_Bangle_getAccel + */ + function getAccel(): any; + + /* range is one of: + *
    + *
  • undefined or 'current' - health data so far in the last 10 minutes is returned,
  • + *
  • 'last' - health data during the last 10 minutes
  • + *
  • 'day' - the health data so far for the day
  • + *
+ * getHealthStatus returns an object containing: + *
    + *
  • movement is the 32 bit sum of all acc.diff readings since power on (and rolls over). It is the difference in accelerometer values as g*8192
  • + *
  • steps is the number of steps during this period
  • + *
  • bpm the best BPM reading from HRM sensor during this period
  • + *
  • bpmConfidence best BPM confidence (0-100%) during this period
  • + *
+ * @url http://www.espruino.com/Reference#l_Bangle_getHealthStatus + */ + function getHealthStatus(range: any): any; + + /*

Feature flag - If true, this Bangle.js firmware reads setting.json and + * modifies beep & buzz behaviour accordingly (the bootloader + * doesn't need to do it).

+ * @url http://www.espruino.com/Reference#l_Bangle_F_BEEPSET + */ + const F_BEEPSET: boolean; + + /* Reads debug info + * @url http://www.espruino.com/Reference#l_Bangle_dbg + */ + function dbg(): any; + + /* Writes a register on the accelerometer + * @url http://www.espruino.com/Reference#l_Bangle_accelWr + */ + function accelWr(reg: number, data: number): void; + + /* Reads a register from the accelerometer + * Note: On Espruino 2v06 and before this function only returns a number (cnt is ignored). + * @url http://www.espruino.com/Reference#l_Bangle_accelRd + */ + function accelRd(reg: number, cnt: number): any; + + /* Writes a register on the barometer IC + * @url http://www.espruino.com/Reference#l_Bangle_barometerWr + */ + function barometerWr(reg: number, data: number): void; + + /* Reads a register from the barometer IC + * @url http://www.espruino.com/Reference#l_Bangle_barometerRd + */ + function barometerRd(reg: number, cnt: number): any; + + /* Writes a register on the Magnetometer/Compass + * @url http://www.espruino.com/Reference#l_Bangle_compassWr + */ + function compassWr(reg: number, data: number): void; + + /* Read a register on the Magnetometer/Compass + * @url http://www.espruino.com/Reference#l_Bangle_compassRd + */ + function compassRd(reg: number, cnt: number): any; + + /* Writes a register on the Heart rate monitor + * @url http://www.espruino.com/Reference#l_Bangle_hrmWr + */ + function hrmWr(reg: number, data: number): void; + + /* Read a register on the Heart rate monitor + * @url http://www.espruino.com/Reference#l_Bangle_hrmRd + */ + function hrmRd(reg: number, cnt: number): any; + + /* Changes a pin state on the IO expander + * @url http://www.espruino.com/Reference#l_Bangle_ioWr + */ + function ioWr(mask: number, isOn: number): void; + + /*

Perform a Spherical Web Mercator projection + * of latitude and longitude into x and y coordinates, which are roughly + * equivalent to meters from {lat:0,lon:0}.

+ *

This is the formula used for most online mapping and is a good way + * to compare GPS coordinates to work out the distance between them.

+ * @url http://www.espruino.com/Reference#l_Bangle_project + */ + function project(latlong: any): any; + + /* Use the piezo speaker to Beep for a certain time period and frequency + * @url http://www.espruino.com/Reference#l_Bangle_beep + */ + function beep(time: number, freq: number): Promise; + + /* Use the vibration motor to buzz for a certain time period + * @url http://www.espruino.com/Reference#l_Bangle_buzz + */ + function buzz(time: number, strength: number): Promise; + + /* Turn Bangle.js off. It can only be woken by pressing BTN1. + * @url http://www.espruino.com/Reference#l_Bangle_off + */ + function off(): void; + + /*

Turn Bangle.js (mostly) off, but keep the CPU in sleep + * mode until BTN1 is pressed to preserve the RTC (current time).

+ * @url http://www.espruino.com/Reference#l_Bangle_softOff + */ + function softOff(): void; + + /*
    + *
  • On platforms with an LCD of >=8bpp this is 222 x 104 x 2 bits
  • + *
  • Otherwise it's 119 x 56 x 1 bits
  • + *
+ * @url http://www.espruino.com/Reference#l_Bangle_getLogo + */ + function getLogo(): any; + + /*

Load all widgets from flash Storage. Call this once at the beginning + * of your application if you want any on-screen widgets to be loaded.

+ *

They will be loaded into a global WIDGETS array, and + * can be rendered with Bangle.drawWidgets.

+ * @url http://www.espruino.com/Reference#l_Bangle_loadWidgets + */ + function loadWidgets(): void; + + /* @url http://www.espruino.com/Reference#l_Bangle_drawWidgets + */ + function drawWidgets(): void; + + /*

Load the Bangle.js app launcher, which will allow the user + * to select an application to launch.

+ * @url http://www.espruino.com/Reference#l_Bangle_showLauncher + */ + function showLauncher(): void; + + /* @url http://www.espruino.com/Reference#l_Bangle_setUI + */ + function setUI(): void; + + /*

Erase all storage and reload it with the default + * contents.

+ *

This is only available on Bangle.js 2.0. On Bangle.js 1.0 + * you need to use Install Default Apps under the More... tab + * of http://banglejs.com/apps

+ * @url http://www.espruino.com/Reference#l_Bangle_factoryReset + */ + function factoryReset(): void; + + /*

Returns the rectangle on the screen that is currently + * reserved for the app.

+ * @url http://www.espruino.com/Reference#l_Bangle_appRect + */ + const appRect: any; + +} + +/* Class containing micro:bit's utility functions. + * @url http://www.espruino.com/Reference#Microbit + */ +declare function Microbit(): void; + +declare namespace Microbit { + /* The micro:bit's speaker pin + * @url http://www.espruino.com/Reference#l_Microbit_SPEAKER + */ + const SPEAKER: Pin; + + /* The micro:bit's microphone pin + * MIC_ENABLE should be set to 1 before using this + * @url http://www.espruino.com/Reference#l_Microbit_MIC + */ + const MIC: Pin; + + /* The micro:bit's microphone enable pin + * @url http://www.espruino.com/Reference#l_Microbit_MIC_ENABLE + */ + const MIC_ENABLE: Pin; + + /* @url http://www.espruino.com/Reference#l_Microbit_mag + */ + function mag(): any; + + /* @url http://www.espruino.com/Reference#l_Microbit_accel + */ + function accel(): any; + + /* Note: This function is only available on the BBC micro:bit board + * Write the given value to the accelerometer + * @url http://www.espruino.com/Reference#l_Microbit_accelWr + */ + function accelWr(addr: number, data: number): void; + + /* Turn on the accelerometer, and create Microbit.accel and Microbit.gesture events. + *

Note: The accelerometer is currently always enabled - this code + * just responds to interrupts and reads

+ * @url http://www.espruino.com/Reference#l_Microbit_accelOn + */ + function accelOn(): void; + + /* Turn off events from the accelerometer (started with Microbit.accelOn) + * @url http://www.espruino.com/Reference#l_Microbit_accelOff + */ + function accelOff(): void; + + /* Play a waveform on the Micro:bit's speaker + * @url http://www.espruino.com/Reference#l_Microbit_play + */ + function play(waveform: any, samplesPerSecond: any, callback: any): void; + + /* Records sound from the micro:bit's onboard microphone and returns the result + * @url http://www.espruino.com/Reference#l_Microbit_record + */ + function record(samplesPerSecond: any, callback: any, samples: any): void; + +} + +/* This is the File object - it allows you to stream data to and from files (As opposed to the require('fs').readFile(..) style functions that read an entire file). + * To create a File object, you must type var fd = E.openFile('filepath','mode') - see E.openFile for more information. + * Note: If you want to remove an SD card after you have started using it, you must call E.unmountSD() or you may cause damage to the card. + * @url http://www.espruino.com/Reference#File + */ +declare function EspruinoFile(): void; + +type EspruinoFile = { + /* Close an open file. + * @url http://www.espruino.com/Reference#l_File_close + */ + close: () => void; + + /* Write data to a file. + *

Note: By default this function flushes all changes to the + * SD card, which makes it slow (but also safe!). You can use + * E.setFlags({unsyncFiles:1}) to disable this behaviour and + * really speed up writes - but then you must be sure to close + * all files you are writing before power is lost or you will + * cause damage to your SD card's filesystem.

+ * @url http://www.espruino.com/Reference#l_File_write + */ + write: (buffer: any) => number; + + /* Read data in a file in byte size chunks + * @url http://www.espruino.com/Reference#l_File_read + */ + read: (length: number) => any; + + /* Skip the specified number of bytes forward in the file + * @url http://www.espruino.com/Reference#l_File_skip + */ + skip: (nBytes: number) => void; + + /* Seek to a certain position in the file + * @url http://www.espruino.com/Reference#l_File_seek + */ + seek: (nBytes: number) => void; + + /* Pipe this file to a stream (an object with a 'write' method) + * @url http://www.espruino.com/Reference#l_File_pipe + */ + pipe: (destination: any, options: any) => void; + +} + +/* This library handles interfacing with a FAT32 filesystem on an SD card. The API is designed to be similar to node.js's - However Espruino does not currently support asynchronous file IO, so the functions behave like node.js's xxxxSync functions. Versions of the functions with 'Sync' after them are also provided for compatibility. + * To use this, you must type var fs = require('fs') to get access to the library + * See the page on File IO for more information, and for examples on wiring up an SD card if your device doesn't come with one. + * Note: If you want to remove an SD card after you have started using it, you must call E.unmountSD() or you may cause damage to the card. + * @url http://www.espruino.com/Reference#l_fs_undefined + */ +declare function fs(): void; + +declare namespace fs { + /* List all files in the supplied directory, returning them as an array of strings. + * NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version. + * @url http://www.espruino.com/Reference#l_fs_readdir + */ + function readdir(path: any): any; + + /* List all files in the supplied directory, returning them as an array of strings. + * @url http://www.espruino.com/Reference#l_fs_readdirSync + */ + function readdirSync(path: any): any; + + /* Write the data to the given file + * NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version. + * @url http://www.espruino.com/Reference#l_fs_writeFile + */ + function writeFile(path: any, data: any): boolean; + + /* Write the data to the given file + * @url http://www.espruino.com/Reference#l_fs_writeFileSync + */ + function writeFileSync(path: any, data: any): boolean; + + /* Append the data to the given file, created a new file if it doesn't exist + * NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version. + * @url http://www.espruino.com/Reference#l_fs_appendFile + */ + function appendFile(path: any, data: any): boolean; + + /* Append the data to the given file, created a new file if it doesn't exist + * @url http://www.espruino.com/Reference#l_fs_appendFileSync + */ + function appendFileSync(path: any, data: any): boolean; + + /* Read all data from a file and return as a string + * NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version. + * @url http://www.espruino.com/Reference#l_fs_readFile + */ + function readFile(path: any): any; + + /* Read all data from a file and return as a string. + * Note: The size of files you can load using this method is limited by the amount of available RAM. To read files a bit at a time, see the File class. + * @url http://www.espruino.com/Reference#l_fs_readFileSync + */ + function readFileSync(path: any): any; + + /* Delete the given file + * NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version. + * @url http://www.espruino.com/Reference#l_fs_unlink + */ + function unlink(path: any): boolean; + + /* Delete the given file + * @url http://www.espruino.com/Reference#l_fs_unlinkSync + */ + function unlinkSync(path: any): boolean; + + /*

Return information on the given file. This returns an object with the following + * fields:

+ *

size: size in bytes + * dir: a boolean specifying if the file is a directory or not + * mtime: A Date structure specifying the time the file was last modified

+ * @url http://www.espruino.com/Reference#l_fs_statSync + */ + function statSync(path: any): any; + + /* Create the directory + * NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version. + * @url http://www.espruino.com/Reference#l_fs_mkdir + */ + function mkdir(path: any): boolean; + + /* Create the directory + * @url http://www.espruino.com/Reference#l_fs_mkdirSync + */ + function mkdirSync(path: any): boolean; + + /* @url http://www.espruino.com/Reference#l_fs_pipe + */ + function pipe(source: any, destination: any, options: any): void; + +} + +/* Class containing utility functions for Pixl.js + * @url http://www.espruino.com/Reference#Pixl + */ +declare function Pixl(): void; + +declare namespace Pixl { + /* DEPRECATED - Please use E.getBattery() instead. + *

Return an approximate battery percentage remaining based on + * a normal CR2032 battery (2.8 - 2.2v)

+ * @url http://www.espruino.com/Reference#l_Pixl_getBatteryPercentage + */ + function getBatteryPercentage(): number; + + /* Set the LCD's contrast + * @url http://www.espruino.com/Reference#l_Pixl_setContrast + */ + function setContrast(c: number): void; + + /* This function can be used to turn Pixl.js's LCD off or on. + *
    + *
  • With the LCD off, Pixl.js draws around 0.1mA
  • + *
  • With the LCD on, Pixl.js draws around 0.25mA
  • + *
+ * @url http://www.espruino.com/Reference#l_Pixl_setLCDPower + */ + function setLCDPower(isOn: boolean): void; + + /* Writes a command directly to the ST7567 LCD controller + * @url http://www.espruino.com/Reference#l_Pixl_lcdw + */ + function lcdw(c: number): void; + + /* Display a menu on Pixl.js's screen, and set up the buttons to navigate through it. + * DEPRECATED: Use E.showMenu + * @url http://www.espruino.com/Reference#l_Pixl_menu + */ + function menu(menu: any): any; + +} + +/*

Web Bluetooth-style GATT server - get this using NRF.connect(address) + * or NRF.requestDevice(options) and response.gatt.connect

+ * https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattserver + * @url http://www.espruino.com/Reference#BluetoothRemoteGATTServer + */ +declare function BluetoothRemoteGATTServer(): void; + +type BluetoothRemoteGATTServer = { + /*

Disconnect from a previously connected BLE device connected with + * BluetoothRemoteGATTServer.connect - this does not disconnect from something that has + * connected to the Espruino.

+ *

Note: While .disconnect is standard Web Bluetooth, in the spec it + * returns undefined not a Promise for implementation reasons. In Espruino + * we return a Promise to make it easier to detect when Espruino is free + * to connect to something else.

+ * @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTServer_disconnect + */ + disconnect: () => Promise; + + /* See NRF.connect for usage examples. + * @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTServer_getPrimaryService + */ + getPrimaryService: (service: any) => Promise; + + /* @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTServer_getPrimaryServices + */ + getPrimaryServices: () => Promise; + +} + +/* Web Bluetooth-style GATT service - get this using BluetoothRemoteGATTServer.getPrimaryService(s) + * https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice + * @url http://www.espruino.com/Reference#BluetoothRemoteGATTService + */ +declare function BluetoothRemoteGATTService(): void; + +type BluetoothRemoteGATTService = { + /* See NRF.connect for usage examples. + * @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTService_getCharacteristic + */ + getCharacteristic: (characteristic: any) => Promise; + + /* @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTService_getCharacteristics + */ + getCharacteristics: () => Promise; + +} + +/* Web Bluetooth-style GATT characteristic - get this using BluetoothRemoteGATTService.getCharacteristic(s) + * https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristic + * @url http://www.espruino.com/Reference#BluetoothRemoteGATTCharacteristic + */ +declare function BluetoothRemoteGATTCharacteristic(): void; + +type BluetoothRemoteGATTCharacteristic = { + /* Stop notifications (that were requested with BluetoothRemoteGATTCharacteristic.startNotifications) + * @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTCharacteristic_stopNotifications + */ + stopNotifications: () => Promise; + +} + +/* This class exists in order to interface Espruino with fast-moving trigger wheels. Trigger wheels are physical discs with evenly spaced teeth cut into them, and often with one or two teeth next to each other missing. A sensor sends a signal whenever a tooth passed by, and this allows a device to measure not only RPM, but absolute position. + * This class is currently in testing - it is NOT AVAILABLE on normal boards. + * @url http://www.espruino.com/Reference#Trig + */ +declare function Trig(): void; + +declare namespace Trig { + /* Get the position of the trigger wheel at the given time (from getTime) + * @url http://www.espruino.com/Reference#l_Trig_getPosAtTime + */ + function getPosAtTime(time: number): number; + + /* Initialise the trigger class + * @url http://www.espruino.com/Reference#l_Trig_setup + */ + function setup(pin: Pin, options: any): void; + + /* Set a trigger for a certain point in the cycle + * @url http://www.espruino.com/Reference#l_Trig_setTrigger + */ + function setTrigger(num: number, pos: number, pins: any, pulseLength: number): void; + + /* Disable a trigger + * @url http://www.espruino.com/Reference#l_Trig_killTrigger + */ + function killTrigger(num: number): void; + + /* Get the current state of a trigger + * @url http://www.espruino.com/Reference#l_Trig_getTrigger + */ + function getTrigger(num: number): any; + + /* Get the RPM of the trigger wheel + * @url http://www.espruino.com/Reference#l_Trig_getRPM + */ + function getRPM(): number; + + /* Get the current error flags from the trigger wheel - and zero them + * @url http://www.espruino.com/Reference#l_Trig_getErrors + */ + function getErrors(): number; + + /* Get the current error flags from the trigger wheel - and zero them + * @url http://www.espruino.com/Reference#l_Trig_getErrorArray + */ + function getErrorArray(): any; + +} + +/* Class containing Puck.js's utility functions. + * @url http://www.espruino.com/Reference#Puck + */ +declare function Puck(): void; + +declare namespace Puck { + /* Turn on the magnetometer, take a single reading, and then turn it off again. + *

An object of the form {x,y,z} is returned containing magnetometer readings. + * Due to residual magnetism in the Puck and magnetometer itself, with + * no magnetic field the Puck will not return {x:0,y:0,z:0}.

+ *

Instead, it's up to you to figure out what the 'zero value' is for your + * Puck in your location and to then subtract that from the value returned. If + * you're not trying to measure the Earth's magnetic field then it's a good idea + * to just take a reading at startup and use that.

+ *

With the aerial at the top of the board, the y reading is vertical, x is + * horizontal, and z is through the board.

+ *

Readings are in increments of 0.1 micro Tesla (uT). The Earth's magnetic field + * varies from around 25-60 uT, so the reading will vary by 250 to 600 depending + * on location.

+ * @url http://www.espruino.com/Reference#l_Puck_mag + */ + function mag(): any; + + /* Turn on the magnetometer, take a single temperature reading from the MAG3110 chip, and then turn it off again. + * (If the magnetometer is already on, this just returns the last reading obtained) + * E.getTemperature() uses the microcontroller's temperature sensor, but this uses the magnetometer's. + *

The reading obtained is an integer (so no decimal places), but the sensitivity is factory trimmed. to 1°C, however the temperature + * offset isn't - so absolute readings may still need calibrating.

+ * @url http://www.espruino.com/Reference#l_Puck_magTemp + */ + function magTemp(): number; + + /* Turn the magnetometer off + * @url http://www.espruino.com/Reference#l_Puck_magOff + */ + function magOff(): void; + + /* Writes a register on the LIS3MDL / MAX3110 Magnetometer. Can be used for configuring advanced functions. + *

Check out the Puck.js page on the magnetometer + * for more information and links to modules that use this function.

+ * @url http://www.espruino.com/Reference#l_Puck_magWr + */ + function magWr(reg: number, data: number): void; + + /* Reads a register from the LIS3MDL / MAX3110 Magnetometer. Can be used for configuring advanced functions. + *

Check out the Puck.js page on the magnetometer + * for more information and links to modules that use this function.

+ * @url http://www.espruino.com/Reference#l_Puck_magRd + */ + function magRd(reg: number): number; + + /* On Puck.js v2.0 this will use the on-board PCT2075TP temperature sensor, but on Puck.js the less accurate on-chip Temperature sensor is used. + * @url http://www.espruino.com/Reference#l_Puck_getTemperature + */ + function getTemperature(): number; + + /* Turn the accelerometer off after it has been turned on by Puck.accelOn(). + *

Check out the Puck.js page on the accelerometer + * for more information.

+ * @url http://www.espruino.com/Reference#l_Puck_accelOff + */ + function accelOff(): void; + + /* Turn on the accelerometer, take a single reading, and then turn it off again. + * The values reported are the raw values from the chip. In normal configuration: + *
    + *
  • accelerometer: full-scale (32768) is 4g, so you need to divide by 8192 to get correctly scaled values
  • + *
  • gyro: full-scale (32768) is 245 dps, so you need to divide by 134 to get correctly scaled values
  • + *
+ * If taking more than one reading, we'd suggest you use Puck.accelOn() and the Puck.accel event. + * @url http://www.espruino.com/Reference#l_Puck_accel + */ + function accel(): any; + + /* Writes a register on the LSM6DS3TR-C Accelerometer. Can be used for configuring advanced functions. + *

Check out the Puck.js page on the accelerometer + * for more information and links to modules that use this function.

+ * @url http://www.espruino.com/Reference#l_Puck_accelWr + */ + function accelWr(reg: number, data: number): void; + + /* Reads a register from the LSM6DS3TR-C Accelerometer. Can be used for configuring advanced functions. + *

Check out the Puck.js page on the accelerometer + * for more information and links to modules that use this function.

+ * @url http://www.espruino.com/Reference#l_Puck_accelRd + */ + function accelRd(reg: number): number; + + /*

Transmit the given set of IR pulses - data should be an array of pulse times + * in milliseconds (as [on, off, on, off, on, etc]).

+ *

For example Puck.IR(pulseTimes) - see http://www.espruino.com/Puck.js+Infrared + * for a full example.

+ *

You can also attach an external LED to Puck.js, in which case + * you can just execute Puck.IR(pulseTimes, led_cathode, led_anode)

+ *

It is also possible to just supply a single pin for IR transmission + * with Puck.IR(pulseTimes, led_anode) (on 2v05 and above).

+ * @url http://www.espruino.com/Reference#l_Puck_IR + */ + function IR(data: any, cathode: Pin, anode: Pin): void; + + /* Capacitive sense - the higher the capacitance, the higher the number returned. + *

If called without arguments, a value depending on the capacitance of what is + * attached to pin D11 will be returned. If you attach a length of wire to D11, + * you'll be able to see a higher value returned when your hand is near the wire + * than when it is away.

+ *

You can also supply pins to use yourself, however if you do this then + * the TX pin must be connected to RX pin and sense plate via a roughly 1MOhm + * resistor.

+ *

When not supplying pins, Puck.js uses an internal resistor between D12(tx) + * and D11(rx).

+ * @url http://www.espruino.com/Reference#l_Puck_capSense + */ + function capSense(tx: Pin, rx: Pin): number; + + /* Return a light value based on the light the red LED is seeing. + *

Note: If called more than 5 times per second, the received light value + * may not be accurate.

+ * @url http://www.espruino.com/Reference#l_Puck_light + */ + function light(): number; + + /* DEPRECATED - Please use E.getBattery() instead. + *

Return an approximate battery percentage remaining based on + * a normal CR2032 battery (2.8 - 2.2v).

+ * @url http://www.espruino.com/Reference#l_Puck_getBatteryPercentage + */ + function getBatteryPercentage(): number; + + /*

Run a self-test, and return true for a pass. This checks for shorts + * between pins, so your Puck shouldn't have anything connected to it.

+ *

Note: This self-test auto starts if you hold the button on your Puck + * down while inserting the battery, leave it pressed for 3 seconds (while + * the green LED is lit) and release it soon after all LEDs turn on. 5 + * red blinks is a fail, 5 green is a pass.

+ *

If the self test fails, it'll set the Puck.js Bluetooth advertising name + * to Puck.js !ERR where ERR is a 3 letter error code.

+ * @url http://www.espruino.com/Reference#l_Puck_selfTest + */ + function selfTest(): boolean; + +} + +/* @url http://www.espruino.com/Reference#l_CC3000_undefined + */ +declare function CC3000(): void; + +declare namespace CC3000 { + /* Initialise the CC3000 and return a WLAN object + * @url http://www.espruino.com/Reference#l_CC3000_connect + */ + function connect(spi: any, cs: Pin, en: Pin, irq: Pin): WLAN; + +} + +/* An instantiation of a WiFi network adaptor + * @url http://www.espruino.com/Reference#WLAN + */ +declare function WLAN(): void; + +type WLAN = { + /* Connect to a wireless network + * @url http://www.espruino.com/Reference#l_WLAN_connect + */ + connect: (ap: any, key: any, callback: any) => boolean; + + /* Completely uninitialise and power down the CC3000. After this you'll have to use require("CC3000").connect() again. + * @url http://www.espruino.com/Reference#l_WLAN_disconnect + */ + disconnect: () => void; + + /* Completely uninitialise and power down the CC3000, then reconnect to the old access point. + * @url http://www.espruino.com/Reference#l_WLAN_reconnect + */ + reconnect: () => void; + + /* Get the current IP address + * @url http://www.espruino.com/Reference#l_WLAN_getIP + */ + getIP: () => any; + + /* Set the current IP address for get an IP from DHCP (if no options object is specified). + * Note: Changes are written to non-volatile memory, but will only take effect after calling wlan.reconnect() + * @url http://www.espruino.com/Reference#l_WLAN_setIP + */ + setIP: (options: any) => boolean; + +} + +/*

This library implements a telnet console for the Espruino interpreter. It requires a network + * connection, e.g. Wifi, and currently only functions on the ESP8266 and on Linux . It uses + * port 23 on the ESP8266 and port 2323 on Linux.

+ * Note: To enable on Linux, run ./espruino --telnet + * @url http://www.espruino.com/Reference#l_TelnetServer_undefined + */ +declare function TelnetServer(): void; + +declare namespace TelnetServer { + /* @url http://www.espruino.com/Reference#l_TelnetServer_setOptions + */ + function setOptions(options: any): void; + +} + +/* Class containing utility functions for the ESP8266 + * @url http://www.espruino.com/Reference#ESP8266 + */ +declare function ESP8266(): void; + +declare namespace ESP8266 { + /* DEPRECATED - please use Wifi.ping instead. + * Perform a network ping request. The parameter can be either a String or a numeric IP address. + * @url http://www.espruino.com/Reference#l_ESP8266_ping + */ + function ping(ipAddr: any, pingCallback: any): void; + + /* Perform a hardware reset/reboot of the esp8266. + * @url http://www.espruino.com/Reference#l_ESP8266_reboot + */ + function reboot(): void; + + /* At boot time the esp8266's firmware captures the cause of the reset/reboot. This function returns this information in an object with the following fields: + *
    + *
  • reason: "power on", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", or "reset pin"
  • + *
  • exccause: exception cause
  • + *
  • epc1, epc2, epc3: instruction pointers
  • + *
  • excvaddr: address being accessed
  • + *
  • depc: (?)
  • + *
+ * @url http://www.espruino.com/Reference#l_ESP8266_getResetInfo + */ + function getResetInfo(): RstInfo; + + /* Enable or disable the logging of debug information. A value of true enables debug logging while a value of false disables debug logging. Debug output is sent to UART1 (gpio2). + * @url http://www.espruino.com/Reference#l_ESP8266_logDebug + */ + function logDebug(enable: boolean): void; + + /* Set the debug logging mode. It can be disabled (which frees ~1.2KB of heap), enabled in-memory only, or in-memory and output to a UART. + * @url http://www.espruino.com/Reference#l_ESP8266_setLog + */ + function setLog(mode: number): void; + + /* Prints the contents of the debug log to the console. + * @url http://www.espruino.com/Reference#l_ESP8266_printLog + */ + function printLog(): void; + + /* Returns one line from the log or up to 128 characters. + * @url http://www.espruino.com/Reference#l_ESP8266_readLog + */ + function readLog(): void; + + /* Dumps info about all sockets to the log. This is for troubleshooting the socket implementation. + * @url http://www.espruino.com/Reference#l_ESP8266_dumpSocketInfo + */ + function dumpSocketInfo(): void; + + /*

Note: This is deprecated. Use E.setClock(80/160) + * Note: + * Set the operating frequency of the ESP8266 processor. The default is 160Mhz.

+ * Warning: changing the cpu frequency affects the timing of some I/O operations, notably of software SPI and I2C, so things may be a bit slower at 80Mhz. + * @url http://www.espruino.com/Reference#l_ESP8266_setCPUFreq + */ + function setCPUFreq(freq: any): void; + + /* Returns an object that contains details about the state of the ESP8266 with the following fields: + *
    + *
  • sdkVersion - Version of the SDK.
  • + *
  • cpuFrequency - CPU operating frequency in Mhz.
  • + *
  • freeHeap - Amount of free heap in bytes.
  • + *
  • maxCon - Maximum number of concurrent connections.
  • + *
  • flashMap - Configured flash size&map: '512KB:256/256' .. '4MB:512/512'
  • + *
  • flashKB - Configured flash size in KB as integer
  • + *
  • flashChip - Type of flash chip as string with manufacturer & chip, ex: '0xEF 0x4016`
  • + *
+ * @url http://www.espruino.com/Reference#l_ESP8266_getState + */ + function getState(): any; + + /* Note: This is deprecated. Use require("Flash").getFree() + * @url http://www.espruino.com/Reference#l_ESP8266_getFreeFlash + */ + function getFreeFlash(): any; + + /* @url http://www.espruino.com/Reference#l_ESP8266_crc32 + */ + function crc32(arrayOfData: any): any; + + /* This function is deprecated. Please use require("neopixel").write(pin, data) instead + * @url http://www.espruino.com/Reference#l_ESP8266_neopixelWrite + */ + function neopixelWrite(pin: Pin, arrayOfData: any): void; + + /*

Put the ESP8266 into 'deep sleep' for the given number of microseconds, + * reducing power consumption drastically.

+ * meaning of option values: + * 0 - the 108th Byte of init parameter decides whether RF calibration will be performed or not. + * 1 - run RF calibration after waking up. Power consumption is high. + * 2 - no RF calibration after waking up. Power consumption is low. + * 4 - no RF after waking up. Power consumption is the lowest. + *

Note: unlike normal Espruino boards' 'deep sleep' mode, ESP8266 deep sleep actually turns off the processor. After the given number of microseconds have elapsed, the ESP8266 will restart as if power had been turned off and then back on. All contents of RAM will be lost. + * Connect GPIO 16 to RST to enable wakeup.

+ * Special: 0 microseconds cause sleep forever until external wakeup RST pull down occurs. + * @url http://www.espruino.com/Reference#l_ESP8266_deepSleep + */ + function deepSleep(micros: any, option: any): void; + +} + +/* This library allows you to create http servers and make http requests + * In order to use this, you will need an extra module to get network connectivity such as the TI CC3000 or WIZnet W5500. + * This is designed to be a cut-down version of the node.js library. Please see the Internet page for more information on how to use it. + * @url http://www.espruino.com/Reference#l_http_undefined + */ +declare function http(): void; + +declare namespace http { + /* Create an HTTP Server + * When a request to the server is made, the callback is called. In the callback you can use the methods on the response (httpSRs) to send data. You can also add request.on('data',function() { ... }) to listen for POSTed data + * @url http://www.espruino.com/Reference#l_http_createServer + */ + function createServer(callback: any): httpSrv; + +} + +/* The HTTP server created by require('http').createServer + * @url http://www.espruino.com/Reference#httpSrv + */ +declare function httpSrv(): void; + +type httpSrv = { + /* Start listening for new HTTP connections on the given port + * @url http://www.espruino.com/Reference#l_httpSrv_listen + */ + listen: (port: number) => any; + + /* Stop listening for new HTTP connections + * @url http://www.espruino.com/Reference#l_httpSrv_close + */ + close: () => void; + +} + +/* The HTTP server request + * @url http://www.espruino.com/Reference#httpSRq + */ +declare function httpSRq(): void; + +type httpSRq = { + /* The headers to sent to the server with this HTTP request. + * @url http://www.espruino.com/Reference#l_httpSRq_headers + */ + headers: any + + /* The HTTP method used with this request. Often "GET". + * @url http://www.espruino.com/Reference#l_httpSRq_method + */ + method: any + + /* The URL requested in this HTTP request, for instance: + *
    + *
  • "/" - the main page
  • + *
  • "/favicon.ico" - the web page's icon
  • + *
+ * @url http://www.espruino.com/Reference#l_httpSRq_url + */ + url: any + + /* Return how many bytes are available to read. If there is already a listener for data, this will always return 0. + * @url http://www.espruino.com/Reference#l_httpSRq_available + */ + available: () => number; + + /* Return a string containing characters that have been received + * @url http://www.espruino.com/Reference#l_httpSRq_read + */ + read: (chars: number) => any; + + /* Pipe this to a stream (an object with a 'write' method) + * @url http://www.espruino.com/Reference#l_httpSRq_pipe + */ + pipe: (destination: any, options: any) => void; + +} + +/* The HTTP server response + * @url http://www.espruino.com/Reference#httpSRs + */ +declare function httpSRs(): void; + +type httpSRs = { + /*

This function writes the data argument as a string. Data that is passed in + * (including arrays) will be converted to a string with the normal JavaScript + * toString method. For more information about sending binary data see Socket.write

+ * @url http://www.espruino.com/Reference#l_httpSRs_write + */ + write: (data: any) => boolean; + + /* See Socket.write for more information about the data argument + * @url http://www.espruino.com/Reference#l_httpSRs_end + */ + end: (data: any) => void; + + /*

Send the given status code and headers. If not explicitly called + * this will be done automatically the first time data is written + * to the response.

+ *

This cannot be called twice, or after data has already been sent + * in the response.

+ * @url http://www.espruino.com/Reference#l_httpSRs_writeHead + */ + writeHead: (statusCode: number, headers: any) => void; + + /* Set a value to send in the header of this HTTP response. This updates the httpSRs.headers property. + * Any headers supplied to writeHead will overwrite any headers with the same name. + * @url http://www.espruino.com/Reference#l_httpSRs_setHeader + */ + setHeader: (name: any, value: any) => void; + +} + +/* The HTTP client request, returned by http.request() and http.get(). + * @url http://www.espruino.com/Reference#httpCRq + */ +declare function httpCRq(): void; + +type httpCRq = { + /*

This function writes the data argument as a string. Data that is passed in + * (including arrays) will be converted to a string with the normal JavaScript + * toString method. For more information about sending binary data see Socket.write

+ * @url http://www.espruino.com/Reference#l_httpCRq_write + */ + write: (data: any) => boolean; + + /* Finish this HTTP request - optional data to append as an argument + * See Socket.write for more information about the data argument + * @url http://www.espruino.com/Reference#l_httpCRq_end + */ + end: (data: any) => void; + +} + +/* The HTTP client response, passed to the callback of http.request() an http.get(). + * @url http://www.espruino.com/Reference#httpCRs + */ +declare function httpCRs(): void; + +type httpCRs = { + /* The headers received along with the HTTP response + * @url http://www.espruino.com/Reference#l_httpCRs_headers + */ + headers: any + + /* The HTTP response's status code - usually "200" if all went well + * @url http://www.espruino.com/Reference#l_httpCRs_statusCode + */ + statusCode: any + + /* The HTTP response's status message - Usually "OK" if all went well + * @url http://www.espruino.com/Reference#l_httpCRs_statusMessage + */ + statusMessage: any + + /* The HTTP version reported back by the server - usually "1.1" + * @url http://www.espruino.com/Reference#l_httpCRs_httpVersion + */ + httpVersion: any + + /* Return how many bytes are available to read. If there is a 'data' event handler, this will always return 0. + * @url http://www.espruino.com/Reference#l_httpCRs_available + */ + available: () => number; + + /* Return a string containing characters that have been received + * @url http://www.espruino.com/Reference#l_httpCRs_read + */ + read: (chars: number) => any; + + /* Pipe this to a stream (an object with a 'write' method) + * @url http://www.espruino.com/Reference#l_httpCRs_pipe + */ + pipe: (destination: any, options: any) => void; + +} + +/* Library that initialises a network device that calls into JavaScript + * @url http://www.espruino.com/Reference#l_NetworkJS_undefined + */ +declare function NetworkJS(): void; + +/* Library for communication with the WIZnet Ethernet module + * @url http://www.espruino.com/Reference#l_WIZnet_undefined + */ +declare function WIZnet(): void; + +declare namespace WIZnet { + /* Initialise the WIZnet module and return an Ethernet object + * @url http://www.espruino.com/Reference#l_WIZnet_connect + */ + function connect(spi: any, cs: Pin): Ethernet; + +} + +/* An instantiation of an Ethernet network adaptor + * @url http://www.espruino.com/Reference#Ethernet + */ +declare function Ethernet(): void; + +type Ethernet = { + /* Get the current IP address, subnet, gateway and mac address. + * @url http://www.espruino.com/Reference#l_Ethernet_getIP + */ + getIP: (options: any) => any; + + /* Set the current IP address or get an IP from DHCP (if no options object is specified) + *

If 'mac' is specified as an option, it must be a string of the form "00:01:02:03:04:05" + * The default mac is 00:08:DC:01:02:03.

+ * @url http://www.espruino.com/Reference#l_Ethernet_setIP + */ + setIP: (options: any, callback: any) => boolean; + + /*

Set hostname allow to set the hosname used during the dhcp request. + * min 8 and max 12 char, best set before calling eth.setIP() + * Default is WIZnet010203, 010203 is the default nic as part of the mac. + * Best to set the hosname before calling setIP().

+ * @url http://www.espruino.com/Reference#l_Ethernet_setHostname + */ + setHostname: (hostname: any, callback: any) => boolean; + + /* Returns the hostname + * @url http://www.espruino.com/Reference#l_Ethernet_getHostname + */ + getHostname: (callback: any) => any; + + /* Get the current status of the ethernet device + * @url http://www.espruino.com/Reference#l_Ethernet_getStatus + */ + getStatus: (options: any) => any; + +} + +/* This class helps to convert URLs into Objects of information ready for http.request/get + * @url http://www.espruino.com/Reference#url + */ +declare function url(): void; + +declare namespace url { + /* A utility function to split a URL into parts + * This is useful in web servers for instance when handling a request. + * For instance url.parse("/a?b=c&d=e",true) returns {"method":"GET","host":"","path":"/a?b=c&d=e","pathname":"/a","search":"?b=c&d=e","port":80,"query":{"b":"c","d":"e"}} + * @url http://www.espruino.com/Reference#l_url_parse + */ + function parse(urlStr: any, parseQuery: boolean): any; + +} + +/* This library allows you to create TCPIP servers and clients + * In order to use this, you will need an extra module to get network connectivity. + * This is designed to be a cut-down version of the node.js library. Please see the Internet page for more information on how to use it. + * @url http://www.espruino.com/Reference#l_net_undefined + */ +declare function net(): void; + +declare namespace net { + /* Create a Server + * When a request to the server is made, the callback is called. In the callback you can use the methods on the connection to send data. You can also add connection.on('data',function() { ... }) to listen for received data + * @url http://www.espruino.com/Reference#l_net_createServer + */ + function createServer(callback: any): Server; + + /* Create a TCP socket connection + * @url http://www.espruino.com/Reference#l_net_connect + */ + function connect(options: any, callback: any): Socket; + +} + +/* The socket server created by require('net').createServer + * @url http://www.espruino.com/Reference#Server + */ +declare function Server(): void; + +type Server = { + /* Start listening for new connections on the given port + * @url http://www.espruino.com/Reference#l_Server_listen + */ + listen: (port: number) => any; + + /* Stop listening for new connections + * @url http://www.espruino.com/Reference#l_Server_close + */ + close: () => void; + +} + +/* An actual socket connection - allowing transmit/receive of TCP data + * @url http://www.espruino.com/Reference#Socket + */ +declare function Socket(): void; + +type Socket = { + /* Return how many bytes are available to read. If there is already a listener for data, this will always return 0. + * @url http://www.espruino.com/Reference#l_Socket_available + */ + available: () => number; + + /* Return a string containing characters that have been received + * @url http://www.espruino.com/Reference#l_Socket_read + */ + read: (chars: number) => any; + + /* Pipe this to a stream (an object with a 'write' method) + * @url http://www.espruino.com/Reference#l_Socket_pipe + */ + pipe: (destination: any, options: any) => void; + + /* Close this socket - optional data to append as an argument. + * See Socket.write for more information about the data argument + * @url http://www.espruino.com/Reference#l_Socket_end + */ + end: (data: any) => void; + +} + +/* This library allows you to create UDP/DATAGRAM servers and clients + * In order to use this, you will need an extra module to get network connectivity. + * This is designed to be a cut-down version of the node.js library. Please see the Internet page for more information on how to use it. + * @url http://www.espruino.com/Reference#l_dgram_undefined + */ +declare function dgram(): void; + +declare namespace dgram { + /* Create a UDP socket + * @url http://www.espruino.com/Reference#l_dgram_createSocket + */ + function createSocket(type: any, callback: any): dgramSocket; + +} + +/* An actual socket connection - allowing transmit/receive of TCP data + * @url http://www.espruino.com/Reference#dgramSocket + */ +declare function dgramSocket(): void; + +type dgramSocket = { + /* @url http://www.espruino.com/Reference#l_dgramSocket_send + */ + send: (buffer: any, offset: any, length: any, args: any) => void; + + /* @url http://www.espruino.com/Reference#l_dgramSocket_bind + */ + bind: (port: number, callback: any) => any; + + /* Close the socket + * @url http://www.espruino.com/Reference#l_dgramSocket_close + */ + close: () => void; + + /* @url http://www.espruino.com/Reference#l_dgramSocket_addMembership + */ + addMembership: (group: any, ip: any) => void; + +} + +/* This library allows you to create TCPIP servers and clients using TLS encryption + * In order to use this, you will need an extra module to get network connectivity. + * This is designed to be a cut-down version of the node.js library. Please see the Internet page for more information on how to use it. + * @url http://www.espruino.com/Reference#l_tls_undefined + */ +declare function tls(): void; + +/* Simple library for compression/decompression using heatshrink, an LZSS compression tool. + * Espruino uses heatshrink internally to compress RAM down to fit in Flash memory when save() is used. This just exposes that functionality. + * Functions here take and return buffers of data. There is no support for streaming, so both the compressed and decompressed data must be able to fit in memory at the same time. + * @url http://www.espruino.com/Reference#l_heatshrink_undefined + */ +declare function heatshrink(): void; + +declare namespace heatshrink { + /* @url http://www.espruino.com/Reference#l_heatshrink_compress + */ + function compress(data: any): ArrayBuffer; + + /* @url http://www.espruino.com/Reference#l_heatshrink_decompress + */ + function decompress(data: any): ArrayBuffer; + +} + +/* Creates a Queue Object + * @url http://www.espruino.com/Reference#l_Queue_Queue + */ +declare function Queue(queueName: any): any; + +type Queue = { + /* reads one character from queue, if available + * @url http://www.espruino.com/Reference#l_Queue_read + */ + read: () => void; + + /* Writes one character to queue + * @url http://www.espruino.com/Reference#l_Queue_writeChar + */ + writeChar: (char: any) => void; + + /* logs list of queues + * @url http://www.espruino.com/Reference#l_Queue_log + */ + log: () => void; + +} + +/* Creates a Task Object + * @url http://www.espruino.com/Reference#l_Task_Task + */ +declare function Task(taskName: any): any; + +type Task = { + /* Suspend task, be careful not to suspend Espruino task itself + * @url http://www.espruino.com/Reference#l_Task_suspend + */ + suspend: () => void; + + /* Resumes a suspended task + * @url http://www.espruino.com/Reference#l_Task_resume + */ + resume: () => void; + + /* returns name of actual task + * @url http://www.espruino.com/Reference#l_Task_getCurrent + */ + getCurrent: () => any; + + /* Sends a binary notify to task + * @url http://www.espruino.com/Reference#l_Task_notify + */ + notify: () => void; + + /* logs list of tasks + * @url http://www.espruino.com/Reference#l_Task_log + */ + log: () => void; + +} + +/* Creates a Timer Object + * @url http://www.espruino.com/Reference#l_Timer_Timer + */ +declare function Timer(timerName: any, group: number, index: number, isrIndex: number): any; + +type Timer = { + /* Starts a timer + * @url http://www.espruino.com/Reference#l_Timer_start + */ + start: (duration: number) => void; + + /* Reschedules a timer, needs to be started at least once + * @url http://www.espruino.com/Reference#l_Timer_reschedule + */ + reschedule: (duration: number) => void; + + /* logs list of timers + * @url http://www.espruino.com/Reference#l_Timer_log + */ + log: () => void; + +} + +/* Class containing utility functions for the ESP32 + * @url http://www.espruino.com/Reference#ESP32 + */ +declare function ESP32(): void; + +declare namespace ESP32 { + /* @url http://www.espruino.com/Reference#l_ESP32_setAtten + */ + function setAtten(pin: Pin, atten: number): void; + + /* Perform a hardware reset/reboot of the ESP32. + * @url http://www.espruino.com/Reference#l_ESP32_reboot + */ + function reboot(): void; + + /* Put device in deepsleep state for "us" microseconds. + * @url http://www.espruino.com/Reference#l_ESP32_deepSleep + */ + function deepSleep(us: number): void; + + /* Returns an object that contains details about the state of the ESP32 with the following fields: + *
    + *
  • sdkVersion - Version of the SDK.
  • + *
  • freeHeap - Amount of free heap in bytes.
  • + *
  • BLE - Status of BLE, enabled if true.
  • + *
  • Wifi - Status of Wifi, enabled if true.
  • + *
  • minHeap - Minimum heap, calculated by heap_caps_get_minimum_free_size
  • + *
+ * @url http://www.espruino.com/Reference#l_ESP32_getState + */ + function getState(): any; + + /* @url http://www.espruino.com/Reference#l_ESP32_setBLE_Debug + */ + function setBLE_Debug(level: number): void; + + /*

Switches Bluetooth off/on, removes saved code from Flash, resets the board, + * and on restart creates jsVars depending on available heap (actual additional 1800)

+ * @url http://www.espruino.com/Reference#l_ESP32_enableBLE + */ + function enableBLE(enable: boolean): void; + + /*

Switches Wifi off/on, removes saved code from Flash, resets the board, + * and on restart creates jsVars depending on available heap (actual additional 3900)

+ * @url http://www.espruino.com/Reference#l_ESP32_enableWifi + */ + function enableWifi(enable: boolean): void; + +} + +/* This is the built-in class for the Arduino-style pin namings on ST Nucleo boards + * @url http://www.espruino.com/Reference#Nucleo + */ +declare function Nucleo(): void; + +declare namespace Nucleo { + /* @url http://www.espruino.com/Reference#l_Nucleo_A0 + */ + const A0: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_A1 + */ + const A1: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_A2 + */ + const A2: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_A3 + */ + const A3: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_A4 + */ + const A4: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_A5 + */ + const A5: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D0 + */ + const D0: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D1 + */ + const D1: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D2 + */ + const D2: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D3 + */ + const D3: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D4 + */ + const D4: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D5 + */ + const D5: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D6 + */ + const D6: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D7 + */ + const D7: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D8 + */ + const D8: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D9 + */ + const D9: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D10 + */ + const D10: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D11 + */ + const D11: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D12 + */ + const D12: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D13 + */ + const D13: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D14 + */ + const D14: Pin; + + /* @url http://www.espruino.com/Reference#l_Nucleo_D15 + */ + const D15: Pin; + +} + +/* This is a built-in class to allow you to use the ESP8266 NodeMCU boards's pin namings to access pins. It is only available on ESP8266-based boards. + * @url http://www.espruino.com/Reference#NodeMCU + */ +declare function NodeMCU(): void; + +declare namespace NodeMCU { + /* @url http://www.espruino.com/Reference#l_NodeMCU_A0 + */ + const A0: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D0 + */ + const D0: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D1 + */ + const D1: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D2 + */ + const D2: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D3 + */ + const D3: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D4 + */ + const D4: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D5 + */ + const D5: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D6 + */ + const D6: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D7 + */ + const D7: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D8 + */ + const D8: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D9 + */ + const D9: Pin; + + /* @url http://www.espruino.com/Reference#l_NodeMCU_D10 + */ + const D10: Pin; + +} + +/* Create a software Serial port. This has limited functionality (only low baud rates), but it can work on any pins. + * Use Serial.setup to configure this port. + * @url http://www.espruino.com/Reference#l_Serial_Serial + */ +declare function Serial(): any; + +type Serial = { + /* Set this Serial port as the port for the JavaScript console (REPL). + *

Unless force is set to true, changes in the connection state of the board + * (for instance plugging in USB) will cause the console to change.

+ * See E.setConsole for a more flexible version of this function. + * @url http://www.espruino.com/Reference#l_Serial_setConsole + */ + setConsole: (force: boolean) => void; + + /*

If the serial (or software serial) device was set up, + * uninitialise it.

+ * @url http://www.espruino.com/Reference#l_Serial_unsetup + */ + unsetup: () => void; + + /* Print a string to the serial port - without a line feed + * Note: This function replaces any occurances of \n in the string with \r\n. To avoid this, use Serial.write. + * @url http://www.espruino.com/Reference#l_Serial_print + */ + print: (string: any) => void; + + /* Print a line to the serial port with a newline (\r\n) at the end of it. + * Note: This function converts data to a string first, eg Serial.print([1,2,3]) is equivalent to Serial.print("1,2,3"). If you'd like to write raw bytes, useSerial.write`. + * @url http://www.espruino.com/Reference#l_Serial_println + */ + println: (string: any) => void; + + /* Write a character or array of data to the serial port + * This method writes unmodified data, eg Serial.write([1,2,3]) is equivalent to Serial.write("\1\2\3"). If you'd like data converted to a string first, use Serial.print. + * @url http://www.espruino.com/Reference#l_Serial_write + */ + write: (data: any) => void; + + /* Return how many bytes are available to read. If there is already a listener for data, this will always return 0. + * @url http://www.espruino.com/Reference#l_Serial_available + */ + available: () => number; + + /* Return a string containing characters that have been received + * @url http://www.espruino.com/Reference#l_Serial_read + */ + read: (chars: number) => any; + + /* Pipe this USART to a stream (an object with a 'write' method) + * @url http://www.espruino.com/Reference#l_Serial_pipe + */ + pipe: (destination: any, options: any) => void; + +} + +declare namespace Serial { + /* Try and find a USART (Serial) hardware device that will work on this pin (eg. Serial1) + * May return undefined if no device can be found. + * @url http://www.espruino.com/Reference#l_Serial_find + */ + function find(pin: Pin): any; + +} + +/* The USB Serial port + * @url http://www.espruino.com/Reference#l__global_USB + */ +declare const USB: Serial; + +/* The first Serial (USART) port + * @url http://www.espruino.com/Reference#l__global_Serial1 + */ +declare const Serial1: Serial; + +/* The second Serial (USART) port + * @url http://www.espruino.com/Reference#l__global_Serial2 + */ +declare const Serial2: Serial; + +/* The third Serial (USART) port + * @url http://www.espruino.com/Reference#l__global_Serial3 + */ +declare const Serial3: Serial; + +/* The fourth Serial (USART) port + * @url http://www.espruino.com/Reference#l__global_Serial4 + */ +declare const Serial4: Serial; + +/* The fifth Serial (USART) port + * @url http://www.espruino.com/Reference#l__global_Serial5 + */ +declare const Serial5: Serial; + +/* The sixth Serial (USART) port + * @url http://www.espruino.com/Reference#l__global_Serial6 + */ +declare const Serial6: Serial; + +/* A loopback serial device. Data sent to LoopbackA comes out of LoopbackB and vice versa + * @url http://www.espruino.com/Reference#l__global_LoopbackA + */ +declare const LoopbackA: Serial; + +/* A loopback serial device. Data sent to LoopbackA comes out of LoopbackB and vice versa + * @url http://www.espruino.com/Reference#l__global_LoopbackB + */ +declare const LoopbackB: Serial; + +/* A telnet serial device that maps to the built-in telnet console server (devices that have built-in wifi only). + * @url http://www.espruino.com/Reference#l__global_Telnet + */ +declare const Telnet: any; + +/* This is the built-in JavaScript class that is the prototype for: + * + *

If you want to access arrays of differing types of data + * you may also find DataView useful.

+ * @url http://www.espruino.com/Reference#ArrayBufferView + */ +declare function EspruinoArrayBufferView(): void; + +type EspruinoArrayBufferView = { + /* The buffer this view references + * @url http://www.espruino.com/Reference#l_ArrayBufferView_buffer + */ + buffer: any + + /* The length, in bytes, of the ArrayBufferView + * @url http://www.espruino.com/Reference#l_ArrayBufferView_byteLength + */ + byteLength: number + + /* The offset, in bytes, to the first byte of the view within the backing ArrayBuffer + * @url http://www.espruino.com/Reference#l_ArrayBufferView_byteOffset + */ + byteOffset: number + + /* Copy the contents of array into this one, mapping this[x+offset]=array[x]; + * @url http://www.espruino.com/Reference#l_ArrayBufferView_set + */ + set: (arr: any, offset: number) => void; + + /* Return an array which is made from the following: A.map(function) = [function(A[0]), function(A[1]), ...] + * Note: This returns an ArrayBuffer of the same type it was called on. To get an Array, use Array.map, eg. [].map.call(myArray, x=>x+1) + * @url http://www.espruino.com/Reference#l_ArrayBufferView_map + */ + map: (fn: any, thisArg: any) => EspruinoArrayBufferView; + + /* Returns a smaller part of this array which references the same data (it doesn't copy it). + * @url http://www.espruino.com/Reference#l_ArrayBufferView_subarray + */ + subarray: (begin: number, end: any) => EspruinoArrayBufferView; + + /* Return the index of the value in the array, or -1 + * @url http://www.espruino.com/Reference#l_ArrayBufferView_indexOf + */ + indexOf: (value: any, startIndex: number) => any; + + /* Return true if the array includes the value, false otherwise + * @url http://www.espruino.com/Reference#l_ArrayBufferView_includes + */ + includes: (value: any, startIndex: number) => boolean; + + /* Join all elements of this array together into one string, using 'separator' between them. eg. [1,2,3].join(' ')=='1 2 3' + * @url http://www.espruino.com/Reference#l_ArrayBufferView_join + */ + join: (separator: any) => any; + + /* Do an in-place quicksort of the array + * @url http://www.espruino.com/Reference#l_ArrayBufferView_sort + */ + sort: (variable: any) => EspruinoArrayBufferView; + + /* Executes a provided function once per array element. + * @url http://www.espruino.com/Reference#l_ArrayBufferView_forEach + */ + forEach: (fn: any, thisArg: any) => void; + + /* Execute previousValue=initialValue and then previousValue = callback(previousValue, currentValue, index, array) for each element in the array, and finally return previousValue. + * @url http://www.espruino.com/Reference#l_ArrayBufferView_reduce + */ + reduce: (callback: any, initialValue: any) => any; + + /* Fill this array with the given value, for every index >= start and < end + * @url http://www.espruino.com/Reference#l_ArrayBufferView_fill + */ + fill: (value: any, start: number, end: any) => EspruinoArrayBufferView; + + /* Return an array which contains only those elements for which the callback function returns 'true' + * @url http://www.espruino.com/Reference#l_ArrayBufferView_filter + */ + filter: (fn: any, thisArg: any) => any; + + /* Return the array element where function returns true, or undefined if it doesn't returns true for any element. + * @url http://www.espruino.com/Reference#l_ArrayBufferView_find + */ + find: (fn: any) => any; + + /* Return the array element's index where function returns true, or -1 if it doesn't returns true for any element. + * @url http://www.espruino.com/Reference#l_ArrayBufferView_findIndex + */ + findIndex: (fn: any) => any; + + /* Reverse the contents of this ArrayBufferView in-place + * @url http://www.espruino.com/Reference#l_ArrayBufferView_reverse + */ + reverse: () => EspruinoArrayBufferView; + + /* Return a copy of a portion of this array (in a new array). + * Note: This currently returns a normal Array, not an ArrayBuffer + * @url http://www.espruino.com/Reference#l_ArrayBufferView_slice + */ + slice: (start: number, end: any) => any[]; + +} + +/* Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array. If an ArrayBufferView (eg. Uint8Array rather than ArrayBuffer) is given, it will be completely copied rather than referenced. + * @url http://www.espruino.com/Reference#l_Uint24Array_Uint24Array + */ +declare function Uint24Array(arr: any, byteOffset: number, length: number): EspruinoArrayBufferView; + +type Uint24Array = { +} + +/* Create a waveform class. This allows high speed input and output of waveforms. It has an internal variable called buffer (as well as buffer2 when double-buffered - see options below) which contains the data to input/output. + * When double-buffered, a 'buffer' event will be emitted each time a buffer is finished with (the argument is that buffer). When the recording stops, a 'finish' event will be emitted (with the first argument as the buffer). + * @url http://www.espruino.com/Reference#l_Waveform_Waveform + */ +declare function Waveform(samples: number, options: any): any; + +type Waveform = { + /* Will start outputting the waveform on the given pin - the pin must have previously been initialised with analogWrite. If not repeating, it'll emit a finish event when it is done. + * @url http://www.espruino.com/Reference#l_Waveform_startOutput + */ + startOutput: (output: Pin, freq: number, options: any) => void; + + /* Will start inputting the waveform on the given pin that supports analog. If not repeating, it'll emit a finish event when it is done. + * @url http://www.espruino.com/Reference#l_Waveform_startInput + */ + startInput: (output: Pin, freq: number, options: any) => void; + + /* Stop a waveform that is currently outputting + * @url http://www.espruino.com/Reference#l_Waveform_stop + */ + stop: () => void; + +} + +/*

This module allows you to read and write part of the nonvolatile flash + * memory of your device using a filesystem-like API.

+ *

Also see the Flash library, which provides a low level, more dangerous way + * to access all parts of your flash memory.

+ * The Storage library provides two distinct types of file: + *
    + *
  • require("Storage").write(...)/require("Storage").read(...)/etc create simple + * contiguous files of fixed length. This is the recommended file type.
  • + *
  • require("Storage").open(...) creates a StorageFile, which stores the file in + * numbered chunks ("filename\1"/"filename\2"/etc). It allows data to be appended + * and for the file to be read line by line.
  • + *
+ *

You must read a file using the same method you used to write it - eg. you can't create a + * file with require("Storage").open(...) and then read it with require("Storage").read(...).

+ *

Note: In firmware 2v05 and later, the maximum length for filenames + * is 28 characters. However in 2v04 and earlier the max length is 8.

+ * @url http://www.espruino.com/Reference#l_Storage_undefined + */ +declare function EspruinoStorage(): void; + +declare namespace EspruinoStorage { + /*

Erase the flash storage area. This will remove all files + * created with require("Storage").write(...) as well + * as any code saved with save() or E.setBootCode().

+ * @url http://www.espruino.com/Reference#l_Storage_eraseAll + */ + function eraseAll(): void; + + /* Erase a single file from the flash storage area. + *

Note: This function should be used with normal files, and not + * StorageFiles created with require("Storage").open(filename, ...)

+ * @url http://www.espruino.com/Reference#l_Storage_erase + */ + function erase(name: any): void; + + /*

Read a file from the flash storage area that has + * been written with require("Storage").write(...).

+ *

This function returns a memory-mapped String that points to the actual + * memory area in read-only memory, so it won't use up RAM.

+ * As such you can check if a file exists efficiently using require("Storage").read(filename)!==undefined. + *

If you evaluate this string with eval, any functions + * contained in the String will keep their code stored + * in flash memory.

+ *

Note: This function should be used with normal files, and not + * StorageFiles created with require("Storage").open(filename, ...)

+ * @url http://www.espruino.com/Reference#l_Storage_read + */ + function read(name: any, offset: number, length: number): any; + + /*

Read a file from the flash storage area that has + * been written with require("Storage").write(...), + * and parse JSON in it into a JavaScript object.

+ *

This is identical to JSON.parse(require("Storage").read(...)). + * It will throw an exception if the data in the file is not + * valid JSON.

+ *

Note: This function should be used with normal files, and not + * StorageFiles created with require("Storage").open(filename, ...)

+ * @url http://www.espruino.com/Reference#l_Storage_readJSON + */ + function readJSON(name: any, noExceptions: boolean): any; + + /*

Read a file from the flash storage area that has + * been written with require("Storage").write(...), + * and return the raw binary data as an ArrayBuffer.

+ * This can be used: + *
    + *
  • In a DataView with new DataView(require("Storage").readArrayBuffer("x"))
  • + *
  • In a Uint8Array/Float32Array/etc with new Uint8Array(require("Storage").readArrayBuffer("x"))
  • + *
+ *

Note: This function should be used with normal files, and not + * StorageFiles created with require("Storage").open(filename, ...)

+ * @url http://www.espruino.com/Reference#l_Storage_readArrayBuffer + */ + function readArrayBuffer(name: any): any; + + /*

Write/create a file in the flash storage area. This is + * nonvolatile and will not disappear when the device resets + * or power is lost.

+ *

Simply write require("Storage").writeJSON("MyFile", [1,2,3]) to write + * a new file, and require("Storage").readJSON("MyFile") to read it.

+ * This is equivalent to: require("Storage").write(name, JSON.stringify(data)) + *

Note: This function should be used with normal files, and not + * StorageFiles created with require("Storage").open(filename, ...)

+ * @url http://www.espruino.com/Reference#l_Storage_writeJSON + */ + function writeJSON(name: any, data: any): boolean; + + /*

The Flash Storage system is journaling. To make the most of the limited + * write cycles of Flash memory, Espruino marks deleted/replaced files as + * garbage and moves on to a fresh part of flash memory. Espruino only + * fully erases those files when it is running low on flash, or when + * compact is called.

+ *

compact may fail if there isn't enough RAM free on the stack to + * use as swap space, however in this case it will not lose data.

+ *

Note: compact rearranges the contents of memory. If code is + * referencing that memory (eg. functions that have their code stored in flash) + * then they may become garbled when compaction happens. To avoid this, + * call eraseFiles before uploading data that you intend to reference to + * ensure that uploaded files are right at the start of flash and cannot be + * compacted further.

+ * @url http://www.espruino.com/Reference#l_Storage_compact + */ + function compact(): void; + + /*

This writes information about all blocks in flash + * memory to the console - and is only useful for debugging + * flash storage.

+ * @url http://www.espruino.com/Reference#l_Storage_debug + */ + function debug(): void; + + /*

Return the amount of free bytes available in + * Storage. Due to fragmentation there may be more + * bytes available, but this represents the maximum + * size of file that can be written.

+ * @url http://www.espruino.com/Reference#l_Storage_getFree + */ + function getFree(): number; + + /*

Open a file in the Storage area. This can be used for appending data + * (normal read/write operations only write the entire file).

+ * Please see StorageFile for more information (and examples). + * Note: These files write through immediately - they do not need closing. + * @url http://www.espruino.com/Reference#l_Storage_open + */ + function open(name: any, mode: any): StorageFile; + +} + +/* This is the built-in JavaScript class for Espruino utility functions. + * @url http://www.espruino.com/Reference#E + */ +declare function E(): void; + +declare namespace E { + /* @url http://www.espruino.com/Reference#l_E_showMenu + */ + function showMenu(): void; + + /* @url http://www.espruino.com/Reference#l_E_showPrompt + */ + function showPrompt(): void; + + /* @url http://www.espruino.com/Reference#l_E_showScroller + */ + function showScroller(): void; + + /* Unmount the SD card, so it can be removed. If you remove the SD card without calling this you may cause corruption, and you will be unable to access another SD card until you reset Espruino or call E.unmountSD(). + * @url http://www.espruino.com/Reference#l_E_unmountSD + */ + function unmountSD(): void; + + /* Open a file + * @url http://www.espruino.com/Reference#l_E_openFile + */ + function openFile(path: any, mode: any): EspruinoFile; + + /* Use the microcontroller's internal thermistor to work out the temperature. + * On Puck.js v2.0 this will use the on-board PCT2075TP temperature sensor, but on other devices it may not be desperately well calibrated. + * While this is implemented on Espruino boards, it may not be implemented on other devices. If so it'll return NaN. + * Note: This is not entirely accurate and varies by a few degrees from chip to chip. It measures the die temperature, so when connected to USB it could be reading 10 over degrees C above ambient temperature. When running from battery with setDeepSleep(true) it is much more accurate though. + * @url http://www.espruino.com/Reference#l_E_getTemperature + */ + function getTemperature(): number; + + /* Check the internal voltage reference. To work out an actual voltage of an input pin, you can use analogRead(pin)*E.getAnalogVRef() + *

Note: This value is calculated by reading the voltage on an internal voltage reference with the ADC. + * It will be slightly noisy, so if you need this for accurate measurements we'd recommend that you call + * this function several times and average the results.

+ * While this is implemented on Espruino boards, it may not be implemented on other devices. If so it'll return NaN. + * @url http://www.espruino.com/Reference#l_E_getAnalogVRef + */ + function getAnalogVRef(): number; + + /* ADVANCED: This is a great way to crash Espruino if you're not sure what you are doing + * Create a native function that executes the code at the given address. Eg. E.nativeCall(0x08012345,'double (double,double)')(1.1, 2.2) + * If you're executing a thumb function, you'll almost certainly need to set the bottom bit of the address to 1. + * Note it's not guaranteed that the call signature you provide can be used - there are limits on the number of arguments allowed. + * When supplying data, if it is a 'flat string' then it will be used directly, otherwise it'll be converted to a flat string and used. + * @url http://www.espruino.com/Reference#l_E_nativeCall + */ + function nativeCall(addr: number, sig: any, data: any): any; + + /* Clip a number to be between min and max (inclusive) + * @url http://www.espruino.com/Reference#l_E_clip + */ + function clip(x: number, min: number, max: number): number; + + /* Sum the contents of the given Array, String or ArrayBuffer and return the result + * @url http://www.espruino.com/Reference#l_E_sum + */ + function sum(arr: any): number; + + /* Work out the variance of the contents of the given Array, String or ArrayBuffer and return the result. This is equivalent to v=0;for (i in arr) v+=Math.pow(mean-arr[i],2) + * @url http://www.espruino.com/Reference#l_E_variance + */ + function variance(arr: any, mean: number): number; + + /* Convolve arr1 with arr2. This is equivalent to v=0;for (i in arr1) v+=arr1[i] * arr2[(i+offset) % arr2.length] + * @url http://www.espruino.com/Reference#l_E_convolve + */ + function convolve(arr1: any, arr2: any, offset: number): number; + + /*

Performs a Fast Fourier Transform (FFT) in 32 bit floats on the supplied data and writes it back into the + * original arrays. Note that if only one array is supplied, the data written back is the modulus of the complex + * result sqrt(r*r+i*i).

+ *

In order to perform the FFT, there has to be enough room on the stack to allocate two arrays of 32 bit + * floating point numbers - this will limit the maximum size of FFT possible to around 1024 items on + * most platforms.

+ *

Note: on the Original Espruino board, FFTs are performed in 64bit arithmetic as there isn't + * space to include the 32 bit maths routines (2x more RAM is required).

+ * @url http://www.espruino.com/Reference#l_E_FFT + */ + function FFT(arrReal: any, arrImage: any, inverse: boolean): void; + + /*

Kicks a Watchdog timer set up with E.enableWatchdog(..., false). See + * E.enableWatchdog for more information.

+ * NOTE: This is only implemented on STM32 and nRF5x devices (all official Espruino boards). + * @url http://www.espruino.com/Reference#l_E_kickWatchdog + */ + function kickWatchdog(): void; + + /* Get and reset the error flags. Returns an array that can contain: + * 'FIFO_FULL': The receive FIFO filled up and data was lost. This could be state transitions for setWatch, or received characters. + * 'BUFFER_FULL': A buffer for a stream filled up and characters were lost. This can happen to any stream - Serial,HTTP,etc. + * 'CALLBACK': A callback (setWatch, setInterval, on('data',...)) caused an error and so was removed. + * 'LOW_MEMORY': Memory is running low - Espruino had to run a garbage collection pass or remove some of the command history + * 'MEMORY': Espruino ran out of memory and was unable to allocate some data that it needed. + * 'UART_OVERFLOW' : A UART received data but it was not read in time and was lost + * @url http://www.espruino.com/Reference#l_E_getErrorFlags + */ + function getErrorFlags(): any; + + /* Get Espruino's interpreter flags that control the way it handles your JavaScript code. + *
    + *
  • deepSleep - Allow deep sleep modes (also set by setDeepSleep)
  • + *
  • pretokenise - When adding functions, pre-minify them and tokenise reserved words
  • + *
  • unsafeFlash - Some platforms stop writes/erases to interpreter memory to stop you bricking the device accidentally - this removes that protection
  • + *
  • unsyncFiles - When writing files, don't flush all data to the SD card after each command (the default is to flush). This is much faster, but can cause filesystem damage if power is lost without the filesystem unmounted.
  • + *
+ * @url http://www.espruino.com/Reference#l_E_getFlags + */ + function getFlags(): any; + + /* Set the Espruino interpreter flags that control the way it handles your JavaScript code. + * Run E.getFlags() and check its description for a list of available flags and their values. + * @url http://www.espruino.com/Reference#l_E_setFlags + */ + function setFlags(flags: any): void; + + /* @url http://www.espruino.com/Reference#l_E_pipe + */ + function pipe(source: any, destination: any, options: any): void; + + /* Create an ArrayBuffer from the given string. This is done via a reference, not a copy - so it is very fast and memory efficient. + * Note that this is an ArrayBuffer, not a Uint8Array. To get one of those, do: new Uint8Array(E.toArrayBuffer('....')). + * @url http://www.espruino.com/Reference#l_E_toArrayBuffer + */ + function toArrayBuffer(str: any): EspruinoArrayBufferView; + + /*

This performs the same basic function as JSON.stringify, + * however JSON.stringify adds extra characters to conform + * to the JSON spec which aren't required if outputting JS.

+ *

E.toJS will also stringify JS functions, whereas + * JSON.stringify ignores them.

+ * For example: + *
    + *
  • JSON.stringify({a:1,b:2}) == '{"a":1,"b":2}'
  • + *
  • E.toJS({a:1,b:2}) == '{a:1,b:2}'
  • + *
+ *

Note: Strings generated with E.toJS can't be + * reliably parsed by JSON.parse - however they are + * valid JS so will work with eval (but this has security + * implications if you don't trust the source of the string).

+ *

On the desktop JSON5 parsers + * will parse the strings produced by E.toJS without trouble.

+ * @url http://www.espruino.com/Reference#l_E_toJS + */ + function toJS(arg: any): string; + + /*

This creates and returns a special type of string, which actually references + * a specific memory address. It can be used in order to use sections of + * Flash memory directly in Espruino (for example to execute code straight + * from flash memory with eval(E.memoryArea( ... )))

+ *

Note: This is only tested on STM32-based platforms (Espruino Original + * and Espruino Pico) at the moment.

+ * @url http://www.espruino.com/Reference#l_E_memoryArea + */ + function memoryArea(addr: number, len: number): string; + + /*

This writes JavaScript code into Espruino's flash memory, to be executed on + * startup. It differs from save() in that save() saves the whole state of + * the interpreter, whereas this just saves JS code that is executed at boot.

+ * Code will be executed before onInit() and E.on('init', ...). + *

If alwaysExec is true, the code will be executed even after a call to + * reset(). This is useful if you're making something that you want to + * program, but you want some code that is always built in (for instance + * setting up a display or keyboard).

+ * To remove boot code that has been saved previously, use E.setBootCode("") + * Note: this removes any code that was previously saved with save() + * @url http://www.espruino.com/Reference#l_E_setBootCode + */ + function setBootCode(code: any, alwaysExec: boolean): void; + + /*

This sets the clock frequency of Espruino's processor. It will return 0 if + * it is unimplemented or the clock speed cannot be changed.

+ *

Note: On pretty much all boards, UART, SPI, I2C, PWM, etc will change + * frequency and will need setting up again in order to work.

+ *

STM32F4

+ *

Options is of the form { M: int, N: int, P: int, Q: int } - see the 'Clocks' + * section of the microcontroller's reference manual for what these mean.

+ *
    + *
  • System clock = 8Mhz N / ( M P )
  • + *
  • USB clock (should be 48Mhz) = 8Mhz N / ( M Q )
  • + *
+ * Optional arguments are: + *
    + *
  • latency - flash latency from 0..15
  • + *
  • PCLK1 - Peripheral clock 1 divisor (default: 2)
  • + *
  • PCLK2 - Peripheral clock 2 divisor (default: 4)
  • + *
+ *

The Pico's default is {M:8, N:336, P:4, Q:7, PCLK1:2, PCLK2:4}, use + * {M:8, N:336, P:8, Q:7, PCLK:1, PCLK2:2} to halve the system clock speed + * while keeping the peripherals running at the same speed (omitting PCLK1/2 + * will lead to the peripherals changing speed too).

+ *

On STM32F4 boards (eg. Espruino Pico), the USB clock needs to be kept at 48Mhz + * or USB will fail to work. You'll also experience USB instability if the processor + * clock falls much below 48Mhz.

+ *

ESP8266

+ * Just specify an integer value, either 80 or 160 (for 80 or 160Mhz) + * @url http://www.espruino.com/Reference#l_E_setClock + */ + function setClock(options: any): number; + + /* Returns the current console device - see E.setConsole for more information. + * @url http://www.espruino.com/Reference#l_E_getConsole + */ + function getConsole(): any; + + /* Reverse the 8 bits in a byte, swapping MSB and LSB. + * For example, E.reverseByte(0b10010000) == 0b00001001. + * Note that you can reverse all the bytes in an array with: arr = arr.map(E.reverseByte) + * @url http://www.espruino.com/Reference#l_E_reverseByte + */ + function reverseByte(x: number): number; + + /* Output the current list of Utility Timer Tasks - for debugging only + * @url http://www.espruino.com/Reference#l_E_dumpTimers + */ + function dumpTimers(): void; + + /* Dump any locked variables that aren't referenced from global - for debugging memory leaks only. + * @url http://www.espruino.com/Reference#l_E_dumpLockedVars + */ + function dumpLockedVars(): void; + + /* Dump any locked variables that aren't referenced from global - for debugging memory leaks only. + * @url http://www.espruino.com/Reference#l_E_dumpFreeList + */ + function dumpFreeList(): void; + + /* Show fragmentation. + *
    + *
  • is free space
  • + *
  • # is a normal variable
  • + *
  • L is a locked variable (address used, cannopt be moved)
  • + *
  • = represents data in a Flat String (must be contiguous)
  • + *
+ * @url http://www.espruino.com/Reference#l_E_dumpFragmentation + */ + function dumpFragmentation(): void; + + /*

Dumps a comma-separated list of all allocated variables + * along with the variables they link to. Can be used + * to visualise where memory is used.

+ * @url http://www.espruino.com/Reference#l_E_dumpVariables + */ + function dumpVariables(): void; + + /* BETA: defragment memory! + * @url http://www.espruino.com/Reference#l_E_defrag + */ + function defrag(): void; + + /*

Return the address in memory of the given variable. This can then + * be used with peek and poke functions. However, changing data in + * JS variables directly (flatAddress=false) will most likely result in a crash.

+ *

This functions exists to allow embedded targets to set up + * peripherals such as DMA so that they write directly to + * JS variables.

+ * See http://www.espruino.com/Internals for more information + * @url http://www.espruino.com/Reference#l_E_getAddressOf + */ + function getAddressOf(v: any, flatAddress: boolean): number; + + /* Search in an Object, Array, or Function + * @url http://www.espruino.com/Reference#l_E_lookupNoCase + */ + function lookupNoCase(haystack: any, needle: any, returnKey: boolean): any; + + /* Get the current interpreter state in a text form such that it can be copied to a new device + * @url http://www.espruino.com/Reference#l_E_dumpStr + */ + function dumpStr(): string; + + /* Set the seed for the random number generator used by Math.random(). + * @url http://www.espruino.com/Reference#l_E_srand + */ + function srand(v: number): void; + + /*

Unlike 'Math.random()' which uses a pseudo-random number generator, this + * method reads from the internal voltage reference several times, xoring and + * rotating to try and make a relatively random value from the noise in the + * signal.

+ * @url http://www.espruino.com/Reference#l_E_hwRand + */ + function hwRand(): number; + + /*

Perform a standard 32 bit CRC (Cyclic redundancy check) on the supplied data (one byte at a time) + * and return the result as an unsigned integer.

+ * @url http://www.espruino.com/Reference#l_E_CRC32 + */ + function CRC32(data: any): any; + + /* Convert hue, saturation and brightness to red, green and blue (packed into an integer if asArray==false or an array if asArray==true). + *

This replaces Graphics.setColorHSB and Graphics.setBgColorHSB. On devices with 24 bit colour it can + * be used as: Graphics.setColor(E.HSBtoRGB(h, s, b))

+ *

You can quickly set RGB items in an Array or Typed Array using array.set(E.HSBtoRGB(h, s, b,true), offset), + * which can be useful with arrays used with require("neopixel").write.

+ * @url http://www.espruino.com/Reference#l_E_HSBtoRGB + */ + function HSBtoRGB(hue: number, sat: number, bri: number, asArray: boolean): any; + + /*

Set a password on the console (REPL). When powered on, Espruino will + * then demand a password before the console can be used. If you want to + * lock the console immediately after this you can call E.lockConsole()

+ * To remove the password, call this function with no arguments. + *

Note: There is no protection against multiple password attempts, so someone + * could conceivably try every password in a dictionary.

+ *

Note: This password is stored in memory in plain text. If someone is able + * to execute arbitrary JavaScript code on the device (eg, you use eval on input + * from unknown sources) or read the device's firmware then they may be able to + * obtain it.

+ * @url http://www.espruino.com/Reference#l_E_setPassword + */ + function setPassword(password: any): void; + + /*

If a password has been set with E.setPassword(), this will lock the console + * so the password needs to be entered to unlock it.

+ * @url http://www.espruino.com/Reference#l_E_lockConsole + */ + function lockConsole(): void; + + /* Set the time zone to be used with Date objects. + * For example E.setTimeZone(1) will be GMT+0100 + * Time can be set with setTime. + * @url http://www.espruino.com/Reference#l_E_setTimeZone + */ + function setTimeZone(zone: number): void; + + /* Provide assembly to Espruino. + *

This function is not part of Espruino. Instead, it is detected + * by the Espruino IDE (or command-line tools) at upload time and is + * replaced with machine code and an E.nativeCall call.

+ * See the documentation on the Assembler for more information. + * @url http://www.espruino.com/Reference#l_E_asm + */ + function asm(callspec: any, assemblycode: any): void; + + /* Provides the ability to write C code inside your JavaScript file. + *

This function is not part of Espruino. Instead, it is detected + * by the Espruino IDE (or command-line tools) at upload time, is sent + * to our web service to be compiled, and is replaced with machine code + * and an E.nativeCall call.

+ * See the documentation on Inline C for more information and examples. + * @url http://www.espruino.com/Reference#l_E_compiledC + */ + function compiledC(code: any): void; + + /*

Forces a hard reboot of the microcontroller - as close as possible + * to if the reset pin had been toggled.

+ *

Note: This is different to reset(), which performs a software + * reset of Espruino (resetting the interpreter and pin states, but not + * all the hardware)

+ * @url http://www.espruino.com/Reference#l_E_reboot + */ + function reboot(): void; + + /*

USB HID will only take effect next time you unplug and re-plug your Espruino. If you're + * disconnecting it from power you'll have to make sure you have save()d after calling + * this function.

+ * @url http://www.espruino.com/Reference#l_E_setUSBHID + */ + function setUSBHID(opts: any): void; + + /* @url http://www.espruino.com/Reference#l_E_sendUSBHID + */ + function sendUSBHID(data: any): boolean; + + /*

In devices that come with batteries, this function returns + * the battery charge percentage as an integer between 0 and 100.

+ *

Note: this is an estimation only, based on battery voltage. + * The temperature of the battery (as well as the load being drawn + * from it at the time E.getBattery is called) will affect the + * readings.

+ * @url http://www.espruino.com/Reference#l_E_getBattery + */ + function getBattery(): number; + + /* Gets the RTC's current prescaler value if calibrate is undefined or false. + *

If calibrate is true, the low speed oscillator's speed is calibrated against the high speed + * oscillator (usually +/- 20 ppm) and a suggested value to be fed into E.setRTCPrescaler(...) is returned.

+ * See E.setRTCPrescaler for more information. + * @url http://www.espruino.com/Reference#l_E_getRTCPrescaler + */ + function getRTCPrescaler(calibrate: boolean): number; + +} + +/* Creates an InternalError object + * @url http://www.espruino.com/Reference#l_InternalError_InternalError + */ +declare function InternalError(message: any): any; + +type InternalError = { +} + +/* Creates a pin from the given argument (or returns undefined if no argument) + * @url http://www.espruino.com/Reference#l_Pin_Pin + */ +declare function Pin(value: any): any; + +type Pin = { + /* Returns the input state of the pin as a boolean. + * Note: if you didn't call pinMode beforehand then this function will also reset the pin's state to "input" + * @url http://www.espruino.com/Reference#l_Pin_read + */ + read: () => boolean; + + /* Sets the output state of the pin to a 1 + * Note: if you didn't call pinMode beforehand then this function will also reset the pin's state to "output" + * @url http://www.espruino.com/Reference#l_Pin_set + */ + set: () => void; + + /* Sets the output state of the pin to a 0 + * Note: if you didn't call pinMode beforehand then this function will also reset the pin's state to "output" + * @url http://www.espruino.com/Reference#l_Pin_reset + */ + reset: () => void; + + /* Sets the output state of the pin to the parameter given + * Note: if you didn't call pinMode beforehand then this function will also reset the pin's state to "output" + * @url http://www.espruino.com/Reference#l_Pin_write + */ + write: (value: boolean) => void; + + /* Sets the output state of the pin to the parameter given at the specified time. + * Note: this doesn't change the mode of the pin to an output. To do that, you need to use pin.write(0) or pinMode(pin, 'output') first. + * @url http://www.espruino.com/Reference#l_Pin_writeAtTime + */ + writeAtTime: (value: boolean, time: number) => void; + + /* Return the current mode of the given pin. See pinMode for more information. + * @url http://www.espruino.com/Reference#l_Pin_getMode + */ + getMode: () => any; + + /* Set the mode of the given pin. See pinMode for more information on pin modes. + * @url http://www.espruino.com/Reference#l_Pin_mode + */ + mode: (mode: any) => void; + + /* Toggles the state of the pin from off to on, or from on to off. + * Note: This method doesn't currently work on the ESP8266 port of Espruino. + * Note: if you didn't call pinMode beforehand then this function will also reset the pin's state to "output" + * @url http://www.espruino.com/Reference#l_Pin_toggle + */ + toggle: () => boolean; + +} + +/* Create a software OneWire implementation on the given pin + * @url http://www.espruino.com/Reference#l_OneWire_OneWire + */ +declare function OneWire(pin: Pin): any; + +type OneWire = { + /* Perform a reset cycle + * @url http://www.espruino.com/Reference#l_OneWire_reset + */ + reset: () => boolean; + + /* Select a ROM - always performs a reset first + * @url http://www.espruino.com/Reference#l_OneWire_select + */ + select: (rom: any) => void; + + /* Skip a ROM + * @url http://www.espruino.com/Reference#l_OneWire_skip + */ + skip: () => void; + + /* Write one or more bytes + * @url http://www.espruino.com/Reference#l_OneWire_write + */ + write: (data: any, power: boolean) => void; + + /* Read a byte + * @url http://www.espruino.com/Reference#l_OneWire_read + */ + read: (count: any) => any; + + /* Search for devices + * @url http://www.espruino.com/Reference#l_OneWire_search + */ + search: (command: number) => any; + +} + +/* This module allows you to read and write the nonvolatile flash memory of your device. + *

Also see the Storage library, which provides a safer file-like + * interface to nonvolatile storage.

+ *

It should be used with extreme caution, as it is easy to overwrite parts of Flash + * memory belonging to Espruino or even its bootloader. If you damage the bootloader + * then you may need external hardware such as a USB-TTL converter to restore it. For + * more information on restoring the bootloader see Advanced Reflashing in your + * board's reference pages.

+ *

To see which areas of memory you can and can't overwrite, look at the values + * reported by process.memory().

+ *

Note: On Nordic platforms there are checks in place to help you avoid + * 'bricking' your device be damaging the bootloader. You can disable these with E.setFlags({unsafeFlash:1})

+ * @url http://www.espruino.com/Reference#l_Flash_undefined + */ +declare function Flash(): void; + +declare namespace Flash { + /* Returns the start and length of the flash page containing the given address. + * @url http://www.espruino.com/Reference#l_Flash_getPage + */ + function getPage(addr: number): any; + + /*

This method returns an array of objects of the form {addr : #, length : #}, representing + * contiguous areas of flash memory in the chip that are not used for anything.

+ *

The memory areas returned are on page boundaries. This means that you can + * safely erase the page containing any address here, and you won't risk + * deleting part of the Espruino firmware.

+ * @url http://www.espruino.com/Reference#l_Flash_getFree + */ + function getFree(): any; + + /* Erase a page of flash memory + * @url http://www.espruino.com/Reference#l_Flash_erasePage + */ + function erasePage(addr: any): void; + + /* Write data into memory at the given address + *

In flash memory you may only turn bits that are 1 into bits that are 0. If + * you're writing data into an area that you have already written (so read + * doesn't return all 0xFF) you'll need to call erasePage to clear the + * entire page.

+ * @url http://www.espruino.com/Reference#l_Flash_write + */ + function write(data: any, addr: number): void; + + /* Read flash memory from the given address + * @url http://www.espruino.com/Reference#l_Flash_read + */ + function read(length: number, addr: number): any; + +} + +/* Built-in class that caches the modules used by the require command + * @url http://www.espruino.com/Reference#Modules + */ +declare function Modules(): void; + +declare namespace Modules { + /* Return an array of module names that have been cached + * @url http://www.espruino.com/Reference#l_Modules_getCached + */ + function getCached(): any; + + /* Remove the given module from the list of cached modules + * @url http://www.espruino.com/Reference#l_Modules_removeCached + */ + function removeCached(id: any): void; + + /* Remove all cached modules + * @url http://www.espruino.com/Reference#l_Modules_removeAllCached + */ + function removeAllCached(): void; + + /* Add the given module to the cache + * @url http://www.espruino.com/Reference#l_Modules_addCached + */ + function addCached(id: any, sourcecode: any): void; + +} + +/* Create a software SPI port. This has limited functionality (no baud rate), but it can work on any pins. + * Use SPI.setup to configure this port. + * @url http://www.espruino.com/Reference#l_SPI_SPI + */ +declare function SPI(): any; + +type SPI = { + /* Send data down SPI, and return the result. Sending an integer will return an integer, a String will return a String, and anything else will return a Uint8Array. + * Sending multiple bytes in one call to send is preferable as they can then be transmitted end to end. Using multiple calls to send() will result in significantly slower transmission speeds. + * For maximum speeds, please pass either Strings or Typed Arrays as arguments. Note that you can even pass arrays of arrays, like [1,[2,3,4],5] + * @url http://www.espruino.com/Reference#l_SPI_send + */ + send: (data: any, nss_pin: Pin) => any; + + /* Write a character or array of characters to SPI - without reading the result back. + * For maximum speeds, please pass either Strings or Typed Arrays as arguments. + * @url http://www.espruino.com/Reference#l_SPI_write + */ + write: (data: any) => void; + + /* Send data down SPI, using 4 bits for each 'real' bit (MSB first). This can be useful for faking one-wire style protocols + * Sending multiple bytes in one call to send is preferable as they can then be transmitted end to end. Using multiple calls to send() will result in significantly slower transmission speeds. + * @url http://www.espruino.com/Reference#l_SPI_send4bit + */ + send4bit: (data: any, bit0: number, bit1: number, nss_pin: Pin) => void; + + /* Send data down SPI, using 8 bits for each 'real' bit (MSB first). This can be useful for faking one-wire style protocols + * Sending multiple bytes in one call to send is preferable as they can then be transmitted end to end. Using multiple calls to send() will result in significantly slower transmission speeds. + * @url http://www.espruino.com/Reference#l_SPI_send8bit + */ + send8bit: (data: any, bit0: number, bit1: number, nss_pin: Pin) => void; + +} + +declare namespace SPI { + /* Try and find an SPI hardware device that will work on this pin (eg. SPI1) + * May return undefined if no device can be found. + * @url http://www.espruino.com/Reference#l_SPI_find + */ + function find(pin: Pin): any; + +} + +/* The first SPI port + * @url http://www.espruino.com/Reference#l__global_SPI1 + */ +declare const SPI1: SPI; + +/* The second SPI port + * @url http://www.espruino.com/Reference#l__global_SPI2 + */ +declare const SPI2: SPI; + +/* The third SPI port + * @url http://www.espruino.com/Reference#l__global_SPI3 + */ +declare const SPI3: SPI; + +/* Create a software I2C port. This has limited functionality (no baud rate), but it can work on any pins. + * Use I2C.setup to configure this port. + * @url http://www.espruino.com/Reference#l_I2C_I2C + */ +declare function I2C(): any; + +type I2C = { + /* Set up this I2C port + * If not specified in options, the default pins are used (usually the lowest numbered pins on the lowest port that supports this peripheral) + * @url http://www.espruino.com/Reference#l_I2C_setup + */ + setup: (options: any) => void; + + /* Transmit to the slave device with the given address. This is like Arduino's beginTransmission, write, and endTransmission rolled up into one. + * @url http://www.espruino.com/Reference#l_I2C_writeTo + */ + writeTo: (address: any, data: any) => void; + + /* Request bytes from the given slave device, and return them as a Uint8Array (packed array of bytes). This is like using Arduino Wire's requestFrom, available and read functions. Sends a STOP + * @url http://www.espruino.com/Reference#l_I2C_readFrom + */ + readFrom: (address: any, quantity: number) => Uint8Array; + +} + +declare namespace I2C { + /* Try and find an I2C hardware device that will work on this pin (eg. I2C1) + * May return undefined if no device can be found. + * @url http://www.espruino.com/Reference#l_I2C_find + */ + function find(pin: Pin): any; + +} + +/* The first I2C port + * @url http://www.espruino.com/Reference#l__global_I2C1 + */ +declare const I2C1: I2C; + +/* The second I2C port + * @url http://www.espruino.com/Reference#l__global_I2C2 + */ +declare const I2C2: I2C; + +/* The third I2C port + * @url http://www.espruino.com/Reference#l__global_I2C3 + */ +declare const I2C3: I2C; + +/* Set the current font + * @url http://www.espruino.com/Reference#l__global_setFont9x18 + */ +declare function setFont9x18(scale: number): Graphics; + +/* Set the current font + * @url http://www.espruino.com/Reference#l__global_setFont12x26 + */ +declare function setFont12x26(scale: number): Graphics; + +/* Set the current font + * @url http://www.espruino.com/Reference#l__global_setFont11x20 + */ +declare function setFont11x20(scale: number): Graphics; + +/* The pin connected to the 'A' button. Reads as 1 when pressed, 0 when not + * @url http://www.espruino.com/Reference#l__global_BTNA + */ +declare const BTNA: Pin; + +/* The pin connected to the 'B' button. Reads as 1 when pressed, 0 when not + * @url http://www.espruino.com/Reference#l__global_BTNB + */ +declare const BTNB: Pin; + +/* The pin connected to the up button. Reads as 1 when pressed, 0 when not + * @url http://www.espruino.com/Reference#l__global_BTNU + */ +declare const BTNU: Pin; + +/* The pin connected to the down button. Reads as 1 when pressed, 0 when not + * @url http://www.espruino.com/Reference#l__global_BTND + */ +declare const BTND: Pin; + +/* The pin connected to the left button. Reads as 1 when pressed, 0 when not + * @url http://www.espruino.com/Reference#l__global_BTNL + */ +declare const BTNL: Pin; + +/* The pin connected to the right button. Reads as 1 when pressed, 0 when not + * @url http://www.espruino.com/Reference#l__global_BTNR + */ +declare const BTNR: Pin; + +/* The pin connected to Corner #1 + * @url http://www.espruino.com/Reference#l__global_CORNER1 + */ +declare const CORNER1: Pin; + +/* The pin connected to Corner #2 + * @url http://www.espruino.com/Reference#l__global_CORNER2 + */ +declare const CORNER2: Pin; + +/* The pin connected to Corner #3 + * @url http://www.espruino.com/Reference#l__global_CORNER3 + */ +declare const CORNER3: Pin; + +/* The pin connected to Corner #4 + * @url http://www.espruino.com/Reference#l__global_CORNER4 + */ +declare const CORNER4: Pin; + +/* The pin connected to Corner #5 + * @url http://www.espruino.com/Reference#l__global_CORNER5 + */ +declare const CORNER5: Pin; + +/* The pin connected to Corner #6 + * @url http://www.espruino.com/Reference#l__global_CORNER6 + */ +declare const CORNER6: Pin; + +/* The Bangle.js's vibration motor. + * @url http://www.espruino.com/Reference#l__global_VIBRATE + */ +declare const VIBRATE: Pin; + +/* On most Espruino board there are LEDs, in which case LED will be an actual Pin. + *

On Bangle.js there are no LEDs, so to remain compatible with example code that might + * expect an LED, this is an object that behaves like a pin, but which just displays + * a circle on the display

+ * @url http://www.espruino.com/Reference#l__global_LED + */ +declare const LED: any; + +/* On most Espruino board there are LEDs, in which case LED1 will be an actual Pin. + *

On Bangle.js there are no LEDs, so to remain compatible with example code that might + * expect an LED, this is an object that behaves like a pin, but which just displays + * a circle on the display

+ * @url http://www.espruino.com/Reference#l__global_LED1 + */ +declare const LED1: any; + +/* On most Espruino board there are LEDs, in which case LED2 will be an actual Pin. + *

On Bangle.js there are no LEDs, so to remain compatible with example code that might + * expect an LED, this is an object that behaves like a pin, but which just displays + * a circle on the display

+ * @url http://www.espruino.com/Reference#l__global_LED2 + */ +declare const LED2: any; + +/* Note: This function is only available on the BBC micro:bit board + * Get the current acceleration of the micro:bit from the on-board accelerometer + * This is deprecated. Please use Microbit.accel instead. + * @url http://www.espruino.com/Reference#l__global_acceleration + */ +declare function acceleration(): any; + +/* Note: This function is only available on the BBC micro:bit board + * Get the current compass position for the micro:bit from the on-board magnetometer + * This is deprecated. Please use Microbit.mag instead. + * @url http://www.espruino.com/Reference#l__global_compass + */ +declare function compass(): any; + +/* The pin marked SDA on the Arduino pin footprint. This is connected directly to pin A4. + * @url http://www.espruino.com/Reference#l__global_SDA + */ +declare const SDA: Pin; + +/* The pin marked SDA on the Arduino pin footprint. This is connected directly to pin A5. + * @url http://www.espruino.com/Reference#l__global_SCL + */ +declare const SCL: Pin; + +/* @url http://www.espruino.com/Reference#l__global_MOS1 + */ +declare const MOS1: Pin; + +/* @url http://www.espruino.com/Reference#l__global_MOS2 + */ +declare const MOS2: Pin; + +/* @url http://www.espruino.com/Reference#l__global_MOS3 + */ +declare const MOS3: Pin; + +/* @url http://www.espruino.com/Reference#l__global_MOS4 + */ +declare const MOS4: Pin; + +/* @url http://www.espruino.com/Reference#l__global_IOEXT0 + */ +declare const IOEXT0: Pin; + +/* @url http://www.espruino.com/Reference#l__global_IOEXT1 + */ +declare const IOEXT1: Pin; + +/* @url http://www.espruino.com/Reference#l__global_IOEXT2 + */ +declare const IOEXT2: Pin; + +/* @url http://www.espruino.com/Reference#l__global_IOEXT3 + */ +declare const IOEXT3: Pin; + +/* On Puck.js V2 (not v1.0) this is the pin that controls the FET, for high-powered outputs. + * @url http://www.espruino.com/Reference#l__global_FET + */ +declare const FET: Pin; + +/* @url http://www.espruino.com/Reference#l__global_HIGH + */ +declare const HIGH: number; + +/* @url http://www.espruino.com/Reference#l__global_LOW + */ +declare const LOW: number; + +/* Read 8 bits of memory at the given location - DANGEROUS! + * @url http://www.espruino.com/Reference#l__global_peek8 + */ +declare function peek8(addr: number, count: number): any; + +/* Write 8 bits of memory at the given location - VERY DANGEROUS! + * @url http://www.espruino.com/Reference#l__global_poke8 + */ +declare function poke8(addr: number, value: any): void; + +/* Read 16 bits of memory at the given location - DANGEROUS! + * @url http://www.espruino.com/Reference#l__global_peek16 + */ +declare function peek16(addr: number, count: number): any; + +/* Write 16 bits of memory at the given location - VERY DANGEROUS! + * @url http://www.espruino.com/Reference#l__global_poke16 + */ +declare function poke16(addr: number, value: any): void; + +/* Read 32 bits of memory at the given location - DANGEROUS! + * @url http://www.espruino.com/Reference#l__global_peek32 + */ +declare function peek32(addr: number, count: number): any; + +/* Write 32 bits of memory at the given location - VERY DANGEROUS! + * @url http://www.espruino.com/Reference#l__global_poke32 + */ +declare function poke32(addr: number, value: any): void; + +/* Get the analog value of the given pin + * This is different to Arduino which only returns an integer between 0 and 1023 + * However only pins connected to an ADC will work (see the datasheet) + * Note: if you didn't call pinMode beforehand then this function will also reset pin's state to "analog" + * @url http://www.espruino.com/Reference#l__global_analogRead + */ +declare function analogRead(pin: Pin): number; + +/* Set the analog Value of a pin. It will be output using PWM. + * Objects can contain: + *
    + *
  • freq - pulse frequency in Hz, eg. analogWrite(A0,0.5,{ freq : 10 }); - specifying a frequency will force PWM output, even if the pin has a DAC
  • + *
  • soft - boolean, If true software PWM is used if hardware is not available.
  • + *
  • forceSoft - boolean, If true software PWM is used even if hardware PWM or a DAC is available

    + * Note: if you didn't call pinMode beforehand then this function will also reset pin's state to "output" + *
  • + *
+ * @url http://www.espruino.com/Reference#l__global_analogWrite + */ +declare function analogWrite(pin: Pin, value: number, options: any): void; + +/* Pulse the pin with the value for the given time in milliseconds. It uses a hardware timer to produce accurate pulses, and returns immediately (before the pulse has finished). Use digitalPulse(A0,1,0) to wait until a previous pulse has finished. + * eg. digitalPulse(A0,1,5); pulses A0 high for 5ms. digitalPulse(A0,1,[5,2,4]); pulses A0 high for 5ms, low for 2ms, and high for 4ms + * Note: if you didn't call pinMode beforehand then this function will also reset pin's state to "output" + * digitalPulse is for SHORT pulses that need to be very accurate. If you're doing anything over a few milliseconds, use setTimeout instead. + * @url http://www.espruino.com/Reference#l__global_digitalPulse + */ +declare function digitalPulse(pin: Pin, value: boolean, time: any): void; + +/* Set the digital value of the given pin. + * Note: if you didn't call pinMode beforehand then this function will also reset pin's state to "output" + *

If pin argument is an array of pins (eg. [A2,A1,A0]) the value argument will be treated + * as an array of bits where the last array element is the least significant bit.

+ *

In this case, pin values are set least significant bit first (from the right-hand side + * of the array of pins). This means you can use the same pin multiple times, for + * example digitalWrite([A1,A1,A0,A0],0b0101) would pulse A0 followed by A1.

+ *

If the pin argument is an object with a write method, the write method will + * be called with the value passed through.

+ * @url http://www.espruino.com/Reference#l__global_digitalWrite + */ +declare function digitalWrite(pin: Pin, value: number): void; + +/* Get the digital value of the given pin. + * Note: if you didn't call pinMode beforehand then this function will also reset pin's state to "input" + *

If the pin argument is an array of pins (eg. [A2,A1,A0]) the value returned will be an number where + * the last array element is the least significant bit, for example if A0=A1=1 and A2=0, digitalRead([A2,A1,A0]) == 0b011

+ *

If the pin argument is an object with a read method, the read method will be called and the integer value it returns + * passed back.

+ * @url http://www.espruino.com/Reference#l__global_digitalRead + */ +declare function digitalRead(pin: Pin): number; + +/* Set the mode of the given pin. + *
    + *
  • auto/undefined - Don't change state, but allow digitalWrite/etc to automatically change state as appropriate
  • + *
  • analog - Analog input
  • + *
  • input - Digital input
  • + *
  • input_pullup - Digital input with internal ~40k pull-up resistor
  • + *
  • input_pulldown - Digital input with internal ~40k pull-down resistor
  • + *
  • output - Digital output
  • + *
  • opendrain - Digital output that only ever pulls down to 0v. Sending a logical 1 leaves the pin open circuit
  • + *
  • opendrain_pullup - Digital output that pulls down to 0v. Sending a logical 1 enables internal ~40k pull-up resistor
  • + *
  • af_output - Digital output from built-in peripheral
  • + *
  • af_opendrain - Digital output from built-in peripheral that only ever pulls down to 0v. Sending a logical 1 leaves the pin open circuit

    + *

    Note: digitalRead/digitalWrite/etc set the pin mode automatically unless pinMode has been called first. + * If you want digitalRead/etc to set the pin mode automatically after you have called pinMode, simply call it again + * with no mode argument (pinMode(pin)), auto as the argument (pinMode(pin, "auto")), or with the 3rd 'automatic' + * argument set to true (pinMode(pin, "output", true)).

    + *
  • + *
+ * @url http://www.espruino.com/Reference#l__global_pinMode + */ +declare function pinMode(pin: Pin, mode: any, automatic: boolean): void; + +/* Return the current mode of the given pin. See pinMode for more information on returned values. + * @url http://www.espruino.com/Reference#l__global_getPinMode + */ +declare function getPinMode(pin: Pin): any; + +/* Clear the Watch that was created with setWatch. If no parameter is supplied, all watches will be removed. + * To avoid accidentally deleting all Watches, if a parameter is supplied but is undefined then an Exception will be thrown. + * @url http://www.espruino.com/Reference#l__global_clearWatch + */ +declare function clearWatch(id: any): void; + +/* When Espruino is busy, set the pin specified here high. Set this to undefined to disable the feature. + * @url http://www.espruino.com/Reference#l__global_setBusyIndicator + */ +declare function setBusyIndicator(pin: Pin): void; + +/* When Espruino is asleep, set the pin specified here low (when it's awake, set it high). Set this to undefined to disable the feature. + * Please see http://www.espruino.com/Power+Consumption for more details on this. + * @url http://www.espruino.com/Reference#l__global_setSleepIndicator + */ +declare function setSleepIndicator(pin: Pin): void; + +/* Set whether we can enter deep sleep mode, which reduces power consumption to around 100uA. This only works on STM32 Espruino Boards (nRF52 boards sleep automatically). + * Please see http://www.espruino.com/Power+Consumption for more details on this. + * @url http://www.espruino.com/Reference#l__global_setDeepSleep + */ +declare function setDeepSleep(sleep: boolean): void; + +/* Output debugging information + * Note: This is not included on boards with low amounts of flash memory, or the Espruino board. + * @url http://www.espruino.com/Reference#l__global_trace + */ +declare function trace(root: any): void; + +/* Output current interpreter state in a text form such that it can be copied to a new device + * Espruino keeps its current state in RAM (even if the function code is stored in Flash). When you type dump() it dumps the current state of code in RAM plus the hardware state, then if there's code saved in flash it writes "// Code saved with E.setBootCode" and dumps that too. + * Note: 'Internal' functions are currently not handled correctly. You will need to recreate these in the onInit function. + * @url http://www.espruino.com/Reference#l__global_dump + */ +declare function dump(): void; + +/*

Restart and load the program out of flash - this has an effect similar to + * completely rebooting Espruino (power off/power on), but without actually + * performing a full reset of the hardware.

+ *

This command only executes when the Interpreter returns to the Idle state - for + * instance a=1;load();a=2; will still leave 'a' as undefined (or what it was + * set to in the saved program).

+ *

Espruino will resume from where it was when you last typed save(). + * If you want code to be executed right after loading (for instance to initialise + * devices connected to Espruino), add an init event handler to E with + * E.on('init', function() { ... your_code ... });. This will then be automatically + * executed by Espruino every time it starts.

+ *

If you specify a filename in the argument then that file will be loaded + * from Storage after reset in much the same way as calling reset() then eval(require("Storage").read(filename))

+ * @url http://www.espruino.com/Reference#l__global_load + */ +declare function load(filename: any): void; + +/*

Save the state of the interpreter into flash (including the results of calling + * setWatch, setInterval, pinMode, and any listeners). The state will then be loaded automatically + * every time Espruino powers on or is hard-reset. To see what will get saved you can call dump().

+ *

Note: If you set up intervals/etc in onInit() and you have already called onInit + * before running save(), when Espruino resumes there will be two copies of your intervals - + * the ones from before the save, and the ones from after - which may cause you problems.

+ *

For more information about this and other options for saving, please see + * the Saving code on Espruino page.

+ *

This command only executes when the Interpreter returns to the Idle state - for + * instance a=1;save();a=2; will save 'a' as 2.

+ *

When Espruino powers on, it will resume from where it was when you typed save(). + * If you want code to be executed right after loading (for instance to initialise + * devices connected to Espruino), add a function called onInit, or add a init + * event handler to E with E.on('init', function() { ... your_code ... });. + * This will then be automatically executed by Espruino every time it starts.

+ *

In order to stop the program saved with this command being loaded automatically, + * check out the Troubleshooting guide

+ * @url http://www.espruino.com/Reference#l__global_save + */ +declare function save(): void; + +/* Reset the interpreter - clear program memory in RAM, and do not load a saved program from flash. This does NOT reset the underlying hardware (which allows you to reset the device without it disconnecting from USB). + * This command only executes when the Interpreter returns to the Idle state - for instance a=1;reset();a=2; will still leave 'a' as undefined. + * The safest way to do a full reset is to hit the reset button. + *

If reset() is called with no arguments, it will reset the board's state in + * RAM but will not reset the state in flash. When next powered on (or when + * load() is called) the board will load the previously saved code.

+ *

Calling reset(true) will cause all saved code in flash memory to + * be cleared as well.

+ * @url http://www.espruino.com/Reference#l__global_reset + */ +declare function reset(clearFlash: boolean): void; + +/* Print the supplied string(s) to the console + * Note: If you're connected to a computer (not a wall adaptor) via USB but you are not running a terminal app then when you print data Espruino may pause execution and wait until the computer requests the data it is trying to print. + * @url http://www.espruino.com/Reference#l__global_print + */ +declare function print(text: any): void; + +/* Fill the console with the contents of the given function, so you can edit it. + * NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it. + * @url http://www.espruino.com/Reference#l__global_edit + */ +declare function edit(funcName: any): void; + +/* Should Espruino echo what you type back to you? true = yes (Default), false = no. When echo is off, the result of executing a command is not returned. Instead, you must use 'print' to send output. + * @url http://www.espruino.com/Reference#l__global_echo + */ +declare function echo(echoOn: boolean): void; + +/* Return the current system time in Seconds (as a floating point number) + * @url http://www.espruino.com/Reference#l__global_getTime + */ +declare function getTime(): number; + +/* Get the serial number of this board + * @url http://www.espruino.com/Reference#l__global_getSerial + */ +declare function getSerial(): any; + +/* Change the Interval on a callback created with setInterval, for example: + * var id = setInterval(function () { print('foo'); }, 1000); // every second + * changeInterval(id, 1500); // now runs every 1.5 seconds + *

This takes effect immediately and resets the timeout, so in the example above, + * regardless of when you call changeInterval, the next interval will occur 1500ms + * after it.

+ * @url http://www.espruino.com/Reference#l__global_changeInterval + */ +declare function changeInterval(id: any, time: number): void; diff --git a/typescript/types/serial.d.ts b/typescript/types/serial.d.ts deleted file mode 100644 index 73702c94c..000000000 --- a/typescript/types/serial.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare type Serial = {}; diff --git a/typescript/types/spi.d.ts b/typescript/types/spi.d.ts deleted file mode 100644 index 5ca3e7bee..000000000 --- a/typescript/types/spi.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -declare type SPI = { - find(pin: Pin): SPIInstance | undefined; - new (): SPIInstance; -}; - -type TOrArray = T | TOrArray[]; -type TArrObj = T | TArrObj[] | { data: TArrObj; count: number }; -type NumStrArr = TOrArray; - -declare type SPIInstance = { - send(data: TArrObj, nss_pin: number): any; - send4bit(data: NumStrArr, bit0: number, bit1: number, nss_pin: number): void; - send8bit(data: NumStrArr, bit0: number, bit1: number, nss_pin: number): void; - setup(options: { - sck?: Pin; - miso?: Pin; - mosi?: Pin; - baud?: number; - mode?: 0 | 1 | 2 | 3; - order?: "msb" | "lsb"; - bits?: number; - }): void; - write(...data: Array): void; -}; diff --git a/typescript/types/storage.d.ts b/typescript/types/storage.d.ts deleted file mode 100644 index d71172ac9..000000000 --- a/typescript/types/storage.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -type FileData = string | Array | Object; - -declare interface Storage { - compact: () => void; - erase: (name: string) => void; - eraseAll: () => void; - getFree: () => number; - hash: (regex?: RegExp) => number; // More specifically it returns Uint32 - list: (regex?: RegExp, filter?: { sf: boolean }) => string[]; - open: (name: string, mode: "r" | "w" | "a") => StorageFile; - read: (name: string, offset?: number, length?: number) => string | undefined; - readArrayBuffer: (name: string) => ArrayBuffer | undefined; - readJSON: (name: string, noExceptions?: boolean) => any; - write: ( - name: string, - data: FileData, - offset?: number, - size?: number - ) => boolean; - writeJSON: (name: string, data: any) => boolean; -} - -declare interface StorageFile { - erase: () => void; - getLength: () => number; - read: (len: number) => string | undefined; - readLine: () => string | undefined; - write: (data: FileData) => string; -} From bc123a77885839b8fdf3ebbcdbfdd6cfc0432b87 Mon Sep 17 00:00:00 2001 From: qucchia Date: Wed, 20 Jul 2022 15:35:57 +0200 Subject: [PATCH 095/821] Fix documentation comments --- typescript/generate.js | 136 +++ typescript/types/main.d.ts | 1986 +++++++++++++++++++++++------------- 2 files changed, 1423 insertions(+), 699 deletions(-) create mode 100644 typescript/generate.js diff --git a/typescript/generate.js b/typescript/generate.js new file mode 100644 index 000000000..ffa8d6bf2 --- /dev/null +++ b/typescript/generate.js @@ -0,0 +1,136 @@ +const fetch = (...args) => + import("node-fetch").then(({ default: fetch }) => fetch(...args)); +const fs = require("fs"); + +const TAKEN_IDENTIFIERS = ["ArrayBufferView", "crypto", "File", "Storage"]; + +function getType(type) { + if (type.startsWith("+")) type = type.substring(1); + if (TAKEN_IDENTIFIERS.includes(type)) return "Espruino" + type; + switch (type) { + case undefined: + case "?": + return "any"; + case "bool": + return "boolean"; + case "String": + return "string"; + case "Array": + return "any[]"; + case "Promise": + return "Promise"; + default: + return type; + } +} + +let indent = 0; +let topLevel = true; +let file = []; + +function add(text) { + if (text) + file = file.concat( + text.split("\n").map((line) => " ".repeat(indent) + line) + ); + else file.push(""); +} + +function get(key, obj, isGlobal) { + if (key.startsWith("!")) return; + if (key === "prototype") return; + if (key in global && isGlobal) return; + if (TAKEN_IDENTIFIERS.includes(key)) key = "Espruino" + key; + add( + "/**\n" + + (obj["!doc"] || "") + .split("\n") + .filter((line) => line) + .map((line) => line.replace(/^

(.*)<\/p>$/, "$1")) + .concat([`@url ${obj["!url"]}`]) + .map((line) => " * " + line) + .join("\n") + + "\n */" + ); + + const type = obj["!type"] || "?"; + const hasProperties = Object.keys(obj).filter( + (key) => !key.startsWith("!") && key !== "prototype" + ).length; + if (type.startsWith("fn(")) { + const returnType = getType( + type.includes("->") ? type.replace(/^.*-> (.*)$/, "$1") : "void" + ); + let args = type.replace(/^fn\((.*)\).*/, "$1"); + if (args) + args = args + .split(", ") + .map((argument) => { + const pair = argument.split(": "); + if (pair[0] === "function") pair[0] = "fn"; + if (pair[0] === "var") pair[0] = "variable"; + pair[1] = getType(pair[1]); + return pair.join(": "); + }) + .join(", "); + if (topLevel) { + add( + `${indent ? "" : "declare "}function ${key}(${args}): ${returnType};` + ); + } else { + add(`${key}: (${args}) => ${returnType};`); + } + + if (hasProperties) { + add(""); + add(`declare namespace ${key} {`); + indent += 2; + for (const key in obj) { + get(key, obj[key], true); + } + indent -= 2; + add("}"); + } + } else { + if (hasProperties) { + add(`${indent ? "" : "declare "}const ${key}: ${getType(type)} & {`); + indent += 2; + topLevel = false; + for (const key in obj) { + get(key, obj[key], true); + } + topLevel = true; + indent -= 2; + add("};"); + } else if (topLevel) { + add(`${indent ? "" : "declare "}const ${key}: ${getType(type)};`); + } else { + add(`${key}: ${getType(type)}`); + } + } + + if (obj.prototype) { + add(""); + add(`type ${key} = {`); + indent += 2; + topLevel = false; + for (const key in obj.prototype) { + get(key, obj.prototype[key], true); + } + topLevel = true; + indent -= 2; + add("}"); + } + + add(""); +} + +fetch("https://espruino.com/json/espruino.json") + .then((response) => response.json()) + .then((json) => { + add("/* Note: This file was automatically generated. */\n"); + for (const key in json) { + get(key, json[key], true); + } + fs.writeFileSync("types/main.d.ts", file.join("\n")); + }); diff --git a/typescript/types/main.d.ts b/typescript/types/main.d.ts index b299f1ea9..af6c8253a 100644 --- a/typescript/types/main.d.ts +++ b/typescript/types/main.d.ts @@ -1,50 +1,59 @@ /* Note: This file was automatically generated. */ -/* This library allows you to write to Neopixel/WS281x/APA10x/SK6812 LED strips +/** + * This library allows you to write to Neopixel/WS281x/APA10x/SK6812 LED strips *

These use a high speed single-wire protocol which needs platform-specific * implementation on some devices - hence this library to simplify things.

* @url http://www.espruino.com/Reference#l_neopixel_undefined */ declare function neopixel(): void; -/* This library provides TV out capability on the Espruino and Espruino Pico. +/** + * This library provides TV out capability on the Espruino and Espruino Pico. * See the Television page for more information. * @url http://www.espruino.com/Reference#l_tv_undefined */ declare function tv(): void; -/* The NRF class is for controlling functionality of the Nordic nRF51/nRF52 chips. +/** + * The NRF class is for controlling functionality of the Nordic nRF51/nRF52 chips. * Most functionality is related to Bluetooth Low Energy, however there are also some functions related to NFC that apply to NRF52-based devices. * @url http://www.espruino.com/Reference#NRF */ declare function NRF(): void; declare namespace NRF { - /* @url http://www.espruino.com/Reference#l_NRF_getSecurityStatus + /** + * @url http://www.espruino.com/Reference#l_NRF_getSecurityStatus */ function getSecurityStatus(): any; - /* Get this device's default Bluetooth MAC address. + /** + * Get this device's default Bluetooth MAC address. *

For Puck.js, the last 5 characters of this (eg. ee:ff) * are used in the device's advertised Bluetooth name.

* @url http://www.espruino.com/Reference#l_NRF_getAddress */ function getAddress(): any; - /* @url http://www.espruino.com/Reference#l_NRF_setServices + /** + * @url http://www.espruino.com/Reference#l_NRF_setServices */ function setServices(data: any, options: any): void; - /* @url http://www.espruino.com/Reference#l_NRF_setAdvertising + /** + * @url http://www.espruino.com/Reference#l_NRF_setAdvertising */ function setAdvertising(data: any, options: any): void; - /* If a device is connected to Espruino, disconnect from it. + /** + * If a device is connected to Espruino, disconnect from it. * @url http://www.espruino.com/Reference#l_NRF_disconnect */ function disconnect(): void; - /*

Disable Bluetooth advertising and disconnect from any device that + /** + *

Disable Bluetooth advertising and disconnect from any device that * connected to Puck.js as a peripheral (this won't affect any devices * that Puck.js initiated connections to).

* This makes Puck.js undiscoverable, so it can't be connected to. @@ -53,14 +62,16 @@ declare namespace NRF { */ function sleep(): void; - /*

Enable Bluetooth advertising (this is enabled by default), which + /** + *

Enable Bluetooth advertising (this is enabled by default), which * allows other devices to discover and connect to Puck.js.

* Use NRF.sleep() to disable advertising. * @url http://www.espruino.com/Reference#l_NRF_wake */ function wake(): void; - /*

Restart the Bluetooth softdevice (if there is currently a BLE connection, + /** + *

Restart the Bluetooth softdevice (if there is currently a BLE connection, * it will queue a restart to be done when the connection closes).

*

You shouldn't need to call this function in normal usage. However, Nordic's * BLE softdevice has some settings that cannot be reset. For example there @@ -70,25 +81,29 @@ declare namespace NRF { */ function restart(callback: any): void; - /* Get the battery level in volts (the voltage that the NRF chip is running off of). + /** + * Get the battery level in volts (the voltage that the NRF chip is running off of). *

This is the battery level of the device itself - it has nothing to with any * device that might be connected.

* @url http://www.espruino.com/Reference#l_NRF_getBattery */ function getBattery(): number; - /*

This is just like NRF.setAdvertising, except instead of advertising + /** + *

This is just like NRF.setAdvertising, except instead of advertising * the data, it returns the packet that would be advertised as an array.

* @url http://www.espruino.com/Reference#l_NRF_getAdvertisingData */ function getAdvertisingData(data: any, options: any): any; - /* Set the BLE radio transmit power. The default TX power is 0 dBm, and + /** + * Set the BLE radio transmit power. The default TX power is 0 dBm, and * @url http://www.espruino.com/Reference#l_NRF_setTxPower */ function setTxPower(power: number): void; - /*

THIS IS DEPRECATED - please use NRF.setConnectionInterval for + /** + *

THIS IS DEPRECATED - please use NRF.setConnectionInterval for * peripheral and NRF.connect(addr, options)/BluetoothRemoteGATTServer.connect(options) * for central connections.

*

This sets the connection parameters - these affect the transfer speed and @@ -106,32 +121,38 @@ declare namespace NRF { */ function setLowPowerConnection(lowPower: boolean): void; - /* Send a USB HID report. HID must first be enabled with NRF.setServices({}, {hid: hid_report}) + /** + * Send a USB HID report. HID must first be enabled with NRF.setServices({}, {hid: hid_report}) * @url http://www.espruino.com/Reference#l_NRF_sendHIDReport */ function sendHIDReport(data: any, callback: any): void; - /* Check if Apple Notification Center Service (ANCS) is currently active on the BLE connection + /** + * Check if Apple Notification Center Service (ANCS) is currently active on the BLE connection * @url http://www.espruino.com/Reference#l_NRF_ancsIsActive */ function ancsIsActive(): boolean; - /* Send an ANCS action for a specific Notification UID. Corresponds to posaction/negaction in the 'ANCS' event that was received + /** + * Send an ANCS action for a specific Notification UID. Corresponds to posaction/negaction in the 'ANCS' event that was received * @url http://www.espruino.com/Reference#l_NRF_ancsAction */ function ancsAction(uid: number, positive: boolean): void; - /* Get ANCS info for a notification, eg: + /** + * Get ANCS info for a notification, eg: * @url http://www.espruino.com/Reference#l_NRF_ancsGetNotificationInfo */ function ancsGetNotificationInfo(uid: number): Promise; - /* Check if Apple Media Service (AMS) is currently active on the BLE connection + /** + * Check if Apple Media Service (AMS) is currently active on the BLE connection * @url http://www.espruino.com/Reference#l_NRF_amsIsActive */ function amsIsActive(): boolean; - /*

Get Apple Media Service (AMS) info for the current media player. + /** + *

Get Apple Media Service (AMS) info for the current media player. * "playbackinfo" returns a concatenation of three comma-separated values:

*