diff --git a/apps/aiclock/ChangeLog b/apps/aiclock/ChangeLog index 43236015e..c8c5c75cb 100644 --- a/apps/aiclock/ChangeLog +++ b/apps/aiclock/ChangeLog @@ -6,3 +6,4 @@ 0.06: ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc. 0.07: Use clock_info.addInteractive instead of a custom implementation 0.08: Use clock_info module as an app +0.09: clock_info now uses app name to maintain settings specifically for this clock face \ No newline at end of file diff --git a/apps/aiclock/aiclock.app.js b/apps/aiclock/aiclock.app.js index 350832367..8d76bc9fe 100644 --- a/apps/aiclock/aiclock.app.js +++ b/apps/aiclock/aiclock.app.js @@ -193,6 +193,7 @@ function queueDraw() { */ let clockInfoItems = clock_info.load(); let clockInfoMenu = clock_info.addInteractive(clockInfoItems, { + app : "aiclock", x : 0, y: 0, w: W, diff --git a/apps/aiclock/metadata.json b/apps/aiclock/metadata.json index ee9059f75..66f5de664 100644 --- a/apps/aiclock/metadata.json +++ b/apps/aiclock/metadata.json @@ -3,7 +3,7 @@ "name": "AI Clock", "shortName":"AI Clock", "icon": "aiclock.png", - "version":"0.08", + "version":"0.09", "readme": "README.md", "supports": ["BANGLEJS2"], "dependencies" : { "clock_info":"module" }, diff --git a/apps/clkinfocal/metadata.json b/apps/clkinfocal/metadata.json index 6d6dd63fc..1d14c3b59 100644 --- a/apps/clkinfocal/metadata.json +++ b/apps/clkinfocal/metadata.json @@ -3,6 +3,7 @@ "version":"0.01", "description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays the day of the month in the icon, and the weekday", "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], "type": "clkinfo", "tags": "clkinfo,calendar", "supports" : ["BANGLEJS2"], diff --git a/apps/clkinfocal/screenshot.png b/apps/clkinfocal/screenshot.png new file mode 100644 index 000000000..bb054e3a4 Binary files /dev/null and b/apps/clkinfocal/screenshot.png differ diff --git a/apps/hasensors/ChangeLog b/apps/hasensors/ChangeLog index 759f68777..7b3a63039 100644 --- a/apps/hasensors/ChangeLog +++ b/apps/hasensors/ChangeLog @@ -1 +1,3 @@ -0.01: New app! \ No newline at end of file +0.01: New app! +0.02: Add sensor icons + Customize code directly, remove config file diff --git a/apps/hasensors/boot.js b/apps/hasensors/boot.js index a9122be5d..efafbc8a3 100644 --- a/apps/hasensors/boot.js +++ b/apps/hasensors/boot.js @@ -1,5 +1,5 @@ (function () { - const sb = () => require('hasensors').sendBattery(); + const sb = () => require("hasensors").sendBattery(); Bangle.on("charging", sb); NRF.on("connect", () => setTimeout(sb, 2000)); setInterval(sb, 10 * 60 * 1000); diff --git a/apps/hasensors/custom.html b/apps/hasensors/custom.html index 805001701..265f80f46 100644 --- a/apps/hasensors/custom.html +++ b/apps/hasensors/custom.html @@ -39,14 +39,27 @@ your user profile.

- +

diff --git a/apps/hasensors/lib.js b/apps/hasensors/lib.js index 60cfb6da4..83072262c 100644 --- a/apps/hasensors/lib.js +++ b/apps/hasensors/lib.js @@ -1,35 +1,43 @@ // split out into a separate file to keep bootcode short. -function s(key) { - return (require('Storage').readJSON('hasensors.settings.js', true) || {})[key]; -} - +// placeholders are replaced by custom.html before upload function post(sensor, data) { - const url = s('url') + '/api/states/sensor.' + s('id') + '_' + sensor; + const url = "{url}/api/states/sensor.{id}_" + sensor; Bangle.http(url, { - method: 'POST', + method: "POST", body: JSON.stringify(data), headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + s('token'), + "Content-Type": "application/json", + Authorization: "Bearer {token}", } }); } exports.sendBattery = function () { if (!NRF.getSecurityStatus().connected) return; - post('battery_level', { - state: E.getBattery(), + const b = E.getBattery(), + c = Bangle.isCharging(); + let i = "mdi:battery"; + if (c) i += "-charging"; + + post("battery_state", { + state: c ? "charging" : "discharging", attributes: { - friendly_name: s('name') + " Battery Level", + friendly_name: "{name} Battery State", + icon: i + (c ? "" : "-minus"), + } + }); + + if (b<10) i += "-outline"; // there is no battery-0 + else if (b<100 || c) i += "-" + Math.floor(b/10)*10; // no battery-100 either + + post("battery_level", { + state: b, + attributes: { + friendly_name: "{name} Battery Level", unit_of_measurement: "%", device_class: "battery", state_class: "measurement", - } - }); - post('battery_state', { - state: Bangle.isCharging() ? 'charging' : 'discharging', - attributes: { - friendly_name: s('name') + " Battery State", + icon: i, } }); } \ No newline at end of file diff --git a/apps/hasensors/metadata.json b/apps/hasensors/metadata.json index 7713fadc7..106f11407 100644 --- a/apps/hasensors/metadata.json +++ b/apps/hasensors/metadata.json @@ -2,7 +2,7 @@ "id": "hasensors", "name": "Home Assistant Sensors", "shortName": "HA sensors", - "version": "0.01", + "version": "0.02", "description": "Send sensor values to Home Assistant using the Android Integration.", "icon": "ha.png", "type": "bootloader", @@ -14,8 +14,5 @@ "storage": [ {"name":"hasensors","url":"lib.js"}, {"name":"hasensors.boot.js","url":"boot.js"} - ], - "data": [ - {"name":"hasensors.settings.json"} ] } diff --git a/apps/kanawatch/ChangeLog b/apps/kanawatch/ChangeLog index 70a2d18bf..b2d2bab86 100644 --- a/apps/kanawatch/ChangeLog +++ b/apps/kanawatch/ChangeLog @@ -4,3 +4,4 @@ 0.04: Show a random kana every minute to improve learning 0.05: Tell clock widgets to hide 0.06: Fix exception when showing missing hiragana 'WO' +0.07: Fix regression in bitmap selection on some code paths diff --git a/apps/kanawatch/app.js b/apps/kanawatch/app.js index 2793e9a93..264058230 100644 --- a/apps/kanawatch/app.js +++ b/apps/kanawatch/app.js @@ -224,6 +224,7 @@ function drawKana (x, y) { g.setColor(0, 0, 0); g.fillRect(0, 0, g.getWidth(), 6 * (h / 8) + 1); g.setColor(1, 1, 1); + kana = hiramode ? hiragana[curkana] : katakana[curkana]; g.drawImage(kana, x + 20, 40, { scale: 1.6 }); g.setColor(1, 1, 1); g.setFont('Vector', 24); @@ -266,4 +267,3 @@ Bangle.setUI('clock'); Bangle.loadWidgets(); tickWatch(); setInterval(tickWatch, 1000 * 60); - diff --git a/apps/kanawatch/metadata.json b/apps/kanawatch/metadata.json index c0b048a37..d7a6f8c23 100644 --- a/apps/kanawatch/metadata.json +++ b/apps/kanawatch/metadata.json @@ -2,7 +2,7 @@ "id": "kanawatch", "name": "Kanawatch", "shortName": "Kanawatch", - "version": "0.06", + "version": "0.07", "type": "clock", "description": "Learn Hiragana and Katakana", "icon": "app.png", diff --git a/apps/lato/ChangeLog b/apps/lato/ChangeLog index 7e6c3b0d5..686f3b707 100644 --- a/apps/lato/ChangeLog +++ b/apps/lato/ChangeLog @@ -1,2 +1,3 @@ 0.01: first release 0.02: Use clock_info module as an app +0.03: clock_info now uses app name to maintain settings specifically for this clock face \ No newline at end of file diff --git a/apps/lato/app.js b/apps/lato/app.js index 6045d7f17..88e723a78 100644 --- a/apps/lato/app.js +++ b/apps/lato/app.js @@ -37,13 +37,13 @@ Graphics.prototype.setFontLatoSmall = function(scale) { { // must be inside our own scope here so that when we are unloaded everything disappears // we also define functions using 'let fn = function() {..}' for the same reason. function decls are global - + let draw = function() { var date = new Date(); var timeStr = require("locale").time(date,1); var h = g.getHeight(); var w = g.getWidth(); - + g.reset(); g.setColor(g.theme.bg); g.fillRect(Bangle.appRect); @@ -66,7 +66,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) { /** * clock_info_support * this is the callback function that get invoked by clockInfoMenu.redraw(); - * + * * We will display the image and text on the same line and centre the combined * length of the image+text * @@ -76,7 +76,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) { //g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg); g.reset().setFontLatoSmall(); g.setBgColor(options.bg).setColor(options.fg); - + //use info.text.toString(), steps does not have length defined var text_w = g.stringWidth(info.text.toString()); // gap between image and text @@ -88,7 +88,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) { // clear the whole info line, allow additional 2 pixels in case LatoFont overflows area g.clearRect(0, options.y -2, g.getWidth(), options.y+ 23 + 2); - + // draw the image if we have one if (info.img) { // image start @@ -110,8 +110,8 @@ Graphics.prototype.setFontLatoSmall = function(scale) { // clock_info_support // setup the way we wish to interact with the menu // the hl property defines the color the of the info when the menu is selected after tapping on it - let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); - + let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app : "lato", x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); + // timeout used to update every minute var drawTimeout; g.clear(); diff --git a/apps/lato/lato.fast.js b/apps/lato/lato.fast.js index 6045d7f17..88e723a78 100644 --- a/apps/lato/lato.fast.js +++ b/apps/lato/lato.fast.js @@ -37,13 +37,13 @@ Graphics.prototype.setFontLatoSmall = function(scale) { { // must be inside our own scope here so that when we are unloaded everything disappears // we also define functions using 'let fn = function() {..}' for the same reason. function decls are global - + let draw = function() { var date = new Date(); var timeStr = require("locale").time(date,1); var h = g.getHeight(); var w = g.getWidth(); - + g.reset(); g.setColor(g.theme.bg); g.fillRect(Bangle.appRect); @@ -66,7 +66,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) { /** * clock_info_support * this is the callback function that get invoked by clockInfoMenu.redraw(); - * + * * We will display the image and text on the same line and centre the combined * length of the image+text * @@ -76,7 +76,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) { //g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg); g.reset().setFontLatoSmall(); g.setBgColor(options.bg).setColor(options.fg); - + //use info.text.toString(), steps does not have length defined var text_w = g.stringWidth(info.text.toString()); // gap between image and text @@ -88,7 +88,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) { // clear the whole info line, allow additional 2 pixels in case LatoFont overflows area g.clearRect(0, options.y -2, g.getWidth(), options.y+ 23 + 2); - + // draw the image if we have one if (info.img) { // image start @@ -110,8 +110,8 @@ Graphics.prototype.setFontLatoSmall = function(scale) { // clock_info_support // setup the way we wish to interact with the menu // the hl property defines the color the of the info when the menu is selected after tapping on it - let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); - + let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app : "lato", x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); + // timeout used to update every minute var drawTimeout; g.clear(); diff --git a/apps/lato/lato.slow.js b/apps/lato/lato.slow.js index f458ecddd..f0d1c09a7 100644 --- a/apps/lato/lato.slow.js +++ b/apps/lato/lato.slow.js @@ -64,7 +64,7 @@ function queueDraw() { /** * clock_info_support * this is the callback function that get invoked by clockInfoMenu.redraw(); - * + * * We will display the image and text on the same line and centre the combined * length of the image+text * @@ -109,8 +109,8 @@ let clockInfoItems = require("clock_info").load(); * selected after tapping on it * */ -let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); - +let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app : "lato", x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); + g.clear(); // Show launcher when middle button pressed diff --git a/apps/lato/metadata.json b/apps/lato/metadata.json index 994fec77d..406413790 100644 --- a/apps/lato/metadata.json +++ b/apps/lato/metadata.json @@ -1,7 +1,7 @@ { "id": "lato", "name": "Lato", - "version": "0.02", + "version": "0.03", "description": "A Lato Font clock with fast load and clock_info", "readme": "README.md", "icon": "app.png", diff --git a/apps/lcdclock/ChangeLog b/apps/lcdclock/ChangeLog index 220369925..56ea03c2c 100644 --- a/apps/lcdclock/ChangeLog +++ b/apps/lcdclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Use clock_info module as an app +0.03: clock_info now uses app name to maintain settings specifically for this clock face \ No newline at end of file diff --git a/apps/lcdclock/app.js b/apps/lcdclock/app.js index 2bc23247c..3808f46fe 100644 --- a/apps/lcdclock/app.js +++ b/apps/lcdclock/app.js @@ -79,6 +79,6 @@ let clockInfoDraw = (itm, info, options) => { }; let clockInfoItems = require("clock_info").load(); -let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:R.x, y:R.y, w:midX-2, h:barY-R.y-2, draw : clockInfoDraw}); -let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { x:midX+2, y:R.y, w:midX-3, h:barY-R.y-2, draw : clockInfoDraw}); +let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:R.x, y:R.y, w:midX-2, h:barY-R.y-2, draw : clockInfoDraw}); +let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:midX+2, y:R.y, w:midX-3, h:barY-R.y-2, draw : clockInfoDraw}); } diff --git a/apps/lcdclock/metadata.json b/apps/lcdclock/metadata.json index e286dc017..b144c125e 100644 --- a/apps/lcdclock/metadata.json +++ b/apps/lcdclock/metadata.json @@ -1,6 +1,6 @@ { "id": "lcdclock", "name": "LCD Clock", - "version":"0.02", + "version":"0.03", "description": "A Casio-style clock, with ClockInfo areas at the top and bottom. Tap them and swipe up/down to toggle between different information", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/loadingscreen/ChangeLog b/apps/loadingscreen/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/loadingscreen/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/loadingscreen/README.md b/apps/loadingscreen/README.md new file mode 100644 index 000000000..2d6128a53 --- /dev/null +++ b/apps/loadingscreen/README.md @@ -0,0 +1,15 @@ +# Loading Screen Settings + +This app allows you to choose the loading screen that's displayed when swapping between apps on Bangle.js. + +## Usage + +Go to the Launcher, then `Settings`, then `Apps` and click on `Loading Screen` - you can then click to choose the loading screen you'll see. + +## Internals + +When reloading an app (fast load doesn't display anything), Bangle.js looks for a file called `.loading` (in versions 2v18+) which should be +an image. If it doesn't exist, the default `Loading...` screen is displayed. If it does exist and is less than 3 bytes long, +nothing is displayed, or if it's an image it is displayed, centered. + +This app sets that image file accordingly, but you can always upload your own file. \ No newline at end of file diff --git a/apps/loadingscreen/app.png b/apps/loadingscreen/app.png new file mode 100644 index 000000000..f9f36d1fc Binary files /dev/null and b/apps/loadingscreen/app.png differ diff --git a/apps/loadingscreen/metadata.json b/apps/loadingscreen/metadata.json new file mode 100644 index 000000000..199f4a2b4 --- /dev/null +++ b/apps/loadingscreen/metadata.json @@ -0,0 +1,14 @@ +{ "id": "loadingscreen", + "name": "Loading Screen", + "version":"0.01", + "description": "On Bangle.js 2v18+, this lets you customize the app loading screen via an new menu in Settings -> Apps", + "icon": "app.png", + "screenshots" : [ { "url":"screenshot1.png" }, { "url":"screenshot2.png" } ], + "tags": "tool", + "type": "settings", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"loadingscreen.settings.js","url":"settings.js"} + ] +} diff --git a/apps/loadingscreen/screenshot1.png b/apps/loadingscreen/screenshot1.png new file mode 100644 index 000000000..2eb4055c8 Binary files /dev/null and b/apps/loadingscreen/screenshot1.png differ diff --git a/apps/loadingscreen/screenshot2.png b/apps/loadingscreen/screenshot2.png new file mode 100644 index 000000000..3f5f81eef Binary files /dev/null and b/apps/loadingscreen/screenshot2.png differ diff --git a/apps/loadingscreen/settings.js b/apps/loadingscreen/settings.js new file mode 100644 index 000000000..8489423a2 --- /dev/null +++ b/apps/loadingscreen/settings.js @@ -0,0 +1,81 @@ +(function(back) { + function goBack() { + var im = require("Storage").read(".loading"); + if (im && im.length>3) { + var i = g.imageMetrics(im); + g.reset().drawImage(im, (g.getWidth()-i.width)/2, (g.getHeight()-i.height)/2); + } + setTimeout(back, 500); + } + + function savePattern(pattern) { + E.showMessage("Please wait..."); + var im = Graphics.createImage(pattern,"string"); + var w = g.getWidth(), h = g.getHeight(); + var b = Graphics.createArrayBuffer(w,h,1,{msb:true}); + for (var y=0;y {require("Storage").erase(".loading");goBack()}, + "No Screen" : () => {require("Storage").write(".loading","NO");goBack()}, // less than 3 chars and nothing is rendered + "Hourglass" : () => {require("Storage").write(".loading",iconHourglass());goBack()}, + "Retro" : () => {require("Storage").write(".loading",iconRetro());goBack()}, + "Stripes" : () => savePattern(` +XX..XX.. +.XX..XX. +..XX..XX +X..XX..X +XX..XX.. +.XX..XX. +..XX..XX +X..XX..X +`), + "Lines" : () => savePattern(` +XXXXXXXX +........ +XXXXXXXX +........ +XXXXXXXX +........ +XXXXXXXX +........ +`), + "Dots" : () => savePattern(` +...... +..XX.. +.XXXX. +.XXXX. +..XX.. +...... +`) + }); + + /* For testing, this generates an image with a different colour surrounding on it +require("FontSinclair").add(Graphics); +var b = Graphics.createArrayBuffer(84,12,2); +b.setBgColor(1).clear(); +b.transparent = 1; +b.setFont("Sinclair").setColor(0); +for (var y=-2;y<=2;y++) for (var x=-2;x<=2;x++) b.drawString("LOADING...",2+x,2+y); +b.setColor(3).drawString("LOADING...",2,2); +g.drawImage(b.asImage("string")); + */ + +}) \ No newline at end of file diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index ba1246d9f..618c5822e 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -239,6 +239,8 @@ TODO: document.getElementById("mapContainer").style.display = ""; document.getElementById("maptiles").style.display="none"; document.getElementById("uploadbuttons").style.display="none"; + map.invalidateSize(); + map.locate({setView: true, maxZoom: 16, enableHighAccuracy:true}); } // ----------------------------------------------------- diff --git a/apps/owmweather/ChangeLog b/apps/owmweather/ChangeLog index dba6a755a..6b4a80e23 100644 --- a/apps/owmweather/ChangeLog +++ b/apps/owmweather/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Do first update request 5s after boot to boot up faster +0.03: Fix updating weather too often diff --git a/apps/owmweather/boot.js b/apps/owmweather/boot.js index 737413940..5ac32c55a 100644 --- a/apps/owmweather/boot.js +++ b/apps/owmweather/boot.js @@ -1,19 +1,21 @@ { let waiting = false; - let settings = require("Storage").readJSON("owmweather.json", 1) || { - enabled: false - }; + let settings = Object.assign( + require('Storage').readJSON("owmweather.default.json", true) || {}, + require('Storage').readJSON("owmweather.json", true) || {} + ); let completion = function(){ waiting = false; + settings.updated = Date.now(); + require('Storage').writeJSON("owmweather.json", settings); } if (settings.enabled) { let weather = require("Storage").readJSON('weather.json') || {}; - let lastUpdate; if (weather && weather.weather && weather.weather.time) lastUpdate = weather.weather.time; - if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){ + if (!settings.updated || settings.updated + settings.refresh * 1000 * 60 < Date.now()){ setTimeout(() => { if (!waiting){ waiting = true; diff --git a/apps/owmweather/lib.js b/apps/owmweather/lib.js index 6ba52b498..67cdc115b 100644 --- a/apps/owmweather/lib.js +++ b/apps/owmweather/lib.js @@ -25,7 +25,7 @@ function parseWeather(response) { json.weather = weather; require("Storage").writeJSON('weather.json', json); - require("weather").emit("update", json.weather); + if (require("Storage").read("weather")!==undefined) require("weather").emit("update", json.weather); return undefined; } else { return /*LANG*/"Not OWM data"; diff --git a/apps/owmweather/metadata.json b/apps/owmweather/metadata.json index 56f9afca7..7ea3dc9bb 100644 --- a/apps/owmweather/metadata.json +++ b/apps/owmweather/metadata.json @@ -1,7 +1,7 @@ { "id": "owmweather", "name": "OpenWeatherMap weather provider", "shortName":"OWM Weather", - "version":"0.02", + "version":"0.03", "description": "Pulls weather from OpenWeatherMap (OWM) API", "icon": "app.png", "type": "bootloader", diff --git a/apps/pebblepp/ChangeLog b/apps/pebblepp/ChangeLog index 7b83706bf..de19d9ba4 100644 --- a/apps/pebblepp/ChangeLog +++ b/apps/pebblepp/ChangeLog @@ -1 +1,3 @@ 0.01: First release +0.02: clock_info now uses app name to maintain settings specifically for this clock face + ensure clockinfo text is usppercase (font doesn't render lowercase) \ No newline at end of file diff --git a/apps/pebblepp/app.js b/apps/pebblepp/app.js index d296cdc1a..555183d3d 100644 --- a/apps/pebblepp/app.js +++ b/apps/pebblepp/app.js @@ -86,14 +86,16 @@ let clockInfoDraw = (itm, info, options) => { } } g.setFontLECO1976Regular22().setFontAlign(0, 0); - g.drawString(info.text, midx,options.y+options.h-12); // draw the text + g.drawString(info.text.toString().toUpperCase(), midx,options.y+options.h-12); // draw the text }; let clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, { + app:"pebblepp", x : 0, y: 0, w: w/2, h:h/2, draw : clockInfoDraw }); let clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, { + app:"pebblepp", x : w/2, y: 0, w: w/2, h:h/2, draw : clockInfoDraw }); diff --git a/apps/pebblepp/metadata.json b/apps/pebblepp/metadata.json index aede7ae12..252584d4f 100644 --- a/apps/pebblepp/metadata.json +++ b/apps/pebblepp/metadata.json @@ -2,7 +2,7 @@ "id": "pebblepp", "name": "Pebble++ Clock", "shortName": "Pebble++", - "version": "0.01", + "version": "0.02", "description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/simplestpp/ChangeLog b/apps/simplestpp/ChangeLog index 51186fbdb..53ccf75db 100644 --- a/apps/simplestpp/ChangeLog +++ b/apps/simplestpp/ChangeLog @@ -1,3 +1,4 @@ 0.01: first release 0.02: removed fast load, minimalism is useful for narrowing down on issues 0.03: Use clock_info module as an app +0.04: clock_info now uses app name to maintain settings specifically for this clock face \ No newline at end of file diff --git a/apps/simplestpp/app.js b/apps/simplestpp/app.js index 0dcb883d3..66caadfe2 100644 --- a/apps/simplestpp/app.js +++ b/apps/simplestpp/app.js @@ -5,7 +5,7 @@ function draw() { var timeStr = require("locale").time(date,1); var h = g.getHeight(); var w = g.getWidth(); - + g.reset(); g.setColor(g.theme.bg); g.fillRect(Bangle.appRect); @@ -34,7 +34,7 @@ function queueDraw() { /** * clock_info_support * this is the callback function that get invoked by clockInfoMenu.redraw(); - * + * * We will display the image and text on the same line and centre the combined * length of the image+text * @@ -42,7 +42,7 @@ function queueDraw() { function clockInfoDraw(itm, info, options) { g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg); - + //use info.text.toString(), steps does not have length defined var text_w = g.stringWidth(info.text.toString()); // gap between image and text @@ -54,7 +54,7 @@ function clockInfoDraw(itm, info, options) { // clear the whole info line g.clearRect(0, options.y -1, g.getWidth(), options.y+24); - + // draw the image if we have one if (info.img) { // image start @@ -82,7 +82,7 @@ let clockInfoItems = require("clock_info").load(); * selected after tapping on it * */ -let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); +let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app:"simplestpp", x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} ); // Clear the screen once, at startup g.clear(); diff --git a/apps/simplestpp/metadata.json b/apps/simplestpp/metadata.json index 64dc19ae2..c81846539 100644 --- a/apps/simplestpp/metadata.json +++ b/apps/simplestpp/metadata.json @@ -2,7 +2,7 @@ "id": "simplestpp", "name": "Simplest++ Clock", "shortName": "Simplest++", - "version": "0.03", + "version": "0.04", "description": "The simplest working clock, with clock_info, acts as a tutorial piece", "readme": "README.md", "icon": "app.png", diff --git a/apps/slopeclockpp/ChangeLog b/apps/slopeclockpp/ChangeLog index c9fb0de7a..39f837386 100644 --- a/apps/slopeclockpp/ChangeLog +++ b/apps/slopeclockpp/ChangeLog @@ -10,3 +10,4 @@ 0.08: Stability improvements - ensure we continue even if a flat string can't be allocated Stop ClockInfo text drawing outside the allocated area 0.09: Use clock_info module as an app +0.10: Option to hide widgets, tweak top widget width to avoid overlap with hour text at 9am \ No newline at end of file diff --git a/apps/slopeclockpp/app.js b/apps/slopeclockpp/app.js index b57650f9e..5b1d898d1 100644 --- a/apps/slopeclockpp/app.js +++ b/apps/slopeclockpp/app.js @@ -58,7 +58,8 @@ let draw = function() { // Now draw this one R = Bangle.appRect; x = R.w / 2; - y = R.y + R.h / 2 - 12; // 12 = room for date + y = R.y + R.h / 2 - 6; + if (!settings.hideWidgets) y-= 6; // extra room for date var date = new Date(); var local_time = require("locale").time(date, 1); var hourStr = local_time.split(":")[0].trim().padStart(2,'0'); @@ -77,6 +78,8 @@ let draw = function() { g2.setColor(0).fillRect(0,0,g2.getWidth(),g2.getHeight()).setFontAlign(1, 0).setFont("PaytoneOne"); g2.setColor(1).drawString(minStr, g2.getWidth()-fontBorder, g2.getHeight()/2).setFont("4x6"); // draw and unload custom font g2.setColor(0).fillPoly([0,0, g2.getWidth(),0, 0,slope*2]); + // redraw the top widget + clockInfoMenu.redraw(); // start the animation *in* animate(true); }; @@ -141,8 +144,14 @@ let clockInfoDraw = (itm, info, options) => { g.setClipRect(0,0,g.getWidth()-1, g.getHeight()-1); }; let clockInfoItems = require("clock_info").load(); -let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app:"slopeclockpp",x:126, y:24, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#f00"/*red*/ }); -let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { app:"slopeclockpp",x:0, y:115, w:50, h:40, draw : clockInfoDraw, bg : bgColor, fg : g.theme.bg, hl : (bgColor=="#000")?"#f00"/*red*/:g.theme.fg }); +let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { // top right + app:"slopeclockpp",x:132, y:settings.hideWidgets ? 12 : 24, w:44, h:40, + draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#f00"/*red*/ +}); +let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { // bottom left + app:"slopeclockpp",x:0, y:115, w:50, h:40, + draw : clockInfoDraw, bg : bgColor, fg : g.theme.bg, hl : (bgColor=="#000")?"#f00"/*red*/:g.theme.fg +}); // Show launcher when middle button pressed Bangle.setUI({ @@ -163,6 +172,7 @@ Bangle.setUI({ }); // Load widgets Bangle.loadWidgets(); +if (settings.hideWidgets) require("widget_utils").swipeOn(); +else setTimeout(Bangle.drawWidgets,0); draw(); -setTimeout(Bangle.drawWidgets,0); -} +} \ No newline at end of file diff --git a/apps/slopeclockpp/metadata.json b/apps/slopeclockpp/metadata.json index 116b6c665..086b8148f 100644 --- a/apps/slopeclockpp/metadata.json +++ b/apps/slopeclockpp/metadata.json @@ -1,6 +1,6 @@ { "id": "slopeclockpp", "name": "Slope Clock ++", - "version":"0.09", + "version":"0.10", "description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows extra information and allows the colors to be selected.", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/slopeclockpp/screenshot.png b/apps/slopeclockpp/screenshot.png index dfa76fed7..1d9cec8e4 100644 Binary files a/apps/slopeclockpp/screenshot.png and b/apps/slopeclockpp/screenshot.png differ diff --git a/apps/slopeclockpp/settings.js b/apps/slopeclockpp/settings.js index 1d285767a..3c0e0a6e9 100644 --- a/apps/slopeclockpp/settings.js +++ b/apps/slopeclockpp/settings.js @@ -15,6 +15,10 @@ let menu ={ '': { 'title': 'Slope Clock ++' }, /*LANG*/'< Back': back, + /*LANG*/'Hide Widgets': { + value: !!settings.hideWidgets, + onchange: x => save('hideWidgets', x), + }, /*LANG*/'Red': { value: !!settings.colorRed, format: () => (settings.colorRed ? 'Yes' : 'No'), diff --git a/apps/stlapview/ChangeLog b/apps/stlapview/ChangeLog index c819919ed..dd7cb5728 100644 --- a/apps/stlapview/ChangeLog +++ b/apps/stlapview/ChangeLog @@ -1,2 +1,3 @@ 0.01: New app! -0.02: Submitted to the app loader \ No newline at end of file +0.02: Submitted to the app loader +0.03: Fix going back from a lap view, and add a main-menu back button diff --git a/apps/stlapview/app.js b/apps/stlapview/app.js index 7a58b5782..c11739af7 100644 --- a/apps/stlapview/app.js +++ b/apps/stlapview/app.js @@ -53,7 +53,7 @@ function view(fileName) { let lapMenu = { '': { 'title': fileNameToDateString(fileName), - 'back': () => { E.showMenu(mainMenu); } + 'back': () => showMainMenu() }, }; lapMenu[`Total time: ${msToHumanReadable(fileData[fileData.length - 1])}`] = () => { }; @@ -89,15 +89,16 @@ function showMainMenu() { let mainMenu = { '': { - 'title': 'Sessions' + 'title': 'Sessions', + 'back': () => load() } }; - //I know eval is evil, but I can't think of any other way to do this. for (let lapFile of LAP_FILES) { - mainMenu[fileNameToDateString(lapFile)] = eval(`(function() { - view('${lapFile}'); - })`); + // `let` variables in JS have special behaviour in loops, + // where capturing them captures that instance of the variable, + // but for espruino we need to do a slightly older trick: + mainMenu[fileNameToDateString(lapFile)] = ((lapFile) => () => view(lapFile))(lapFile); } if (LAP_FILES.length == 0) { diff --git a/apps/stlapview/metadata.json b/apps/stlapview/metadata.json index aa54a7b67..fb3818fc0 100644 --- a/apps/stlapview/metadata.json +++ b/apps/stlapview/metadata.json @@ -1,7 +1,7 @@ { "id": "stlapview", "name": "Stopwatch laps", - "version": "0.02", + "version": "0.03", "description": "Optional lap viewer for my stopwatch app", "icon": "icon.png", "type": "app", diff --git a/apps/widclkinfo/ChangeLog b/apps/widclkinfo/ChangeLog index 4c21f3ace..3ca502120 100644 --- a/apps/widclkinfo/ChangeLog +++ b/apps/widclkinfo/ChangeLog @@ -1 +1,2 @@ 0.01: New Widget! +0.02: Now use an app ID (to avoid conflicts with clocks that also use ClockInfo) \ No newline at end of file diff --git a/apps/widclkinfo/metadata.json b/apps/widclkinfo/metadata.json index 3848563c6..282e80b76 100644 --- a/apps/widclkinfo/metadata.json +++ b/apps/widclkinfo/metadata.json @@ -1,8 +1,8 @@ { "id": "widclkinfo", "name": "Clock Info Widget", - "version":"0.01", + "version":"0.02", "description": "Use 'Clock Info' in the Widget bar. Tap on the widget to select, then drag up/down/left/right to choose what information is displayed.", - "icon": "widget.png", + "icon": "widget.png", "screenshots" : [ { "url":"screenshot.png" }], "type": "widget", "tags": "widget,clkinfo", diff --git a/apps/widclkinfo/widget.js b/apps/widclkinfo/widget.js index 403f289e7..95bc9a111 100644 --- a/apps/widclkinfo/widget.js +++ b/apps/widclkinfo/widget.js @@ -3,6 +3,7 @@ if (!require("clock_info").loadCount) { // don't load if a clock_info was alread let clockInfoItems = require("clock_info").load(); // Add the let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { + app : "widclkinfo", // Add the dimensions we're rendering to here - these are used to detect taps on the clock info area x : 0, y: 0, w: 72, h:24, // You can add other information here you want to be passed into 'options' in 'draw' @@ -19,8 +20,8 @@ if (!require("clock_info").loadCount) { // don't load if a clock_info was alread let clockInfoInfo; // when clockInfoMenu.draw is called we set this up // The actual widget we're displaying - WIDGETS["clkinfo"] = { - area:"tl", + WIDGETS["clkinfo"] = { + area:"tl", width: clockInfoMenu.w, draw:function(e) { clockInfoMenu.x = e.x; diff --git a/apps/widhid/ChangeLog b/apps/widhid/ChangeLog index a6ed84766..b0e479108 100644 --- a/apps/widhid/ChangeLog +++ b/apps/widhid/ChangeLog @@ -1,3 +1,5 @@ 0.01: New widget - music control via a swipe 0.02: Improve interactivity - avoid responding to swipes when a menu or launcher is active. +0.03: Handle errors when sending input over BLE and the special-case of + replacing a single handler diff --git a/apps/widhid/metadata.json b/apps/widhid/metadata.json index 84334636b..b819c9b64 100644 --- a/apps/widhid/metadata.json +++ b/apps/widhid/metadata.json @@ -2,7 +2,7 @@ "id": "widhid", "name": "Bluetooth Music Swipe Control Widget", "shortName": "BLE Swipe Widget", - "version": "0.02", + "version": "0.03", "description": "Based on Swipe Bluetooth Music Controls (based on Bluetooth Music Controls). Swipe down to enable, then swipe up/down for volume, left/right for previous and next and tap for play/pause. Enable HID in settings, pair with your phone/computer, then use this widget to control music from your watch!", "icon": "icon.png", "readme": "README.md", diff --git a/apps/widhid/wid.js b/apps/widhid/wid.js index dd411513e..06ef0cea4 100644 --- a/apps/widhid/wid.js +++ b/apps/widhid/wid.js @@ -81,7 +81,10 @@ if (!wasActive) { waitForRelease = true; Bangle.on("drag", onDrag); - Bangle["#ondrag"] = [onDrag].concat(Bangle["#ondrag"].filter(function (f) { return f !== onDrag; })); + var dragHandlers = Bangle["#ondrag"]; + if (dragHandlers && typeof dragHandlers !== "function") { + Bangle["#ondrag"] = [onDrag].concat(dragHandlers.filter(function (f) { return f !== onDrag; })); + } redraw(); } if (activeTimeout) @@ -120,7 +123,12 @@ redraw(); }); var sendHid = function (code) { - NRF.sendHIDReport([1, code], function () { return NRF.sendHIDReport([1, 0]); }); + try { + NRF.sendHIDReport([1, code], function () { return NRF.sendHIDReport([1, 0]); }); + } + catch (e) { + console.log("sendHIDReport:", e); + } }; var next = function () { return sendHid(0x01); }; var prev = function () { return sendHid(0x02); }; diff --git a/apps/widhid/wid.ts b/apps/widhid/wid.ts index 3291c188d..e788be1a8 100644 --- a/apps/widhid/wid.ts +++ b/apps/widhid/wid.ts @@ -23,7 +23,7 @@ const onDrag = (e => { // Espruino/35c8cb9be11 - (E as any).stopEventPropagation && (E as any).stopEventPropagation(); + E.stopEventPropagation && E.stopEventPropagation(); if(e.b === 0){ // released @@ -84,10 +84,15 @@ waitForRelease = true; // wait for first touch up before accepting gestures Bangle.on("drag", onDrag); + // move our drag to the start of the event listener array - (Bangle as any)["#ondrag"] = [onDrag].concat( - (Bangle as any)["#ondrag"].filter((f: unknown) => f !== onDrag) - ); + const dragHandlers = (Bangle as BangleEvents)["#ondrag"] + + if(dragHandlers && typeof dragHandlers !== "function"){ + (Bangle as BangleEvents)["#ondrag"] = [onDrag as undefined | typeof onDrag].concat( + dragHandlers.filter((f: unknown) => f !== onDrag) + ); + } redraw(); } @@ -140,10 +145,14 @@ //const DEBUG = true; const sendHid = (code: number) => { //if(DEBUG) return; - NRF.sendHIDReport( - [1, code], - () => NRF.sendHIDReport([1, 0]), - ); + try{ + NRF.sendHIDReport( + [1, code], + () => NRF.sendHIDReport([1, 0]), + ); + }catch(e){ + console.log("sendHIDReport:", e); + } }; const next = () => /*DEBUG ? console.log("next") : */ sendHid(0x01); diff --git a/index.html b/index.html index 0808f31fa..30f660717 100644 --- a/index.html +++ b/index.html @@ -195,7 +195,7 @@ - + diff --git a/typescript/types/bangle_extensions.d.ts b/typescript/types/bangle_extensions.d.ts new file mode 100644 index 000000000..e5d94d079 --- /dev/null +++ b/typescript/types/bangle_extensions.d.ts @@ -0,0 +1,10 @@ +type BangleHandler any> = T | (T | undefined)[]; + +type BangleEvents = { + ["#ontap"]?: BangleHandler<(data: { dir: "left" | "right" | "top" | "bottom" | "front" | "back", double: boolean, x: TapAxis, y: TapAxis, z: TapAxis }) => void>, + ["#ongesture"]?: BangleHandler<(xyz: Int8Array) => void>, + ["#onswipe"]?: BangleHandler, + ["#ontouch"]?: BangleHandler, + ["#ondrag"]?: BangleHandler, + ["#onstroke"]?: BangleHandler<(event: { xy: Uint8Array, stroke?: string }) => void>, +}; diff --git a/typescript/types/main.d.ts b/typescript/types/main.d.ts index 4e4dd224e..59fb18d30 100644 --- a/typescript/types/main.d.ts +++ b/typescript/types/main.d.ts @@ -465,6 +465,46 @@ declare class ESP32 { */ static deepSleep(us: number): void; + /** + * Put device in deepsleep state until interrupted by pin "pin". + * Eligible pin numbers are restricted to those [GPIOs designated + * as RTC GPIOs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary). + * + * @param {Pin} pin - Pin to trigger wakeup + * @param {number} level - Logic level to trigger + * @url http://www.espruino.com/Reference#l_ESP32_deepSleepExt0 + */ + static deepSleepExt0(pin: Pin, level: number): void; + + /** + * Put device in deepsleep state until interrupted by pins in the "pinVar" array. + * The trigger "mode" determines the pin state which will wake up the device. + * Valid modes are: + * * `0: ESP_EXT1_WAKEUP_ALL_LOW` - all nominated pins must be set LOW to trigger wakeup + * * `1: ESP_EXT1_WAKEUP_ANY_HIGH` - any of nominated pins set HIGH will trigger wakeup + * Eligible pin numbers are restricted to those [GPIOs designated + * as RTC GPIOs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary). + * + * @param {any} pinVar - Array of Pins to trigger wakeup + * @param {number} mode - Trigger mode + * @url http://www.espruino.com/Reference#l_ESP32_deepSleepExt1 + */ + static deepSleepExt1(pinVar: any, mode: number): void; + + /** + * Returns a variable identifying the cause of wakeup from deep sleep. + * Possible causes include: + * * `0: ESP_SLEEP_WAKEUP_UNDEFINED` - reset was not caused by exit from deep sleep + * * `2: ESP_SLEEP_WAKEUP_EXT0` - Wakeup caused by external signal using RTC_IO + * * `3: ESP_SLEEP_WAKEUP_EXT1` - Wakeup caused by external signal using RTC_CNTL + * * `4: ESP_SLEEP_WAKEUP_TIMER` - Wakeup caused by timer + * * `5: ESP_SLEEP_WAKEUP_TOUCHPAD` - Wakeup caused by touchpad + * * `6: ESP_SLEEP_WAKEUP_ULP` - Wakeup caused by ULP program + * @returns {number} The cause of the ESP32's wakeup from sleep + * @url http://www.espruino.com/Reference#l_ESP32_getWakeupCause + */ + static getWakeupCause(): number; + /** * Returns an object that contains details about the state of the ESP32 with the * following fields: @@ -5597,7 +5637,9 @@ declare class Graphics { * `width,height,bpp,[transparent,]image_bytes...`. If a transparent colour is * specified the top bit of `bpp` should be set. * * An ArrayBuffer Graphics object (if `bpp<8`, `msb:true` must be set) - this is - * disabled on devices without much flash memory available + * disabled on devices without much flash memory available. If a Graphics object + * is supplied, it can also contain transparent/palette fields as if it were + * an image. * Draw an image at the specified position. * * If the image is 1 bit, the graphics foreground/background colours will be * used. @@ -5678,6 +5720,9 @@ declare class Graphics { * * 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. + * If the `Graphics` object contains `transparent` or `pelette` fields, + * [as you might find in an image](http://www.espruino.com/Graphics#images-bitmaps), + * those will be included in the generated image too. * * @param {any} type - The type of image to return. Either `object`/undefined to return an image object, or `string` to return an image string * @returns {any} An Image that can be used with `Graphics.drawImage` @@ -5800,6 +5845,19 @@ declare class Graphics { */ transformVertices(arr: number[], transformation: { x?: number, y?: number, scale?: number, rotate?: number } | [number, number, number, number, number, number]): number[]; + /** + * Flood fills the given Graphics instance out from a particular point. + * **Note:** This only works on Graphics instances that support readback with `getPixel`. It + * is also not capable of filling over dithered patterns (eg non-solid colours on Bangle.js 2) + * + * @param {number} x - X coordinate to start from + * @param {number} y - Y coordinate to start from + * @param {any} col - The color to fill with (if undefined, foreground is used) + * @returns {any} The instance of Graphics this was called on, to allow call chaining + * @url http://www.espruino.com/Reference#l_Graphics_floodFill + */ + floodFill(x: number, y: number, col: any): Graphics; + /** * Returns an object of the form: * ``` @@ -8223,6 +8281,10 @@ declare class E { /** * Dump any locked variables that aren't referenced from `global` - for debugging * memory leaks only. + * **Note:** This does a linear scan over memory, finding variables + * that are currently locked. In some cases it may show variables + * like `Unknown 66` which happen when *part* of a string has ended + * up placed in memory ahead of the String that it's part of. See https://github.com/espruino/Espruino/issues/2345 * @url http://www.espruino.com/Reference#l_E_dumpLockedVars */ static dumpLockedVars(): void; @@ -8655,6 +8717,24 @@ declare class E { */ static decodeUTF8(str: string, lookup: string[], replaceFn: string | ((charCode: number) => string)): string; + /** + * When using events with `X.on('foo', function() { ... })` + * and then `X.emit('foo')` you might want to stop subsequent + * event handlers from being executed. + * Calling this function doing the execution of events will + * ensure that no subsequent event handlers are executed. + * ``` + * var X = {}; // in Espruino all objects are EventEmitters + * X.on('foo', function() { print("A"); }) + * X.on('foo', function() { print("B"); E.stopEventPropagation(); }) + * X.on('foo', function() { print("C"); }) + * X.emit('foo'); + * // prints A,B but not C + * ``` + * @url http://www.espruino.com/Reference#l_E_stopEventPropagation + */ + static stopEventPropagation(): void; + } @@ -8959,6 +9039,9 @@ interface Object { * o.emit('answer', 44); * // nothing printed * ``` + * If you have more than one handler for an event, and you'd + * like that handler to stop the event being passed to other handlers + * then you can call `E.stopEventPropagation()` in that handler. * * @param {any} event - The name of the event, for instance 'data' * @param {any} listener - The listener to call when this event is received @@ -10074,6 +10157,12 @@ declare class Serial { * @url http://www.espruino.com/Reference#l_Serial_pipe */ pipe(destination: any, options?: PipeOptions): void + + /** + * Flush this serial stream (pause execution until all data has been sent) + * @url http://www.espruino.com/Reference#l_Serial_flush + */ + flush(): void; } interface StringConstructor {