diff --git a/apps.json b/apps.json index 0a4be55e2..f7bec7695 100644 --- a/apps.json +++ b/apps.json @@ -13,6 +13,21 @@ ], "sortorder" : -10 }, + { "id": "health", + "name": "Health Tracking", + "tags": "tool,system,b2", + "icon": "app.png", + "version":"0.01", + "description": "Logs health data and provides an app to view it (BETA - requires firmware 2v11)", + "readme": "README.md", + "storage": [ + {"name":"health.app.js","url":"app.js"}, + {"name":"health.img","url":"app-icon.js","evaluate":true}, + {"name":"health.boot.js","url":"boot.js"}, + {"name":"health","url":"lib.js"} + ], + "sortorder" : -10 + }, { "id": "moonphase", "name": "Moonphase", "icon": "app.png", @@ -55,7 +70,7 @@ "name": "Launcher (Bangle.js 2)", "shortName":"Launcher", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications. It will not work on Bangle.js 1.0.", "tags": "tool,system,launcher,b2,bno1", "type":"launch", @@ -67,7 +82,7 @@ { "id": "about", "name": "About", "icon": "app.png", - "version":"0.08", + "version":"0.09", "description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers", "tags": "tool,system,b2", "allow_emulator":true, @@ -500,9 +515,9 @@ { "id": "speedo", "name": "Speedo", "icon": "speedo.png", - "version":"0.04", + "version":"0.05", "description": "Show the current speed according to the GPS", - "tags": "tool,outdoors,gps", + "tags": "tool,outdoors,gps,b2", "storage": [ {"name":"speedo.app.js","url":"speedo.js"}, {"name":"speedo.img","url":"speedo-icon.js","evaluate":true} @@ -653,7 +668,7 @@ "name": "Battery Level Widget (with percentage)", "shortName": "Battery Widget", "icon": "widget.png", - "version":"0.12", + "version":"0.13", "description": "Show the current battery level and charging status in the top right of the clock, with charge percentage", "tags": "widget,battery,b2", "type":"widget", @@ -1421,7 +1436,7 @@ "icon": "chrono.png", "version":"0.01", "description": "Single click BTN1 to add 5 minutes. Single click BTN2 to add 30 seconds. Single click BTN3 to add 5 seconds. Tap to pause or play to timer. Double click BTN1 to reset. When timer finishes the watch vibrates.", - "tags": "Tools", + "tags": "tool", "storage": [ {"name":"chrono.app.js","url":"chrono.js"}, {"name":"chrono.img","url":"chrono-icon.js","evaluate":true} @@ -1562,7 +1577,7 @@ "icon": "app.png", "version":"0.03", "description": "Chronometer (timer) which runs as widget.", - "tags": "tools,widget", + "tags": "tool,widget,b2", "readme": "README.md", "storage": [ {"name":"chronowid.wid.js","url":"widget.js"}, @@ -3502,7 +3517,7 @@ "name": "Pastel Clock", "shortName": "Pastel", "icon": "pastel.png", - "version":"0.03", + "version":"0.04", "description": "A Configurable clock with custom fonts and background", "tags": "clock,b2", "type":"clock", @@ -3573,6 +3588,17 @@ {"name":"score.json"} ] }, +{ "id": "menusmall", + "name": "Small Menus", + "icon": "app.png", + "version":"0.01", + "description": "Replace Bangle.js 2's menus with a version that contains smaller text", + "tags": "b2,bno1,system", + "type": "boot", + "storage": [ + {"name":"menusmall.boot.js","url":"boot.js"} + ] +}, { "id": "ffcniftya", "name": "Nifty-A Clock", "icon": "app.png", diff --git a/apps/about/ChangeLog b/apps/about/ChangeLog index 62e8d0126..ccc80148c 100644 --- a/apps/about/ChangeLog +++ b/apps/about/ChangeLog @@ -6,3 +6,4 @@ 0.06: Actual pixels as of 12 Jun 2020 0.07: Pressing a button now exits immediately (fix #618) 0.08: Make about (mostly) work on non-240px screens +0.09: Actual Bangle.js 1 pixels as of 13 Oct 2021 diff --git a/apps/about/app.js b/apps/about/app.js index 43b45010b..28a292376 100644 --- a/apps/about/app.js +++ b/apps/about/app.js @@ -32,7 +32,7 @@ g.setFontAlign(0,-1); g.flip(); // Pixel chooser image -g.drawImage(require("heatshrink").decompress(atob("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")),0,y+8); +g.drawImage(require("heatshrink").decompress(atob("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")),0,y+8); g.drawString(NRF.getAddress(),g.getWidth()/2,g.getHeight()-8,true); g.flip(); diff --git a/apps/chronowid/README.md b/apps/chronowid/README.md index f422dd956..ec1d5dd46 100644 --- a/apps/chronowid/README.md +++ b/apps/chronowid/README.md @@ -5,11 +5,15 @@ The advantage is, that you can still see your normal watchface and other widgets The widget is always active, but only shown when the timer is on. Hours, minutes, seconds and timer status can be set with an app. -Depending on when you start the timer, it may alert up to 0,999 seconds early. This is because it checks only for full seconds. When there is less than one seconds left, it buzzes. This cannot be avoided without checking more than every second, which I would like to avoid. +When there is less than one seconds left on the timer it buzzes. + +The widget has been tested on Bangle 1 and Bangle 2 ## Screenshots -TBD +![](chrono_with_wave.jpg) +![](chrono_with_pastel.jpg) + ## Features diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/health/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/health/README.md b/apps/health/README.md new file mode 100644 index 000000000..0ba0d8228 --- /dev/null +++ b/apps/health/README.md @@ -0,0 +1,38 @@ +# Health Tracking + +Logs health data to a file every 10 minutes, and provides an app to view it + +**BETA - requires firmware 2v11** + +## Usage + +Once installed, health data is logged automatically. + +To view data, run the `Health` app from your watch. + +## Features + +Stores: + +* Heart rate (TODO) +* Step count +* Movement + +## Technical Info + +Once installed, the `health.boot.js` hooks onto the `Bangle.health` event and +writes data to a binary file (one per month). + +A library (that can be used with `require("health").readXYZ` can then be used +to grab historical health info. + +## TODO + +* **Extend file format to include combined data for each day (to make graphs faster)** +* `interface` page for desktop to allow data to be viewed and exported in common formats +* More features in app: + * Step counting goal (ensure pedometers use this) + * Calendar view showing steps per day + * Yearly view + * Heart rate 'zone' graph + * .. other diff --git a/apps/health/app-icon.js b/apps/health/app-icon.js new file mode 100644 index 000000000..d522d9a9a --- /dev/null +++ b/apps/health/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA///8H5AYM7/5L/ACsBqtQAgMFqtABYcVqtVAgIDBqgLDAwITBDYNVrQiEAANQEQNVtWAFIYfCE4Xq0AuEAAdX1W0BZFe1XUHQgADvWrJogAE9WtBYl66ouD2oLEtQGBFwQQBBYgeBFwYjFA4QuCBYgfCFwYLCL4IICFwacCPwetEwYLCR4QJBFwbFCU4QhBFwbMDNAYuCHQQwFFwowFFwowFFwwwEFwzNGFwjxFFwowEFw7aFBQwwDFwwwEFwwwEFw4wDBRAwBFxAwCFxAwCFxIA/AB4A=")) diff --git a/apps/health/app.js b/apps/health/app.js new file mode 100644 index 000000000..cb8651f4c --- /dev/null +++ b/apps/health/app.js @@ -0,0 +1,60 @@ +function menuMain() { + E.showMenu({ + "":{title:"Health Tracking"}, + "< Back":()=>load(), + "Step Counting":()=>menuStepCount(), + "Movement":()=>menuMovement() + }); +} + +function menuStepCount() { + E.showMenu({ + "":{title:"Step Counting"}, + "per hour":()=>stepsPerHour() + }); +} + +function menuMovement() { + E.showMenu({ + "":{title:"Movement"}, + "per hour":()=>movementPerHour() + }); +} + +function stepsPerHour() { + E.showMessage("Loading..."); + var data = new Uint16Array(24); + require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); + g.clear(1); + Bangle.drawWidgets(); + g.reset(); + require("graph").drawBar(g, data, { + y:24, + miny: 0, + axes : true, + gridx : 6, + gridy : 500 + }); + Bangle.setUI("updown", ()=>menuStepCount()); +} + +function movementPerHour() { + E.showMessage("Loading..."); + var data = new Uint16Array(24); + require("health").readDay(new Date(), h=>data[h.hr]+=h.movement); + g.clear(1); + Bangle.drawWidgets(); + g.reset(); + require("graph").drawLine(g, data, { + y:24, + miny: 0, + axes : true, + gridx : 6, + ylabel : null + }); + Bangle.setUI("updown", ()=>menuStepCount()); +} + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +menuMain(); diff --git a/apps/health/app.png b/apps/health/app.png new file mode 100644 index 000000000..04f1fee5e Binary files /dev/null and b/apps/health/app.png differ diff --git a/apps/health/boot.js b/apps/health/boot.js new file mode 100644 index 000000000..d6b84ce98 --- /dev/null +++ b/apps/health/boot.js @@ -0,0 +1,38 @@ +Bangle.on("health", health => { + // ensure we write health info for *last* block + var d = new Date(Date.now() - 590000); + + const DB_RECORD_LEN = 4; + const DB_RECORDS_PER_HR = 6; + const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24; + const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31; + const DB_HEADER_LEN = 8; + const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; + + function getRecordFN(d) { + return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw"; + } + function getRecordIdx(d) { + return (DB_RECORDS_PER_DAY*(d.getDate()-1)) + + (DB_RECORDS_PER_HR*d.getHours()) + + (0|(d.getMinutes()*DB_RECORDS_PER_HR/60)); + } + + var rec = getRecordIdx(d); + var fn = getRecordFN(d); + var f = require("Storage").read(fn); + if (f) { + var dt = f.substr(DB_HEADER_LEN+(rec*DB_RECORD_LEN), DB_RECORD_LEN); + if (dt!="\xFF\xFF\xFF\xFF") { + print("HEALTH ERR: Already written!"); + return; + } + } else { + require("Storage").write(fn, "HEALTH1\0", 0, DB_FILE_LEN); // header + } + var recordData = String.fromCharCode( + health.steps>>8,health.steps&255, // 16 bit steps + health.bpm, // 8 bit bpm + Math.min(health.movement / 8, 255)); // movement + require("Storage").write(fn, recordData, DB_HEADER_LEN+(rec*DB_RECORD_LEN), DB_FILE_LEN); +}); diff --git a/apps/health/lib.js b/apps/health/lib.js new file mode 100644 index 000000000..791c4ce22 --- /dev/null +++ b/apps/health/lib.js @@ -0,0 +1,61 @@ +const DB_RECORD_LEN = 4; +const DB_RECORDS_PER_HR = 6; +const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24; +const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31; +const DB_HEADER_LEN = 8; +const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; + +function getRecordFN(d) { + return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw"; +} +function getRecordIdx(d) { + return (DB_RECORDS_PER_DAY*(d.getDate()-1)) + + (DB_RECORDS_PER_HR*d.getHours()) + + (0|(d.getMinutes()*DB_RECORDS_PER_HR/60)); +} + +// Read all records from the given month +exports.readAllRecords = function(d, cb) { + var rec = getRecordIdx(d); + var fn = getRecordFN(d); + var f = require("Storage").read(fn); + var idx = DB_HEADER_LEN; + for (var day=0;day<31;day++) { + for (var hr=0;hr<24;hr++) { + for (var m=0;m{ if (app.icon) app.icon = s.read(app.icon); // should just be a link to a memory area }); +if (g.wrapString) { // FIXME: check not needed after 2v11 + g.setFont(font); + apps.forEach(app=>app.name = g.wrapString(app.name, g.getWidth()-64).join("\n")); +} function drawApp(i) { var y = 24+i*APPH-menuScroll; var app = apps[i]; if (!app || y<-APPH || y>=g.getHeight()) return; - g.setFont("6x8",2).setFontAlign(-1,0).drawString(app.name,64,y+32); + g.setFont(font).setFontAlign(-1,0).drawString(app.name,64,y+32); if (app.icon) try {g.drawImage(app.icon,8,y+8);} catch(e){} } diff --git a/apps/menusmall/ChangeLog b/apps/menusmall/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/menusmall/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/menusmall/app.png b/apps/menusmall/app.png new file mode 100644 index 000000000..094ee447c Binary files /dev/null and b/apps/menusmall/app.png differ diff --git a/apps/menusmall/boot.js b/apps/menusmall/boot.js new file mode 100644 index 000000000..59e47b178 --- /dev/null +++ b/apps/menusmall/boot.js @@ -0,0 +1,121 @@ +"";//not entirely sure why we need this - related to how bootupdate adds these to .boot0 +E.showMenu = function(items) { + g.clear(1).flip(); // clear screen if no menu supplied + Bangle.drawWidgets(); + if (!items) { + Bangle.setUI(); + return; + } + var w = g.getWidth(); + var h = g.getHeight(); + var menuItems = Object.keys(items); + var options = items[""]; + if (options) menuItems.splice(menuItems.indexOf(""),1); + if (!(options instanceof Object)) options = {}; + options.fontHeight=14; + options.x=0; + options.x2=w-1; + options.y=24; + options.y2=h-12; + if (options.selected === undefined) + options.selected = 0; + var x = 0|options.x; + var x2 = options.x2||(g.getWidth()-1); + var y = 0|options.y; + var y2 = options.y2||(g.getHeight()-1); + if (options.title) + y += 15; + var loc = require("locale"); + var l = { + lastIdx : 0, + draw : function(rowmin,rowmax) { + var rows = 0|Math.min((y2-y) / options.fontHeight,menuItems.length); + var idx = E.clip(options.selected-(rows>>1),0,menuItems.length-rows); + if (idx!=l.lastIdx) rowmin=undefined; // redraw all if we scrolled + l.lastIdx = idx; + var iy = y; + g.reset().setFontAlign(0,-1,0); + g.setFontCustom(atob("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"), 32, atob("AwIGCAgICAMFBQYIAwYDBwcFBgYHBgYGBgYDAwYHBgcHBgYGBgYGBgYEBgYGBgYGBgYGBgYGBggGBgYEBwQGBwQGBgYGBgYHBgYGBgYGBgYGBgYGBgYGBgYGBgQCBAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAHCAYGBgAGBgYGAAYGBQYABgMGBgQABgYHBgAGBgYGBgYGBgYGBgYGBgYEBAYGBgYGBgYGAAYGBgYGBgYHBgYGBgYGBgYGBgYGBgYGBwcGBgYGBgYABgYGBgYGBg=="), 15); + + if (rowmin===undefined && options.title) + g.drawString(options.title,(x+x2)/2,y-14).drawLine(x,y-2,x2,y-2). + setColor(g.theme.fg).setBgColor(g.theme.bg); + iy += 12; + g.setColor((idx>0)?g.theme.fg:g.theme.bg).fillPoly([72,iy,104,iy,88,iy-12]); + if (rowmin!==undefined) { + if (idxrowmax) { + rows = 1+rowmax-rowmin; + } + } + var less = idx>0; + while (rows--) { + var name = menuItems[idx]; + var item = items[name]; + var hl = (idx==options.selected && !l.selectEdit); + g.setColor(hl ? g.theme.bgH : g.theme.bg); + g.fillRect(x,iy,x2,iy+options.fontHeight-1); + g.setColor(hl ? g.theme.fgH : g.theme.fg); + g.setFontAlign(-1,-1); + g.drawString(loc.translate(name),x+1,iy+1); + if ("object" == typeof item) { + var xo = x2; + var v = item.value; + if (item.format) v=item.format(v); + v = loc.translate(""+v); + if (l.selectEdit && idx==options.selected) { + xo -= 24 + 1; + g.setColor(g.theme.bgH).fillRect(xo-(g.stringWidth(v)+4),iy,x2,iy+options.fontHeight-1); + g.setColor(g.theme.fgH).drawImage("\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",xo,iy+(options.fontHeight-10)/2,{scale:2}); + } + g.setFontAlign(1,-1); + g.drawString(v,xo-2,iy+1); + } + g.setColor(g.theme.fg); + iy += options.fontHeight; + idx++; + } + g.setFontAlign(-1,-1); + g.setColor((idxitem.max) item.value = item.max; + if (item.onchange) item.onchange(item.value); + l.draw(options.selected,options.selected); + } else { + var a=options.selected; + options.selected = (dir+options.selected)%menuItems.length; + if (options.selected<0) options.selected += menuItems.length; + l.draw(Math.min(a,options.selected), Math.max(a,options.selected)); + } + } + }; + l.draw(); + Bangle.setUI("updown",dir => { + if (dir) l.move(dir); + else l.select(); + }); + return l; +}; diff --git a/apps/openstmap/app.js b/apps/openstmap/app.js index 99f6d0c73..c33acd8ad 100644 --- a/apps/openstmap/app.js +++ b/apps/openstmap/app.js @@ -8,6 +8,7 @@ function redraw() { m.draw(); drawMarker(); if (WIDGETS["gpsrec"] && WIDGETS["gpsrec"].plotTrack) { + g.flip(); // force immediate draw on double-buffered screens - track will update later g.setColor(0.75,0.2,0); WIDGETS["gpsrec"].plotTrack(m); } diff --git a/apps/pastel/ChangeLog b/apps/pastel/ChangeLog index a0f660237..e0e967166 100644 --- a/apps/pastel/ChangeLog +++ b/apps/pastel/ChangeLog @@ -1,3 +1,4 @@ 0.01: First release 0.02: Display 12 hour clock as 12:xx not 00:xx when just into PM 0.03: Make it work with Gadgetbridge, Notifications fullscreen on a Bangle 2 +0.04: Leave space at the bottom for Chrono widget, set back option at first option diff --git a/apps/pastel/pastel.app.js b/apps/pastel/pastel.app.js index b97c02fc7..98f8af7f9 100644 --- a/apps/pastel/pastel.app.js +++ b/apps/pastel/pastel.app.js @@ -87,19 +87,19 @@ function draw() { // avoid flicker on a bangle 1 by comparing with previous minute if (mm_prev != mm) { mm_prev = mm; - g.clearRect(0, 30, w, h); + g.clearRect(0, 30, w, h - 24); } } else { // on a b2 safe to just clear anyway as there is no flicker - g.clearRect(0, 30, w, h); + g.clearRect(0, 30, w, h - 24); } // draw a grid like graph paper if (settings.grid && process.env.HWVERSION !=1) { g.setColor("#0f0"); for (var gx=20; gx <= w; gx += 20) - g.drawLine(gx, 30, gx, h); - for (var gy=30; gy <= h; gy += 20) + g.drawLine(gx, 30, gx, h - 24); + for (var gy=30; gy <= h - 24; gy += 20) g.drawLine(0, gy, w, gy); } diff --git a/apps/pastel/pastel.settings.js b/apps/pastel/pastel.settings.js index db7206dbb..2e4afadc8 100644 --- a/apps/pastel/pastel.settings.js +++ b/apps/pastel/pastel.settings.js @@ -26,6 +26,7 @@ E.showMenu({ '': { 'title': 'Pastel Clock' }, + '< Back': back, 'Font': { value: 0 | font_options.indexOf(s.font), min: 0, max: 4, @@ -50,7 +51,6 @@ s.date = !s.date save() }, - }, - '< Back': back, + } }) }) diff --git a/apps/speedo/ChangeLog b/apps/speedo/ChangeLog index 35cef4520..91df52211 100644 --- a/apps/speedo/ChangeLog +++ b/apps/speedo/ChangeLog @@ -3,3 +3,4 @@ 0.03: Use offscreen buffer (not doublebuffer) Use 'locale' to get internationalised speed 0.04: Start GPS after loading app, just in case widgets affect it (#449) +0.05: Use Layout lib for Bangle.js 2 compatibility diff --git a/apps/speedo/speedo.js b/apps/speedo/speedo.js index 174702d71..2e729c114 100644 --- a/apps/speedo/speedo.js +++ b/apps/speedo/speedo.js @@ -1,33 +1,61 @@ -var buf = Graphics.createArrayBuffer(240,120,1,{msb:true}); -var lastFix = {fix:0,satellites:0}; +var Layout = require("Layout"); +var layout; + +var lastFix = {fix:-1,satellites:0}; + +function speedoImage() { + return require("heatshrink").decompress(atob("kkdxH+ABteAAwWOECImZDQ2CAQglUD4us2fX68ymQDB1omFESWtDgIACEYYACrolPBwddmWIEZWsmVWJYgiLwXX2YcB1gdDq+BAodWGIWsEhQiDRAWBmQdEAAhGBroFC1ojMC4etERIlDAggkHNIgAWSYYjFVwNWGwgAP5KkBEYoFC1ihBagwAL5W72vKJAxpExCiDABnQ4W12vD6AHBEYxnT4YhB3ghCSIhqDe4SIP3giBM4LfFEYpiMDoQhC3fDCA7+DfBwiCAARmFAAmtEYlYagMywISHEQhEId4UyEYleqwABEZBHERQwABroZBq5rR6BGLNZKzMAAPKRZKzJr2tfaAAKxD7CfgRsD1g1GAAwME2YGDwQjFNgOzwMyCwuCwIAEBg0yHoKODEYmCcYNWCwutAAuzBgg4BCwJGEEgj7JV5r7BIwgjEWrDVCEQYkCWgYAWNYIjF/z8awQfD")); +} + function onGPS(fix) { + if (lastFix.fix != fix.fix) { + // if fix is different, change the layout + if (fix.fix) { + layout = new Layout( { + type:"v", c: [ + {type:"txt", font:"6x8:2", label:"Speed" }, + {type:"h", c: [ + {type:"img", src:speedoImage, pad:4 }, + {type:"txt", font:"35%", label:"--", fillx:true, id:"speed" }, + ]}, + {type:"txt", font:"6x8", label:"--", id:"units" }, + {type:"h", c: [ + {type:"txt", font:"10%", label:fix.satellites, pad:2, id:"sat" }, + {type:"txt", font:"6x8", pad:3, label:"Satellites" } + ]}, + ]},[],{lazy:true}); + } else { + layout = new Layout( { + type:"v", c: [ + {type:"txt", font:"6x8:2", label:"Speed" }, + {type:"img", src:speedoImage, pad:4 }, + {type:"txt", font:"6x8", label:"Waiting for GPS" }, + {type:"h", c: [ + {type:"txt", font:"10%", label:fix.satellites, pad:2, id:"sat" }, + {type:"txt", font:"6x8", pad:3, label:"Satellites" } + ]}, + ]},[],{lazy:true}); + } + g.clearRect(0,24,g.getWidth(),g.getHeight()); + layout.render(); + } lastFix = fix; - buf.clear(); - buf.setFontAlign(0,0); - buf.setFont("6x8"); - buf.drawString(fix.satellites+" satellites",120,6); - if (fix.fix) { + + if (fix.fix && isFinite(fix.speed)) { var speed = require("locale").speed(fix.speed); var m = speed.match(/([0-9,\.]+)(.*)/); // regex splits numbers from units var txt = (fix.speed<20) ? fix.speed.toFixed(1) : Math.round(fix.speed); - var value = m[1], units = m[2]; - var s = 80; - buf.setFontVector(s); - buf.drawString(value,120,10+s/2); - buf.setFont("6x8",2); - buf.drawString(units,120,s+26); - } else { - buf.setFont("6x8",2); - buf.drawString("Waiting for GPS",120,56); + layout.speed.label = m[1]; + layout.units.label = m[2]; } - g.reset(); - g.drawImage({width:buf.getWidth(),height:buf.getHeight(),bpp:1,buffer:buf.buffer},0,70); - g.flip(); + layout.sat.label = fix.satellites; + layout.render(); } g.clear(); -onGPS(lastFix); +onGPS({fix:0,satellites:0}); +// onGPS({fix:1,satellites:3,speed:200}); // testing Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.on('GPS', onGPS); -Bangle.setGPSPower(1); +Bangle.setGPSPower(1, "app"); diff --git a/apps/widbatpc/ChangeLog b/apps/widbatpc/ChangeLog index 64482db71..09e4fabf4 100644 --- a/apps/widbatpc/ChangeLog +++ b/apps/widbatpc/ChangeLog @@ -9,3 +9,4 @@ 0.10: Add 'hide if charge greater than' 0.11: Don't overwrite existing settings on app update 0.12: Fixed for Bangle 2 +0.13: Fillbar setting added, see README diff --git a/apps/widbatpc/README.md b/apps/widbatpc/README.md index 6e8fd10cc..c75154f72 100644 --- a/apps/widbatpc/README.md +++ b/apps/widbatpc/README.md @@ -4,5 +4,13 @@ Show the current battery level and charging status in the top right of the clock Works with Bangle 1 and Bangle 2 -![](screenshot.jpg) +When the fillbar setting is on the level colour will fill the entire +bar. This makes for an easier to read dsiplay when the charge is +below 50%. + +![](widbatpc.full.jpg) + +When the fillbar setting is off the level colour will follow the battry percentage + +![](widbatpc.part.jpg) diff --git a/apps/widbatpc/screenshot.jpg b/apps/widbatpc/screenshot.jpg deleted file mode 100644 index 48f9893ec..000000000 Binary files a/apps/widbatpc/screenshot.jpg and /dev/null differ diff --git a/apps/widbatpc/settings.js b/apps/widbatpc/settings.js index 009fa4994..b7a5db9e6 100644 --- a/apps/widbatpc/settings.js +++ b/apps/widbatpc/settings.js @@ -10,6 +10,7 @@ let s = { 'color': COLORS[0], 'percentage': true, + 'fillbar': false, 'charger': true, 'hideifmorethan': 100, } @@ -54,6 +55,11 @@ save('color')(s.color) } }, + 'Fill Bar': { + value: s.fillbar, + format: onOffFormat, + onchange: save('fillbar'), + }, 'Hide if >': { value: s.hideifmorethan||100, min: 10, diff --git a/apps/widbatpc/widbatpc.full.jpg b/apps/widbatpc/widbatpc.full.jpg new file mode 100644 index 000000000..3df2184fe Binary files /dev/null and b/apps/widbatpc/widbatpc.full.jpg differ diff --git a/apps/widbatpc/widbatpc.part.jpg b/apps/widbatpc/widbatpc.part.jpg new file mode 100644 index 000000000..d59276e22 Binary files /dev/null and b/apps/widbatpc/widbatpc.part.jpg differ diff --git a/apps/widbatpc/widget.js b/apps/widbatpc/widget.js index 223db5f70..caecf8ae4 100644 --- a/apps/widbatpc/widget.js +++ b/apps/widbatpc/widget.js @@ -80,7 +80,12 @@ var s = 39; var x = this.x, y = this.y; const l = E.getBattery(); - const xl = x+4+l*(s-12)/100 + let xl = x+4+l*(s-12)/100; + + // show bar full in the level color, as you cant see the color if the bar is too small + if (setting('fillbar')) + xl = x+4+100*(s-12)/100; + c = levelColor(l); if (Bangle.isCharging() && setting('charger')) { diff --git a/index.html b/index.html index e1c195f7d..a5ae7bff0 100644 --- a/index.html +++ b/index.html @@ -40,10 +40,6 @@ -
- -  Bangle.js 2 is now on KickStarter!  Check it out here -