From 532d2d36950bf3e6effec67acb6d3cde34af7e6d Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 30 Oct 2019 20:34:41 +0000 Subject: [PATCH] Add more apps --- apps.json | 53 +++++++++++ apps/clock-icon.js | 1 + apps/clock-morphing.js | 190 +++++++++++++++++++++++++++++++++++++++ apps/clock.json | 5 ++ apps/gpstime-icon.js | 1 + apps/gpstime.js | 64 +++++++++++++ apps/gpstime.json | 5 ++ apps/openlocation.js | 23 +++++ apps/openlocation.json | 4 + apps/speedo-icon.js | 1 + apps/speedo.js | 24 +++++ apps/speedo.json | 5 ++ apps/spiritlevel-icon.js | 2 + apps/spiritlevel.js | 23 +++++ apps/spiritlevel.json | 5 ++ 15 files changed, 406 insertions(+) create mode 100644 apps/clock-icon.js create mode 100644 apps/clock-morphing.js create mode 100644 apps/clock.json create mode 100644 apps/gpstime-icon.js create mode 100644 apps/gpstime.js create mode 100644 apps/gpstime.json create mode 100644 apps/openlocation.js create mode 100644 apps/openlocation.json create mode 100644 apps/speedo-icon.js create mode 100644 apps/speedo.js create mode 100644 apps/speedo.json create mode 100644 apps/spiritlevel-icon.js create mode 100644 apps/spiritlevel.js create mode 100644 apps/spiritlevel.json diff --git a/apps.json b/apps.json index 7be3bf78d..dbdc58c43 100644 --- a/apps.json +++ b/apps.json @@ -20,5 +20,58 @@ {"name":"-compass","file":"compass.js"}, {"name":"*compass","file":"compass-icon.js"} ] + }, + { "id": "clock", + "name": "Morphing Clock", + "icon": "clock-morphing.png", + "description": "7 segment clock that morphs between minutes and hours", + "tags": "clock", + "storage": [ + {"name":"+clock","file":"clock.json"}, + {"name":"-clock","file":"clock-morphing.js"}, + {"name":"*clock","file":"clock-icon.js"} + ] + }, + { "id": "gpstime", + "name": "GPS Time", + "icon": "gpstime.png", + "description": "Update the Bangle.js's clock based on the time from the GPS receiver", + "tags": "tool", + "storage": [ + {"name":"+gpstime","file":"gpstime.json"}, + {"name":"-gpstime","file":"gpstime.js"}, + {"name":"*gpstime","file":"gpstime-icon.js"} + ] + }, + { "id": "openloc", + "name": "Open Location / Plus Codes", + "description": "Convert your current GPS location to a series of characters", + "tags": "tool,outdoors", + "storage": [ + {"name":"+openloc","file":"openlocation.json"}, + {"name":"-openloc","file":"openlocation.js"} + ] + }, + { "id": "speedo", + "name": "Speedo", + "icon": "speedo.png", + "description": "Show the current speed according to the GPS", + "tags": "tool,outdoors", + "storage": [ + {"name":"+speedo","file":"speedo.json"}, + {"name":"-speedo","file":"speedo.js"}, + {"name":"*speedo","file":"speedo-icon.js"} + ] + }, + { "id": "slevel", + "name": "Spirit Level", + "icon": "spiritlevel.png", + "description": "Show the current angle of the watch, so you can use it to make sure something is absolutely flat", + "tags": "tool", + "storage": [ + {"name":"+slevel","file":"spiritlevel.json"}, + {"name":"-slevel","file":"spiritlevel.js"}, + {"name":"*slevel","file":"spiritlevel-icon.js"} + ] } ] diff --git a/apps/clock-icon.js b/apps/clock-icon.js new file mode 100644 index 000000000..1d5e211f0 --- /dev/null +++ b/apps/clock-icon.js @@ -0,0 +1 @@ +equire("heatshrink").decompress(atob("mEwghC/AE8IxAAEwAWVDB4WIDBwWJAAIWPmf//8zDBpFDwYVBAAc4JJYWJDAoXKn4SC+EPAgXzC5JGCx4qDC4n//BIIEIRCEC4v/GBBdHC4xhCIw5dDC5BhCJAgXCRQoXGJAQXEUhAXHJAyNGC5KRCC7p2FC5B4CC5kggQXOBwvyBQMvSA4XL+EIwCoIC8ZHCgYXNO44LBBIiPPCAIwFC5DXGAAMwGAjvPGA4XIwYXHGALBDnAXFhCQHGAaOFwAXGPA4bFC4xIMIxIXDJBJGEC4xICSJCNEIwowEMJBdCFwwXEMJBdCC5BICDA4WDIw4wEAAMzCoMzBAgWIDAwAGCxRJEAAxFJDBgWNDBAWPAH4AYA==")) diff --git a/apps/clock-morphing.js b/apps/clock-morphing.js new file mode 100644 index 000000000..7b42a99ae --- /dev/null +++ b/apps/clock-morphing.js @@ -0,0 +1,190 @@ +// Enable 'Set Current Time' in Settings -> Communications before sending +(function(){ // make our own scope so this is GC'd when intervals are cleared +// Offscreen buffer +var buf = Graphics.createArrayBuffer(240,86,1,{msb:true}); +function flip() { g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer},0,50); +} +// The last time that we displayed +var lastTime = " "; +// If animating, this is the interval's id +var animInterval; + +/* Get array of lines from digit d to d+1. + n is the amount (0..1) + maxFive is true is this digit only counts 0..5 */ +const DIGITS = { +" ":n=>[], +"0":n=>[ +[n,0,1,0], +[1,0,1,1], +[1,1,1,2], +[n,2,1,2], +[n,1,n,2], +[n,0,n,1]], +"1":n=>[ +[1-n,0,1,0], +[1,0,1,1], +[1-n,1,1,1], +[1-n,1,1-n,2], +[1-n,2,1,2]], +"2":n=>[ +[0,0,1,0], +[1,0,1,1], +[0,1,1,1], +[0,1+n,0,2], +[1,2-n,1,2], +[0,2,1,2]], +"3":n=>[ +[0,0,1-n,0], +[0,0,0,n], +[1,0,1,1], +[0,1,1,1], +[1,1,1,2], +[n,2,1,2]], +"4":n=>[ +[0,0,0,1], +[1,0,1-n,0], +[1,0,1,1-n], +[0,1,1,1], +[1,1,1,2], +[1-n,2,1,2]], +"5": (n,maxFive)=>maxFive ? [ // 5 -> 0 +[0,0,0,1], +[0,0,1,0], +[n,1,1,1], +[1,1,1,2], +[0,2,1,2], +[0,2,0,2], +[1,1-n,1,1], +[0,1,0,1+n]] : [ // 5 -> 6 +[0,0,0,1], +[0,0,1,0], +[0,1,1,1], +[1,1,1,2], +[0,2,1,2], +[0,2-n,0,2]], +"6":n=>[ +[0,0,0,1-n], +[0,0,1,0], +[n,1,1,1], +[1,1-n,1,1], +[1,1,1,2], +[n,2,1,2], +[0,1-n,0,2-2*n]], +"7":n=>[ +[0,0,0,n], +[0,0,1,0], +[1,0,1,1], +[1-n,1,1,1], +[1,1,1,2], +[1-n,2,1,2], +[1-n,1,1-n,2]], +"8":n=>[ +[0,0,0,1], +[0,0,1,0], +[1,0,1,1], +[0,1,1,1], +[1,1,1,2], +[0,2,1,2], +[0,1,0,2-n]], +"9":n=>[ +[0,0,0,1], +[0,0,1,0], +[1,0,1,1], +[0,1,1-n,1], +[0,1,0,1+n], +[1,1,1,2], +[0,2,1,2]], +":":n=>[ +[0.4,0.4,0.6,0.4], +[0.6,0.4,0.6,0.6], +[0.6,0.6,0.4,0.6], +[0.4,0.4,0.4,0.6], +[0.4,1.4,0.6,1.4], +[0.6,1.4,0.6,1.6], +[0.6,1.6,0.4,1.6], +[0.4,1.4,0.4,1.6]] +}; + +/* Draw a transition between lastText and thisText. + 'n' is the amount - 0..1 */ +function draw(lastText,thisText,n) { + buf.clear(); + var x = 1; // x offset + const p = 2; // padding around digits + var y = p; // y offset + const s = 34; // character size + for (var i=0;i{ + if (c[0]!=c[2]) // horiz + buf.fillRect(x+c[0]*s,y+c[1]*s-p,x+c[2]*s,y+c[3]*s+p); + else if (c[1]!=c[3]) // vert + buf.fillRect(x+c[0]*s-p,y+c[1]*s,x+c[2]*s+p,y+c[3]*s); + }); + if (thisCh==":") x-=4; + x+=s+p+7; + } + y += 2*s; + var d = new Date(); + buf.setFont("6x8"); + buf.setFontAlign(-1,-1); + buf.drawString(("0"+d.getSeconds()).substr(-2), x, y-8); + // date + buf.setFontAlign(0,-1); + var date = d.toString().substr(0,15); + buf.drawString(date, buf.getWidth()/2, y+8); + flip(); +} + +/* Show the current time, and animate if needed */ +function showTime() { + if (!Bangle.isLCDOn()) return; + if (animInterval) return; // in animation - quit + var d = new Date(); + var t = (" "+d.getHours()).substr(-2)+":"+ + ("0"+d.getMinutes()).substr(-2); + var l = lastTime; + // same - don't animate + if (t==l) { + draw(t,l,0); + return; + } + var n = 0; + animInterval = setInterval(function() { + n += 1/10; + if (n>=1) { + n=1; + clearInterval(animInterval); + animInterval=0; + } + draw(l,t,n); + }, 20); + lastTime = t; +} + +Bangle.on('lcdPower',function(on) { + if (on) { + showTime(); + drawWidgets(); + } +}); + +g.clear(); +// Update time once a second +setInterval(showTime, 1000); +showTime(); +})(); diff --git a/apps/clock.json b/apps/clock.json new file mode 100644 index 000000000..1440a2ba2 --- /dev/null +++ b/apps/clock.json @@ -0,0 +1,5 @@ +{ + "name":"Clock", + "icon":"*clock", + "src":"-clock" +} diff --git a/apps/gpstime-icon.js b/apps/gpstime-icon.js new file mode 100644 index 000000000..665c8d5f6 --- /dev/null +++ b/apps/gpstime-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwghC/AH8A1QWVhWq0AuVAAIuVAAIwT1WinQwTFwMzmQwTCYMjlUqGCIuBlWi0UzC6JdBIoMjC4UDmAuOkYXBPAWgmczLp2ilUiVAUDC4IwLFwIUBLoJ2BFwQwM1WjCgJ1DFwQwLFwJ1B0SQCkQWDGBQXBCgK9BDgKQBAAgwJOwUzRgIDBC54wCkZdGPBwACRgguDBIIwLFxEJBQIwLFxGaBYQwKFxQwLgAWGmQuBcAQwJC48ifYYwJgUidgsyC4L7DGBIXBdohnBCgL7BcYIXIGAqMCIoL7DL5IwERgIUBLoL7BO5QXBGAK7DkWiOxQXGFwOjFoUyFxZhDgBdCCgJ1CCxYxCgBABkcqOwIuNGAQXC0S9BLpgAFXoIwBmYuPAAYwCLp4wHFyYwDFyYwDFygwCCyoA/AFQA=")) diff --git a/apps/gpstime.js b/apps/gpstime.js new file mode 100644 index 000000000..285fb64ba --- /dev/null +++ b/apps/gpstime.js @@ -0,0 +1,64 @@ +var img = require("heatshrink").decompress(atob("mEwghC/AH8A1QWVhWq0AuVAAIuVAAIwT1WinQwTFwMzmQwTCYMjlUqGCIuBlWi0UzC6JdBIoMjC4UDmAuOkYXBPAWgmczLp2ilUiVAUDC4IwLFwIUBLoJ2BFwQwM1WjCgJ1DFwQwLFwJ1B0SQCkQWDGBQXBCgK9BDgKQBAAgwJOwUzRgIDBC54wCkZdGPBwACRgguDBIIwLFxEJBQIwLFxGaBYQwKFxQwLgAWGmQuBcAQwJC48ifYYwJgUidgsyC4L7DGBIXBdohnBCgL7BcYIXIGAqMCIoL7DL5IwERgIUBLoL7BO5QXBGAK7DkWiOxQXGFwOjFoUyFxZhDgBdCCgJ1CCxYxCgBABkcqOwIuNGAQXC0S9BLpgAFXoIwBmYuPAAYwCLp4wHFyYwDFyYwDFygwCCyoA/AFQA=")); + +Bangle.setLCDPower(1); +Bangle.setLCDTimeout(0); + +g.clear(); + + + +var fix; +Bangle.on('GPS',function(f) { + fix = f; + g.setFont("6x8",2); + g.setFontAlign(0,0); + g.clearRect(90,30,239,90); + if (fix.fix) { + g.drawString("GPS",170,40); + g.drawString("Acquired",170,60); + } else { + g.drawString("Waiting for",170,40); + g.drawString("GPS Fix",170,60); + } + g.setFont("6x8"); + g.drawString(fix.satellites+" satellites",170,80); + + g.clearRect(0,100,239,239); + var t = fix.time.toString().split(" ");/* + [ + "Sun", + "Nov", + "10", + "2019", + "15:55:35", + "GMT+0100" + ] + */ + //g.setFont("6x8",2); + //g.drawString(t[0],120,110); // day + g.setFont("6x8",3); + g.drawString(t[1]+" "+t[2],120,135); // date + g.setFont("6x8",2); + g.drawString(t[3],120,160); // year + g.setFont("6x8",3); + g.drawString(t[4],120,185); // time + // timezone + var tz = (new Date()).getTimezoneOffset()/60; + if (tz==0) tz="UTC"; + else if (tz>0) tz="UTC+"+tz; + else tz="UTC"+tz; + g.setFont("6x8",2); + g.drawString(tz,120,210); // gmt + g.setFontAlign(0,0,3); + g.drawString("Set",230,120); + g.setFontAlign(0,0); +}); + +setInterval(function() { + g.drawImage(img,48,48,{scale:1.5,rotate:Math.sin(getTime()*2)/2}); +},100); +setWatch(function() { + setTime(fix.time.getTime()/1000); +}, BTN2, {repeat:true}); + +Bangle.setGPSPower(1) diff --git a/apps/gpstime.json b/apps/gpstime.json new file mode 100644 index 000000000..d8416f676 --- /dev/null +++ b/apps/gpstime.json @@ -0,0 +1,5 @@ +{ + "name":"GPS Time", + "icon":"*gpstime", + "src":"-gpstime" +} diff --git a/apps/openlocation.js b/apps/openlocation.js new file mode 100644 index 000000000..b1c730a21 --- /dev/null +++ b/apps/openlocation.js @@ -0,0 +1,23 @@ +!function(e,t){"function"==typeof define&&define.amd?define(["b"],function(r){return e.returnExportsGlobal=t()}):"object"==typeof module&&module.exports?module.exports=t(require("b")):e.OpenLocationCode=t()}(this,function(){var e={};e.CODE_PRECISION_NORMAL=10,e.CODE_PRECISION_EXTRA=11;var t="23456789CFGHJMPQRVWX",r=t.length,n=[20,1,.05,.0025,125e-6],o=(e.getAlphabet=function(){return t},e.isValid=function(e){if(!e||"string"!=typeof e)return!1;if(-1==e.indexOf("+"))return!1;if(e.indexOf("+")!=e.lastIndexOf("+"))return!1;if(1==e.length)return!1;if(e.indexOf("+")>8||e.indexOf("+")%2==1)return!1;if(e.indexOf("0")>-1){if(0==e.indexOf("0"))return!1;var r=e.match(new RegExp("(0+)","g"));if(r.length>1||r[0].length%2==1||r[0].length>6)return!1;if("+"!=e.charAt(e.length-1))return!1}if(e.length-e.indexOf("+")-1==1)return!1;for(var n=0,o=(e=e.replace(new RegExp("\\++"),"").replace(new RegExp("0+"),"")).length;n=0&&e.indexOf("+")<8)},a=e.isFull=function(e){return!!o(e)&&(!i(e)&&(!(t.indexOf(e.charAt(0).toUpperCase())*r>=180)&&!(e.length>1&&t.indexOf(e.charAt(1).toUpperCase())*r>=360)))},u=e.encode=function(t,r,n){if(t=Number(t),r=Number(r),n=void 0===n?e.CODE_PRECISION_NORMAL:Number(n),isNaN(t)||isNaN(r)||isNaN(n))throw"ValueError: Parameters are not numbers";if(n<2||n<10&&n%2==1)throw"IllegalArgumentException: Invalid Open Location Code length";t=l(t),r=h(r),90==t&&(t-=d(n));var o=s(t,r,Math.min(n,10));return n>10&&(o+=c(t,r,n-10)),o},f=e.decode=function(e){if(!a(e))throw"IllegalArgumentException: Passed Open Location Code is not a valid full code: "+e;e=(e=(e=e.replace("+","")).replace(new RegExp("0+"),"")).toUpperCase();var t=g(e.substring(0,10));if(e.length<=10)return t;var r=C(e.substring(10));return x(t.latitudeLo+r.latitudeLo,t.longitudeLo+r.longitudeLo,t.latitudeLo+r.latitudeHi,t.longitudeLo+r.longitudeHi,t.codeLength+r.codeLength)},l=(e.recoverNearest=function(e,t,r){if(!i(e)){if(a(e))return e;throw"ValueError: Passed short code is not valid: "+e}if(t=Number(t),r=Number(r),isNaN(t)||isNaN(r))throw"ValueError: Reference position are not numbers";t=l(t),r=h(r);var n=8-(e=e.toUpperCase()).indexOf("+"),o=Math.pow(20,2-n/2),d=o/2,s=f(u(t,r).substr(0,n)+e);return t+d=-90?s.latitudeCenter-=o:t-d>s.latitudeCenter&&s.latitudeCenter+o<=90&&(s.latitudeCenter+=o),r+ds.longitudeCenter&&(s.longitudeCenter+=o),u(s.latitudeCenter,s.longitudeCenter,s.codeLength)},e.shorten=function(e,t,r){if(!a(e))throw"ValueError: Passed code is not valid and full: "+e;if(-1!=e.indexOf("0"))throw"ValueError: Cannot shorten padded codes: "+e;var e=e.toUpperCase(),o=f(e);if(o.codeLength<6)throw"ValueError: Code length must be at least 6";if(t=Number(t),r=Number(r),isNaN(t)||isNaN(r))throw"ValueError: Reference position are not numbers";t=l(t),r=h(r);for(var i=Math.max(Math.abs(o.latitudeCenter-t),Math.abs(o.longitudeCenter-r)),u=n.length-2;u>=1;u--)if(i<.3*n[u])return e.substring(2*(u+1));return e},function(e){return Math.min(90,Math.max(-90,e))}),d=function(e){return e<=10?Math.pow(20,Math.floor(e/-2+2)):Math.pow(20,-3)/Math.pow(5,e-10)},h=function(e){for(;e<-180;)e+=360;for(;e>=180;)e-=360;return e},s=function(e,r,o){for(var i="",a=e+90,u=r+180,f=0;f