diff --git a/apps/bikespeedo/ChangeLog b/apps/bikespeedo/ChangeLog index 10752ee2b..b47f8cdc3 100644 --- a/apps/bikespeedo/ChangeLog +++ b/apps/bikespeedo/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Barometer altitude adjustment setting 0.03: Use default Bangle formatter for booleans +0.04: Add options for units in locale and recording GPS diff --git a/apps/bikespeedo/app.js b/apps/bikespeedo/app.js index a62a429e5..6f462a820 100644 --- a/apps/bikespeedo/app.js +++ b/apps/bikespeedo/app.js @@ -403,6 +403,8 @@ function onGPS(fix) { if ( sp < 10 ) sp = sp.toFixed(1); else sp = Math.round(sp); + if (isNaN(sp)) sp = '---'; + if (parseFloat(sp) > parseFloat(max.spd) && max.n > 15 ) max.spd = parseFloat(sp); // Altitude @@ -416,6 +418,12 @@ function onGPS(fix) { // Age of last fix (secs) age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); + } else { + // populate spd_unit + if (cfg.spd == 0) { + m = require("locale").speed(0).match(/[0-9,\.]+(.*)/); + cfg.spd_unit = m[1]; + } } if ( cfg.modeA == 1 ) { @@ -465,7 +473,7 @@ function updateClock() { // Read settings. let cfg = require('Storage').readJSON('bikespeedo.json',1)||{}; -cfg.spd = 1; // Multiplier for speed unit conversions. 0 = use the locale values for speed +cfg.spd = !cfg.localeUnits; // Multiplier for speed unit conversions. 0 = use the locale values for speed cfg.spd_unit = 'km/h'; // Displayed speed unit cfg.alt = 1; // Multiplier for altitude unit conversions. (feet:'0.3048') cfg.alt_unit = 'm'; // Displayed altitude units ('feet') @@ -499,14 +507,6 @@ function onPressure(dat) { altiBaro = Number(dat.altitude.toFixed(0)) + Number(cfg.altDiff); } -Bangle.setBarometerPower(1); // needs some time... -g.clearRect(0,screenYstart,screenW,screenH); -onGPS(lf); -Bangle.setGPSPower(1); -Bangle.on('GPS', onGPS); -Bangle.on('pressure', onPressure); - -Bangle.setCompassPower(1); var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; if (!CALIBDATA) calibrateCompass = true; function Compass_tiltfixread(O,S){ @@ -544,11 +544,33 @@ function Compass_reading() { Compass_heading = Compass_newHeading(d,Compass_heading); hdngCompass = Compass_heading.toFixed(0); } -if (!calibrateCompass) setInterval(Compass_reading,200); -setButtons(); -if (emulator) setInterval(updateClock, 2000); -else setInterval(updateClock, 10000); +function start() { + Bangle.setBarometerPower(1); // needs some time... + g.clearRect(0,screenYstart,screenW,screenH); + onGPS(lf); + Bangle.setGPSPower(1); + Bangle.on('GPS', onGPS); + Bangle.on('pressure', onPressure); + + Bangle.setCompassPower(1); + if (!calibrateCompass) setInterval(Compass_reading,200); + + setButtons(); + if (emulator) setInterval(updateClock, 2000); + else setInterval(updateClock, 10000); + + Bangle.drawWidgets(); +} Bangle.loadWidgets(); -Bangle.drawWidgets(); +if (cfg.record && WIDGETS["recorder"]) { + WIDGETS["recorder"] + .setRecording(true) + .then(start); + + if (cfg.recordStopOnExit) + E.on('kill', () => WIDGETS["recorder"].setRecording(false)); +} else { + start(); +} diff --git a/apps/bikespeedo/metadata.json b/apps/bikespeedo/metadata.json index 80b91427c..ea74db9a1 100644 --- a/apps/bikespeedo/metadata.json +++ b/apps/bikespeedo/metadata.json @@ -2,7 +2,7 @@ "id": "bikespeedo", "name": "Bike Speedometer (beta)", "shortName": "Bike Speedometer", - "version": "0.03", + "version": "0.04", "description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources", "icon": "app.png", "screenshots": [{"url":"Screenshot.png"}], diff --git a/apps/bikespeedo/settings.js b/apps/bikespeedo/settings.js index f41524263..bb943c081 100644 --- a/apps/bikespeedo/settings.js +++ b/apps/bikespeedo/settings.js @@ -11,9 +11,34 @@ '< Back': back, '< Load Bike Speedometer': ()=>{load('bikespeedo.app.js');}, 'Barometer Altitude adjustment' : function() { E.showMenu(altdiffMenu); }, - 'Kalman Filters' : function() { E.showMenu(kalMenu); } + 'Kalman Filters' : function() { E.showMenu(kalMenu); }, + 'Speed units': { + value: !!settings.localeUnits, + format: b => b ? "Locale" : "km/h", + onchange: b => { + settings.localeUnits = b; + writeSettings(); + } + }, }; + if (global.WIDGETS && WIDGETS["recorder"]) { + appMenu[/*LANG*/"Record rides"] = { + value : !!settings.record, + onchange : v => { + settings.record = v; + writeSettings(); + } + }; + appMenu[/*LANG*/"Stop record on exit"] = { + value : !!settings.recordStopOnExit, + onchange : v => { + settings.recordStopOnExit = v; + writeSettings(); + } + }; + } + const altdiffMenu = { '': { 'title': 'Altitude adjustment' }, '< Back': function() { E.showMenu(appMenu); }, diff --git a/apps/calendar/ChangeLog b/apps/calendar/ChangeLog index 27e1e2517..c7902e263 100644 --- a/apps/calendar/ChangeLog +++ b/apps/calendar/ChangeLog @@ -13,3 +13,4 @@ 0.12: Mark dated events on a day 0.13: Switch to swipe left/right for month and up/down for year selection Display events for current month on touch +0.14: Add support for holidays diff --git a/apps/calendar/README.md b/apps/calendar/README.md index ec1c0c55a..5f90d0d52 100644 --- a/apps/calendar/README.md +++ b/apps/calendar/README.md @@ -10,6 +10,7 @@ Basic calendar - Swipe down (Bangle.js 2 only) to go to the next year - Touch to display events for current month - Press the button (button 3 on Bangle.js 1) to exit +- Holidays have same color as weekends and can be edited with the 'Download'-interface, e.g. by uploading an iCalendar file. ## Settings diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index 6aab1aecd..d7c43eb1f 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -16,6 +16,7 @@ const white = "#ffffff"; const red = "#d41706"; const blue = "#0000ff"; const yellow = "#ffff00"; +const cyan = "#00ffff"; let bgColor = color4; let bgColorMonth = color1; let bgColorDow = color2; @@ -23,6 +24,7 @@ let bgColorWeekend = color3; let fgOtherMonth = gray1; let fgSameMonth = white; let bgEvent = blue; +let bgOtherEvent = "#ff8800"; const eventsPerDay=6; // how much different events per day we can display const date = new Date(); @@ -36,9 +38,17 @@ const events = (require("Storage").readJSON("sched.json",1) || []).filter(a => a date.setHours(time.h); date.setMinutes(time.m); date.setSeconds(time.s); - return {date: date, msg: a.msg}; + return {date: date, msg: a.msg, type: "e"}; +}); +// add holidays & other events +(require("Storage").readJSON("calendar.days.json",1) || []).forEach(d => { + const date = new Date(d.date); + const o = {date: date, msg: d.name, type: d.type}; + if (d.repeat) { + o.repeat = d.repeat; + } + events.push(o); }); -events.sort((a,b) => a.date - b.date); if (settings.ndColors === undefined) { settings.ndColors = !g.theme.dark; @@ -52,68 +62,16 @@ if (settings.ndColors === true) { fgOtherMonth = blue; fgSameMonth = black; bgEvent = color2; + bgOtherEvent = cyan; } function getDowLbls(locale) { - let dowLbls; - //TODO: Find some clever way to generate this programmatically from locale lib - switch (locale) { - case "de_AT": - case "de_CH": - case "de_DE": - if (startOnSun) { - dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]; - } else { - dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]; - } - break; - case "nl_NL": - if (startOnSun) { - dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"]; - } else { - dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"]; - } - break; - case "fr_BE": - case "fr_CH": - case "fr_FR": - if (startOnSun) { - dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"]; - } else { - dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"]; - } - break; - case "sv_SE": - if (startOnSun) { - dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"]; - } else { - dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"]; - } - break; - case "it_CH": - case "it_IT": - if (startOnSun) { - dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"]; - } else { - dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"]; - } - break; - case "oc_FR": - if (startOnSun) { - dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"]; - } else { - dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"]; - } - break; - default: - if (startOnSun) { - dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]; - } else { - dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]; - } - break; - } - return dowLbls; + let days = startOnSun ? [0, 1, 2, 3, 4, 5, 6] : [1, 2, 3, 4, 5, 6, 0]; + const d = new Date(); + return days.map(i => { + d.setDate(d.getDate() + (i + 7 - d.getDay()) % 7); + return require("locale").dow(d, 1); + }); } function sameDay(d1, d2) { @@ -206,8 +164,13 @@ function drawCalendar(date) { weekBeforeMonth.setDate(weekBeforeMonth.getDate() - 7); const week2AfterMonth = new Date(date.getFullYear(), date.getMonth()+1, 0); week2AfterMonth.setDate(week2AfterMonth.getDate() + 14); + events.forEach(ev => { + if (ev.repeat === "y") { + ev.date.setFullYear(ev.date.getMonth() < 6 ? week2AfterMonth.getFullYear() : weekBeforeMonth.getFullYear()); + } + }); const eventsThisMonth = events.filter(ev => ev.date > weekBeforeMonth && ev.date < week2AfterMonth); - + eventsThisMonth.sort((a,b) => a.date - b.date); let i = 0; for (y = 0; y < rowN - 1; y++) { for (x = 0; x < colN; x++) { @@ -220,6 +183,33 @@ function drawCalendar(date) { const y1 = y * rowH + headerH + rowH; const x2 = x * colW + colW; const y2 = y * rowH + headerH + rowH + rowH; + + if (eventsThisMonth.length > 0) { + // Display events for this day + eventsThisMonth.forEach((ev, idx) => { + if (sameDay(ev.date, curDay)) { + switch(ev.type) { + case "e": // alarm/event + const hour = ev.date.getHours() + ev.date.getMinutes()/60.0; + const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59 + const height = (y2-2) - (y1+2); // height of a cell + const sliceHeight = height/eventsPerDay; + const ystart = (y1+2) + slice*sliceHeight; + g.setColor(bgEvent).fillRect(x1+1, ystart, x2-2, ystart+sliceHeight); + break; + case "h": // holiday + g.setColor(bgColorWeekend).fillRect(x1+1, y1+1, x2-1, y2-1); + break; + case "o": // other + g.setColor(bgOtherEvent).fillRect(x1+1, y1+1, x2-1, y2-1); + break; + } + + eventsThisMonth.splice(idx, 1); // this event is no longer needed + } + }); + } + if (isToday) { g.setColor(red); g.drawRect(x1, y1, x2, y2); @@ -231,23 +221,6 @@ function drawCalendar(date) { ); } - if (eventsThisMonth.length > 0) { - // Display events for this day - g.setColor(bgEvent); - eventsThisMonth.forEach((ev, idx) => { - if (sameDay(ev.date, curDay)) { - const hour = ev.date.getHours() + ev.date.getMinutes()/60.0; - const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59 - const height = (y2-2) - (y1+2); // height of a cell - const sliceHeight = height/eventsPerDay; - const ystart = (y1+2) + slice*sliceHeight; - g.fillRect(x1+1, ystart, x2-2, ystart+sliceHeight); - - eventsThisMonth.splice(idx, 1); // this event is no longer needed - } - }); - } - require("Font8x12").add(Graphics); g.setFont("8x12", fontSize); g.setColor(day < 50 ? fgOtherMonth : fgSameMonth); @@ -286,10 +259,11 @@ function setUI() { }, btn: (n) => n === (process.env.HWVERSION === 2 ? 1 : 3) && load(), touch: (n,e) => { + events.sort((a,b) => a.date - b.date); const menu = events.filter(ev => ev.date.getFullYear() === date.getFullYear() && ev.date.getMonth() === date.getMonth()).map(e => { const dateStr = require("locale").date(e.date, 1); const timeStr = require("locale").time(e.date, 1); - return { title: `${dateStr} ${timeStr}` + (e.msg ? " " + e.msg : "") }; + return { title: `${dateStr} ${e.type === "e" ? timeStr : ""}` + (e.msg ? " " + e.msg : "") }; }); if (menu.length === 0) { menu.push({title: /*LANG*/"No events"}); diff --git a/apps/calendar/interface.html b/apps/calendar/interface.html new file mode 100644 index 000000000..280a96c0b --- /dev/null +++ b/apps/calendar/interface.html @@ -0,0 +1,234 @@ + + + + + + + + + +

Holidays

+ +
+ +
+ + + + + + + + + + + + + +
DateHolidayTypeRepeat
+ +
+
+
+
+ +
+
+ +
+
+
+
+ + + + + diff --git a/apps/calendar/metadata.json b/apps/calendar/metadata.json index 87599e3f4..44a68d879 100644 --- a/apps/calendar/metadata.json +++ b/apps/calendar/metadata.json @@ -1,7 +1,7 @@ { "id": "calendar", "name": "Calendar", - "version": "0.13", + "version": "0.14", "description": "Simple calendar", "icon": "calendar.png", "screenshots": [{"url":"screenshot_calendar.png"}], @@ -9,10 +9,11 @@ "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "allow_emulator": true, + "interface": "interface.html", "storage": [ {"name":"calendar.app.js","url":"calendar.js"}, {"name":"calendar.settings.js","url":"settings.js"}, {"name":"calendar.img","url":"calendar-icon.js","evaluate":true} ], - "data": [{"name":"calendar.json"}] + "data": [{"name":"calendar.json"}, {"name":"calendar.days.json"}] } diff --git a/apps/clkinfom/ChangeLog b/apps/clkinfom/ChangeLog new file mode 100644 index 000000000..7f837e50e --- /dev/null +++ b/apps/clkinfom/ChangeLog @@ -0,0 +1 @@ +0.01: First version diff --git a/apps/clkinfom/README.md b/apps/clkinfom/README.md new file mode 100644 index 000000000..baa241fc7 --- /dev/null +++ b/apps/clkinfom/README.md @@ -0,0 +1,11 @@ +# RAM Clock Info + +![](app.png) + +A clock info that displays the % memory used + +## Screenshots + +![](screenshot.png) + +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/) diff --git a/apps/clkinfom/app.png b/apps/clkinfom/app.png new file mode 100644 index 000000000..aea4bcbcd Binary files /dev/null and b/apps/clkinfom/app.png differ diff --git a/apps/clkinfom/clkinfo.js b/apps/clkinfom/clkinfo.js new file mode 100644 index 000000000..a3dae43e1 --- /dev/null +++ b/apps/clkinfom/clkinfo.js @@ -0,0 +1,61 @@ +(function () { + var timeout; + + var debug = function(o) { + //console.log(o); + }; + + var clearTimer = function() { + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + debug("timer cleared"); + } + }; + + var queueRedraw = function() { + clearTimer(); + timeout = setTimeout(function() { + timeout = undefined; + queueRedraw(); + }, 60000); + info.items[0].emit("redraw"); + debug("queued"); + }; + + var img = function() { + return atob("GBgBAAAAAAAAAAAAB//gD//wH//4HgB4HAA4HAA4HAA4HDw4HDw4HDw4HDw4HAA4HAA4HAA4HgB4H//4D//wB//gAAAAAAAAAAAA"); + }; + + var text = function() { + var val = process.memory(false); + return '' + Math.round(val.usage*100 / val.total) + '%'; + }; + + var info = { + name: "Bangle", + items: [ + { + name: "ram", + get: function () { return ({ + img: img(), + text: text() + }); }, + run : function() { + debug("run"); + queueRedraw(); + }, + show: function () { + debug("show"); + this.run(); + }, + hide: function() { + debug("hide"); + clearTimer(); + } + } + ] + }; + + return info; +}); diff --git a/apps/clkinfom/metadata.json b/apps/clkinfom/metadata.json new file mode 100644 index 000000000..36ab221cd --- /dev/null +++ b/apps/clkinfom/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "clkinfom", + "name": "RAM Clock Info", + "version":"0.01", + "description": "Clockinfo that displays % used memory", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "clkinfo", + "tags": "clkinfo", + "supports" : ["BANGLEJS2"], + "readme":"README.md", + "storage": [ + {"name":"ram.clkinfo.js","url":"clkinfo.js"} + ] +} diff --git a/apps/clkinfom/screenshot.png b/apps/clkinfom/screenshot.png new file mode 100644 index 000000000..4ae613edd Binary files /dev/null and b/apps/clkinfom/screenshot.png differ diff --git a/apps/cscsensor/metadata.json b/apps/cscsensor/metadata.json index ba250c914..87eb5d12f 100644 --- a/apps/cscsensor/metadata.json +++ b/apps/cscsensor/metadata.json @@ -5,7 +5,7 @@ "version": "0.08", "description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch", "icon": "icons8-cycling-48.png", - "tags": "outdoors,exercise,ble,bluetooth", + "tags": "outdoors,exercise,ble,bluetooth,bike,cycle,bicycle", "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", "storage": [ diff --git a/apps/health/README.md b/apps/health/README.md index 960e5565b..b5f6191cc 100644 --- a/apps/health/README.md +++ b/apps/health/README.md @@ -2,8 +2,6 @@ Logs health data to a file in a defined interval, and provides an app to view it -**BETA - requires firmware 2v11 or later** - ## Usage Once installed, health data is logged automatically. diff --git a/apps/messages/lib.js b/apps/messages/lib.js index 95a8d6ad8..7a515a1f8 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -217,7 +217,7 @@ exports.buzz = function(msgSrc) { if (repeat===undefined) repeat = 4; // repeat may be zero if (repeat) { - exports.buzzTimeout = setInterval(() => require("buzz").pattern(pattern), repeat*1000); + exports.buzzInterval = setInterval(() => require("buzz").pattern(pattern), repeat*1000); let vibrateTimeout = msgSettings.vibrateTimeout; if (vibrateTimeout===undefined) vibrateTimeout = 60; if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopBuzz, vibrateTimeout*1000); @@ -228,8 +228,8 @@ exports.buzz = function(msgSrc) { * Stop buzzing */ exports.stopBuzz = function() { - if (exports.buzzTimeout) clearTimeout(exports.buzzTimeout); - delete exports.buzzTimeout; + if (exports.buzzInterval) clearInterval(exports.buzzInterval); + delete exports.buzzInterval; if (exports.stopTimeout) clearTimeout(exports.stopTimeout); delete exports.stopTimeout; }; diff --git a/apps/patriotclk/ChangeLog b/apps/patriotclk/ChangeLog new file mode 100644 index 000000000..c9f08fcc2 --- /dev/null +++ b/apps/patriotclk/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Show the date inside the widget bar \ No newline at end of file diff --git a/apps/patriotclk/app-icon.js b/apps/patriotclk/app-icon.js new file mode 100644 index 000000000..1dfde113e --- /dev/null +++ b/apps/patriotclk/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA///8H55lCsHZHmEKgEVqoLIBQNVqgLGh4KBAANQBAUQBY1VoApBB4QLFDYoL/Bf4L/BbLrBBZAKBgEBBY0KAQIABgoLCCYQLEgEVBQYLGAAoL/Bf4LPAFw")) \ No newline at end of file diff --git a/apps/patriotclk/app-preview-btm.png b/apps/patriotclk/app-preview-btm.png new file mode 100644 index 000000000..98008996b Binary files /dev/null and b/apps/patriotclk/app-preview-btm.png differ diff --git a/apps/patriotclk/app-preview.png b/apps/patriotclk/app-preview.png new file mode 100644 index 000000000..b79009c94 Binary files /dev/null and b/apps/patriotclk/app-preview.png differ diff --git a/apps/patriotclk/app.js b/apps/patriotclk/app.js new file mode 100644 index 000000000..e618b0106 --- /dev/null +++ b/apps/patriotclk/app.js @@ -0,0 +1,89 @@ +Graphics.prototype.setFontAudiowide = function() { + // Actual height 33 (36 - 4) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('AB/wAgcB/AFVgFgHbkYAok4AogvEgYFEg4FEj4FEn4FE//gKQf/4AcD/4QDh/8Djf+DhN/T4YcFgYcKh4cEh68Eh4cDAoOAAocORYkMf1JxBIYcf/6PDn//MIYEB/5KBOIIABKwIFFO4V/UQMHEIMfFQMHAQP3AQJ3BDIKABh/ggf7ApHAg/5AonxAocPAokf8IFE4IFDn4FEv+BAokBAof/AofB/wFE/gFD4YFE4/4AohgBAoXPAonvMAIFD4AFCVgIFBQYX3wCGCR4T+CTYqtLX4rLC/zXIcYoAQQYIFiJoR9CArgAlToIpDRQIFDSwI7C4CiBApN/Apb1D4F+Av4Fd8H+Aof/AoaTB/gFIgaBBAoSrB+AFCgF/8AFDAESP/Av3wv0HZYYABYoYAB+AFGZYIAB8DLCAAPAZYQFBZUhHC/gFE/wFaAAN+Av4Fqv53EboYFdAFIvB4EBGofwAon4Aon8ApX+AofAAot+Av4Fev8DAojFDAo0/S4IFGAAMf//gV4mAAoUD/zYgFwP8AoRGB/4FCAgI1CgIFC4A5BAoRHBg4FCKYMH/l+n5fC+F+g5rC8F+PoYFFZf7XVw7XNAALXNTYLXCVoYAQF4IFZjAFEnAFELIZCBAojRDAoMfAol/AohrCAoJfBNYIFBNYOAAoUf/xBDv/8AoXBRAcP4aCDh/PDgSNCDgQFCHIIFDUoafFAoJ3EGYQFCDgYFBXgZuBGYQAba4pDEhzvE/4ABKoMBAogbBAAJKBg4EBw4FEX4Z9BgIFC8AFE4F+Av5HFKYhfFAoRxCO4qDFgF/AokATYgfCZwcD/zTdAAV/Z4RBCHIZNBJYI5D/gFFOJEP+DF/a7N+ZYQFG+F+g7XFRYIFFbobLBboajCAoTRCcYTiEUQYAdgYCBsACBMwJlCAqUHJYLxDAAMgHSQ'))), + 46, + atob("CiAsESQjJSQkHyQkDA=="), + 48|65536 + ); +}; + +{ + + let img = require("Storage").read("patriotclk.bg.img"); + let options = require("Storage").readJSON("patriotclk.opts",1)||{}; + + // timeout used to update every minute + let drawTimeout, widgetTimeout; + + // draw everything + let draw = function() { + var x = g.getWidth()/2; + var y; + if (options.bottomText) + y = g.getHeight() - 24; + else + y = 4+(g.getHeight())/2; // middle + g.reset(); + g.drawImage(img,0,0); + // work out locale-friendly date/time + var date = new Date(); + var timeStr = require("locale").time(date,1); + var dateStr = require("locale").date(date); + // draw time + g.setFontAlign(0,0).setFont("Audiowide"); + // draw a shadow by shifting left/right/up/down + g.drawString(timeStr,x-6,y); + g.drawString(timeStr,x+6,y); + g.drawString(timeStr,x,y-6); + g.drawString(timeStr,x,y+6); + g.drawString(timeStr,x-4,y+4); + g.drawString(timeStr,x+4,y+4); + g.drawString(timeStr,x-4,y-4); + g.drawString(timeStr,x+4,y-4); + // finally draw in the foreground color + g.setColor(g.theme.bg).drawString(timeStr,x,y); + // draw date + //y += 35; + //g.setFontAlign(0,0).setFont("6x8").g.setColor(g.theme.fg); + //g.drawString(dateStr,x,y); + // queue draw in one minute + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); + }; + + // Show launcher when middle button pressed + Bangle.setUI({mode:"clock", remove:function() { //f ree memory + if (drawTimeout) clearTimeout(drawTimeout); + if (widgetTimeout) clearTimeout(widgetTimeout); + require("widget_utils").show(); + var e = WIDGETS["patriot"]; + g.reset().clearRect(e.x,e.y,e.x+63,e.y+23); + delete WIDGETS["patriot"]; + delete Graphics.prototype.setFontAudiowide; + }}); + // Load widgets (make them swipeable) + Bangle.loadWidgets(); + WIDGETS["patriot"] = { + area:"tl", + width: 64, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout + draw : function(e) { + g.reset().clearRect(e.x,e.y,e.x+63,e.y+23); + var d = new Date(); + g.setFont("6x8").setFontAlign(-1,0).drawString(require("locale").dow(d,0), e.x+2, e.y+8); + g.setFont("6x8").setFontAlign(-1,0).drawString(require("locale").date(d).trim(), e.x+2, e.y+16); + widgetTimeout = setTimeout(function() { // redraw every hour (it's just easier that working out timezones) + widgetTimeout = undefined; + WIDGETS["patriot"].draw(WIDGETS["patriot"]); + }, 3600000 - (Date.now() % 3600000)); + } + }; + require("widget_utils").swipeOn(); + // Clear the screen once, at startup + g.clear(); + // draw immediately at first, queue update + draw(); +} \ No newline at end of file diff --git a/apps/patriotclk/app.png b/apps/patriotclk/app.png new file mode 100644 index 000000000..781ab8c60 Binary files /dev/null and b/apps/patriotclk/app.png differ diff --git a/apps/patriotclk/custom.html b/apps/patriotclk/custom.html new file mode 100644 index 000000000..a6fc36abc --- /dev/null +++ b/apps/patriotclk/custom.html @@ -0,0 +1,143 @@ + + + + + + +

Please choose your country's flag:

+
+

Is your flag not here? Please add it here!

+
Preview:
+
+ + +
+ + +

Click

+ + + + + + + + + + + \ No newline at end of file diff --git a/apps/patriotclk/img/README.md b/apps/patriotclk/img/README.md new file mode 100644 index 000000000..53c26165c --- /dev/null +++ b/apps/patriotclk/img/README.md @@ -0,0 +1,9 @@ +Flags +------ + +These flags come from https://icons8.com/icon/set/flags/color and are 480x480px + +If you want to add your own flag ensure it's in the same style and then also list the image file in custom.html in the root directory. + +If your flag is listed in https://icons8.com/icon/set/flags/color and you can't download it in the right size, please file an issue and we'll download it with our account. + diff --git a/apps/patriotclk/img/icons8-australia-480.png b/apps/patriotclk/img/icons8-australia-480.png new file mode 100644 index 000000000..edf34cdee Binary files /dev/null and b/apps/patriotclk/img/icons8-australia-480.png differ diff --git a/apps/patriotclk/img/icons8-austria-480.png b/apps/patriotclk/img/icons8-austria-480.png new file mode 100644 index 000000000..8450a8840 Binary files /dev/null and b/apps/patriotclk/img/icons8-austria-480.png differ diff --git a/apps/patriotclk/img/icons8-belgium-480.png b/apps/patriotclk/img/icons8-belgium-480.png new file mode 100644 index 000000000..395fef8d1 Binary files /dev/null and b/apps/patriotclk/img/icons8-belgium-480.png differ diff --git a/apps/patriotclk/img/icons8-brazil-480.png b/apps/patriotclk/img/icons8-brazil-480.png new file mode 100644 index 000000000..570ee39f9 Binary files /dev/null and b/apps/patriotclk/img/icons8-brazil-480.png differ diff --git a/apps/patriotclk/img/icons8-canada-480.png b/apps/patriotclk/img/icons8-canada-480.png new file mode 100644 index 000000000..3d3080768 Binary files /dev/null and b/apps/patriotclk/img/icons8-canada-480.png differ diff --git a/apps/patriotclk/img/icons8-china-480.png b/apps/patriotclk/img/icons8-china-480.png new file mode 100644 index 000000000..c3c3faba7 Binary files /dev/null and b/apps/patriotclk/img/icons8-china-480.png differ diff --git a/apps/patriotclk/img/icons8-denmark-480.png b/apps/patriotclk/img/icons8-denmark-480.png new file mode 100644 index 000000000..2add9dbce Binary files /dev/null and b/apps/patriotclk/img/icons8-denmark-480.png differ diff --git a/apps/patriotclk/img/icons8-england-480.png b/apps/patriotclk/img/icons8-england-480.png new file mode 100644 index 000000000..5c9a836a6 Binary files /dev/null and b/apps/patriotclk/img/icons8-england-480.png differ diff --git a/apps/patriotclk/img/icons8-flag-of-europe-480.png b/apps/patriotclk/img/icons8-flag-of-europe-480.png new file mode 100644 index 000000000..654da87ba Binary files /dev/null and b/apps/patriotclk/img/icons8-flag-of-europe-480.png differ diff --git a/apps/patriotclk/img/icons8-france-480.png b/apps/patriotclk/img/icons8-france-480.png new file mode 100644 index 000000000..e3cb56348 Binary files /dev/null and b/apps/patriotclk/img/icons8-france-480.png differ diff --git a/apps/patriotclk/img/icons8-germany-480.png b/apps/patriotclk/img/icons8-germany-480.png new file mode 100644 index 000000000..0f86aa568 Binary files /dev/null and b/apps/patriotclk/img/icons8-germany-480.png differ diff --git a/apps/patriotclk/img/icons8-great-britain-480.png b/apps/patriotclk/img/icons8-great-britain-480.png new file mode 100644 index 000000000..0acdcc191 Binary files /dev/null and b/apps/patriotclk/img/icons8-great-britain-480.png differ diff --git a/apps/patriotclk/img/icons8-greece-480.png b/apps/patriotclk/img/icons8-greece-480.png new file mode 100644 index 000000000..319cf93cb Binary files /dev/null and b/apps/patriotclk/img/icons8-greece-480.png differ diff --git a/apps/patriotclk/img/icons8-hungary-480.png b/apps/patriotclk/img/icons8-hungary-480.png new file mode 100644 index 000000000..ab838afea Binary files /dev/null and b/apps/patriotclk/img/icons8-hungary-480.png differ diff --git a/apps/patriotclk/img/icons8-italy-480.png b/apps/patriotclk/img/icons8-italy-480.png new file mode 100644 index 000000000..f917cd85a Binary files /dev/null and b/apps/patriotclk/img/icons8-italy-480.png differ diff --git a/apps/patriotclk/img/icons8-lgbt-flag-480.png b/apps/patriotclk/img/icons8-lgbt-flag-480.png new file mode 100644 index 000000000..10f869a0a Binary files /dev/null and b/apps/patriotclk/img/icons8-lgbt-flag-480.png differ diff --git a/apps/patriotclk/img/icons8-netherlands-480.png b/apps/patriotclk/img/icons8-netherlands-480.png new file mode 100644 index 000000000..b271c5448 Binary files /dev/null and b/apps/patriotclk/img/icons8-netherlands-480.png differ diff --git a/apps/patriotclk/img/icons8-new-zealand-480.png b/apps/patriotclk/img/icons8-new-zealand-480.png new file mode 100644 index 000000000..fc1ca7ec1 Binary files /dev/null and b/apps/patriotclk/img/icons8-new-zealand-480.png differ diff --git a/apps/patriotclk/img/icons8-norway-480.png b/apps/patriotclk/img/icons8-norway-480.png new file mode 100644 index 000000000..270f5af8b Binary files /dev/null and b/apps/patriotclk/img/icons8-norway-480.png differ diff --git a/apps/patriotclk/img/icons8-scotland-480.png b/apps/patriotclk/img/icons8-scotland-480.png new file mode 100644 index 000000000..991652211 Binary files /dev/null and b/apps/patriotclk/img/icons8-scotland-480.png differ diff --git a/apps/patriotclk/img/icons8-spain-480.png b/apps/patriotclk/img/icons8-spain-480.png new file mode 100644 index 000000000..19c8dac04 Binary files /dev/null and b/apps/patriotclk/img/icons8-spain-480.png differ diff --git a/apps/patriotclk/img/icons8-sweden-480.png b/apps/patriotclk/img/icons8-sweden-480.png new file mode 100644 index 000000000..60f412840 Binary files /dev/null and b/apps/patriotclk/img/icons8-sweden-480.png differ diff --git a/apps/patriotclk/img/icons8-switzerland-480.png b/apps/patriotclk/img/icons8-switzerland-480.png new file mode 100644 index 000000000..801ba4776 Binary files /dev/null and b/apps/patriotclk/img/icons8-switzerland-480.png differ diff --git a/apps/patriotclk/img/icons8-ukraine-480.png b/apps/patriotclk/img/icons8-ukraine-480.png new file mode 100644 index 000000000..3762c1112 Binary files /dev/null and b/apps/patriotclk/img/icons8-ukraine-480.png differ diff --git a/apps/patriotclk/img/icons8-usa-480.png b/apps/patriotclk/img/icons8-usa-480.png new file mode 100644 index 000000000..d6288404e Binary files /dev/null and b/apps/patriotclk/img/icons8-usa-480.png differ diff --git a/apps/patriotclk/img/icons8-wales-480.png b/apps/patriotclk/img/icons8-wales-480.png new file mode 100644 index 000000000..6c2941342 Binary files /dev/null and b/apps/patriotclk/img/icons8-wales-480.png differ diff --git a/apps/patriotclk/metadata.json b/apps/patriotclk/metadata.json new file mode 100644 index 000000000..689f1422d --- /dev/null +++ b/apps/patriotclk/metadata.json @@ -0,0 +1,18 @@ +{ "id": "patriotclk", + "name": "Patriotic Clock", + "shortName":"Patriot", + "version":"0.02", + "description": "Show your Patriotism with your Country's flag as a clock face (configurable). Swipe down to show widgets and date.", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "clock", + "tags": "clock,flag", + "supports" : ["BANGLEJS2"], + "custom": "custom.html", + "storage": [ + {"name":"patriotclk.app.js","url":"app.js"}, + {"name":"patriotclk.bg.img"}, + {"name":"patriotclk.opts"}, + {"name":"patriotclk.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/patriotclk/screenshot.png b/apps/patriotclk/screenshot.png new file mode 100644 index 000000000..1f8b1f5f5 Binary files /dev/null and b/apps/patriotclk/screenshot.png differ diff --git a/apps/popconlaunch/ChangeLog b/apps/popconlaunch/ChangeLog index 5560f00bc..8a2124e33 100644 --- a/apps/popconlaunch/ChangeLog +++ b/apps/popconlaunch/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Trim old entries from the popcon app cache diff --git a/apps/popconlaunch/README.md b/apps/popconlaunch/README.md index 2814082a7..18a5354a4 100644 --- a/apps/popconlaunch/README.md +++ b/apps/popconlaunch/README.md @@ -3,6 +3,6 @@ Popcon Display apps sorted by regular use. No config - install the app and all your launchers will sort apps by most popular, based off launch counts within the last month, and then sort them by the most recently launched app. -:warning: Warning: this app overrides [`Storage.readJSON`], so may slow down your watch when using apps that perform I/O. +⚠️ Warning: this app overrides [`Storage.readJSON`], so may slow down your watch when using apps that perform I/O. [`Storage.readJSON`]: https://www.espruino.com/ReferenceBANGLEJS2#l_Storage_readJSON diff --git a/apps/popconlaunch/boot.js b/apps/popconlaunch/boot.js index eb3f18e1b..9124344b2 100644 --- a/apps/popconlaunch/boot.js +++ b/apps/popconlaunch/boot.js @@ -1,6 +1,7 @@ { var oldRead_1 = require("Storage").readJSON; - var monthAgo_1 = Date.now() - 1000 * 86400 * 28; + var oneMonth_1 = 1000 * 86400 * 28; + var monthAgo_1 = Date.now() - oneMonth_1; var cache_1; var ensureCache_1 = function () { if (!cache_1) { @@ -10,8 +11,20 @@ } return cache_1; }; - var saveCache_1 = function (orderChanged) { - require("Storage").writeJSON("popcon.cache.json", cache_1); + var trimCache_1 = function (cache) { + var threeMonthsBack = Date.now() - oneMonth_1 * 3; + var del = []; + for (var k in cache) + if (cache[k].last < threeMonthsBack) + del.push(k); + for (var _i = 0, del_1 = del; _i < del_1.length; _i++) { + var k = del_1[_i]; + delete cache[k]; + } + }; + var saveCache_1 = function (cache, orderChanged) { + trimCache_1(cache); + require("Storage").writeJSON("popcon.cache.json", cache); if (orderChanged) { var info = oldRead_1("popcon.info", true); info.cacheBuster = !info.cacheBuster; @@ -74,7 +87,7 @@ ent.pop++; ent.last = Date.now(); var orderChanged = sortCache_1(); - saveCache_1(orderChanged); + saveCache_1(cache_3, orderChanged); } return oldLoad_1(src); }; diff --git a/apps/popconlaunch/boot.ts b/apps/popconlaunch/boot.ts index a7ac73518..306d4844a 100644 --- a/apps/popconlaunch/boot.ts +++ b/apps/popconlaunch/boot.ts @@ -1,9 +1,6 @@ { type Timestamp = number; - -const oldRead = require("Storage").readJSON; -const monthAgo = Date.now() - 1000 * 86400 * 28; -let cache: undefined | { +type Cache = { [key: string]: { sortorder: number, pop: number, // amount of launches @@ -11,6 +8,11 @@ let cache: undefined | { } }; +const oldRead = require("Storage").readJSON; +const oneMonth = 1000 * 86400 * 28; +const monthAgo = Date.now() - oneMonth; +let cache: undefined | Cache; + const ensureCache = (): NonNull => { if(!cache){ cache = oldRead("popcon.cache.json", true); @@ -20,7 +22,19 @@ const ensureCache = (): NonNull => { return cache; }; -const saveCache = (orderChanged: boolean) => { +const trimCache = (cache: Cache) => { + const threeMonthsBack = Date.now() - oneMonth * 3; + const del = []; + for(const k in cache) + if(cache[k]!.last < threeMonthsBack) + del.push(k); + + for(const k of del) + delete cache[k]; +}; + +const saveCache = (cache: Cache, orderChanged: boolean) => { + trimCache(cache); require("Storage").writeJSON("popcon.cache.json", cache); if(orderChanged){ // ensure launchers reload their caches: @@ -94,7 +108,7 @@ global.load = (src: string) => { ent.pop++; ent.last = Date.now(); const orderChanged = sortCache(); - saveCache(orderChanged); + saveCache(cache, orderChanged); } return oldLoad(src); diff --git a/apps/popconlaunch/metadata.json b/apps/popconlaunch/metadata.json index 0ccc54d9e..1acab2b6c 100644 --- a/apps/popconlaunch/metadata.json +++ b/apps/popconlaunch/metadata.json @@ -2,7 +2,7 @@ "id": "popconlaunch", "name": "Popcon Launcher", "shortName": "Popcon", - "version": "0.01", + "version": "0.02", "description": "Launcher modification - your launchers will display your favourite (popular) apps first. Overrides `readJSON`, may slow down your watch", "readme": "README.md", "icon": "app.png", diff --git a/apps/powermanager/ChangeLog b/apps/powermanager/ChangeLog index d1c288f97..de3fd021c 100644 --- a/apps/powermanager/ChangeLog +++ b/apps/powermanager/ChangeLog @@ -9,4 +9,4 @@ 0.07: Convert Yes/No On/Off in settings to checkboxes 0.08: Fix the wrapping of intervals/timeouts with parameters Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called -0.09: Cache the app-launch info \ No newline at end of file +0.09: Accidental version bump \ No newline at end of file diff --git a/apps/ratchet_launch/ChangeLog b/apps/ratchet_launch/ChangeLog index af7f83942..e60ca42d2 100644 --- a/apps/ratchet_launch/ChangeLog +++ b/apps/ratchet_launch/ChangeLog @@ -1 +1,2 @@ 0.01: Initial release +0.02: Cache the app-launch info diff --git a/apps/ratchet_launch/metadata.json b/apps/ratchet_launch/metadata.json index 45057b0b9..7ebe0c4cd 100644 --- a/apps/ratchet_launch/metadata.json +++ b/apps/ratchet_launch/metadata.json @@ -2,7 +2,7 @@ "id": "ratchet_launch", "name": "Ratchet Launcher", "shortName": "Ratchet", - "version": "0.01", + "version": "0.02", "description": "Launcher with discrete scrolling for quicker app selection", "icon": "app.png", "type": "launch", diff --git a/apps/recorder/README.md b/apps/recorder/README.md index 34955b986..0dd208af5 100644 --- a/apps/recorder/README.md +++ b/apps/recorder/README.md @@ -15,10 +15,11 @@ You can record * **Time** The current time * **GPS** GPS Latitude, Longitude and Altitude -* **Steps** Steps counted by the step counter * **HR** Heart rate and confidence * **BAT** Battery percentage and voltage -* **Core** CoreTemp body temperature +* **Steps** Steps counted by the step counter +* **Baro** (Bangle.js 2) Using the built-in barometer to record Temperature, Pressure and Altitude +* **Core** CoreTemp body temperature *if* you have a CoreTemp device and the https://banglejs.com/apps/?id=coretemp app installed You can then start/stop recording from the Recorder app itself (and as long as widgets are enabled in the app you're using, you can move to another app and continue recording). diff --git a/apps/sched/interface.html b/apps/sched/interface.html index 366e597a2..f1ace7d0c 100644 --- a/apps/sched/interface.html +++ b/apps/sched/interface.html @@ -3,7 +3,7 @@ - + +
+

3-Bit Color Picker

+
+ +
+ +
+ +
+
+
+
+ + + + diff --git a/apps/shadowclk/metadata.json b/apps/shadowclk/metadata.json index 972f34ad6..e1debfb6d 100644 --- a/apps/shadowclk/metadata.json +++ b/apps/shadowclk/metadata.json @@ -1,16 +1,35 @@ { "id": "shadowclk", "name": "Shadow Clock", - "version": "0.01", - "description": "A simple clock using the Londrina font with color and a shadowed outline. Based on the Anton Clock.", + "version": "0.02", + "description": "A simple clock using the Londrina font in color with a shadowed outline. Based on the Anton Clock.", "icon": "app.png", - "screenshots": [{"url":"screenshot.png"},{"url":"screenshot-1.png"}], + "screenshots": [{ + "url": "screenshot.png" + }, { + "url": "screenshot-1.png" + }], "type": "clock", "tags": "clock", - "supports": ["BANGLEJS","BANGLEJS2"], + "supports": ["BANGLEJS", "BANGLEJS2"], + "readme": "README.md", + "interface": "interface.html", "allow_emulator": true, - "storage": [ - {"name":"shadowclk.app.js","url":"app.js"}, - {"name":"shadowclk.img","url":"app-icon.js","evaluate":true} - ] -} + "storage": [{ + "name": "shadowclk.app.js", + "url": "app.js" + }, + { + "name": "shadowclk.settings.js", + "url": "settings.js" + }, + { + "name": "shadowclk.img", + "url": "app-icon.js", + "evaluate": true + } + ], + "data": [{ + "name": "shadowclk.json" + }] +} \ No newline at end of file diff --git a/apps/shadowclk/screenshot-1.png b/apps/shadowclk/screenshot-1.png index 3f4e0870e..2d4933110 100644 Binary files a/apps/shadowclk/screenshot-1.png and b/apps/shadowclk/screenshot-1.png differ diff --git a/apps/shadowclk/screenshot.png b/apps/shadowclk/screenshot.png index 5f0567243..790294ca3 100644 Binary files a/apps/shadowclk/screenshot.png and b/apps/shadowclk/screenshot.png differ diff --git a/apps/shadowclk/settings.js b/apps/shadowclk/settings.js new file mode 100644 index 000000000..fd16d92e2 --- /dev/null +++ b/apps/shadowclk/settings.js @@ -0,0 +1,104 @@ +(function(back) { + let teletextColors = ["#000", "#f00", "#0f0", "#ff0", "#00f", "#f0f", "#0ff", "#fff"]; + let teletextColorNames = ["Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"]; + + // Load and set default settings + let appSettings = Object.assign({ + color: teletextColors[6], + theme: 'light', + }, require('Storage').readJSON("shadowclk.json", true) || {}); + + // Save settings to storage + function writeSettings() { + require('Storage').writeJSON("shadowclk.json", appSettings); + } + + // Colors from 'Light BW' and 'Dark BW' themes + function createThemeColors(mode) { + let cl = x => g.setColor(x).getColor(); + return mode ? { + fg: cl("#fff"), + bg: cl("#000"), + fg2: cl("#fff"), + bg2: cl("#004"), + fgH: cl("#fff"), + bgH: cl("#00f"), + dark: true + } : { + fg: cl("#000"), + bg: cl("#fff"), + fg2: cl("#000"), + bg2: cl("#cff"), + fgH: cl("#000"), + bgH: cl("#0ff"), + dark: false + }; + } + + // Switch theme and save to storage + function switchTheme(mode) { + if (mode === g.theme.dark) return; + let s = require('Storage').readJSON("setting.json", 1) || {}; + s.theme = createThemeColors(mode); + require('Storage').writeJSON("setting.json", s); + updateTheme(mode); + } + + // Update the current menu with the new theme + function updateTheme(mode) { + let newTheme = createThemeColors(mode); + g.theme = newTheme; + appSettings.theme = mode ? 'dark' : 'light'; + writeSettings(); + delete g.reset; + g._reset = g.reset; + g.reset = function(n) { + return g._reset().setColor(newTheme.fg).setBgColor(newTheme.bg); + }; + g.clear = function(n) { + if (n) g.reset(); + return g.clearRect(0, 0, g.getWidth(), g.getHeight()); + }; + g.clear(1); + Bangle.drawWidgets(); + showMenu(); + } + + // Read the current system theme + function getCurrentTheme() { + let s = require('Storage').readJSON("setting.json", 1) || {}; + if (!s.theme) { + return appSettings.theme; // fallback to appSettings.theme (light or dark) + } + return s.theme.dark ? 'dark' : 'light'; + } + + function showMenu() { + appSettings.theme = getCurrentTheme(); + E.showMenu({ + "": { + "title": "Shadow Clock" + }, + "< Back": () => back(), + 'Theme:': { + value: (appSettings.theme === 'dark'), + format: v => v ? "Dark" : "Light", + onchange: v => { + switchTheme(v); + } + }, + 'Color:': { + value: teletextColors.indexOf(appSettings.color), + min: 0, + max: 7, + onchange: v => { + appSettings.color = teletextColors[v]; + writeSettings(); + }, + format: v => teletextColorNames[v] + } + }); + } + // Initially show the menu + showMenu(); +}); diff --git a/apps/stlap/ChangeLog b/apps/stlap/ChangeLog index 35ba8b130..0c21e6efd 100644 --- a/apps/stlap/ChangeLog +++ b/apps/stlap/ChangeLog @@ -1,3 +1,4 @@ 0.01: New app! 0.02: Bug fixes -0.03: Submitted to the app loader \ No newline at end of file +0.03: Submitted to the app loader +0.04: Reduce battery consumption when the user's not looking \ No newline at end of file diff --git a/apps/stlap/app.js b/apps/stlap/app.js index 0bd18311f..ad5463180 100644 --- a/apps/stlap/app.js +++ b/apps/stlap/app.js @@ -8,7 +8,7 @@ const BUTTON_ICONS = { reset: heatshrink.decompress(atob("jEYwMA/4BB/+BAQPDAQPnAQIAKv///0///8j///EP//wAQQICBwQUCEhgyCHAQ+CIgI=")) }; -let state = storage.readJSON(STATE_PATH); +let state = storage.readJSON(STATE_PATH, 1); const STATE_DEFAULT = { wasRunning: false, //If the stopwatch was ever running since being reset sessionStart: 0, //When the stopwatch was first started @@ -157,7 +157,7 @@ function firstTimeStart(now, time) { elapsedTime: 0, }; lapFile = 'stlap-' + state.sessionStart + '.json'; - setupTimerInterval(); + setupTimerIntervalFast(); Bangle.buzz(200); drawButtons(); } @@ -201,13 +201,15 @@ function start(now, time) { state.elapsedTime += (state.pausedTime - state.startTime); state.startTime = now; state.running = true; - setupTimerInterval(); + setupTimerIntervalFast(); Bangle.buzz(200); drawTime(); drawButtons(); } Bangle.on("touch", (button, xy) => { + setupTimerIntervalFast(); + //In gesture mode, just turn on the light and then return if (gestureMode) { Bangle.setLCDPower(true); @@ -242,6 +244,8 @@ Bangle.on("touch", (button, xy) => { }); Bangle.on('swipe', direction => { + setupTimerIntervalFast(); + let now = (new Date()).getTime(); let time = getTime(); @@ -272,12 +276,23 @@ setWatch(() => { }, BTN1, { repeat: true }); let timerInterval; +let userWatching = false; + +function setupTimerIntervalFast() { + userWatching = true; + setupTimerInterval(); + + setTimeout(() => { + userWatching = false; + setupTimerInterval(); + }, 5000); +} function setupTimerInterval() { if (timerInterval !== undefined) { clearInterval(timerInterval); } - timerInterval = setInterval(drawTime, 10); + timerInterval = setInterval(drawTime, userWatching ? 10 : 1000); } function stopTimerInterval() { @@ -289,7 +304,7 @@ function stopTimerInterval() { drawTime(); if (state.running) { - setupTimerInterval(); + setupTimerIntervalFast(); } //Save our state when the app is closed @@ -300,5 +315,8 @@ E.on('kill', () => { } }); +// change interval depending of whether the user's looking +Bangle.on("twist", setupTimerIntervalFast); + Bangle.loadWidgets(); Bangle.drawWidgets(); \ No newline at end of file diff --git a/apps/stlap/metadata.json b/apps/stlap/metadata.json index 1ecdc5b6e..ac846dfd7 100644 --- a/apps/stlap/metadata.json +++ b/apps/stlap/metadata.json @@ -1,7 +1,7 @@ { "id": "stlap", "name": "Stopwatch", - "version": "0.03", + "version": "0.04", "description": "A stopwatch that remembers its state, with a lap timer and a gesture mode (enable by swiping)", "icon": "icon.png", "type": "app", @@ -20,5 +20,9 @@ "url": "icon.js", "evaluate": true } + ], + "data": [ + {"name":"stlap.state.json"}, + {"wildcard":"stlap-*.json"} ] } \ No newline at end of file diff --git a/apps/tempmonitor/ChangeLog b/apps/tempmonitor/ChangeLog index af69e6e0a..c14b9b1bb 100644 --- a/apps/tempmonitor/ChangeLog +++ b/apps/tempmonitor/ChangeLog @@ -1,3 +1,4 @@ 0.01: 1st version: saves values to csv 0.02: added HTML interface 0.03: Added Stop/start recording, change BG color, filesize info +0.04: Support for negative degree, Min/Max, random for emulator, clean of code diff --git a/apps/tempmonitor/README.md b/apps/tempmonitor/README.md index e50ac07cb..59a0af58e 100644 --- a/apps/tempmonitor/README.md +++ b/apps/tempmonitor/README.md @@ -1,5 +1,5 @@ # Temperature Monitor (with logging) -Temperature monitor that shows temperature on real time but also allows to store in a file for a later process. +Temperature / Thermometer monitor that not only shows degrees on real time but also allows to store this info in a file for a later process. Compatible with BangleJS1,BangleJS2,and EMSCRIPTENx emulators diff --git a/apps/tempmonitor/metadata.json b/apps/tempmonitor/metadata.json index 45d4d2c35..2ab4e414c 100644 --- a/apps/tempmonitor/metadata.json +++ b/apps/tempmonitor/metadata.json @@ -1,8 +1,8 @@ { "id": "tempmonitor", "name": "Temperature monitor", - "version": "0.03", - "description": "Displays the current temperature and stores in a CSV file", + "version": "0.04", + "description": "Another thermometer, besides displaying current temperature, stores it in a CSV file", "icon": "app.png", "tags": "tool", "interface": "interface.html", diff --git a/apps/tempmonitor/old_ss_emul_bjs1.png b/apps/tempmonitor/old_ss_emul_bjs1.png new file mode 100644 index 000000000..da7a53bd1 Binary files /dev/null and b/apps/tempmonitor/old_ss_emul_bjs1.png differ diff --git a/apps/tempmonitor/old_ss_emul_bjs2.png b/apps/tempmonitor/old_ss_emul_bjs2.png new file mode 100644 index 000000000..1cc2140f9 Binary files /dev/null and b/apps/tempmonitor/old_ss_emul_bjs2.png differ diff --git a/apps/tempmonitor/ss_emul_bjs1.png b/apps/tempmonitor/ss_emul_bjs1.png index da7a53bd1..cd8deddca 100644 Binary files a/apps/tempmonitor/ss_emul_bjs1.png and b/apps/tempmonitor/ss_emul_bjs1.png differ diff --git a/apps/tempmonitor/ss_emul_bjs1_help.png b/apps/tempmonitor/ss_emul_bjs1_help.png new file mode 100644 index 000000000..4d85847b9 Binary files /dev/null and b/apps/tempmonitor/ss_emul_bjs1_help.png differ diff --git a/apps/tempmonitor/ss_emul_bjs2.png b/apps/tempmonitor/ss_emul_bjs2.png index 1cc2140f9..2cdc192f8 100644 Binary files a/apps/tempmonitor/ss_emul_bjs2.png and b/apps/tempmonitor/ss_emul_bjs2.png differ diff --git a/apps/tempmonitor/tempmonitor.app.js b/apps/tempmonitor/tempmonitor.app.js index 9a6e85645..e34ebedc1 100644 --- a/apps/tempmonitor/tempmonitor.app.js +++ b/apps/tempmonitor/tempmonitor.app.js @@ -1,7 +1,7 @@ // Temperature monitor that saves a log of measures // standalone ver for developer, to remove testing lines // delimiter ; (excel) or , (oldscool) -/* REFACTOR and remove commented code related to +/* REFACTOR and remove commented code related to SetUI, Layout, and setWatch( function(b) { }, BTN1, { repeat: true, edge:'falling' }) */ { @@ -12,7 +12,7 @@ var history = []; var readFreq=4000; //ms //PEND add to settings if (v_mode_debug>0) var saveFreq=6000; //ms for testin 6sec else var saveFreq=60000; //ms 1min -var v_saveToFile= new Boolean(true); //true save //false +var v_saveToFile= new Boolean(true); //true save //false //with upload file º is not displayed properly //with upload RAM º is displayed var v_t_symbol="";//ºC @@ -23,6 +23,8 @@ var v_model=process.env.BOARD; var v_color_erase=g.getBgColor(); //original BG color overwritten on SetVariables var v_color=g.getColor();//original FG color var id_rec_intv; //var for the recording interval +var v_t_max=-50; //preset with an opposite and impossible record measure +var v_t_min=70; if (readFreq>saveFreq) console.log("Read refresh freq should be higher than saving"); if (v_mode_debug>0) console.log("original BG/FG color="+v_color_erase+" / "+v_color); @@ -32,281 +34,303 @@ if (v_mode_debug>0) console.log("original BG/FG color="+v_color_erase+" / "+v_co function SetVariables(){ //EMSCRIPTEN,EMSCRIPTEN2 if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') { - v_font_size1=16; - v_font_size2=50; - }else{ - //Banglejs2 or others - v_font_size1=11; //too small? - v_font_size2=40; - } - //overwriting default BG, is better detect? - if (g.theme.dark==1) v_color_erase=0x0000; //dynamic; //bg black - else if (g.theme.dark==0) v_color_erase=0xFFFF; //dynamic; //bg white +v_font_size1=16; +v_font_size2=50; +}else{ +//Banglejs2 or others +v_font_size1=11; //too small? +v_font_size2=40; +} +//overwriting default BG, is better detect? +if (g.theme.dark==1) v_color_erase=0x0000; //dynamic; //bg black +else if (g.theme.dark==0) v_color_erase=0xFFFF; //dynamic; //bg white } //print result function printTemperature(v_temp) { - if (v_mode_debug>1) console.log("v_temp in "+v_temp+" entries "+v_saved_entries); - ClearBox(); - //g.setFont("6x8",2).setFontAlign(0,0); - g.setFontVector(v_font_size1).setFontAlign(0,0); - var x = (rect.x+(rect.x2-60))/2;//-60 space for graph and layout buttons - var y = (rect.y+rect.y2)/2 + 20; +if (v_mode_debug>1) console.log("v_temp in "+v_temp+" entries "+v_saved_entries); - if (v_saveToFile==true) { - // if (v_mode_debug>0) console.log("prev color="+v_color); - printInfo("Recording : "+v_saved_entries, '#CC3333',x,rect.y+30); - //g.setColor('#CC3333'); //red - // g.drawString("Recording : "+v_saved_entries, x, rect.y+35); - //g.setColor(v_color);//restore default color - } - else printInfo("Rec paused : "+v_saved_entries, v_color,x,rect.y+30); - //else g.drawString("Rec paused : "+v_saved_entries, x, rect.y+35); - //space for printing info - g.drawString("Temperature:", x, rect.y+45+(v_font_size1*2)); - //dynamic font (g.getWidth() > 200 ? 60 : 40) - g.setFontVector(v_font_size2).setFontAlign(0,0); - // Avg of temperature readings - while (history.length>4) history.shift(); - history.push(v_temp); - var avrTemp = E.sum(history) / history.length; - //var t = require('locale').temp(avrTemp); - //.replace("'","°"); - lastMeasure=avrTemp.toString(); - if (lastMeasure.length>4) lastMeasure=lastMeasure.substr(0,4); - //DRAW temperature in the center - //remove g.drawString(" ", x-20, y); - g.drawString(v_temp+v_t_symbol, x, y); - g.flip(); +// Avg of temperature readings +while (history.length>4) history.shift(); +history.push(v_temp); +var avrTemp = E.sum(history) / history.length; +//var t = require('locale').temp(avrTemp); +//.replace("'","°"); +lastMeasure=avrTemp.toString(); +if (lastMeasure.length>4) lastMeasure=lastMeasure.substr(0,4); + + +ClearBox(); +//g.setFont("6x8",2).setFontAlign(0,0); +g.setFontVector(v_font_size1).setFontAlign(0,0); +var x = (rect.x+(rect.x2-60))/2;//-60 space for graph and layout buttons +var y = (rect.y+rect.y2)/2 + 20; + +if (v_saveToFile==true) { +// if (v_mode_debug>0) console.log("prev color="+v_color); +printInfo("Recording : "+v_saved_entries, '#CC3333',x,rect.y+30); +//g.setColor('#CC3333'); //red +// g.drawString("Recording : "+v_saved_entries, x, rect.y+35); +//g.setColor(v_color);//restore default color +} +else printInfo("Rec paused : "+v_saved_entries, v_color,x,rect.y+30); +//else g.drawString("Rec paused : "+v_saved_entries, x, rect.y+35); +//space for printing info +g.drawString("Temperature:", x, rect.y+45+(v_font_size1*2)); + +if (v_temp>v_t_max) { +g.setColor(v_color_erase); +g.drawString(v_t_max.toString().substr(0,5), rect.x2-40,(rect.y2/2)-30); +v_t_max=v_temp; +g.setColor(v_color); +} +g.drawString(v_t_max.toString().substr(0,5), rect.x2-40,(rect.y2/2)-30); +if (v_temp 200 ? 60 : 40) +g.setFontVector(v_font_size2).setFontAlign(0,0); + +//DRAW temperature in the center +//5 char required for negative degrees +g.drawString((v_temp.toString().substr(0,5))+v_t_symbol, x, y); +g.flip(); } // from: BJS2 pressure sensor, BJS1 inbuilt thermistor function getTemperature() { - if(v_model.substr(0,10)!='EMSCRIPTEN'){ - if (Bangle.getPressure) { - Bangle.getPressure().then(p =>{if (p) printTemperature(p);}); - } else printTemperature(E.getTemperature()); - } - else printTemperature(11.25);//fake temperature medition for emulators +if(v_model.substr(0,10)!='EMSCRIPTEN'){ +if (Bangle.getPressure) { +Bangle.getPressure().then(p =>{if (p) printTemperature(p);}); +} else printTemperature(E.getTemperature()); +} +else printTemperature(-11.2+Math.random());//fake temperature medition for emulators } /* Note that it changes BG and also FG to an opposite*/ -function changeBGcolor(){ - //pend to refactor - if (v_mode_debug>1) console.log("before BG/FG "+v_color_erase+" /"+v_color); - v_color_erase=0xFFFF-v_color_erase; - v_color=0xFFFF-v_color; - if (v_mode_debug>1) console.log("after result BG/FG "+v_color_erase+" /"+v_color); - //g.setColor(color_result); - g.setBgColor(v_color_erase);// 0 white, 1 black - g.setColor(v_color); - //move to event? - ClearScreen(); - ClearBox(); - drawGraph(); - getTemperature(); - //setDrawLayout(); //uncomment if layout can work with setUI - //g.clear();//impact on widgets +function changeBGcolor(){ +//pend to refactor +if (v_mode_debug>1) console.log("before BG/FG "+v_color_erase+" /"+v_color); +v_color_erase=0xFFFF-v_color_erase; +v_color=0xFFFF-v_color; +if (v_mode_debug>1) console.log("after result BG/FG "+v_color_erase+" /"+v_color); +//g.setColor(color_result); +g.setBgColor(v_color_erase);// 0 white, 1 black +g.setColor(v_color); +//move to event? +ClearScreen(); +ClearBox();//? +getTemperature(); +drawGraph(); +//setDrawLayout(); //uncomment if layout can work with setUI +//g.clear();//impact on widgets } function saveToFile(){ - //input global vars: lastMeasure - var a=new Date(); - var strlastSaveTime=new String(); - strlastSaveTime=a.toISOString(); - //strlastSaveTime=strlastSaveTime.concat(a.getFullYear(),a.getMonth()+1,a.getDate(),a.getHours(),a.getMinutes());; - if (v_mode_debug>1) console.log("saving="+strlastSaveTime+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure); - if (v_saveToFile==true){ - //write(strlastSaveTime+";"+ - //var f = require("Storage").open(v_filename,"r"); - // f=require("Storage").read(v_filename+"\1");//suffix required load completely!! - //note that .read uses Storage Class .open uses StorageFile Class , difference in file chunks - // if (v_mode_debug>0) console.log("f "+f); - var f = require("Storage").open(v_filename,"r"); - if ((v_mode_debug>0) && (v_saved_entries==0)) console.log("file info:"+f); - if (f.len>0) { - if (!f) { - require("Storage").open(v_filename,"w").write("Month;Day;Time;Temp"+"\n"); - if (v_mode_debug>0) console.log("not exist but created "+f); - } - else{ - require("Storage").open(v_filename,"a").write((a.getMonth()+1)+";"+a.getDate()+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure+"\n"); - //(getTime()+","); - v_saved_entries=v_saved_entries+1; - if (v_mode_debug>1) console.log("append to already exist "+f.name+" , "+v_saved_entries); - } - } - } - else if (v_mode_debug>0) console.log("recording mode stopped"); +//input global vars: lastMeasure +var a=new Date(); +var strlastSaveTime=new String(); +strlastSaveTime=a.toISOString(); +//strlastSaveTime=strlastSaveTime.concat(a.getFullYear(),a.getMonth()+1,a.getDate(),a.getHours(),a.getMinutes());; +if (v_mode_debug>1) console.log("saving="+strlastSaveTime+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure); +if (v_saveToFile==true){ +//write(strlastSaveTime+";"+ +//var f = require("Storage").open(v_filename,"r"); +// f=require("Storage").read(v_filename+"\1");//suffix required load completely!! +//note that .read uses Storage Class .open uses StorageFile Class , difference in file chunks +// if (v_mode_debug>0) console.log("f "+f); +var f = require("Storage").open(v_filename,"r"); +if ((v_mode_debug>0) && (v_saved_entries==0)) console.log("file info:"+f); +if (f.len>0) { +if (!f) { +require("Storage").open(v_filename,"w").write("Month;Day;Time;Temp"+"\n"); +if (v_mode_debug>0) console.log("not exist but created "+f); +} +else{ +require("Storage").open(v_filename,"a").write((a.getMonth()+1)+";"+a.getDate()+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure+"\n"); +//(getTime()+","); +v_saved_entries=v_saved_entries+1; +if (v_mode_debug>1) console.log("append to already exist "+f.name+" , "+v_saved_entries); +} +} +} +else if (v_mode_debug>0) console.log("recording mode stopped"); } function drawGraph(){ - var img_obj_thermo = { - width : 36, height : 36, bpp : 3, - transparent : 0, - buffer : require("heatshrink").decompress(atob("AEFt2AMKm3bsAMJjdt23ABhEB+/7tgaJ///DRUP//7tuADRP923YDRXbDRfymwaJhu/koaK7eyiwaK3cLDRlWDRY1NKBY1Ztu5kjmJg3cyVI7YMHgdu5Mkyu2fxHkyVJjdgDRFJkmRDRPsDQNbDQ5QBGoONKBJrBoxQIQwO2eRcbtu24AMIFIQLJAH4AMA==")) - }; - g.drawImage(img_obj_thermo,rect.x2-60,rect.y2/2); - g.flip(); +if (v_mode_debug>1) console.log("drawGraph"); +var img_obj_thermo = { +width : 36, height : 36, bpp : 3, +transparent : 0, +buffer : require("heatshrink").decompress(atob("AEFt2AMKm3bsAMJjdt23ABhEB+/7tgaJ///DRUP//7tuADRP923YDRXbDRfymwaJhu/koaK7eyiwaK3cLDRlWDRY1NKBY1Ztu5kjmJg3cyVI7YMHgdu5Mkyu2fxHkyVJjdgDRFJkmRDRPsDQNbDQ5QBGoONKBJrBoxQIQwO2eRcbtu24AMIFIQLJAH4AMA==")) +}; +g.drawImage(img_obj_thermo,rect.x2-60,rect.y2/2); +g.flip(); } function ClearScreen(){ - //avoid widget areas - g.setBgColor(v_color_erase); - g.clearRect(rect.x, rect.y+24, rect.x2, rect.y2-24); - g.flip(); +//avoid widget areas +g.setBgColor(v_color_erase); +g.clearRect(rect.x, rect.y+24, rect.x2, rect.y2-24); +g.flip(); } function ClearBox(){ - //custom boxarea , left space for static graph at right - g.setBgColor(v_color_erase); - g.clearRect(rect.x, rect.y+24, rect.x2-60, rect.y2-24); - g.flip(); +//custom boxarea , left space for static graph at right +g.setBgColor(v_color_erase); +g.clearRect(rect.x, rect.y+24, rect.x2-60, rect.y2-24); +g.flip(); } function introPage(){ - //g.setFont("6x8",2).setFontAlign(0,0); - g.setFontVector(v_font_size1).setFontAlign(-1,0); - //x alignment. -1=left (default), 0=center, 1=right - var x=3; - //dynamic positions as height for BJS1 is double than BJS2 - var y = (rect.y+rect.y2)/2 + 10; - g.drawString(" Default values ", x, y - ((v_font_size1*3)+2)); - g.drawString("--------------------", x, y - ((v_font_size1*2)+2)); - g.drawString("Mode debug: "+v_mode_debug, x, y - ((v_font_size1*1)+2)); - g.drawString("Read freq(ms): "+readFreq, x, y ); - g.drawString("Save to file: "+v_saveToFile, x, y+ ((v_font_size1*1)+2) ); - g.drawString("Save freq(ms):"+saveFreq, x, y+((v_font_size1*2)+2) ); - fr=require("Storage").read(v_filename+"\1");//suffix required - if (fr) g.drawString("Filesize:"+fr.length.toString()+"kb", x, y+((v_font_size1*3)+2) ); - else g.drawString("File not exist", x, y+((v_font_size1*3)+2)); +//g.setFont("6x8",2).setFontAlign(0,0); +g.setFontVector(v_font_size1).setFontAlign(-1,0); +//x alignment. -1=left (default), 0=center, 1=right +var x=3; +//dynamic positions as height for BJS1 is double than BJS2 +var y = (rect.y+rect.y2)/2 + 10; +g.drawString(" Default values ", x, y - ((v_font_size1*3)+2)); +g.drawString("--------------------", x, y - ((v_font_size1*2)+2)); +g.drawString("Mode debug: "+v_mode_debug, x, y - ((v_font_size1*1)+2)); +g.drawString("Read frq(ms): "+readFreq, x, y ); +g.drawString("Save file: "+v_saveToFile, x, y+ ((v_font_size1*1)+2) ); +g.drawString("Save frq(ms):"+saveFreq, x, y+((v_font_size1*2)+2) ); +fr=require("Storage").read(v_filename+"\1");//suffix required +if (fr) g.drawString("Filesize:"+fr.length.toString()+"kb", x, y+((v_font_size1*3)+2) ); +else g.drawString("File not exist", x, y+((v_font_size1*3)+2)); } function printInfo(pmsg, pcolor,px,py){ - g.setColor(pcolor); - g.setFontVector(v_font_size1).setFontAlign(0,0); - g.drawString(pmsg, px,py+v_font_size1); - g.setColor(v_color);//restore default color +g.setColor(pcolor); +g.setFontVector(v_font_size1).setFontAlign(0,0); +g.drawString(pmsg, px,py+v_font_size1); +g.setColor(v_color);//restore default color } function toggleRecMode(duration, exectime){ - //bydefault float, standard epoch requires *1000 - if (v_mode_debug>0) console.log("duration"+duration); - if (duration>2) { //delete file - var x = (rect.x+(rect.x2-60))/2; - printInfo("Deleting file",'#CC3333',x, rect.y+32+v_font_size1); - // g.setColor('#CC3333'); //red +//bydefault float, standard epoch requires *1000 +if (v_mode_debug>0) console.log("duration"+duration); +if (duration>2) { //delete file +var x = (rect.x+(rect.x2-60))/2; +printInfo("Deleting file",'#CC3333',x, rect.y+32+v_font_size1); +// g.setColor('#CC3333'); //red - //too long "Deleting file: "+v_filename, - // for StorageFiles created with require("Storage").open(filename, ...) - //require("Storage").erase(v_filename); - //TODO refactor in a new function - //var mifile = require("Storage").open(v_filename,"w"); - var mifile = require("Storage").open("temphistory.csv","w"); - var v_output=mifile.erase(); - //mifile.StorageFile.erase(); - if (v_mode_debug>0) console.log("output"+v_output); - setTimeout(function() { if (v_mode_debug>0) console.log("pause for 1 sec");},1000); - return; //leave this function - } - if (v_saveToFile) v_saveToFile=false; - else v_saveToFile=true; - if (v_mode_debug>0) console.log("recording? "+v_saveToFile); - setRecordingFreq(); +//too long "Deleting file: "+v_filename, +// for StorageFiles created with require("Storage").open(filename, ...) +//require("Storage").erase(v_filename); +//TODO refactor in a new function +//var mifile = require("Storage").open(v_filename,"w"); +var mifile = require("Storage").open("temphistory.csv","w"); +var v_output=mifile.erase(); +//mifile.StorageFile.erase(); +if (v_mode_debug>0) console.log("output"+v_output); +setTimeout(function() { if (v_mode_debug>0) console.log("pause for 1 sec");},1000); +return; //leave this function +} +if (v_saveToFile) v_saveToFile=false; +else v_saveToFile=true; +if (v_mode_debug>0) console.log("recording? "+v_saveToFile); +setRecordingFreq(); } function setRecordingFreq(){ - if (v_saveToFile==true) { //TODO now start on false btn will no enable - id_rec_intv=setInterval(function() { - saveToFile(); - }, saveFreq); //ms - if (v_mode_debug>0) console.log("interval id / frq"+id_rec_intv+" / "+saveFreq); - } - else if (id_rec_intv){ - clearInterval(id_rec_intv); - if (v_mode_debug>0) console.log("rec interval removed, id "+id_rec_intv); - id_rec_intv=0; // to reset var - } +if (v_saveToFile==true) { //TODO now start on false btn will no enable +id_rec_intv=setInterval(function() { +saveToFile(); +}, saveFreq); //ms +if (v_mode_debug>0) console.log("interval id / frq"+id_rec_intv+" / "+saveFreq); +} +else if (id_rec_intv){ +clearInterval(id_rec_intv); +if (v_mode_debug>0) console.log("rec interval removed, id "+id_rec_intv); +id_rec_intv=0; // to reset var +} } -function UserInput(){ - //theoretically incompatible with Layout - Bangle.setUI({ - mode : "custom", - //adds a back icon on top widget area - back : function() {load();}, - //touch : function(n,e) {}, // optional - handler for 'touch' events - // righ/Left 1/-1 , updown - swipe : function(dir_rl,dir_ud) { - if(dir_rl == 1) { - if (v_mode_debug>0) console.log("swipe right: "); - getFileInfo(v_filename); - } - else if (dir_rl == -1){ - if (v_mode_debug>0) console.log("swipe left: "); - changeBGcolor(); - } - }, - touch : function(tzone,tobj){ - if ((process.env.HWVERSION == 2)&&(v_mode_debug>0)){ - console.log("tobj x,y,type : "+tobj.x+" "+tobj.y+" "+tobj.type); - } - switch(tzone){ - //case 1: //left , back managed by setUI - case 2: // right disable/enable recording - toggleRecMode(0); //toggleRecMode(duration, exectime) - break; - // case 3: console.log("Touch 3 aka 1+2 not for BJS1 emul");//center 1+2 - // break; - } - }, - //inferior to - btn : function(btn) { - if(btn == 1) { - if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') toggleRecMode(1); //console.log("btn1 BJS1"); - else mainBtnShortcut(); //console.log("btn1 BJS2"); - } - else if (btn == 2) mainBtnShortcut(); //console.log("btn2 BJS1"); - else if (btn == 3) changeBGcolor(); //console.log("btn3 BJS1"); - } - }); //endof setUI +function UserInput(){ +//theoretically incompatible with Layout +Bangle.setUI({ +mode : "custom", +//adds a back icon on top widget area +back : function() {load();}, +//touch : function(n,e) {}, // optional - handler for 'touch' events +// righ/Left 1/-1 , updown +swipe : function(dir_rl,dir_ud) { +if(dir_rl == 1) { +if (v_mode_debug>0) console.log("swipe right: "); +getFileInfo(v_filename); +} +else if (dir_rl == -1){ +if (v_mode_debug>0) console.log("swipe left: "); +changeBGcolor(); +} +}, +touch : function(tzone,tobj){ +if ((process.env.HWVERSION == 2)&&(v_mode_debug>0)){ +console.log("tobj x,y,type : "+tobj.x+" "+tobj.y+" "+tobj.type); +} +switch(tzone){ +//case 1: //left , back managed by setUI +case 2: // right disable/enable recording +toggleRecMode(0); //toggleRecMode(duration, exectime) +break; +// case 3: console.log("Touch 3 aka 1+2 not for BJS1 emul");//center 1+2 +// break; +} +}, +//inferior to +btn : function(btn) { +if(btn == 1) { +if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') toggleRecMode(1); //console.log("btn1 BJS1"); +else mainBtnShortcut(); //console.log("btn1 BJS2"); +} +else if (btn == 2) mainBtnShortcut(); //console.log("btn2 BJS1"); +else if (btn == 3) changeBGcolor(); //console.log("btn3 BJS1"); +} +}); //endof setUI } -function mainBtnShortcut() { - //if messages app installed shortcut otherwise default access to launcher - if (require("Storage").read("messagegui.app.js")===undefined) - { - if (require("Storage").read("messagelist.app.js")===undefined) Bangle.showLauncher(); // implies btn2(js1) btn(js2)- launcher - else if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') load("messagelist.app.js"); - else load("messagelist.app.js"); - } - else if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') load("messagegui.app.js"); - else load("messagegui.app.js"); - } +function mainBtnShortcut() { +//if messages app installed shortcut otherwise default access to launcher +if (require("Storage").read("messagegui.app.js")===undefined) +{ +if (require("Storage").read("messagelist.app.js")===undefined) Bangle.showLauncher(); // implies btn2(js1) btn(js2)- launcher +else if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') load("messagelist.app.js"); +else load("messagelist.app.js"); +} +else if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') load("messagegui.app.js"); +else load("messagegui.app.js"); +} // Show file size -function getFileInfo(v_filename) { - var f = require("Storage").open(v_filename,"r"); - //todo refactor and reuse common code - g.setFontVector(v_font_size1).setFontAlign(0,0); - var x = (rect.x+(rect.x2-60))/2; - printInfo("file size:"+f.len,v_color,x, rect.y+32+v_font_size1); - // g.drawString("file size:"+f.len, x, rect.y+37+v_font_size1); - if (v_mode_debug>0) console.log("file "+v_filename+" size: "+f.len); +function getFileInfo(v_filename) { +var f = require("Storage").open(v_filename,"r"); +//todo refactor and reuse common code +g.setFontVector(v_font_size1).setFontAlign(0,0); +var x = (rect.x+(rect.x2-60))/2; +printInfo("file size:"+f.len,v_color,x, rect.y+32+v_font_size1); +// g.drawString("file size:"+f.len, x, rect.y+37+v_font_size1); +if (v_mode_debug>0) console.log("file "+v_filename+" size: "+f.len); }// not used //MAIN SetVariables(); Bangle.loadWidgets(); - ClearScreen(); introPage(); - //setDrawLayout(); //uncomment if layout can work with setUI UserInput(); //inc SetUI and back icon setInterval(function() { - getTemperature(); +getTemperature(); }, readFreq); //ms - +//??need +drawGraph(); setRecordingFreq(); } \ No newline at end of file diff --git a/apps/tempmonitor/tempmonitor.info b/apps/tempmonitor/tempmonitor.info index f31704b57..4d20912ac 100644 --- a/apps/tempmonitor/tempmonitor.info +++ b/apps/tempmonitor/tempmonitor.info @@ -1 +1 @@ -{"id":"tempmonitor","name":"tempmonitor","src":"tempmonitor.app.js","icon":"tempmonitor.img","version":"0.03","files":"tempmonitor.info,tempmonitor.app.js,tempmonitor.img"} \ No newline at end of file +{"id":"tempmonitor","name":"tempmonitor","src":"tempmonitor.app.js","icon":"tempmonitor.img","version":"0.04","files":"tempmonitor.info,tempmonitor.app.js,tempmonitor.img"} \ No newline at end of file diff --git a/apps/widclkinfo/ChangeLog b/apps/widclkinfo/ChangeLog new file mode 100644 index 000000000..4c21f3ace --- /dev/null +++ b/apps/widclkinfo/ChangeLog @@ -0,0 +1 @@ +0.01: New Widget! diff --git a/apps/widclkinfo/metadata.json b/apps/widclkinfo/metadata.json new file mode 100644 index 000000000..3848563c6 --- /dev/null +++ b/apps/widclkinfo/metadata.json @@ -0,0 +1,13 @@ +{ "id": "widclkinfo", + "name": "Clock Info Widget", + "version":"0.01", + "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", + "screenshots" : [ { "url":"screenshot.png" }], + "type": "widget", + "tags": "widget,clkinfo", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"widclkinfo.wid.js","url":"widget.js"} + ] +} diff --git a/apps/widclkinfo/screenshot.png b/apps/widclkinfo/screenshot.png new file mode 100644 index 000000000..d36f6bf05 Binary files /dev/null and b/apps/widclkinfo/screenshot.png differ diff --git a/apps/widclkinfo/widget.js b/apps/widclkinfo/widget.js new file mode 100644 index 000000000..403f289e7 --- /dev/null +++ b/apps/widclkinfo/widget.js @@ -0,0 +1,49 @@ +if (!require("clock_info").loadCount) { // don't load if a clock_info was already loaded + // Load the clock infos + let clockInfoItems = require("clock_info").load(); + // Add the + let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { + // 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' + // This function draws the info + draw : (itm, info, options) => { + // itm: the item containing name/hasRange/etc + // info: data returned from itm.get() containing text/img/etc + // options: options passed into addInteractive + clockInfoInfo = info; + if (WIDGETS["clkinfo"]) + WIDGETS["clkinfo"].draw(WIDGETS["clkinfo"]); + } + }); + let clockInfoInfo; // when clockInfoMenu.draw is called we set this up + + // The actual widget we're displaying + WIDGETS["clkinfo"] = { + area:"tl", + width: clockInfoMenu.w, + draw:function(e) { + clockInfoMenu.x = e.x; + clockInfoMenu.y = e.y; + var o = clockInfoMenu; + // Clear the background + g.reset(); + // indicate focus - make background reddish + //if (clockInfoMenu.focus) g.setBgColor(g.blendColor(g.theme.bg, "#f00", 0.25)); + if (clockInfoMenu.focus) g.setColor("#f00"); + g.clearRect(o.x, o.y, o.x+o.w-1, o.y+o.h); + if (clockInfoInfo) { + var x = o.x; + if (clockInfoInfo.img) { + g.drawImage(clockInfoInfo.img, x,o.y); // draw the image + x+=24; + } + var availableWidth = o.x+clockInfoMenu.w - (x+2); + g.setFont("6x8:2").setFontAlign(-1,0); + if (g.stringWidth(clockInfoInfo.text) > availableWidth) + g.setFont("6x8"); + g.drawString(clockInfoInfo.text, x+2,o.y+12); // draw the text + } + } + }; +} \ No newline at end of file diff --git a/apps/widclkinfo/widget.png b/apps/widclkinfo/widget.png new file mode 100644 index 000000000..16ecea9f8 Binary files /dev/null and b/apps/widclkinfo/widget.png differ diff --git a/apps/widhid/README.md b/apps/widhid/README.md new file mode 100644 index 000000000..7651d74eb --- /dev/null +++ b/apps/widhid/README.md @@ -0,0 +1,21 @@ +# Description + +A music control widget based on [Swipe Bluetooth Music Controls] (based on [Bluetooth Music Controls]). +By operating as a widget, you can control music without leaving your current app (e.g. on a run, bike ride or just watching the clock). + + +[Swipe Bluetooth Music Controls]: https://github.com/espruino/BangleApps/tree/master/apps/hidmsicswipe +[Bluetooth Music Controls]: https://github.com/espruino/BangleApps/tree/master/apps/hidmsic + +# Usage + +Swipe down to enable - note the icon changes from blue to orange, indicating it's listening for your instruction. Then drag up/down for volume, left/right for previous and next and tap for play/pause. + +All other watch interaction is disabled for 3 seconds, to prevent clashing taps/drags - this period is extended as you continue to alter the volume, play/pause and jump between tracks. + + +# Setup / Technical details + +Note that HID must be enabled in settings. Then provided you're paired with your phone/computer, the widget icon will appear and you can control music from your clock face! + +The app disables all other drag and tap handlers while this widget is "active" (in a similar manner to [`backswipe`](https://github.com/espruino/BangleApps/pull/2524#issuecomment-1406230564) and [`lightswitch`](https://github.com/espruino/Espruino/issues/2151#issuecomment-1042423211)). diff --git a/apps/widhid/icon.js b/apps/widhid/icon.js new file mode 100644 index 000000000..a8f9cbdb4 --- /dev/null +++ b/apps/widhid/icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBiIA/AEhtDNSSHGCyIbFGJ0QFAowIA48QE4oGBBAomBHAxXHA4IJED5IXJCAcQIAxGGC4YKEI44HCBAxAGO4wXBB4JYGNRBfHC/6HFB4wXHUA6YIC4oOCGA6YGU4quHJ5LXGdJIXNF65fIC5AQFQorHJXxwXJK5xGJC65GsgJG/Iw4uUfgIuUC4QWTIwIusLq4WBFy50tC1YXBCyoA/ADw=")) diff --git a/apps/widhid/icon.png b/apps/widhid/icon.png new file mode 100644 index 000000000..2ccc88883 Binary files /dev/null and b/apps/widhid/icon.png differ diff --git a/apps/widhid/metadata.json b/apps/widhid/metadata.json new file mode 100644 index 000000000..10e75fadc --- /dev/null +++ b/apps/widhid/metadata.json @@ -0,0 +1,16 @@ +{ + "id": "widhid", + "name": "Bluetooth Music Swipe Control Widget", + "shortName": "BLE Swipe Widget", + "version": "0.01", + "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", + "type": "widget", + "tags": "widget,bluetooth,music", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"widhid.wid.js","url":"wid.js"}, + {"name":"widhid.img","url":"icon.js","evaluate":true} + ] +} diff --git a/apps/widhid/wid.js b/apps/widhid/wid.js new file mode 100644 index 000000000..ed1e78e76 --- /dev/null +++ b/apps/widhid/wid.js @@ -0,0 +1,176 @@ +(function () { + var settings = require("Storage").readJSON("setting.json", true) || { HID: false }; + if (settings.HID !== "kbmedia") { + console.log("widhid: can't enable, HID setting isn't \"kbmedia\""); + return; + } + delete settings; + var anchor = { x: 0, y: 0 }; + var start = { x: 0, y: 0 }; + var dragging = false; + var activeTimeout; + var waitForRelease = true; + var onSwipe = (function (_lr, ud) { + if (Bangle.CLKINFO_FOCUS) + return; + if (!activeTimeout && ud > 0) { + listen(); + Bangle.buzz(20); + } + }); + var onDrag = (function (e) { + if (Bangle.CLKINFO_FOCUS) + return; + if (e.b === 0) { + var wasDragging = dragging; + dragging = false; + if (waitForRelease) { + waitForRelease = false; + return; + } + if (!wasDragging + || (Math.abs(e.x - anchor.x) < 2 && Math.abs(e.y - anchor.y) < 2)) { + toggle(); + onEvent(); + return; + } + } + if (waitForRelease) + return; + if (e.b && !dragging) { + dragging = true; + setStart(e); + Object.assign(anchor, start); + return; + } + var dx = e.x - start.x; + var dy = e.y - start.y; + if (Math.abs(dy) > 25 && Math.abs(dx) > 25) { + setStart(e); + return; + } + if (dx > 40) { + next(); + onEvent(); + waitForRelease = true; + } + else if (dx < -40) { + prev(); + onEvent(); + waitForRelease = true; + } + else if (dy > 30) { + down(); + onEvent(); + setStart(e); + } + else if (dy < -30) { + up(); + onEvent(); + setStart(e); + } + }); + var setStart = function (_a) { + var x = _a.x, y = _a.y; + start.x = x; + start.y = y; + }; + var onEvent = function () { + Bangle.buzz(20); + listen(); + }; + var listen = function () { + var wasActive = !!activeTimeout; + if (!wasActive) { + suspendOthers(); + waitForRelease = true; + Bangle.on("drag", onDrag); + redraw(); + } + if (activeTimeout) + clearTimeout(activeTimeout); + activeTimeout = setTimeout(function () { + activeTimeout = undefined; + Bangle.removeListener("drag", onDrag); + resumeOthers(); + redraw(); + }, 3000); + }; + var redraw = function () { return setTimeout(Bangle.drawWidgets, 50); }; + var connected = NRF.getSecurityStatus().connected; + WIDGETS["hid"] = { + area: "tr", + sortorder: -20, + draw: function () { + if (this.width === 0) + return; + g.drawImage(activeTimeout + ? require("heatshrink").decompress(atob("jEYxH+AEfH44XXAAYXXDKIXZDYp3pC/6KHUMwWHC/4XvUy4YGdqoA/AFoA==")) + : require("heatshrink").decompress(atob("jEYxH+AEcdjoXXAAYXXDKIXZDYp3pC/6KHUMwWHC/4XvUy4YGdqoA/AFoA==")), this.x + 2, this.y + 2); + }, + width: connected ? 24 : 0, + }; + if (connected) + Bangle.on("swipe", onSwipe); + delete connected; + NRF.on("connect", function () { + WIDGETS["hid"].width = 24; + Bangle.on("swipe", onSwipe); + redraw(); + }); + NRF.on("disconnect", function () { + WIDGETS["hid"].width = 0; + Bangle.removeListener("swipe", onSwipe); + redraw(); + }); + var sendHid = function (code) { + NRF.sendHIDReport([1, code], function () { return NRF.sendHIDReport([1, 0]); }); + }; + var next = function () { return sendHid(0x01); }; + var prev = function () { return sendHid(0x02); }; + var toggle = function () { return sendHid(0x10); }; + var up = function () { return sendHid(0x40); }; + var down = function () { return sendHid(0x80); }; + var touchEvents = { + tap: null, + gesture: null, + aiGesture: null, + swipe: null, + touch: null, + drag: null, + stroke: null, + }; + var suspendOthers = function () { + for (var event in touchEvents) { + var handlers = Bangle["#on".concat(event)]; + if (!handlers) + continue; + var newEvents = void 0; + if (handlers instanceof Array) + newEvents = handlers.slice(); + else + newEvents = [handlers]; + for (var _i = 0, newEvents_1 = newEvents; _i < newEvents_1.length; _i++) { + var handler = newEvents_1[_i]; + Bangle.removeListener(event, handler); + } + touchEvents[event] = newEvents; + } + }; + var resumeOthers = function () { + for (var event in touchEvents) { + var handlers = touchEvents[event]; + touchEvents[event] = null; + if (handlers) + for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { + var handler = handlers_1[_i]; + try { + Bangle.on(event, handler); + } + catch (e) { + console.log("couldn't restore \"".concat(event, "\" handler:"), e); + } + } + } + }; +})(); diff --git a/apps/widhid/wid.ts b/apps/widhid/wid.ts new file mode 100644 index 000000000..6b5e38855 --- /dev/null +++ b/apps/widhid/wid.ts @@ -0,0 +1,199 @@ +(() => { + const settings: Settings = require("Storage").readJSON("setting.json", true) || { HID: false } as Settings; + if (settings.HID !== "kbmedia") { + console.log("widhid: can't enable, HID setting isn't \"kbmedia\""); + return; + } + // @ts-ignore + delete settings; + + let anchor = {x:0,y:0}; + let start = {x:0,y:0}; + let dragging = false; + let activeTimeout: number | undefined; + let waitForRelease = true; + + const onSwipe = ((_lr, ud) => { + if((Bangle as BangleExt).CLKINFO_FOCUS) return; + + if(!activeTimeout && ud! > 0){ + listen(); + Bangle.buzz(20); + } + }) satisfies SwipeCallback; + + const onDrag = (e => { + if((Bangle as BangleExt).CLKINFO_FOCUS) return; + + if(e.b === 0){ + // released + const wasDragging = dragging; + dragging = false; + + if(waitForRelease){ + waitForRelease = false; + return; + } + + if(!wasDragging // i.e. tap + || (Math.abs(e.x - anchor.x) < 2 && Math.abs(e.y - anchor.y) < 2)) + { + toggle(); + onEvent(); + return; + } + } + if(waitForRelease) return; + + if(e.b && !dragging){ + dragging = true; + setStart(e); + Object.assign(anchor, start); + return; + } + + const dx = e.x - start.x; + const dy = e.y - start.y; + + if(Math.abs(dy) > 25 && Math.abs(dx) > 25){ + // diagonal, ignore + setStart(e); + return; + } + + // had a drag in a single axis + if(dx > 40){ next(); onEvent(); waitForRelease = true; } + else if(dx < -40){ prev(); onEvent(); waitForRelease = true; } + else if(dy > 30){ down(); onEvent(); setStart(e); } + else if(dy < -30){ up(); onEvent(); setStart(e); } + }) satisfies DragCallback; + + const setStart = ({ x, y }: { x: number, y: number }) => { + start.x = x; + start.y = y; + }; + + const onEvent = () => { + Bangle.buzz(20); // feedback event sent + listen(); // had an event, keep listening for more + }; + + const listen = () => { + const wasActive = !!activeTimeout; + if(!wasActive){ + suspendOthers(); + waitForRelease = true; // wait for first touch up before accepting gestures + Bangle.on("drag", onDrag); + redraw(); + } + + if(activeTimeout) clearTimeout(activeTimeout); + activeTimeout = setTimeout(() => { + activeTimeout = undefined; + + Bangle.removeListener("drag", onDrag); + resumeOthers(); + + redraw(); + }, 3000); + }; + + const redraw = () => setTimeout(Bangle.drawWidgets, 50); + + const connected = NRF.getSecurityStatus().connected; + WIDGETS["hid"] = { + area: "tr", + sortorder: -20, + draw: function() { + if(this.width === 0) return; + g.drawImage( + activeTimeout + ? require("heatshrink").decompress(atob("jEYxH+AEfH44XXAAYXXDKIXZDYp3pC/6KHUMwWHC/4XvUy4YGdqoA/AFoA==")) + : require("heatshrink").decompress(atob("jEYxH+AEcdjoXXAAYXXDKIXZDYp3pC/6KHUMwWHC/4XvUy4YGdqoA/AFoA==")), + this.x! + 2, + this.y! + 2 + ); + }, + width: connected ? 24 : 0, + }; + + if(connected) + Bangle.on("swipe", onSwipe); + // @ts-ignore + delete connected; + + NRF.on("connect", () => { + WIDGETS["hid"]!.width = 24; + Bangle.on("swipe", onSwipe); + redraw(); + }); + NRF.on("disconnect", () => { + WIDGETS["hid"]!.width = 0; + Bangle.removeListener("swipe", onSwipe); + redraw(); + }); + + //const DEBUG = true; + const sendHid = (code: number) => { + //if(DEBUG) return; + NRF.sendHIDReport( + [1, code], + () => NRF.sendHIDReport([1, 0]), + ); + }; + + const next = () => /*DEBUG ? console.log("next") : */ sendHid(0x01); + const prev = () => /*DEBUG ? console.log("prev") : */ sendHid(0x02); + const toggle = () => /*DEBUG ? console.log("toggle") : */ sendHid(0x10); + const up = () => /*DEBUG ? console.log("up") : */ sendHid(0x40); + const down = () => /*DEBUG ? console.log("down") : */ sendHid(0x80); + + // similarly to the lightswitch app, we tangle with the listener arrays to + // disable event handlers + type Handler = () => void; + const touchEvents: { + [key: string]: null | Handler[] + } = { + tap: null, + gesture: null, + aiGesture: null, + swipe: null, + touch: null, + drag: null, + stroke: null, + }; + + const suspendOthers = () => { + for(const event in touchEvents){ + const handlers: Handler[] | Handler | undefined + = (Bangle as any)[`#on${event}`]; + + if(!handlers) continue; + + let newEvents; + if(handlers instanceof Array) + newEvents = handlers.slice(); + else + newEvents = [handlers /* single fn */]; + + for(const handler of newEvents) + Bangle.removeListener(event, handler); + + touchEvents[event] = newEvents; + } + }; + const resumeOthers = () => { + for(const event in touchEvents){ + const handlers = touchEvents[event]; + touchEvents[event] = null; + + if(handlers) + for(const handler of handlers) + try{ + Bangle.on(event as any, handler); + }catch(e){ + console.log(`couldn't restore "${event}" handler:`, e); + } + } + }; +})() diff --git a/apps/widhid/widget-active.png b/apps/widhid/widget-active.png new file mode 100644 index 000000000..74ce4b2a0 Binary files /dev/null and b/apps/widhid/widget-active.png differ diff --git a/apps/widhid/widget.png b/apps/widhid/widget.png new file mode 100644 index 000000000..dfb108060 Binary files /dev/null and b/apps/widhid/widget.png differ diff --git a/apps/widtemp/ChangeLog b/apps/widtemp/ChangeLog index af7f83942..8fd2f7ca1 100644 --- a/apps/widtemp/ChangeLog +++ b/apps/widtemp/ChangeLog @@ -1 +1,2 @@ 0.01: Initial release +0.02: Disallow emulator diff --git a/apps/widtemp/metadata.json b/apps/widtemp/metadata.json index 7b28acd38..5ec0f2f0e 100644 --- a/apps/widtemp/metadata.json +++ b/apps/widtemp/metadata.json @@ -1,7 +1,7 @@ { "id": "widtemp", "name": "Temperature widget", - "version": "0.01", + "version": "0.02", "description": "A temperature widget, showing the current internal temperature", "readme": "README.md", "icon": "widtemp.png", @@ -10,7 +10,7 @@ "tags": "widget,health", "supports": ["BANGLEJS","BANGLEJS2"], "dependencies" : {}, - "allow_emulator":true, + "allow_emulator":false, "storage": [ {"name":"widtemp.wid.js","url":"widtemp.wid.js"} ] diff --git a/apps/wohrm/ChangeLog b/apps/wohrm/ChangeLog index 2ca405365..8cdb24153 100644 --- a/apps/wohrm/ChangeLog +++ b/apps/wohrm/ChangeLog @@ -9,3 +9,4 @@ 0.09: Ported to Bangle.js2 Home returns to clock, instead of menu Add settings +0.10: Increased(font sizes and bg height for current HR), BJS2 (added support for bottom widgets) diff --git a/apps/wohrm/README.md b/apps/wohrm/README.md index 87b1a65da..d35feb253 100644 --- a/apps/wohrm/README.md +++ b/apps/wohrm/README.md @@ -30,3 +30,9 @@ Pressing middle button will switch off the HRM of the watch and return you to th # HRM usage The HRM is switched on when the app is started. It stays switch on while the app is running, even when the watch screen goes to stand-by. + + +Screenshot BJS2 emul + +![](bangle2-workout-HRM-screenshot.png) + diff --git a/apps/wohrm/app.js b/apps/wohrm/app.js index ab579463c..e32ab36ba 100644 --- a/apps/wohrm/app.js +++ b/apps/wohrm/app.js @@ -18,7 +18,7 @@ let lowerLimitChanged = true; let limitSetter = Setter.NONE; -let currentHeartRate = 0; +let currentHeartRate = 0; //use for emuls let hrConfidence = -1; let hrChanged = true; let confidenceChanged = true; @@ -31,7 +31,7 @@ const upperLshape = isB1 ? { left: 210, bottom: 40, top: 210, - rectWidth: 30, + rectWidth: 30, cornerRoundness: 5, orientation: -1, color: '#f00' @@ -39,7 +39,7 @@ const upperLshape = isB1 ? { right: Bangle.appRect.x2-100, left: Bangle.appRect.x2, bottom: 24, - top: Bangle.appRect.y2, + top: Bangle.appRect.y2-24, //for bottom widget rectWidth: 26, cornerRoundness: 4, orientation: -1, // rotated 180° @@ -58,19 +58,20 @@ const lowerLshape = { }; const centerBar = { - minY: (upperLshape.bottom + upperLshape.top - upperLshape.rectWidth)/2, - maxY: (upperLshape.bottom + upperLshape.top + upperLshape.rectWidth)/2, + //1.5 =height*2 + minY: (upperLshape.bottom + upperLshape.top - (upperLshape.rectWidth*1.5))/2, + maxY: (upperLshape.bottom + upperLshape.top + (upperLshape.rectWidth*1.5))/2, confidenceWidth: isB1 ? 10 : 8, - minX: isB1 ? 55 : upperLshape.rectWidth + 14, + minX: isB1 ? 55 : upperLshape.rectWidth + 14, maxX: isB1 ? 165 : Bangle.appRect.x2 - upperLshape.rectWidth - 14 }; const fontSizes = isB1 ? { - limits: 13, - heartRate: 24 + limits: 14, + heartRate: 44 } : { - limits: 12, - heartRate: 20 + limits: 16, + heartRate: 34 }; function fillEllipse(x, y, x2, y2) { @@ -178,10 +179,11 @@ function renderCurrentHeartRate() { g.setColor(g.theme.bg); g.setFontVector(fontSizes.heartRate); - g.setFontAlign(1, 0, 0); + g.setFontAlign(0, 0, 0);//center g.drawString(currentHeartRate, - Math.max(upperLshape.right+upperLshape.cornerRoundness, - lowerLshape.right-lowerLshape.cornerRoundness), + // Math.max(upperLshape.right+upperLshape.cornerRoundness, lowerLshape.right-lowerLshape.cornerRoundness), + // Math.max(upperLshape.right+upperLshape.cornerRoundness, lowerLshape.right-lowerLshape.cornerRoundness)-(g.stringWidth(currentHeartRate)/2), + centerBar.minX+((centerBar.maxX-centerBar.minX)/2), (centerBar.minY+centerBar.maxY)/2); //Reset alignment to defaults diff --git a/apps/wohrm/bangle2-workout-HRM-screenshot.png b/apps/wohrm/bangle2-workout-HRM-screenshot.png new file mode 100644 index 000000000..b75c1b12c Binary files /dev/null and b/apps/wohrm/bangle2-workout-HRM-screenshot.png differ diff --git a/apps/wohrm/metadata.json b/apps/wohrm/metadata.json index e41c65023..1e2ac2cb0 100644 --- a/apps/wohrm/metadata.json +++ b/apps/wohrm/metadata.json @@ -1,7 +1,7 @@ { "id": "wohrm", "name": "Workout HRM", - "version": "0.09", + "version": "0.10", "description": "Workout heart rate monitor notifies you with a buzz if your heart rate goes above or below the set limits.", "icon": "app.png", "type": "app", diff --git a/modules/widget_utils.js b/modules/widget_utils.js index 2a6a407be..e83555729 100644 --- a/modules/widget_utils.js +++ b/modules/widget_utils.js @@ -88,7 +88,7 @@ exports.swipeOn = function(autohide) { } for (var w of global.WIDGETS) { - if (w._draw) return; // already hidden + if (w._draw) continue; // already hidden w._draw = w.draw; w.draw = function() { g=og; diff --git a/typescript/types/clock_info.d.ts b/typescript/types/clock_info.d.ts index 06a2d400b..b12732683 100644 --- a/typescript/types/clock_info.d.ts +++ b/typescript/types/clock_info.d.ts @@ -57,3 +57,7 @@ declare module ClockInfo { focus: boolean, }; } + +interface BangleExt { + CLKINFO_FOCUS?: true; +} diff --git a/typescript/types/main.d.ts b/typescript/types/main.d.ts index 29118b7c6..4e4dd224e 100644 --- a/typescript/types/main.d.ts +++ b/typescript/types/main.d.ts @@ -1584,7 +1584,7 @@ declare class NRF { * @param {any} callback - A callback function to be called when the data is sent * @url http://www.espruino.com/Reference#l_NRF_sendHIDReport */ - static sendHIDReport(data: any, callback: any): void; + static sendHIDReport(data: number[], callback?: () => void): void /** * Check if Apple Notification Center Service (ANCS) is currently active on the BLE @@ -3188,8 +3188,10 @@ declare class Puck { * Check out [the Puck.js page on the * accelerometer](http://www.espruino.com/Puck.js#on-board-peripherals) for more * information. + * **Note:** Puck.js cannot currently read every sample from the + * accelerometer at sample rates above 208Hz. * - * @param {number} samplerate - The sample rate in Hz, or undefined + * @param {number} samplerate - The sample rate in Hz, or `undefined` (default is 12.5 Hz) * @url http://www.espruino.com/Reference#l_Puck_accelOn */ static accelOn(samplerate: number): void; @@ -3819,6 +3821,9 @@ declare class Bangle { * 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. + * * `hrmSportMode` - on the newest Bangle.js 2 builds with with the proprietary + * heart rate algorithm, this is the sport mode passed to the algorithm. See `libs/misc/vc31_binary/algo.h` + * for more info. 0 = normal (default), 1 = running, 2 = ... * * `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` @@ -13222,7 +13227,7 @@ declare module "Storage" { * @returns {any} An object containing parsed JSON from the file, or undefined * @url http://www.espruino.com/Reference#l_Storage_readJSON */ - function readJSON(name: string, noExceptions: boolean): any; + function readJSON(name: string, noExceptions: ShortBoolean): any; /** * Read a file from the flash storage area that has been written with diff --git a/typescript/types/settings.d.ts b/typescript/types/settings.d.ts new file mode 100644 index 000000000..a46b6ace7 --- /dev/null +++ b/typescript/types/settings.d.ts @@ -0,0 +1,51 @@ +type Settings = { + beep: boolean, + vibrate: boolean, + quiet: number, + + ble: boolean, + blerepl: boolean, + HID?: false | "kbmedia" | "kb" | "com" | "joy", + + passkey?: string, + whitelist_disabled?: boolean, + whitelist: string[], + + theme: Theme, + + brightness: number, + timeout: number, + rotate: number, + + options: SettingsOptions, + + timezone: number, + log: number, + + clock: string, + clockHasWidgets: boolean, + launcher: string, +}; + +type SettingsTheme = { + fg: string, + bg: string, + fg2: string, + bg2: string, + fgH: string, + bgH: string, + dark: boolean, +}; + +type SettingsOptions = { + wakeOnBTN1: boolean, + wakeOnBTN2: boolean, + wakeOnBTN3: boolean, + wakeOnFaceUp: boolean, + wakeOnTouch: boolean, + + wakeOnTwist: boolean, + twistThreshold: number, + twistMaxY: number, + twistTimeout: number, +};