diff --git a/apps.json b/apps.json index 9db1ba425..940176014 100644 --- a/apps.json +++ b/apps.json @@ -1351,6 +1351,22 @@ {"name":"pparrot.img","url":"party-parrot-icon.js","evaluate":true} ] }, + { + "id": "hralarm", + "name": "Heart rate alarm", + "shortName":"HR Alarm", + "version":"0.01", + "description": "This invisible widget vibrates whenever the heart rate gets close to the upper limit or goes over or under the configured limits", + "icon": "widget.png", + "type": "widget", + "tags": "widget", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"hralarm.wid.js","url":"widget.js"}, + {"name":"hralarm.settings.js","url":"settings.js"} + ] + }, { "id": "hrings", "name": "Hypno Rings", @@ -1504,7 +1520,7 @@ { "id": "gpsinfo", "name": "GPS Info", - "version": "0.08", + "version": "0.09", "description": "An application that displays information about altitude, lat/lon, satellites and time", "icon": "gps-info.png", "type": "app", @@ -4500,7 +4516,7 @@ "name": "LCARS Clock", "shortName":"LCARS", "icon": "lcars.png", - "version":"0.09", + "version":"0.11", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "Library Computer Access Retrieval System (LCARS) clock.", @@ -5007,7 +5023,7 @@ { "id": "lapcounter", "name": "Lap Counter", - "version": "0.01", + "version": "0.02", "description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], @@ -5042,7 +5058,7 @@ { "id": "circlesclock", "name": "Circles clock", "shortName":"Circles clock", - "version":"0.04", + "version":"0.05", "description": "A clock with circles for different data at the bottom in a probably familiar style", "icon": "app.png", "screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}], @@ -5093,6 +5109,25 @@ {"name":"ltherm.img","url":"icon.js","evaluate":true} ] }, + { + "id": "ftclock", + "name": "Four Twenty Clock", + "version": "0.01", + "description": "A clock that tells when and where it's going to be 4:20 next", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}, {"url":"screenshot1.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS","BANGLEJS2"], + "allow_emulator": true, + "readme": "README.md", + "storage": [ + {"name":"ftclock.app.js","url":"app.js"}, + {"name":"fourTwenty","url":"fourTwenty.js"}, + {"name":"fourTwentyTz","url":"fourTwentyTz.js"}, + {"name":"ftclock.img","url":"app-icon.js","evaluate":true} + ] + }, { "id": "mmind", "name": "Classic Mind Game", diff --git a/apps/circlesclock/ChangeLog b/apps/circlesclock/ChangeLog index ca1da6e21..5464a8103 100644 --- a/apps/circlesclock/ChangeLog +++ b/apps/circlesclock/ChangeLog @@ -5,3 +5,5 @@ Add step distance and weather Allow switching visibility of widgets Make circles and text slightly bigger +0.05: Show correct percentage values in circles + Show humidity as weather circle data diff --git a/apps/circlesclock/README.md b/apps/circlesclock/README.md index 9aaa4bc8e..c3704e3d7 100644 --- a/apps/circlesclock/README.md +++ b/apps/circlesclock/README.md @@ -10,6 +10,9 @@ It can show the following information (this can be configured): * Heart rate (automatically updates when screen is on and unlocked) * Battery (including charging status and battery low warning) * Weather (requires [weather app](https://banglejs.com/apps/#weather)) + * Humidity as circle progress + * Temperature inside circle + * Condition as icon below circle ## Screenshots ![Screenshot dark theme](screenshot-dark.png) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 91d4937c4..822802afa 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -283,6 +283,7 @@ function drawWeather(w) { if (!w) w = getCirclePosition("weather"); const weather = getWeather(); const tempString = weather ? locale.temp(weather.temp - 273.15) : undefined; + const humidity = weather ? weather.hum : undefined; const code = weather ? weather.code : -1; // Draw rectangle background: @@ -292,6 +293,10 @@ function drawWeather(w) { g.setColor(colorGrey); g.fillCircle(w, h3, radiusOuter); + if (humidity >= 0) { + drawGauge(w, h3, humidity / 100, colorYellow); + } + g.setColor(colorBg); g.fillCircle(w, h3, radiusInner); @@ -363,22 +368,21 @@ function radians(a) { } function drawGauge(cx, cy, percent, color) { - let offset = 30; - let end = 300; - var i = 0; - var r = radiusInner + 3; + const offset = 15; + const end = 345; + const r = radiusInner + 3; if (percent <= 0) return; if (percent > 1) percent = 1; - var startrot = -offset; - var endrot = startrot - ((end - offset) * percent) - 35; + const startrot = -offset; + const endrot = startrot - ((end - offset) * percent); g.setColor(color); const size = radiusOuter - radiusInner - 2; // draw gauge - for (i = startrot; i > endrot - size; i -= size) { + for (let i = startrot; i > endrot - size; i -= size) { x = cx + r * Math.sin(radians(i)); y = cy + r * Math.cos(radians(i)); g.fillCircle(x, y, size); diff --git a/apps/ftclock/.gitignore b/apps/ftclock/.gitignore new file mode 100644 index 000000000..b384cf1f2 --- /dev/null +++ b/apps/ftclock/.gitignore @@ -0,0 +1,4 @@ +timezonedb.csv.zip +country.csv +zone.csv +timezone.csv diff --git a/apps/ftclock/ChangeLog b/apps/ftclock/ChangeLog new file mode 100644 index 000000000..9db0e26c5 --- /dev/null +++ b/apps/ftclock/ChangeLog @@ -0,0 +1 @@ +0.01: first release diff --git a/apps/ftclock/README.md b/apps/ftclock/README.md new file mode 100644 index 000000000..f30151552 --- /dev/null +++ b/apps/ftclock/README.md @@ -0,0 +1,24 @@ +# Four Twenty Clock + +A clock that tells when and where it's going to be [4:20](https://en.wikipedia.org/wiki/420_%28cannabis_culture%29) next + +![screensot](screenshot.png) ![screenshot at 4:20](screenshot1.png) + +## Generating `fourTwentyTz.js` + +Once in a while we need to regenerate it for 2 reasons: + +* One or more places got in or out of daylight saving time (DST) mode. +* The database saying _when_ places enter/exit DST mode got updated. + +I'll do my best to release a new version every time this happens, +but if you ever need to do this yourself, here's how: + +* `cd` to the `ftclock` folder +* If you haven't done so yet, run `npm install` there (this would create the `node_modules` folder). +* Get and unzip the latest `timezone.csv.zip` from https://timezonedb.com/download +* Run `npm run make` + +## Creator + +[Nimrod Kerrett](zzzen.com) diff --git a/apps/ftclock/app-icon.js b/apps/ftclock/app-icon.js new file mode 100644 index 000000000..297847e95 --- /dev/null +++ b/apps/ftclock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwghC/AH4A/AH4A/AAMHu4ACuwHBs4HDsEGBIQLCsADBgwPDCAQGEuwXFBwI0GEAMHuAGCCoMHC4pMHEAIXEAgIGEBwI9BC4wSCC8IVCMAwIBs4XKUQJfITQgXCDwp8EHAqaECoLFEu4cDBIggBs6uFZozuGBAVmC4g+FMgZQEZQ5vGC4iRIC5IrDN4h5EC5J3BCoIKGgyaEC44VBC46yEDgoeDgxqLC5SCMAgoTFY47GFC4xFBdwwPBD4oWFAH4A/AH4A/AH4AjA==")) diff --git a/apps/ftclock/app.js b/apps/ftclock/app.js new file mode 100644 index 000000000..1aed8da54 --- /dev/null +++ b/apps/ftclock/app.js @@ -0,0 +1,52 @@ +let getNextFourTwenty = require("fourTwenty").getNextFourTwenty; +require("FontTeletext10x18Ascii").add(Graphics); +let leaf_img = "\x17\x18\x81\x00\x00\x10\x00\x00 \x00\x00@\x00\x01\xc0\x00\x03\x80\x00\x0f\x80\x00\x1f\x00\x00>\x00\x00|\x00\xc0\xf8\x19\xe1\xf0\xf1\xe3\xe3\xc3\xf7\xdf\x83\xff\xfe\x03\xff\xf8\x03\xff\xe0\x03\xff\x80\x03\xfe\x00\x7f\xff\xc0\xff\xff\xc0\x06\xe0\x00\x18\xc0\x00 \x80\x00\x00\x00"; + +// timeout used to update every minute +let drawTimeout; + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + + +function draw() { + g.reset(); + g.setBgColor("#ffffff"); + let date = new Date(); + let timeStr = require("locale").time(date,1); + let next420 = getNextFourTwenty(); + g.clearRect(0,26,g.getWidth(),g.getHeight()); + g.setColor("#00ff00").setFontAlign(0,-1).setFont("Teletext10x18Ascii",2); + g.drawString(next420.minutes? timeStr: `\0${leaf_img}${timeStr}\0${leaf_img}`, g.getWidth()/2, 28); + g.setColor("#000000"); + g.setFontAlign(-1,-1).setFont("Teletext10x18Ascii"); + g.drawString(g.wrapString(next420.text, g.getWidth()-8).join("\n"),4,60); + + // queue draw in one minute + queueDraw(); +} + +// Clear the screen once, at startup +g.clear(); +// Load widgets +Bangle.loadWidgets(); +Bangle.drawWidgets(); +// draw immediately at first, queue update +draw(); +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); +// Show launcher when middle button pressed +Bangle.setUI("clock"); diff --git a/apps/ftclock/app.png b/apps/ftclock/app.png new file mode 100644 index 000000000..0553837ca Binary files /dev/null and b/apps/ftclock/app.png differ diff --git a/apps/ftclock/fourTwenty.js b/apps/ftclock/fourTwenty.js new file mode 100644 index 000000000..ac15f40e6 --- /dev/null +++ b/apps/ftclock/fourTwenty.js @@ -0,0 +1,45 @@ +let timezones = require("fourTwentyTz").timezones; + +function get420offset() { + let current_time = Math.floor((Date.now()%(24*3600*1000))/60000); + let current_min = current_time%60; + if (current_min>20 && current_min<25) { + current_time -= current_min-20; // 5 minutes grace period + } + let offset = 16*60+20-current_time; + if (offset<0) { + offset += 24*60; + } + return offset; +} + +function makeFourTwentyText(minutes, places) { + //let plural = minutes==1? "": "s"; + //let msgprefix = minutes? `${minutes} minute${plural} to`: "It is now"; + let msgprefix = minutes? `${minutes}m to`: "It is now"; + let msgsuffix = places.length>1? ", and other fine places": ""; + let msgplace = places[Math.floor(Math.random()*places.length)]; + return `${msgprefix} 4:20 at ${msgplace}${msgsuffix}.`; +} + +function getNextFourTwenty() { + let offs = get420offset(); + for (let i=0; i { + countries[r[0]] = r[1]; + }) + .on('end', () => { + fs.createReadStream(__dirname+'/zone.csv') + .pipe(csv.parse()) + .on('data', (r) => { + let parts = r[2].replace('_',' ').split('/'); + let city = parts[parts.length-1]; + let country =''; + if (parts.length>2) { // e.g. America/North_Dakota/New_Salem + country = parts[1]; // e.g. North Dakota + } else { + country = countries[r[1]]; // e.g. United States + } + zones[parseInt(r[0])] = {"name": `${city}, ${country}`}; + }) + .on('end', () => { + fs.createReadStream(__dirname+'/timezone.csv') + .pipe(csv.parse()) + .on('data', (r) => { + code = parseInt(r[0]); + if (!(code in zones)) return; + starttime = parseInt(r[2] || "0"); // Bugger. They're feeding us blanks for UTC now + offs = parseInt(r[3]); + if (offs<0) { + offs += 60*60*24; + } + zone = zones[code]; + if (starttime { + for (z in zones) { + zone = zones[z]; + if (zone.offs%60) continue; // One a dem funky timezones. Ignore. + zonelist = offsdict[zone.offs] || []; + zonelist.push(zone.name); + offsdict[zone.offs] = zonelist; + } + fs.open("fourTwentyTz.js","w", (err, fd) => { + if (err) { + console.log("Can't open output file"); + return; + } + fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite); + fs.write(fd, `// ${Date()}\n`, handleWrite); + fs.write(fd, "// Data source: https://timezonedb.com/files/timezonedb.csv.zip\n", handleWrite); + fs.write(fd, "exports.timezones = ", handleWrite); + fs.write(fd, JSON.stringify(offsdict, null, 4), handleWrite); + console.log('Done.'); + }); + }) + }) + }); diff --git a/apps/ftclock/package.json b/apps/ftclock/package.json new file mode 100644 index 000000000..b124b88f9 --- /dev/null +++ b/apps/ftclock/package.json @@ -0,0 +1,15 @@ +{ + "name": "mkfourtwentytz", + "version": "1.0.0", + "description": "Convert timezonedb.com CSV to fourTwentyTz.js for BangleJS ftclock app", + "main": "mkFourTwentyTz.js", + "scripts": { + "make": "node mkFourTwentyTz.js" + }, + "keywords": [], + "author": "", + "license": "GPL-3.0", + "dependencies": { + "csv": "^6.0.5" + } +} diff --git a/apps/ftclock/screenshot.png b/apps/ftclock/screenshot.png new file mode 100644 index 000000000..9247e0e04 Binary files /dev/null and b/apps/ftclock/screenshot.png differ diff --git a/apps/ftclock/screenshot1.png b/apps/ftclock/screenshot1.png new file mode 100644 index 000000000..dae98d315 Binary files /dev/null and b/apps/ftclock/screenshot1.png differ diff --git a/apps/gpsinfo/ChangeLog b/apps/gpsinfo/ChangeLog index 8d428ce85..414b9d9fb 100644 --- a/apps/gpsinfo/ChangeLog +++ b/apps/gpsinfo/ChangeLog @@ -5,3 +5,4 @@ 0.06: Add number of satellites in view and fix crash with GPS time 0.07: Resolve one FIFO_FULL case and exit App with button press 0.08: Leave GPS power switched on on exit (will switch off after 0.5 seconds anyway) +0.09: Fix FIFO_FULL error diff --git a/apps/gpsinfo/gps-info.js b/apps/gpsinfo/gps-info.js index fdac3d403..f4521f265 100644 --- a/apps/gpsinfo/gps-info.js +++ b/apps/gpsinfo/gps-info.js @@ -4,7 +4,7 @@ function satelliteImage() { var Layout = require("Layout"); var layout; -Bangle.setGPSPower(1, "app"); +//Bangle.setGPSPower(1, "app"); E.showMessage("Loading..."); // avoid showing rubbish on screen var lastFix = { @@ -19,7 +19,7 @@ var lastFix = { var SATinView = 0; var nofBD = 0; var nofGP = 0; -var listenerGPSraw = 1; +var listenerGPSraw = 0; function formatTime(now) { if (now == undefined) { @@ -87,12 +87,12 @@ function onGPS(fix) { {type:"txt", font:"6x8", pad:3, label:"Satellites used" } ]}, {type:"txt", font:"6x8", label:"", fillx:true, id:"progress" } - ]},{lazy:true}); + ]},{lazy:false}); } g.clearRect(0,24,g.getWidth(),g.getHeight()); layout.render(); } - lastFix = fix; + //lastFix = fix; if (fix.fix) { if (listenerGPSraw == 1) { Bangle.removeListener('GPS-raw', onGPSraw); @@ -108,15 +108,28 @@ function onGPS(fix) { layout.time.label = "Time: "+formatTime(fix.time); layout.sat.label = "Satellites: "+satellites; layout.maidenhead.label = "Maidenhead: "+maidenhead; + layout.render(); } else { - if (listenerGPSraw == 0) { - Bangle.on('GPS-raw', onGPSraw); - listenerGPSraw = 1; + if (fix.satelites != lastFix.satelites) { + layout.clear(layout.sat); + layout.sat.label = fix.satellites; + layout.render(layout.sat); + } + if (SATinView != lastFix.SATinView) { + layout.clear(layout.progress); + layout.progress.label = "in view: " + SATinView; + layout.render(layout.progress); } - layout.sat.label = fix.satellites; - layout.progress.label = "in view: " + SATinView; } - layout.render(); + //layout.render(); + + if (listenerGPSraw == 0 && !fix.fix) { + setTimeout(() => Bangle.on('GPS-raw', onGPSraw), 10); + listenerGPSraw = 1; + } + + lastFix = fix; + lastFix.SATinView = SATinView; } function onGPSraw(nmea) { @@ -129,7 +142,8 @@ function onGPSraw(nmea) { Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.on('GPS', onGPS); -Bangle.on('GPS-raw', onGPSraw); +//Bangle.on('GPS-raw', onGPSraw); +Bangle.setGPSPower(1, "app"); function exitApp() { load(); diff --git a/apps/hralarm/ChangeLog b/apps/hralarm/ChangeLog new file mode 100644 index 000000000..4c21f3ace --- /dev/null +++ b/apps/hralarm/ChangeLog @@ -0,0 +1 @@ +0.01: New Widget! diff --git a/apps/hralarm/README.md b/apps/hralarm/README.md new file mode 100644 index 000000000..37b14ad9d --- /dev/null +++ b/apps/hralarm/README.md @@ -0,0 +1,15 @@ +# Heart rate alarm + +This invisible widget vibrates whenever the heart rate gets close to the upper limit or goes over or under the configured limits. + +## Usage + +Configure the heart rate limits in the apps settings. This widget uses both 'HRM' and 'BTHRM' events. + +## Features + +Long vibration every 10 seconds on reaching upper limit, short vibrations between upper limit and warning threshold and an single vibration when reaching the lower limit again. + +## Requests/Creator + +https://github.com/halemmerich diff --git a/apps/hralarm/settings.js b/apps/hralarm/settings.js new file mode 100644 index 000000000..3158ab8b7 --- /dev/null +++ b/apps/hralarm/settings.js @@ -0,0 +1,57 @@ +(function(back) { + var FILE = "hralarm.json"; + + var settings = Object.assign({ + enabled: false, + upper: 180, + warning: 170, + lower: 150, + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + E.showMenu({ + '': { 'title': 'HR Alarm' }, + '< Back': back, + 'Enabled': { + value: !!settings.enabled, + format: v => settings.enabled ? "On" : "Off", + onchange: v => { + settings.enabled = v; + writeSettings(); + } + }, + 'Upper limit': { + value: settings.upper, + min: 0, + step:5, + max: 300, + onchange: v => { + settings.upper = v; + writeSettings(); + } + }, + 'Lower limit': { + value: settings.lower, + min: 0, + step:5, + max: 300, + onchange: v => { + settings.lower = v; + writeSettings(); + } + }, + 'Warning at': { + value: settings.warning, + min: 0, + step:5, + max: 300, + onchange: v => { + settings.warning = v; + writeSettings(); + } + } + }); +}) diff --git a/apps/hralarm/widget.js b/apps/hralarm/widget.js new file mode 100644 index 000000000..30a94fdf2 --- /dev/null +++ b/apps/hralarm/widget.js @@ -0,0 +1,27 @@ +(() => { + var settings = require('Storage').readJSON("hralarm.json", true) || {}; + if (!settings.enabled){ Bangle.setHRMPower(0, 'hralarm'); return; } + Bangle.setHRMPower(1, 'hralarm'); + var hitLimit = 0; + var checkHr = function(hr){ + if (hr.bpm > settings.warning && hr.bpm <= settings.upper){ + Bangle.buzz(100, 1); + } + if (hitLimit < getTime() && hr.bpm > settings.upper){ + hitLimit = getTime() + 10; + Bangle.buzz(2000, 1); + } + if (hitLimit > 0 && hr.bpm < settings.lower){ + hitLimit = 0; + Bangle.buzz(500, 1); + } + }; + Bangle.on("HRM", checkHr); + Bangle.on("BTHRM", checkHr); + + WIDGETS["hralarm"]={ + area:"tl", + width: 0, + draw: function(){} + }; +})() diff --git a/apps/hralarm/widget.png b/apps/hralarm/widget.png new file mode 100644 index 000000000..726cf3f9b Binary files /dev/null and b/apps/hralarm/widget.png differ diff --git a/apps/lapcounter/ChangeLog b/apps/lapcounter/ChangeLog index 9db0e26c5..146ff1b05 100644 --- a/apps/lapcounter/ChangeLog +++ b/apps/lapcounter/ChangeLog @@ -1 +1,2 @@ 0.01: first release +0.02: Themeable app icon diff --git a/apps/lapcounter/app-icon.js b/apps/lapcounter/app-icon.js index a443b3a41..354c07124 100644 --- a/apps/lapcounter/app-icon.js +++ b/apps/lapcounter/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap")) +require("heatshrink").decompress(atob("mEwwI0xg+evPsAon+ApX8Aon4AonwAod78AFDv4FWvoFE/IFDz4FXvIFD3wFE/wFW7wFDh5xBAoUfAok/Aol/BZUXAogA6A=")) diff --git a/apps/lcars/ChangeLog b/apps/lcars/ChangeLog index f5d8346da..45d7a8dd7 100644 --- a/apps/lcars/ChangeLog +++ b/apps/lcars/ChangeLog @@ -6,4 +6,6 @@ 0.06: Fix - Alarm disabled, if clock was closed. 0.07: Added settings to adjust data that is shown for each row. 0.08: Support for multiple screens. 24h graph for steps + HRM. Fullscreen Mode. -0.09: Tab anywhere to open the launcher. \ No newline at end of file +0.09: Tab anywhere to open the launcher. +0.10: Fix - Clock is unresponsive, if gadgetbridge connects. +0.11: Added getting the gadgetbridge weather \ No newline at end of file diff --git a/apps/lcars/lcars.app.js b/apps/lcars/lcars.app.js index 167adad2d..09998ccf5 100644 --- a/apps/lcars/lcars.app.js +++ b/apps/lcars/lcars.app.js @@ -1,5 +1,6 @@ const SETTINGS_FILE = "lcars.setting.json"; const Storage = require("Storage"); +const weather = require('weather'); // ...and overwrite them with any saved values @@ -32,8 +33,8 @@ let cGrey = "#9E9E9E"; let lcarsViewPos = 0; let drag; let hrmValue = 0; -var connected = NRF.getSecurityStatus().connected; var plotWeek = false; +var disableInfoUpdate = true; // When gadgetbridge connects, step infos cannot be loaded /* * Requirements and globals @@ -145,13 +146,21 @@ function printData(key, y, c){ text = "VREF"; value = E.getAnalogVRef().toFixed(2) + "V"; + } else if (key == "Weather"){ + text = "TEMP"; + const w = weather.get(); + if (!w) { + value = "ERR"; + } else { + value = require('locale').temp(w.temp-273.15); // applies conversion + } } g.setColor(c); - g.fillRect(79, y-2, 87 ,y+18); + g.fillRect(79, y-2, 85 ,y+18); - g.setFontAlign(1,-1,0); - g.drawString(value, 131, y); + g.setFontAlign(0,-1,0); + g.drawString(value, 110, y); g.setColor(c); g.setFontAlign(-1,-1,0); @@ -170,7 +179,7 @@ function drawHorizontalBgLine(color, x1, x2, y, h){ } -function drawLock(){ +function drawInfo(){ if(lcarsViewPos != 0){ return; } @@ -179,7 +188,8 @@ function drawLock(){ g.setColor(cOrange); g.clearRect(120, 10, g.getWidth(), 75); g.drawString("LCARS", 128, 13); - if(connected){ + + if(NRF.getSecurityStatus().connected){ g.drawString("CONN", 128, 33); } else { g.drawString("NOCON", 128, 33); @@ -240,11 +250,11 @@ function drawPosition0(){ // The last line is a battery indicator too var bat = E.getBattery() / 100.0; var batX2 = parseInt((172 - 35) * bat + 35); - drawHorizontalBgLine(cOrange, 35, batX2, 171, 5); - drawHorizontalBgLine(cGrey, batX2+10, 172, 171, 5); + drawHorizontalBgLine(cOrange, 35, batX2-5, 171, 5); + drawHorizontalBgLine(cGrey, batX2+5, 172, 171, 5); - // Draw logo - drawLock(); + // Draw Infos + drawInfo(); // Write time g.setFontAlign(-1, -1, 0); @@ -252,15 +262,15 @@ function drawPosition0(){ var currentDate = new Date(); var timeStr = locale.time(currentDate,1); g.setFontAntonioLarge(); - g.drawString(timeStr, 29, 10); + g.drawString(timeStr, 27, 10); // Write date g.setColor(cWhite); g.setFontAntonioMedium(); var dayStr = locale.dow(currentDate, true).toUpperCase(); dayStr += " " + currentDate.getDate(); - dayStr += " " + currentDate.getFullYear(); - g.drawString(dayStr, 32, 56); + dayStr += " " + locale.month(currentDate, 1).toUpperCase(); + g.drawString(dayStr, 30, 56); // Draw data g.setFontAlign(-1, -1, 0); @@ -407,6 +417,7 @@ function draw(){ */ function getSteps() { var steps = 0; + let health; try { health = require("health"); } catch(ex) { @@ -467,24 +478,17 @@ function handleAlarm(){ Bangle.on('lcdPower',on=>{ if (on) { // Whenever we connect to Gadgetbridge, reading data from - // health failed. Therefore, we update and read data from - // health iff the connection state did not change. - if(connected == NRF.getSecurityStatus().connected) { - draw(); - } else { - connected = NRF.getSecurityStatus().connected - drawLock(); - } + // health failed. Therefore, we update only partially... + drawInfo(); + drawState(); } else { // stop draw timer if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; } - - connected = NRF.getSecurityStatus().connected }); Bangle.on('lock', function(isLocked) { - drawLock(); + drawInfo(); }); Bangle.on('charging',function(charging) { @@ -517,49 +521,53 @@ function decreaseAlarm(){ Storage.writeJSON(SETTINGS_FILE, settings); } +function feedback(){ + Bangle.buzz(40, 0.3); +} -// Thanks to the app "gbmusic" for this code to detect swipes in all 4 directions. -Bangle.on("drag", e => { - if (!drag) { // start dragging - drag = {x: e.x, y: e.y}; - } else if (!e.b) { // released - const dx = e.x-drag.x, dy = e.y-drag.y; - drag = null; +// Touch gestures to control clock. We don't use swipe to be compatible with the bangle ecosystem +Bangle.on('touch', function(btn, e){ + var left = parseInt(g.getWidth() * 0.2); + var right = g.getWidth() - left; + var upper = parseInt(g.getHeight() * 0.2); + var lower = g.getHeight() - upper; - // Horizontal swipe - if (Math.abs(dx)>Math.abs(dy)+10) { - if(dx > 0){ - lcarsViewPos = 0; - } else { - lcarsViewPos = 1; - } - - // Vertical swipe - } else if (Math.abs(dy)>Math.abs(dx)+10) { - if(lcarsViewPos == 0){ - if(dy > 0){ - decreaseAlarm(); - } else { - increaseAlarm(); - } - - // Only update the state and return to - // avoid a full draw as this is much faster. - drawState(); - return; - } - - if(lcarsViewPos == 1){ - plotWeek = dy < 0 ? true : false; - } - } + var is_left = e.x < left; + var is_right = e.x > right; + var is_upper = e.y < upper; + var is_lower = e.y > lower; + if(is_left && lcarsViewPos == 1){ + feedback(); + lcarsViewPos = 0; draw(); - } -}); + return; -Bangle.on("touch", e => { - Bangle.showLauncher(); + } else if(is_right && lcarsViewPos == 0){ + feedback(); + lcarsViewPos = 1; + draw(); + return; + } + + if(lcarsViewPos == 0){ + if(is_upper){ + feedback(); + increaseAlarm(); + drawState(); + return; + } if(is_lower){ + feedback(); + decreaseAlarm(); + drawState(); + return; + } + } else if (lcarsViewPos == 1 && (is_upper || is_lower) && plotWeek != is_lower){ + feedback(); + plotWeek = is_lower; + draw(); + return; + } }); diff --git a/apps/lcars/lcars.settings.js b/apps/lcars/lcars.settings.js index 0d004b002..a0e54f9b4 100644 --- a/apps/lcars/lcars.settings.js +++ b/apps/lcars/lcars.settings.js @@ -18,14 +18,14 @@ storage.write(SETTINGS_FILE, settings) } - var data_options = ["Battery", "Steps", "Temp.", "HRM", "VREF"]; + var data_options = ["Battery", "Steps", "Temp.", "HRM", "VREF", "Weather"]; E.showMenu({ '': { 'title': 'LCARS Clock' }, '< Back': back, 'Row 1': { value: 0 | data_options.indexOf(settings.dataRow1), - min: 0, max: 4, + min: 0, max: 5, format: v => data_options[v], onchange: v => { settings.dataRow1 = data_options[v]; @@ -34,7 +34,7 @@ }, 'Row 2': { value: 0 | data_options.indexOf(settings.dataRow2), - min: 0, max: 4, + min: 0, max: 5, format: v => data_options[v], onchange: v => { settings.dataRow2 = data_options[v]; @@ -43,7 +43,7 @@ }, 'Row 3': { value: 0 | data_options.indexOf(settings.dataRow3), - min: 0, max: 4, + min: 0, max: 5, format: v => data_options[v], onchange: v => { settings.dataRow3 = data_options[v]; diff --git a/apps/lcars/screenshot.png b/apps/lcars/screenshot.png index fba55a9f7..5d7603b45 100644 Binary files a/apps/lcars/screenshot.png and b/apps/lcars/screenshot.png differ