From b7b5d6764b357649fc258c3530c88dec25c168e6 Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Thu, 26 Mar 2020 22:30:34 +0100 Subject: [PATCH 01/40] Adds gbridge call notification and refactor widget --- apps.json | 8 +- apps/gbridge/ChangeLog | 1 + apps/gbridge/gbridge-call-ico.js | 1 + apps/gbridge/gbridge-music-ico.js | 1 + apps/gbridge/gbridge-off-ico.js | 1 + apps/gbridge/gbridge-on-ico.js | 1 + apps/gbridge/widget.js | 356 ++++++++++++++++++++---------- 7 files changed, 250 insertions(+), 119 deletions(-) create mode 100644 apps/gbridge/gbridge-call-ico.js create mode 100644 apps/gbridge/gbridge-music-ico.js create mode 100644 apps/gbridge/gbridge-off-ico.js create mode 100644 apps/gbridge/gbridge-on-ico.js diff --git a/apps.json b/apps.json index d5b079f7a..b5350d349 100644 --- a/apps.json +++ b/apps.json @@ -66,13 +66,17 @@ { "id": "gbridge", "name": "Gadgetbridge", "icon": "app.png", - "version":"0.04", + "version":"0.05", "description": "The default notification handler for Gadgetbridge notifications from Android", "tags": "tool,system,android,widget", "storage": [ {"name":"gbridge.app.js","url":"app.js"}, {"name":"gbridge.img","url":"app-icon.js","evaluate":true}, - {"name":"gbridge.wid.js","url":"widget.js"} + {"name":"gbridge.wid.js","url":"widget.js"}, + {"name":"gbridge-music-ico.img","url":"gbridge-music-ico.js","evaluate":true}, + {"name":"gbridge-off-ico.img","url":"gbridge-off-ico.js","evaluate":true}, + {"name":"gbridge-on-ico.img","url":"gbridge-on-ico.js","evaluate":true}, + {"name":"gbridge-call-ico.img","url":"gbridge-call-ico.js","evaluate":true} ] }, { "id": "mclock", diff --git a/apps/gbridge/ChangeLog b/apps/gbridge/ChangeLog index 28789ec04..3a6dd3bfd 100644 --- a/apps/gbridge/ChangeLog +++ b/apps/gbridge/ChangeLog @@ -2,3 +2,4 @@ 0.02: Increase contrast (darker notification background, white text) 0.03: Gadgetbridge widget now shows connection state 0.04: Tweaks for variable size widget system +0.05: Show incoming call notification \ No newline at end of file diff --git a/apps/gbridge/gbridge-call-ico.js b/apps/gbridge/gbridge-call-ico.js new file mode 100644 index 000000000..ce722a9a8 --- /dev/null +++ b/apps/gbridge/gbridge-call-ico.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw=")) \ No newline at end of file diff --git a/apps/gbridge/gbridge-music-ico.js b/apps/gbridge/gbridge-music-ico.js new file mode 100644 index 000000000..ff8f80883 --- /dev/null +++ b/apps/gbridge/gbridge-music-ico.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")) \ No newline at end of file diff --git a/apps/gbridge/gbridge-off-ico.js b/apps/gbridge/gbridge-off-ico.js new file mode 100644 index 000000000..1a722f372 --- /dev/null +++ b/apps/gbridge/gbridge-off-ico.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")) \ No newline at end of file diff --git a/apps/gbridge/gbridge-on-ico.js b/apps/gbridge/gbridge-on-ico.js new file mode 100644 index 000000000..f40c4149f --- /dev/null +++ b/apps/gbridge/gbridge-on-ico.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")) \ No newline at end of file diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index a787d7e0b..151dd3ad3 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -1,125 +1,247 @@ -(function() { - var musicState = "stop"; - var musicInfo = {"artist":"","album":"","track":""}; - var scrollPos = 0; - function gb(j) { - Bluetooth.println(JSON.stringify(j)); - } - function show(size,render) { - var oldMode = Bangle.getLCDMode(); - Bangle.setLCDMode("direct"); - g.setClipRect(0,240,239,319); - g.setColor("#222222"); - g.fillRect(1,241,238,318); - render(320-size); - g.setColor("#ffffff"); - g.fillRect(0,240,1,319); - g.fillRect(238,240,239,319); - g.fillRect(2,318,238,319); - Bangle.setLCDPower(1); // light up - Bangle.setLCDMode(oldMode); // clears cliprect - function anim() { - scrollPos-=2; - if (scrollPos<-size) scrollPos=-size; - Bangle.setLCDOffset(scrollPos); - if (scrollPos>-size) setTimeout(anim,10); - } - anim(); - } - function hide() { - function anim() { - scrollPos+=4; - if (scrollPos>0) scrollPos=0; - Bangle.setLCDOffset(scrollPos); - if (scrollPos<0) setTimeout(anim,10); - } - anim(); - } +(() => { - Bangle.on('touch',function() { - if (scrollPos) hide(); - }); - Bangle.on('swipe',function(dir) { - if (musicState=="play") { - gb({t:"music",n:dir>0?"next":"previous"}); - } - }); - gb({t:"status",bat:E.getBattery()}); + const gb = { + musicState: { + STOP: "stop", + PLAY: "play", + PAUSE: "pause" + }, - global.GB = function(j) { - switch (j.t) { - case "notify": - show(80,function(y) { - // TODO: icon based on src? - var x = 120; - g.setFontAlign(0,0); - g.setFont("6x8",1); - g.setColor("#40d040"); - g.drawString(j.src,x,y+7); - g.setColor("#ffffff"); - g.setFont("6x8",2); - g.drawString(j.title,x,y+25); - g.setFont("6x8",1); - g.setColor("#ffffff"); - // split text up a word boundaries - var txt = j.body.split("\n"); - var MAXCHARS = 38; - for (var i=0;iMAXCHARS) { - var p = MAXCHARS; - while (p>MAXCHARS-8 && !" \t-_".includes(l[p])) - p--; - if (p==MAXCHARS-8) p=MAXCHARS; - txt[i] = l.substr(0,p); - txt.splice(i+1,0,l.substr(p)); - } - } - g.setFontAlign(-1,-1); - g.drawString(txt.join("\n"),10,y+40); - Bangle.buzz(); - }); - break; - case "musicinfo": - musicInfo = j; - break; - case "musicstate": - musicState = j.state; - if (musicState=="play") - show(40,function(y) { - g.setColor("#ffffff"); - g.drawImage( require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")),8,y+8); - g.setFontAlign(-1,-1); - g.setFont("6x8",1); - var x = 40; - g.setFont("4x6",2); - g.setColor("#ffffff"); - g.drawString(musicInfo.artist,x,y+8); - g.setFont("6x8",1); - g.setColor("#ffffff"); - g.drawString(musicInfo.track,x,y+22); - }); - if (musicState=="pause") - hide(); - break; + muiscControl: { + NEXT: "next", + PREV: "previous" + }, + + callCommands: { + UNDEFINED: "undefined", + ACCEPT: "accept", + INCOMING: "incoming", + OUTGOING: "outgoing", + REJECT: "reject", + START: "start", + END: "end" + }, + + send: (message) => { + Bluetooth.println(JSON.stringify(message)); + }, + + controlMusic: (operation) => { + gb.send({ t: "music", n: operation }); + }, + + reportBatteryLevel: () => { + gb.send({ t: "status", bat: E.getBattery() }); + }, + }; + + const state = { + music: gb.musicState.STOP, + + musicInfo: { + artist: "", + album: "", + track: "" + }, + debug: false, + }; + + const notification = { + + backgroundColor: "#222222", + frameColor: "#ffffff", + titleColor: "#40d040", + contentColor: "#ffffff", + scrollPos: 0, + + show: (size, content) => { + var oldMode = Bangle.getLCDMode(); + Bangle.setLCDMode("direct"); + + g.setClipRect(0, 240, 239, 319); + g.setColor(notification.backgroundColor); + g.fillRect(1, 241, 238, 318); + + notification.drawContent(320 - size, content); + + g.setColor(notification.frameColor); + g.fillRect(0, 240, 1, 319); + g.fillRect(238, 240, 239, 319); + g.fillRect(2, 318, 238, 319); + + Bangle.setLCDPower(1); // light up + Bangle.setLCDMode(oldMode); // clears cliprect + + function anim() { + notification.scrollPos -= 2; + if (notification.scrollPos < -size) notification.scrollPos = -size; + Bangle.setLCDOffset(notification.scrollPos); + if (notification.scrollPos > -size) setTimeout(anim, 10); + } + anim(); + }, + + drawContent: (y, content) => { + + if (content.icon !== undefined) { + g.setColor(notification.contentColor); + const icon = require("Storage").read(content.icon); + g.drawImage(icon, 8, y + 8); + } + + var x = 120; + g.setFontAlign(0, 0); + + g.setFont("6x8", 1); + g.setColor(notification.titleColor); + g.drawString(content.title, x, y + 7); + + g.setColor(notification.contentColor); + g.setFont("6x8", 2); + g.drawString(content.header, x, y + 25); + + g.setFont("6x8", 1); + g.setColor(notification.contentColor); + g.setFontAlign(-1, -1); + g.drawString(content.body, 10, y + 40); + }, + + hide: () => { + function anim() { + notification.scrollPos += 4; + if (notification.scrollPos > 0) notification.scrollPos = 0; + Bangle.setLCDOffset(notification.scrollPos); + if (notification.scrollPos < 0) setTimeout(anim, 10); + } + anim(); + }, + + isVisible: () => { + return notification.scrollPos != 0; } }; -function draw() { - g.setColor(-1); - if (NRF.getSecurityStatus().connected) - g.drawImage(require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")),this.x+1,this.y+1); - else - g.drawImage(require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")),this.x+1,this.y+1); -} -function changed() { - WIDGETS["gbridgew"].draw(); - g.flip();// turns screen on -} -NRF.on('connected',changed); -NRF.on('disconnected',changed); + function showNotification(src, title, body) { -WIDGETS["gbridgew"]={area:"tl",width:24,draw:draw}; + // split text up at word boundaries + var txt = body.split("\n"); + var MAXCHARS = 38; + for (var i = 0; i < txt.length; i++) { + txt[i] = txt[i].trim(); + var l = txt[i]; + if (l.length > MAXCHARS) { + var p = MAXCHARS; + while (p > MAXCHARS - 8 && !" \t-_".includes(l[p])) + p--; + if (p == MAXCHARS - 8) p = MAXCHARS; + txt[i] = l.substr(0, p); + txt.splice(i + 1, 0, l.substr(p)); + } + } + var content = { + title: src, + header: title, + body: txt.join("\n") + }; + + notification.show(80, content); + Bangle.buzz(); + } + + function updateMusicInfo() { + if (state.music == gb.musicState.PLAY) { + + var content = { + title: "Now playing", + icon: "gbridge-music-ico.img", + header: state.musicInfo.artist, + body: state.musicInfo.track + }; + + notification.show(40, content); + } else { + notification.hide(); + } + } + + function handleCall(cmd, name, number) { + switch(cmd) { + + case gb.callCommands.ACCEPT: + notification.show(80, { + title: "Call incoming", + icon: "gbridge-call-ico.img", + header: name, + body: number + }); + Bangle.buzz(); + break; + + default: + if (state.debug) { + showNotification(cmd, name, number); + } + } + } + + global.GB = (event) => { + switch (event.t) { + case "notify": + showNotification(event.src, event.title, event.body); + break; + case "musicinfo": + state.musicInfo = event; + break; + case "musicstate": + state.musicInfo = event.state; + updateMusicInfo(); + break; + case "call": + handleCall(event.cmd, event.name, event.number); + break; + default: + if (state.debug) { + showNotification("Gadgetbridge", event.t, JSON.stringify(event)); + } + } + }; + + // Touch control + Bangle.on("touch", () => { + if (notification.isVisible()) { + notification.hide(); + } + }); + + Bangle.on("swipe", (dir) => { + if (state.music == gb.musicState.PLAY) { + gb.controlMusic(dir > 0 ? gb.muiscControl.NEXT : gb.muiscControl.PREV); + } + }); + + function drawIcon() { + g.setColor(-1); + + let icon; + if (NRF.getSecurityStatus().connected) { + icon = require("Storage").read("gbridge-on-ico.img"); + } else { + icon = require("Storage").read("gbridge-off-ico.img"); + } + + g.drawImage(icon, this.x + 1, this.y + 1); + } + + function changedConnectionState() { + WIDGETS["gbridgew"].draw(); + g.flip(); // turns screen on + } + + NRF.on("connected", changedConnectionState); + NRF.on("disconnected", changedConnectionState); + + WIDGETS["gbridgew"] = { area: "tl", width: 24, draw: drawIcon }; + + gb.reportBatteryLevel(); })(); From 33a88627d9876ce48fe8d26ffa5d973289fe0c1a Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Sat, 28 Mar 2020 13:30:13 +0100 Subject: [PATCH 02/40] Fix music notifications --- apps/gbridge/widget.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index 151dd3ad3..0b68d3e1b 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -153,10 +153,10 @@ if (state.music == gb.musicState.PLAY) { var content = { - title: "Now playing", + title: state.musicInfo.artist, icon: "gbridge-music-ico.img", - header: state.musicInfo.artist, - body: state.musicInfo.track + header: state.musicInfo.track, + body:"" }; notification.show(40, content); @@ -194,7 +194,7 @@ state.musicInfo = event; break; case "musicstate": - state.musicInfo = event.state; + state.music = event.state; updateMusicInfo(); break; case "call": From c42be52076e69eea35318e33787f9a421ee39ffa Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Wed, 1 Apr 2020 21:14:34 +0100 Subject: [PATCH 03/40] Create AstroCalc app. View info on the Sun and Moon from your wrist. --- apps.json | 21 ++ apps/astrocalc/ChangeLog | 1 + apps/astrocalc/astrocalc-app.js | 348 +++++++++++++++++++++++ apps/astrocalc/astrocalc-icon.js | 1 + apps/astrocalc/astrocalc.png | Bin 0 -> 952 bytes apps/astrocalc/first-quarter-icon.json | 6 + apps/astrocalc/full-icon.json | 7 + apps/astrocalc/last-quarter-icon.json | 7 + apps/astrocalc/new-icon.json | 6 + apps/astrocalc/suncalc.js | 328 +++++++++++++++++++++ apps/astrocalc/waning-crescent-icon.json | 7 + apps/astrocalc/waning-gibbous-icon.json | 6 + apps/astrocalc/waxing-crescent-icon.json | 6 + apps/astrocalc/waxing-gibbous-icon.json | 7 + 14 files changed, 751 insertions(+) create mode 100644 apps/astrocalc/ChangeLog create mode 100644 apps/astrocalc/astrocalc-app.js create mode 100644 apps/astrocalc/astrocalc-icon.js create mode 100644 apps/astrocalc/astrocalc.png create mode 100644 apps/astrocalc/first-quarter-icon.json create mode 100644 apps/astrocalc/full-icon.json create mode 100644 apps/astrocalc/last-quarter-icon.json create mode 100644 apps/astrocalc/new-icon.json create mode 100644 apps/astrocalc/suncalc.js create mode 100644 apps/astrocalc/waning-crescent-icon.json create mode 100644 apps/astrocalc/waning-gibbous-icon.json create mode 100644 apps/astrocalc/waxing-crescent-icon.json create mode 100644 apps/astrocalc/waxing-gibbous-icon.json diff --git a/apps.json b/apps.json index a84a8010c..ea334a0b4 100644 --- a/apps.json +++ b/apps.json @@ -951,5 +951,26 @@ {"name":"chrono.app.js","url":"chrono.js"}, {"name":"chrono.img","url":"chrono-icon.js","evaluate":true} ] + }, + { "id": "astrocalc", + "name": "Astrocalc", + "icon": "astrocalc.png", + "version":"0.01", + "description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.", + "tags": "app,sun,moon,cycles,tool,outdoors", + "allow_emulator":true, + "storage": [ + {"name":"astrocalc.app.js","url":"astrocalc-app.js"}, + {"name":"suncalc.js","url":"suncalc.js"}, + {"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true}, + {"name":"first-quarter-icon.json","url":"first-quarter-icon.json"}, + {"name":"last-quarter-icon.json","url":"last-quarter-icon.json"}, + {"name":"waning-crescent-icon.json","url":"waning-crescent-icon.json"}, + {"name":"waning-gibbous-icon.json","url":"waning-gibbous-icon.json"}, + {"name":"full-icon.json","url":"full-icon.json"}, + {"name":"new-icon.json","url":"new-icon.json"}, + {"name":"waxing-gibbous-icon.json","url":"waxing-gibbous-icon.json"}, + {"name":"waxing-crescent-icon.json","url":"waxing-crescent-icon.json"} + ] } ] diff --git a/apps/astrocalc/ChangeLog b/apps/astrocalc/ChangeLog new file mode 100644 index 000000000..0c8adeb61 --- /dev/null +++ b/apps/astrocalc/ChangeLog @@ -0,0 +1 @@ +0.01: Create astrocalc app diff --git a/apps/astrocalc/astrocalc-app.js b/apps/astrocalc/astrocalc-app.js new file mode 100644 index 000000000..655544723 --- /dev/null +++ b/apps/astrocalc/astrocalc-app.js @@ -0,0 +1,348 @@ +/** + * Inspired by: https://www.timeanddate.com + */ + +const SunCalc = require("suncalc.js"); + +function drawMoon(phase, x, y) { + const moonImgFiles = [ + "new-icon.json", + "waxing-crescent-icon.json", + "first-quarter-icon.json", + "waxing-gibbous-icon.json", + "full-icon.json", + "waning-gibbous-icon.json", + "last-quarter-icon.json", + "waning-crescent-icon.json" + ]; + + imgObj = require("Storage").readJSON(moonImgFiles[phase]); + imgObj.buffer = require("heatshrink").decompress(atob(imgObj.image)); + g.drawImage(imgObj, x /*- (imgObj.width / 2)*/, y /*- (imgObj.height)*/); +} + +// linear interpolation between two values a and b +// u controls amount of a/b and is in range [0.0,1.0] +function lerp(a,b,u) { + return (1-u) * a + u * b; +} + +function titlizeKey(key) { + return (key[0].toUpperCase() + key.slice(1)).match(/[A-Z][a-z]+/g).join(" "); +} + +function dateToTimeString(date) { + const hrs = ("0" + date.getHours()).substr(-2); + const mins = ("0" + date.getMinutes()).substr(-2); + const secs = ("0" + date.getMinutes()).substr(-2); + + return `${hrs}:${mins}:${secs}`; +} + +function drawTitle(key) { + const fontHeight = 16; + const x = 0; + const x2 = g.getWidth() - 1; + const y = fontHeight + 26; + const y2 = g.getHeight() - 1; + const title = titlizeKey(key); + + g.setFont("6x8", 2); + g.setFontAlign(0,-1); + g.drawString(title,(x+x2)/2,y-fontHeight-2); + g.drawLine(x,y-2,x2,y-2); +} + +/** + * @params {Number} angle Angle of point around a radius + * @params {Number} radius Radius of the point to be drawn, default 2 + * @params {Object} color Color of the point + * @params {Number} color.r Red 0-1 + * @params {Number} color.g Green 0-1 + * @params {Number} color.b Blue 0-1 + */ +function drawPoint(angle, radius, color) { + const pRad = Math.PI / 180; + const faceWidth = 80; // watch face radius + const centerPx = g.getWidth() / 2; + + const a = angle * pRad; + const x = centerPx + Math.sin(a) * faceWidth; + const y = centerPx - Math.cos(a) * faceWidth; + + if (!radius) radius = 2; + + g.setColor(color.r, color.g, color.b); + g.fillCircle(x, y + 20, radius); +} + +function drawPoints() { + const startColor = {r: 140, g: 255, b: 255}; // light blue + const endColor = {r: 0, g: 0, b: 140}; // dark turquoise + + const steps = 60; + const step_u = 1.0 / (steps / 2); + let u = 0.0; + + for (let i = 0; i < steps; i++) { + const colR = lerp(startColor.r, endColor.r, u) / 255; + const colG = lerp(startColor.g, endColor.g, u) / 255; + const colB = lerp(startColor.b, endColor.b, u) / 255; + const col = {r: colR, g: colG, b: colB}; + + if (i >= 0 && i <= 30) { + u += step_u; + } else { + u -= step_u; + } + + drawPoint((360 * i) / steps, 2, col); + } +} + +function drawData(title, obj, startX, startY) { + g.clear(); + drawTitle(title); + + let xPos, yPos; + + if (typeof(startX) === "undefined" || startX === null) { + // Center text + g.setFontAlign(0,-1); + xPos = (0 + g.getWidth() - 2) / 2; + } else { + xPos = startX; + } + + if (typeof(startY) === "undefined") { + yPos = 5; + } else { + yPos = startY; + } + + g.setFont("6x8", 1); + + Object.keys(obj).forEach((key) => { + g.drawString(`${key}: ${obj[key]}`, xPos, yPos += 20); + }); + + g.flip(); +} + +function drawMoonPositionPage(gps, title) { + const pos = SunCalc.getMoonPosition(new Date(), gps.lat, gps.lon); + + const pageData = { + Azimuth: pos.azimuth.toFixed(2), + Altitude: pos.altitude.toFixed(2), + Distance: `${pos.distance.toFixed(0)} km`, + "Parallactic Ang": pos.parallacticAngle.toFixed(2), + }; + const azimuthDegrees = parseInt(pos.azimuth * 180 / Math.PI); + + drawData(title, pageData, null, 80); + drawPoints(); + drawPoint(azimuthDegrees, 8, {r: 1, g: 1, b: 1}); + + let m = setWatch(() => { + let m = moonIndexPageMenu(gps); + }, BTN3, {repeat: false, edge: "falling"}); +} + +function drawMoonIlluminationPage(gps, title) { + const phaseNames = [ + "New Moon", "Waxing Crescent", "First Quarter", "Waxing Gibbous", + "Full Moon", "Waning Gibbous", "Last Quater", "Waning Crescent", + ]; + + const phase = SunCalc.getMoonIllumination(new Date()); + const pageData = { + Phase: phaseNames[phase.phase], + }; + + drawData(title, pageData, null, 35); + drawMoon(phase.phase, g.getWidth() / 2, g.getHeight() / 2); + + let m = setWatch(() => { + let m = moonIndexPageMenu(gps); + }, BTN3, {repease: false, edge: "falling"}); +} + + +function drawMoonTimesPage(gps, title) { + const times = SunCalc.getMoonTimes(new Date(), gps.lat, gps.lon); + + const pageData = { + Rise: dateToTimeString(times.rise), + Set: dateToTimeString(times.set), + }; + + drawData(title, pageData, null, 105); + drawPoints(); + + // Draw the moon rise position + const risePos = SunCalc.getMoonPosition(times.rise, gps.lat, gps.lon); + const riseAzimuthDegrees = parseInt(risePos.azimuth * 180 / Math.PI); + drawPoint(riseAzimuthDegrees, 8, {r: 1, g: 1, b: 1}); + + // Draw the moon set position + const setPos = SunCalc.getMoonPosition(times.set, gps.lat, gps.lon); + const setAzimuthDegrees = parseInt(setPos.azimuth * 180 / Math.PI); + drawPoint(setAzimuthDegrees, 8, {r: 1, g: 1, b: 1}); + + let m = setWatch(() => { + let m = moonIndexPageMenu(gps); + }, BTN3, {repease: false, edge: "falling"}); +} + +function drawSunShowPage(gps, key, date) { + const pos = SunCalc.getPosition(date, gps.lat, gps.lon); + + const hrs = ("0" + date.getHours()).substr(-2); + const mins = ("0" + date.getMinutes()).substr(-2); + const secs = ("0" + date.getMinutes()).substr(-2); + const time = `${hrs}:${mins}:${secs}`; + + const azimuth = Number(pos.azimuth.toFixed(2)); + const azimuthDegrees = parseInt(pos.azimuth * 180 / Math.PI); + const altitude = Number(pos.altitude.toFixed(2)); + + const pageData = { + Time: time, + Altitude: altitude, + Azimumth: azimuth, + Degrees: azimuthDegrees + }; + + drawData(key, pageData, null, 85); + + drawPoints(); + + // Draw the suns position + drawPoint(azimuthDegrees, 8, {r: 1, g: 1, b: 0}); + + m = setWatch(() => { + m = sunIndexPageMenu(gps); + }, BTN3, {repeat: false, edge: "falling"}); + + return null; +} + +function sunIndexPageMenu(gps) { + const sunTimes = SunCalc.getTimes(new Date(), gps.lat, gps.lon); + + const sunMenu = { + "": { + "title": "-- Sun --", + }, + "Current Pos": () => { + m = E.showMenu(); + drawSunShowPage(gps, "Current Pos", new Date()); + }, + }; + + Object.keys(sunTimes).sort().reduce((menu, key) => { + const title = titlizeKey(key); + menu[title] = () => { + m = E.showMenu(); + drawSunShowPage(gps, key, sunTimes[key]); + }; + return menu; + }, sunMenu); + + sunMenu["< Back"] = () => m = indexPageMenu(gps); + + return E.showMenu(sunMenu); +} + + +function moonIndexPageMenu(gps) { + const moonMenu = { + "": { + "title": "-- Moon --", + }, + "Times": () => { + m = E.showMenu(); + drawMoonTimesPage(gps, "Times"); + }, + "Position": () => { + m = E.showMenu(); + drawMoonPositionPage(gps, "Position"); + }, + "Illumination": () => { + m = E.showMenu(); + drawMoonIlluminationPage(gps, "Illumination"); + }, + "< Back": () => m = indexPageMenu(gps), + }; + + return E.showMenu(moonMenu); +} + +function indexPageMenu(gps) { + const menu = { + "": { + "title": "Select", + }, + "Sun": () => { + m = sunIndexPageMenu(gps); + }, + "Moon": () => { + m = moonIndexPageMenu(gps); + }, + "< Exit": () => { load(); } + }; + + return E.showMenu(menu); +} + +/** + * GPS wait page, shows GPS locating animation until it gets a lock, then moves to the Sun page + */ +function drawGPSWaitPage() { + const img = require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AW43GF1wwsFwYwqFwowoFw4wmFxIwdE5YAPF/4vM5nN6YAE5vMF8YtHGIgvhFpQxKF7AuOGA4vXFyAwGF63MFyIABF6xeWMC4UDLwvNGpAJG5gwSdhIIDRBLyWCIgcJHAgJJDoouQF4vMQoICBBJoeGFx6GGACIfHL6YvaX6gvZeCIdFc4gAFXogvGFxgwFDwovQCAguOGAnMMBxeG5guTGAggGGAwNKFySREcA3N5vM5gDBdpQvXEY4AKXqovGGCKbFF7AwPZQwvZGJgtGF7vGdQItG5gSIF7gASF/44WEzgwRF0wwHF1AwFF1QwDF1gvwAH4A/AFAA==")) + + g.clear(); + g.drawImage(img, 100, 50); + g.setFont("6x8", 1); + g.drawString("Astrocalc v0.01", 80, 105); + g.drawString("Locating GPS", 85, 140); + g.drawString("Please wait...", 80, 155); + g.flip(); + + const DEBUG = false; + if (DEBUG) { + const gps = { + "lat": 56.45783133333, + "lon": -3.02188583333, + "alt": 75.3, + "speed": 0.070376, + "course": NaN, + "time":new Date(), + "satellites": 4, + "fix": 1 + }; + + m = indexPageMenu(gps); + + return; + } + + Bangle.on('GPS', (gps) => { + if (gps.fix === 0) return; + + Bangle.setGPSPower(0); + Bangle.buzz(); + Bangle.setLCDPower(true); + + m = indexPageMenu(gps); + }); +} + +function init() { + Bangle.setGPSPower(1); + drawGPSWaitPage(); +} + +let m; +init(); \ No newline at end of file diff --git a/apps/astrocalc/astrocalc-icon.js b/apps/astrocalc/astrocalc-icon.js new file mode 100644 index 000000000..aa04c2805 --- /dev/null +++ b/apps/astrocalc/astrocalc-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AW43GF1wwsFwYwqFwowoFw4wmFxIwdE5YAPF/4vM5nN6YAE5vMF8YtHGIgvhFpQxKF7AuOGA4vXFyAwGF63MFyIABF6xeWMC4UDLwvNGpAJG5gwSdhIIDRBLyWCIgcJHAgJJDoouQF4vMQoICBBJoeGFx6GGACIfHL6YvaX6gvZeCIdFc4gAFXogvGFxgwFDwovQCAguOGAnMMBxeG5guTGAggGGAwNKFySREcA3N5vM5gDBdpQvXEY4AKXqovGGCKbFF7AwPZQwvZGJgtGF7vGdQItG5gSIF7gASF/44WEzgwRF0wwHF1AwFF1QwDF1gvwAH4A/AFAA==")) \ No newline at end of file diff --git a/apps/astrocalc/astrocalc.png b/apps/astrocalc/astrocalc.png new file mode 100644 index 0000000000000000000000000000000000000000..c26a651ec60072f49fa8969f7c7d8e1479e391f0 GIT binary patch literal 952 zcmV;p14sOcP)zXZD_Mrh6h6{gWm$`>@vk&pxfS3vIN~Mrd$m z$vXvJ{cj$Da7x}e{e>pN3d5)~OCF0CuHqSMisz<%85f`+Ux#rKMg=q>Ux!f<#sp}R zufv!KZ2?XJ$Am*&SW|l3IznR*k3k|Pp37CJR6}uNPC&$hj15^E29gkqHr7{9U}GDK zC0H#%&CGzB5sxRDaS{W8q|6hc}=YXTw`Opd^4`eOZh67c9YEU&}I zrItn`xPXWSkH=x)T3{253_^D&yjut?A`;m7yljFiQ4EM+0>e~R=F_ex0crbu z$+V6L-$g7953+V+qmvrE{S2GC;yu*|Pwqf8dhvGIekg9bMc@~9C`UjU+YBTd-xpT| zsh$K}=@QS2zq|%@+6^YX{hEw~bOe;-<1z0Ed%X|#58+`>Op&=IQRaqDyI2RrJD_qb z6CoW5Pr&3GRrYqn(+AQjV|eoccK@hqJyEQ83R#qu#zV>9T<|;3B-}g#tSM$dv1kq} zfq7{|<-;OxJ>#XjSh@zRDQ27kfNSv_9gC>ay$%3Ye#6&QJ(9Pwa5LpCuO7?hzmh!o zY@)UeYsXloB4Qe$%>JW`Us#jjOwk;u|CJ#*xGU;?ug}{k8Ob+{*6POqlZDnxV zZbBY3PGXOUVhQ#XyToW3?&ri;b|V@Qbz_Rqd%p~8TNffPnrWgTC(hU+o>$?hD%Ql# zX~>YgDiWE_n7Yrqg$4oP=ESQdSXx&{q@6Js%jnztzSj8lFM#0rz}%9^aa+a4HhfwR z+&#E}nj?n&izM#R#qxSf*%-8%6W1J(oNw)m-Ni4dKaagK%>M{HZO+5Yt;Fhi%w6sln8qD; zsJ&_(QhKHgLQ8}}^1_^9_=+%0UYHC4QV}7L4;4;<>3@Inz1IHY<8r~7CEsu<) converted to rad: + return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); +} + +// general sun calculations + +function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } + +function eclipticLongitude(M) { + + var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center + P = rad * 102.9372; // perihelion of the Earth + + return M + C + P + PI; +} + +function sunCoords(d) { + + var M = solarMeanAnomaly(d), + L = eclipticLongitude(M); + + return { + dec: declination(L, 0), + ra: rightAscension(L, 0) + }; +} + + +var SunCalc = {}; + + +// calculates sun position for a given date and latitude/longitude + +SunCalc.getPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + + c = sunCoords(d), + H = siderealTime(d, lw) - c.ra; + + return { + azimuth: azimuth(H, phi, c.dec), + altitude: altitude(H, phi, c.dec) + }; +}; + + +// sun times configuration (angle, morning name, evening name) + +var times = SunCalc.times = [ + [-0.833, 'sunrise', 'sunset' ], + [ -0.3, 'sunriseEnd', 'sunsetStart' ], + [ -6, 'dawn', 'dusk' ], + [ -12, 'nauticalDawn', 'nauticalDusk'], + [ -18, 'nightEnd', 'night' ], + [ 6, 'goldenHourEnd', 'goldenHour' ] +]; + +// adds a custom time to the times config + +SunCalc.addTime = function (angle, riseName, setName) { + times.push([angle, riseName, setName]); +}; + + +// calculations for sun times + +var J0 = 0.0009; + +function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); } + +function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; } +function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); } + +function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); } +function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; } + +// returns set time for the given sun altitude +function getSetJ(h, lw, phi, dec, n, M, L) { + + var w = hourAngle(h, phi, dec), + a = approxTransit(w, lw, n); + return solarTransitJ(a, M, L); +} + + +// calculates sun times for a given date, latitude/longitude, and, optionally, +// the observer height (in meters) relative to the horizon + +SunCalc.getTimes = function (date, lat, lng, height) { + + height = height || 0; + + var lw = rad * -lng, + phi = rad * lat, + + dh = observerAngle(height), + + d = toDays(date), + n = julianCycle(d, lw), + ds = approxTransit(0, lw, n), + + M = solarMeanAnomaly(ds), + L = eclipticLongitude(M), + dec = declination(L, 0), + + Jnoon = solarTransitJ(ds, M, L), + + i, len, time, h0, Jset, Jrise; + + + var result = { + solarNoon: new Date(fromJulian(Jnoon)), + nadir: new Date(fromJulian(Jnoon - 0.5)) + }; + + for (i = 0, len = times.length; i < len; i += 1) { + time = times[i]; + h0 = (time[0] + dh) * rad; + + Jset = getSetJ(h0, lw, phi, dec, n, M, L); + Jrise = Jnoon - (Jset - Jnoon); + + result[time[1]] = new Date(fromJulian(Jrise) - (dayMs / 2)); + result[time[2]] = new Date(fromJulian(Jset) + (dayMs / 2)); + } + + return result; +}; + + +// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas + +function moonCoords(d) { // geocentric ecliptic coordinates of the moon + + var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude + M = rad * (134.963 + 13.064993 * d), // mean anomaly + F = rad * (93.272 + 13.229350 * d), // mean distance + + l = L + rad * 6.289 * sin(M), // longitude + b = rad * 5.128 * sin(F), // latitude + dt = 385001 - 20905 * cos(M); // distance to the moon in km + + return { + ra: rightAscension(l, b), + dec: declination(l, b), + dist: dt + }; +} + +SunCalc.getMoonPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + + c = moonCoords(d), + H = siderealTime(d, lw) - c.ra, + h = altitude(H, phi, c.dec), + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); + + h = h + astroRefraction(h); // altitude correction for refraction + + return { + azimuth: azimuth(H, phi, c.dec), + altitude: h, + distance: c.dist, + parallacticAngle: pa + }; +}; + + +// calculations for illumination parameters of the moon, +// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and +// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + +// Function updated from gist: https://gist.github.com/endel/dfe6bb2fbe679781948c + +SunCalc.getMoonIllumination = function (date) { + let month = date.getMonth(); + let year = date.getFullYear(); + let day = date.getDate(); + + let c = 0; + let e = 0; + let jd = 0; + let b = 0; + + if (month < 3) { + year--; + month += 12; + } + + ++month; + c = 365.25 * year; + e = 30.6 * month; + jd = c + e + day - 694039.09; // jd is total days elapsed + jd /= 29.5305882; // divide by the moon cycle + b = parseInt(jd); // int(jd) -> b, take integer part of jd + jd -= b; // subtract integer part to leave fractional part of original jd + b = Math.round(jd * 8); // scale fraction from 0-8 and round + + if (b >= 8) b = 0; // 0 and 8 are the same so turn 8 into 0 + + return {phase: b}; +}; + + +function hoursLater(date, h) { + return new Date(date.valueOf() + h * dayMs / 24); +} + +// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article + +SunCalc.getMoonTimes = function (date, lat, lng, inUTC) { + var t = date; + if (inUTC) t.setUTCHours(0, 0, 0, 0); + else t.setHours(0, 0, 0, 0); + + var hc = 0.133 * rad, + h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, + h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (var i = 1; i <= 24; i += 2) { + h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; + h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; + + a = (h0 + h2) / 2 - h1; + b = (h2 - h0) / 2; + xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) roots++; + if (Math.abs(x2) <= 1) roots++; + if (x1 < -1) x1 = x2; + } + + if (roots === 1) { + if (h0 < 0) rise = i + x1; + else set = i + x1; + + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + + if (rise && set) break; + + h0 = h2; + } + + var result = {}; + + if (rise) result.rise = hoursLater(t, rise); + if (set) result.set = hoursLater(t, set); + + if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; + + return result; +}; + + +// export as Node module / AMD module / browser variable +if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc; +else if (typeof define === 'function' && define.amd) define(SunCalc); +else global.SunCalc = SunCalc; + +}()); diff --git a/apps/astrocalc/waning-crescent-icon.json b/apps/astrocalc/waning-crescent-icon.json new file mode 100644 index 000000000..802a79fda --- /dev/null +++ b/apps/astrocalc/waning-crescent-icon.json @@ -0,0 +1,7 @@ + +{ + "width": 92, + "height": 92, + "bpp": 1, + "image": "AH4AI8AKJvgKJj+ABREH/AWJ/4KJn44Jg/+BREB//AEJP8EJP/J5P/J5MfCxMD//wCxF/MxI4BCxP/MxI4BPpA4BCxM/PpI4BPpJPBCxEPCxX/CxJPBShDBBShJPBCxBmBC0BmBCxB9BYRIWBYRAWLBQIWISgIW/C34WTW6wWNfhE/fhMDC0MfCxMBCxR+B/iVK4BnJCxJ+BCxBQBCxJnB8BQJ/wWIKAIWIHIQKIKALDIHIQKIHIKVIHISVIHIIWJOYJ+IYgJ+JEQJnJgZ+JIoJnJEQJQJgJQJRYJQJgEOBRIA/AAIA=" +} \ No newline at end of file diff --git a/apps/astrocalc/waning-gibbous-icon.json b/apps/astrocalc/waning-gibbous-icon.json new file mode 100644 index 000000000..5b7c52c41 --- /dev/null +++ b/apps/astrocalc/waning-gibbous-icon.json @@ -0,0 +1,6 @@ +{ + "width": 92, + "height": 92, + "bpp": 1, + "image": "AGMB/+ABRP+CxH///wBQ9/BYIiHj4KB/gKGg4KB//gG44ACCw0/BQQ5GEIY5GEIn4EJP/4AhJKAwKDKAxlCAAKKFgYWEKAqHCM444ECwwKEM4o4EM4o4FCwpxEM4o4FPwpPFCwkPCxQKFPwhPFSojBECyJmGCwhmFCxasEMwqsEPowWbBQwWDSgwW/C34WOZ1s/CxMDBQv+CxXgBQUBCxUfCyp+GCwaVG/gKDM43APxIWEM4wWEM4v4BQZQGwBQJCwg5GBQhQFVYY5HBQg5FPog5FSgg5GMwhzFPojEGMwgiFMwoiFBQoiEMwoiEJ4xoEJ4qLEHAx0EBQ46CRAoLEEJAAqA=" +} \ No newline at end of file diff --git a/apps/astrocalc/waxing-crescent-icon.json b/apps/astrocalc/waxing-crescent-icon.json new file mode 100644 index 000000000..55d950730 --- /dev/null +++ b/apps/astrocalc/waxing-crescent-icon.json @@ -0,0 +1,6 @@ +{ + "width": 92, + "height": 92, + "bpp": 1, + "image": "AH4ALuAKJgfgBZMfwAKIgP+CxMP+ALJv/ABREHERU//BQJ/4iJj4iJgP/IpMf/5oJ//8M5P/RZP/HJMHHJV/HJIWBHJM/OZJ+BHJIWBKBAWB/4WKKBCVBKBKVBYhAWCKBAWBM5AWZM5DDBPxIWYPxAWBSpIW/C34WLZz78BCxD8C4AWJ/gWV/AWKwAWSgZ9Kn6UJCwJ9JCwJ9Ig5mJgF/MxIWBJ5AWCBRDCBHBDCBJ5IWBHBCUBHBKUBBRAWBHBM/OJKUBYJBmBEJMHEJJmBBRMfEJMBMpMAnwKJg4KJAHw=" +} \ No newline at end of file diff --git a/apps/astrocalc/waxing-gibbous-icon.json b/apps/astrocalc/waxing-gibbous-icon.json new file mode 100644 index 000000000..652710aca --- /dev/null +++ b/apps/astrocalc/waxing-gibbous-icon.json @@ -0,0 +1,7 @@ +{ + "width": 92, + "height": 92, + "bpp": 1, + "transparent": 0, + "image": "AGUf/AKIg//BZP/BZMfBYPgBQ0DBQP/wALGv4KB/wKGh4WCFw4KC//AHBAiHgIWDEQ0/ERJPDEQ5PCAAPwRAwADNAoKE/hmIRY4KEHIpmDHIxmEHIwWFHIhmEOYx9EHIoWGKAgWF/59JKAqUEKAoWGYggWRM4gWGM4arFCwoKFM4gWGPwgWdPwYWGSogW/C34WQZ1vABQUHBQv8Cwd/CyH4CxWACyoKDgaUJgE/MxIWGPoYWGMwkBMxMAj5PJCwwKDVgw4EVgpPECww4EPwo4ESowKEM4o4FPwhxEM4zBDM4whFKAghGM4ghFKAghGKAZlFHIiHFHIosGRQYKIEQIKIAFIA=" +} \ No newline at end of file From eb2450a177b0f9a2f23dd291538cb130cb897063 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 2 Apr 2020 12:12:27 +0100 Subject: [PATCH 04/40] Fix moon icon positioning --- apps/astrocalc/astrocalc-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/astrocalc/astrocalc-app.js b/apps/astrocalc/astrocalc-app.js index 655544723..c1a29219d 100644 --- a/apps/astrocalc/astrocalc-app.js +++ b/apps/astrocalc/astrocalc-app.js @@ -18,7 +18,7 @@ function drawMoon(phase, x, y) { imgObj = require("Storage").readJSON(moonImgFiles[phase]); imgObj.buffer = require("heatshrink").decompress(atob(imgObj.image)); - g.drawImage(imgObj, x /*- (imgObj.width / 2)*/, y /*- (imgObj.height)*/); + g.drawImage(imgObj, x - (imgObj.width / 2), y); } // linear interpolation between two values a and b From fe0f189455e2eebf55ded7a890a2be598e1e51dd Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 2 Apr 2020 12:15:10 +0100 Subject: [PATCH 05/40] Re-add tests to take screen shots of app for PR --- apps/astrocalc/astrocalc-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/astrocalc/astrocalc-app.js b/apps/astrocalc/astrocalc-app.js index c1a29219d..6819a6917 100644 --- a/apps/astrocalc/astrocalc-app.js +++ b/apps/astrocalc/astrocalc-app.js @@ -310,7 +310,7 @@ function drawGPSWaitPage() { g.drawString("Please wait...", 80, 155); g.flip(); - const DEBUG = false; + const DEBUG = true; if (DEBUG) { const gps = { "lat": 56.45783133333, From 538abc003ab8225de53ce22d330175616f60d5e5 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 2 Apr 2020 12:23:17 +0100 Subject: [PATCH 06/40] Revert test mode --- apps/astrocalc/astrocalc-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/astrocalc/astrocalc-app.js b/apps/astrocalc/astrocalc-app.js index 6819a6917..c1a29219d 100644 --- a/apps/astrocalc/astrocalc-app.js +++ b/apps/astrocalc/astrocalc-app.js @@ -310,7 +310,7 @@ function drawGPSWaitPage() { g.drawString("Please wait...", 80, 155); g.flip(); - const DEBUG = true; + const DEBUG = false; if (DEBUG) { const gps = { "lat": 56.45783133333, From bcece63915b9c45c4f16bcb7e6c97eb7c38ce8c9 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 2 Apr 2020 13:45:27 +0100 Subject: [PATCH 07/40] Load images in an efficient manner --- apps.json | 16 ++++++++-------- apps/astrocalc/astrocalc-app.js | 22 +++++++++++----------- apps/astrocalc/first-quarter-icon.js | 1 + apps/astrocalc/first-quarter-icon.json | 6 ------ apps/astrocalc/full-icon.js | 1 + apps/astrocalc/full-icon.json | 7 ------- apps/astrocalc/last-quarter-icon.js | 1 + apps/astrocalc/last-quarter-icon.json | 7 ------- apps/astrocalc/new-icon.js | 1 + apps/astrocalc/new-icon.json | 6 ------ apps/astrocalc/waning-crescent-icon.js | 1 + apps/astrocalc/waning-crescent-icon.json | 7 ------- apps/astrocalc/waning-gibbous-icon.js | 1 + apps/astrocalc/waning-gibbous-icon.json | 6 ------ apps/astrocalc/waxing-crescent-icon.js | 1 + apps/astrocalc/waxing-crescent-icon.json | 6 ------ apps/astrocalc/waxing-gibbous-icon.js | 1 + apps/astrocalc/waxing-gibbous-icon.json | 7 ------- 18 files changed, 27 insertions(+), 71 deletions(-) create mode 100644 apps/astrocalc/first-quarter-icon.js delete mode 100644 apps/astrocalc/first-quarter-icon.json create mode 100644 apps/astrocalc/full-icon.js delete mode 100644 apps/astrocalc/full-icon.json create mode 100644 apps/astrocalc/last-quarter-icon.js delete mode 100644 apps/astrocalc/last-quarter-icon.json create mode 100644 apps/astrocalc/new-icon.js delete mode 100644 apps/astrocalc/new-icon.json create mode 100644 apps/astrocalc/waning-crescent-icon.js delete mode 100644 apps/astrocalc/waning-crescent-icon.json create mode 100644 apps/astrocalc/waning-gibbous-icon.js delete mode 100644 apps/astrocalc/waning-gibbous-icon.json create mode 100644 apps/astrocalc/waxing-crescent-icon.js delete mode 100644 apps/astrocalc/waxing-crescent-icon.json create mode 100644 apps/astrocalc/waxing-gibbous-icon.js delete mode 100644 apps/astrocalc/waxing-gibbous-icon.json diff --git a/apps.json b/apps.json index ea334a0b4..90d50ccaa 100644 --- a/apps.json +++ b/apps.json @@ -963,14 +963,14 @@ {"name":"astrocalc.app.js","url":"astrocalc-app.js"}, {"name":"suncalc.js","url":"suncalc.js"}, {"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true}, - {"name":"first-quarter-icon.json","url":"first-quarter-icon.json"}, - {"name":"last-quarter-icon.json","url":"last-quarter-icon.json"}, - {"name":"waning-crescent-icon.json","url":"waning-crescent-icon.json"}, - {"name":"waning-gibbous-icon.json","url":"waning-gibbous-icon.json"}, - {"name":"full-icon.json","url":"full-icon.json"}, - {"name":"new-icon.json","url":"new-icon.json"}, - {"name":"waxing-gibbous-icon.json","url":"waxing-gibbous-icon.json"}, - {"name":"waxing-crescent-icon.json","url":"waxing-crescent-icon.json"} + {"name":"first-quarter.img","url":"first-quarter-icon.js","evaluate":true}, + {"name":"last-quarter.img","url":"last-quarter-icon.js","evaluate":true}, + {"name":"waning-crescent.img","url":"waning-crescent-icon.js","evaluate":true}, + {"name":"waning-gibbous.img","url":"waning-gibbous-icon.js","evaluate":true}, + {"name":"full.img","url":"full-icon.js","evaluate":true}, + {"name":"new.img","url":"new-icon.js","evaluate":true}, + {"name":"waxing-gibbous.img","url":"waxing-gibbous-icon.js","evaluate":true}, + {"name":"waxing-crescent.img","url":"waxing-crescent-icon.js","evaluate":true} ] } ] diff --git a/apps/astrocalc/astrocalc-app.js b/apps/astrocalc/astrocalc-app.js index c1a29219d..318147b13 100644 --- a/apps/astrocalc/astrocalc-app.js +++ b/apps/astrocalc/astrocalc-app.js @@ -6,19 +6,19 @@ const SunCalc = require("suncalc.js"); function drawMoon(phase, x, y) { const moonImgFiles = [ - "new-icon.json", - "waxing-crescent-icon.json", - "first-quarter-icon.json", - "waxing-gibbous-icon.json", - "full-icon.json", - "waning-gibbous-icon.json", - "last-quarter-icon.json", - "waning-crescent-icon.json" + "new", + "waxing-crescent", + "first-quarter", + "waxing-gibbous", + "full", + "waning-gibbous", + "last-quarter", + "waning-crescent", ]; - imgObj = require("Storage").readJSON(moonImgFiles[phase]); - imgObj.buffer = require("heatshrink").decompress(atob(imgObj.image)); - g.drawImage(imgObj, x - (imgObj.width / 2), y); + img = require("Storage").read(`${moonImgFiles[phase]}.img`); + // image width & height = 92px + g.drawImage(img, x - parseInt(92 / 2), y); } // linear interpolation between two values a and b diff --git a/apps/astrocalc/first-quarter-icon.js b/apps/astrocalc/first-quarter-icon.js new file mode 100644 index 000000000..e726c5d37 --- /dev/null +++ b/apps/astrocalc/first-quarter-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AGUD/ALJn4LJgf/+AWJ//gCxP/wAWJ/wWKHRAWB//ACxIiICwQiHCwQiICwQiHCwZ0HCwRoHCwf8CxSLGCwY5HCwY5GCwY5HCwY5GCwhzGCwY5GCwhQGCwf/CxRQGCwhQFCwjEGCx5nGCwhnFCyBnFCwh+GCzZ+FCwiVGC34W/CxzOt4AWJ/gWV/AWKwAWVBQoWESgoWEMwwWEPooWEMwwWEMwoWEJ4wWEBQoWEHAwWDJ4wWEHAwWDHAwWEBQwWDHAwWDOIwWEYIoWEEI4WCEI4WDEI4WCEI4WCMo4WCQ44WDBRAWBG44WCBRIAo")); \ No newline at end of file diff --git a/apps/astrocalc/first-quarter-icon.json b/apps/astrocalc/first-quarter-icon.json deleted file mode 100644 index b0018a433..000000000 --- a/apps/astrocalc/first-quarter-icon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "width": 92, - "height": 92, - "bpp": 1, - "image": "AGUD/ALJn4LJgf/+AWJ//gCxP/wAWJ/wWKHRAWB//ACxIiICwQiHCwQiICwQiHCwZ0HCwRoHCwf8CxSLGCwY5HCwY5GCwY5HCwY5GCwhzGCwY5GCwhQGCwf/CxRQGCwhQFCwjEGCx5nGCwhnFCyBnFCwh+GCzZ+FCwiVGC34W/CxzOt4AWJ/gWV/AWKwAWVBQoWESgoWEMwwWEPooWEMwwWEMwoWEJ4wWEBQoWEHAwWDJ4wWEHAwWDHAwWEBQwWDHAwWDOIwWEYIoWEEI4WCEI4WDEI4WCEI4WCMo4WCQ44WDBRAWBG44WCBRIAo" -} \ No newline at end of file diff --git a/apps/astrocalc/full-icon.js b/apps/astrocalc/full-icon.js new file mode 100644 index 000000000..11aaadbbb --- /dev/null +++ b/apps/astrocalc/full-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AH4AfgP//AKJBZIKB//wBQ0/BYXABQsPBQX/BQsDBQf8BYt/BYfgBQkfBQf/wAhIEQwhEEQpDEIopwCAAZ0EMoZoGg4KF/yHGAAaLDLQpcEHAw5EHAw5DHAw5EOIpzEOIxzEBQ5QCJ45QCJ45QDJ45QCYIrEFCxRmHM4ZmHM4QWWPpB+CBRB+BC34W/CxLOtgYKH/gWBn4LH4AWMj4LHwEAgIKH/AWBPxAKBPxB9BM5BmCM5BmBKBBPCKBBPBKBBPCKBAKCHI44CHI44DHI44COY5xCOY5xDYg7BBAARcFLQYiGEIoiFEIhFFIYhoFMoiLGBQx0DOAgLFBRAA/AC4A==")); \ No newline at end of file diff --git a/apps/astrocalc/full-icon.json b/apps/astrocalc/full-icon.json deleted file mode 100644 index 2dc5c59aa..000000000 --- a/apps/astrocalc/full-icon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "width": 92, - "height": 92, - "bpp": 1, - "transparent": 0, - "image": "AH4AfgP//AKJBZIKB//wBQ0/BYXABQsPBQX/BQsDBQf8BYt/BYfgBQkfBQf/wAhIEQwhEEQpDEIopwCAAZ0EMoZoGg4KF/yHGAAaLDLQpcEHAw5EHAw5DHAw5EOIpzEOIxzEBQ5QCJ45QCJ45QDJ45QCYIrEFCxRmHM4ZmHM4QWWPpB+CBRB+BC34W/CxLOtgYKH/gWBn4LH4AWMj4LHwEAgIKH/AWBPxAKBPxB9BM5BmCM5BmBKBBPCKBBPBKBBPCKBAKCHI44CHI44DHI44COY5xCOY5xDYg7BBAARcFLQYiGEIoiFEIhFFIYhoFMoiLGBQx0DOAgLFBRAA/AC4A==" -} \ No newline at end of file diff --git a/apps/astrocalc/last-quarter-icon.js b/apps/astrocalc/last-quarter-icon.js new file mode 100644 index 000000000..1de62e58a --- /dev/null +++ b/apps/astrocalc/last-quarter-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AGMB/gKJ//ABZH/CxN/CxMfCxMH/4WIG4IWJn4WJEIQWHEIQWIEIQWHEIQWIBQQWHMoIWIgYWKQ4IWIHAYWHBQQWHHAYWGHAYWHOIQWHHAYWHJ4YWGh4WKBQYWGJ4YWGYIYWSMwgWGMwYWSMwYWGPogWdBQYWGSggW/C34WQZ1s/CxMDCysBCxUfCyp+ECwqVECwxnECwx+DCwxnECwxnDCwxQECwxQDCww5ECwxQDCw45DCww5DCw45DCww5DCw5zDCw7ECCw4iDCw4iCCxAiCCw4iCCxBoCCxCLBCxB0CCxA6BCxILBCxIApA=")); \ No newline at end of file diff --git a/apps/astrocalc/last-quarter-icon.json b/apps/astrocalc/last-quarter-icon.json deleted file mode 100644 index 31e855eac..000000000 --- a/apps/astrocalc/last-quarter-icon.json +++ /dev/null @@ -1,7 +0,0 @@ - -{ - "width": 92, - "height": 92, - "bpp": 1, - "image": "AGMB/gKJ//ABZH/CxN/CxMfCxMH/4WIG4IWJn4WJEIQWHEIQWIEIQWHEIQWIBQQWHMoIWIgYWKQ4IWIHAYWHBQQWHHAYWGHAYWHOIQWHHAYWHJ4YWGh4WKBQYWGJ4YWGYIYWSMwgWGMwYWSMwYWGPogWdBQYWGSggW/C34WQZ1s/CxMDCysBCxUfCyp+ECwqVECwxnECwx+DCwxnECwxnDCwxQECwxQDCww5ECwxQDCw45DCww5DCw45DCww5DCw5zDCw7ECCw4iDCw4iCCxAiCCw4iCCxBoCCxCLBCxB0CCxA6BCxILBCxIApA=" -} \ No newline at end of file diff --git a/apps/astrocalc/new-icon.js b/apps/astrocalc/new-icon.js new file mode 100644 index 000000000..91f7034e7 --- /dev/null +++ b/apps/astrocalc/new-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AAcP///BRQLHg4KC/wKFgIKC//4BYt/BYfgBQkfBQf/wAsHFw4HCBwXwBQc/AwYLB4AhEIARIBEQn//gECgYiEIYJ2FIoQQBE4YzBDgd/NoguBNAUPKoo/BB4YhEEQIdCAYYiECQMHUwwHDEIweBLgMPWIwiBAQSlENwQTBDIQAFFQMDHAw5BOYN/HAwfB8ANCAAofCHA45B+EPHA4UBKQQAGMgMfUYQAFv+DJ45QCn5PHKAPDJ45QB/hmICwPnT4yhC/1/Mw5nBCxZmIM4P/PpB+BC34WEVZCsB/7CIYYIWWOX4WbfiwWL/gKHgf+n/ABY8/4YWJ/k/VhF/4LDIg/4j5nI/+APxEP+EPM48BCgN/KA5CBg5QHMwINCJ4/AgY5Hh4fBj45GHAKeBAQSfFMgIZCHAoqCv45GA4QOBEQsfDwQDDEIgSC/4iFv6dCg4iFj60Dn4iEEIKRCL4K5E/5uDh4QDDgKFEv4uDj4/EE4IRCDYIzEAwIvBAQKnFEQIADMIhFBAAayFNAIACMoZtDBYa9GFwbrHBQR2EBYoKEA=")); \ No newline at end of file diff --git a/apps/astrocalc/new-icon.json b/apps/astrocalc/new-icon.json deleted file mode 100644 index 368c7b376..000000000 --- a/apps/astrocalc/new-icon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "width": 92, - "height": 92, - "bpp": 1, - "image": "AAcP///BRQLHg4KC/wKFgIKC//4BYt/BYfgBQkfBQf/wAsHFw4HCBwXwBQc/AwYLB4AhEIARIBEQn//gECgYiEIYJ2FIoQQBE4YzBDgd/NoguBNAUPKoo/BB4YhEEQIdCAYYiECQMHUwwHDEIweBLgMPWIwiBAQSlENwQTBDIQAFFQMDHAw5BOYN/HAwfB8ANCAAofCHA45B+EPHA4UBKQQAGMgMfUYQAFv+DJ45QCn5PHKAPDJ45QB/hmICwPnT4yhC/1/Mw5nBCxZmIM4P/PpB+BC34WEVZCsB/7CIYYIWWOX4WbfiwWL/gKHgf+n/ABY8/4YWJ/k/VhF/4LDIg/4j5nI/+APxEP+EPM48BCgN/KA5CBg5QHMwINCJ4/AgY5Hh4fBj45GHAKeBAQSfFMgIZCHAoqCv45GA4QOBEQsfDwQDDEIgSC/4iFv6dCg4iFj60Dn4iEEIKRCL4K5E/5uDh4QDDgKFEv4uDj4/EE4IRCDYIzEAwIvBAQKnFEQIADMIhFBAAayFNAIACMoZtDBYa9GFwbrHBQR2EBYoKEA=" -} \ No newline at end of file diff --git a/apps/astrocalc/waning-crescent-icon.js b/apps/astrocalc/waning-crescent-icon.js new file mode 100644 index 000000000..e142dc47b --- /dev/null +++ b/apps/astrocalc/waning-crescent-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AH4AI8AKJvgKJj+ABREH/AWJ/4KJn44Jg/+BREB//AEJP8EJP/J5P/J5MfCxMD//wCxF/MxI4BCxP/MxI4BPpA4BCxM/PpI4BPpJPBCxEPCxX/CxJPBShDBBShJPBCxBmBC0BmBCxB9BYRIWBYRAWLBQIWISgIW/C34WTW6wWNfhE/fhMDC0MfCxMBCxR+B/iVK4BnJCxJ+BCxBQBCxJnB8BQJ/wWIKAIWIHIQKIKALDIHIQKIHIKVIHISVIHIIWJOYJ+IYgJ+JEQJnJgZ+JIoJnJEQJQJgJQJRYJQJgEOBRIA/AAIA=")); \ No newline at end of file diff --git a/apps/astrocalc/waning-crescent-icon.json b/apps/astrocalc/waning-crescent-icon.json deleted file mode 100644 index 802a79fda..000000000 --- a/apps/astrocalc/waning-crescent-icon.json +++ /dev/null @@ -1,7 +0,0 @@ - -{ - "width": 92, - "height": 92, - "bpp": 1, - "image": "AH4AI8AKJvgKJj+ABREH/AWJ/4KJn44Jg/+BREB//AEJP8EJP/J5P/J5MfCxMD//wCxF/MxI4BCxP/MxI4BPpA4BCxM/PpI4BPpJPBCxEPCxX/CxJPBShDBBShJPBCxBmBC0BmBCxB9BYRIWBYRAWLBQIWISgIW/C34WTW6wWNfhE/fhMDC0MfCxMBCxR+B/iVK4BnJCxJ+BCxBQBCxJnB8BQJ/wWIKAIWIHIQKIKALDIHIQKIHIKVIHISVIHIIWJOYJ+IYgJ+JEQJnJgZ+JIoJnJEQJQJgJQJRYJQJgEOBRIA/AAIA=" -} \ No newline at end of file diff --git a/apps/astrocalc/waning-gibbous-icon.js b/apps/astrocalc/waning-gibbous-icon.js new file mode 100644 index 000000000..0939d0454 --- /dev/null +++ b/apps/astrocalc/waning-gibbous-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AGMB/+ABRP+CxH///wBQ9/BYIiHj4KB/gKGg4KB//gG44ACCw0/BQQ5GEIY5GEIn4EJP/4AhJKAwKDKAxlCAAKKFgYWEKAqHCM444ECwwKEM4o4EM4o4FCwpxEM4o4FPwpPFCwkPCxQKFPwhPFSojBECyJmGCwhmFCxasEMwqsEPowWbBQwWDSgwW/C34WOZ1s/CxMDBQv+CxXgBQUBCxUfCyp+GCwaVG/gKDM43APxIWEM4wWEM4v4BQZQGwBQJCwg5GBQhQFVYY5HBQg5FPog5FSgg5GMwhzFPojEGMwgiFMwoiFBQoiEMwoiEJ4xoEJ4qLEHAx0EBQ46CRAoLEEJAAqA=")); \ No newline at end of file diff --git a/apps/astrocalc/waning-gibbous-icon.json b/apps/astrocalc/waning-gibbous-icon.json deleted file mode 100644 index 5b7c52c41..000000000 --- a/apps/astrocalc/waning-gibbous-icon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "width": 92, - "height": 92, - "bpp": 1, - "image": "AGMB/+ABRP+CxH///wBQ9/BYIiHj4KB/gKGg4KB//gG44ACCw0/BQQ5GEIY5GEIn4EJP/4AhJKAwKDKAxlCAAKKFgYWEKAqHCM444ECwwKEM4o4EM4o4FCwpxEM4o4FPwpPFCwkPCxQKFPwhPFSojBECyJmGCwhmFCxasEMwqsEPowWbBQwWDSgwW/C34WOZ1s/CxMDBQv+CxXgBQUBCxUfCyp+GCwaVG/gKDM43APxIWEM4wWEM4v4BQZQGwBQJCwg5GBQhQFVYY5HBQg5FPog5FSgg5GMwhzFPojEGMwgiFMwoiFBQoiEMwoiEJ4xoEJ4qLEHAx0EBQ46CRAoLEEJAAqA=" -} \ No newline at end of file diff --git a/apps/astrocalc/waxing-crescent-icon.js b/apps/astrocalc/waxing-crescent-icon.js new file mode 100644 index 000000000..0756408c6 --- /dev/null +++ b/apps/astrocalc/waxing-crescent-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AH4ALuAKJgfgBZMfwAKIgP+CxMP+ALJv/ABREHERU//BQJ/4iJj4iJgP/IpMf/5oJ//8M5P/RZP/HJMHHJV/HJIWBHJM/OZJ+BHJIWBKBAWB/4WKKBCVBKBKVBYhAWCKBAWBM5AWZM5DDBPxIWYPxAWBSpIW/C34WLZz78BCxD8C4AWJ/gWV/AWKwAWSgZ9Kn6UJCwJ9JCwJ9Ig5mJgF/MxIWBJ5AWCBRDCBHBDCBJ5IWBHBCUBHBKUBBRAWBHBM/OJKUBYJBmBEJMHEJJmBBRMfEJMBMpMAnwKJg4KJAHw=")); \ No newline at end of file diff --git a/apps/astrocalc/waxing-crescent-icon.json b/apps/astrocalc/waxing-crescent-icon.json deleted file mode 100644 index 55d950730..000000000 --- a/apps/astrocalc/waxing-crescent-icon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "width": 92, - "height": 92, - "bpp": 1, - "image": "AH4ALuAKJgfgBZMfwAKIgP+CxMP+ALJv/ABREHERU//BQJ/4iJj4iJgP/IpMf/5oJ//8M5P/RZP/HJMHHJV/HJIWBHJM/OZJ+BHJIWBKBAWB/4WKKBCVBKBKVBYhAWCKBAWBM5AWZM5DDBPxIWYPxAWBSpIW/C34WLZz78BCxD8C4AWJ/gWV/AWKwAWSgZ9Kn6UJCwJ9JCwJ9Ig5mJgF/MxIWBJ5AWCBRDCBHBDCBJ5IWBHBCUBHBKUBBRAWBHBM/OJKUBYJBmBEJMHEJJmBBRMfEJMBMpMAnwKJg4KJAHw=" -} \ No newline at end of file diff --git a/apps/astrocalc/waxing-gibbous-icon.js b/apps/astrocalc/waxing-gibbous-icon.js new file mode 100644 index 000000000..2f6a665e2 --- /dev/null +++ b/apps/astrocalc/waxing-gibbous-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AGUf/AKIg//BZP/BZMfBYPgBQ0DBQP/wALGv4KB/wKGh4WCFw4KC//AHBAiHgIWDEQ0/ERJPDEQ5PCAAPwRAwADNAoKE/hmIRY4KEHIpmDHIxmEHIwWFHIhmEOYx9EHIoWGKAgWF/59JKAqUEKAoWGYggWRM4gWGM4arFCwoKFM4gWGPwgWdPwYWGSogW/C34WQZ1vABQUHBQv8Cwd/CyH4CxWACyoKDgaUJgE/MxIWGPoYWGMwkBMxMAj5PJCwwKDVgw4EVgpPECww4EPwo4ESowKEM4o4FPwhxEM4zBDM4whFKAghGM4ghFKAghGKAZlFHIiHFHIosGRQYKIEQIKIAFIA=")); \ No newline at end of file diff --git a/apps/astrocalc/waxing-gibbous-icon.json b/apps/astrocalc/waxing-gibbous-icon.json deleted file mode 100644 index 652710aca..000000000 --- a/apps/astrocalc/waxing-gibbous-icon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "width": 92, - "height": 92, - "bpp": 1, - "transparent": 0, - "image": "AGUf/AKIg//BZP/BZMfBYPgBQ0DBQP/wALGv4KB/wKGh4WCFw4KC//AHBAiHgIWDEQ0/ERJPDEQ5PCAAPwRAwADNAoKE/hmIRY4KEHIpmDHIxmEHIwWFHIhmEOYx9EHIoWGKAgWF/59JKAqUEKAoWGYggWRM4gWGM4arFCwoKFM4gWGPwgWdPwYWGSogW/C34WQZ1vABQUHBQv8Cwd/CyH4CxWACyoKDgaUJgE/MxIWGPoYWGMwkBMxMAj5PJCwwKDVgw4EVgpPECww4EPwo4ESowKEM4o4FPwhxEM4zBDM4whFKAghGM4ghFKAghGKAZlFHIiHFHIosGRQYKIEQIKIAFIA=" -} \ No newline at end of file From 9727f68447f500ffdc9613ce0955b03690fa86dc Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Thu, 2 Apr 2020 14:00:28 +0100 Subject: [PATCH 08/40] Update image to be compressed as image string --- apps/astrocalc/first-quarter-icon.js | 2 +- apps/astrocalc/full-icon.js | 2 +- apps/astrocalc/last-quarter-icon.js | 2 +- apps/astrocalc/new-icon.js | 2 +- apps/astrocalc/waning-crescent-icon.js | 2 +- apps/astrocalc/waning-gibbous-icon.js | 2 +- apps/astrocalc/waxing-crescent-icon.js | 2 +- apps/astrocalc/waxing-gibbous-icon.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/astrocalc/first-quarter-icon.js b/apps/astrocalc/first-quarter-icon.js index e726c5d37..d88ec79b5 100644 --- a/apps/astrocalc/first-quarter-icon.js +++ b/apps/astrocalc/first-quarter-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AGUD/ALJn4LJgf/+AWJ//gCxP/wAWJ/wWKHRAWB//ACxIiICwQiHCwQiICwQiHCwZ0HCwRoHCwf8CxSLGCwY5HCwY5GCwY5HCwY5GCwhzGCwY5GCwhQGCwf/CxRQGCwhQFCwjEGCx5nGCwhnFCyBnFCwh+GCzZ+FCwiVGC34W/CxzOt4AWJ/gWV/AWKwAWVBQoWESgoWEMwwWEPooWEMwwWEMwoWEJ4wWEBQoWEHAwWDJ4wWEHAwWDHAwWEBQwWDHAwWDOIwWEYIoWEEI4WCEI4WDEI4WCEI4WCMo4WCQ44WDBRAWBG44WCBRIAo")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgI1ygf4BZM/BZMD//wCxP/8AWJ/+ACxP+CxQ6ICwP/4AWJERAWCEQ4WCERAWCEQ4WDOg4WCNA4WD/gWKRYwWDHI4WDHIwWDHI4WDHIwWEOYwWDHIwWEKAwWD/4WKKAwWEKAoWEYgwWPM4wWEM4oWQM4oWEPwwWbPwoWESowW/C34WOZ1vACxP8Cyv4CxWACyoKFCwiUFCwhmGCwh9FCwhmGCwhmFCwhPGCwgKFCwg4GCwZPGCwg4GCwY4GCwgKGCwY4GCwZxGCwjBFCwghHCwQhHCwYhHCwQhHCwRlHCwSHHCwYKICwI3HCwQKJAFAA==")) \ No newline at end of file diff --git a/apps/astrocalc/full-icon.js b/apps/astrocalc/full-icon.js index 11aaadbbb..8bc04f7fc 100644 --- a/apps/astrocalc/full-icon.js +++ b/apps/astrocalc/full-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AH4AfgP//AKJBZIKB//wBQ0/BYXABQsPBQX/BQsDBQf8BYt/BYfgBQkfBQf/wAhIEQwhEEQpDEIopwCAAZ0EMoZoGg4KF/yHGAAaLDLQpcEHAw5EHAw5DHAw5EOIpzEOIxzEBQ5QCJ45QCJ45QDJ45QCYIrEFCxRmHM4ZmHM4QWWPpB+CBRB+BC34W/CxLOtgYKH/gWBn4LH4AWMj4LHwEAgIKH/AWBPxAKBPxB9BM5BmCM5BmBKBBPCKBBPBKBBPCKBAKCHI44CHI44DHI44COY5xCOY5xDYg7BBAARcFLQYiGEIoiFEIhFFIYhoFMoiLGBQx0DOAgLFBRAA/AC4A==")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgJC/AD8B//4BRILJBQP/+AKGn4LC4AKFh4KC/4KFgYKD/gLFv4LD8AKEj4KD/+AEJAiGEIgiFIYhFFOAQADOghlDNA0HBQv+Q4wADRYZaFLgg4GHIg4GHIY4GHIhxFOYhxGOYgKHKARPHKARPHKAZPHKATBFYgoWKMw5nDMw5nCCyx9IPwQKIPwIW/C34WJZ1sDBQ/8CwM/BY/ACxkfBY+AgEBBQ/4CwJ+IBQJ+IPoJnIMwRnIMwJQIJ4RQIJ4JQIJ4RQIBQQ5HHAQ5HHAY5HHARzHOIRzHOIbEHYIIACLgpaDEQwhFEQohEIopDENAplERYwKGOgZwEBYoKIAH4AXA==")) \ No newline at end of file diff --git a/apps/astrocalc/last-quarter-icon.js b/apps/astrocalc/last-quarter-icon.js index 1de62e58a..b6517f66b 100644 --- a/apps/astrocalc/last-quarter-icon.js +++ b/apps/astrocalc/last-quarter-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AGMB/gKJ//ABZH/CxN/CxMfCxMH/4WIG4IWJn4WJEIQWHEIQWIEIQWHEIQWIBQQWHMoIWIgYWKQ4IWIHAYWHBQQWHHAYWGHAYWHOIQWHHAYWHJ4YWGh4WKBQYWGJ4YWGYIYWSMwgWGMwYWSMwYWGPogWdBQYWGSggW/C34WQZ1s/CxMDCysBCxUfCyp+ECwqVECwxnECwx+DCwxnECwxnDCwxQECwxQDCww5ECwxQDCw45DCww5DCw45DCww5DCw5zDCw7ECCw4iDCw4iCCxAiCCw4iCCxBoCCxCLBCxB0CCxA6BCxILBCxIApA=")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgI0xgP8BRP/4ALI/4WJv4WJj4WJg//CxA3BCxM/CxIhCCw4hCCxAhCCw4hCCxAKCCw5lBCxEDCxSHBCxA4DCw4KCCw44DCww4DCw5xCCw44DCw5PDCw0PCxQKDCwxPDCwzBDCyRmECwxmDCyRmDCwx9ECzoKDCwyUEC34W/CyDOtn4WJgYWVgIWKj4WVPwgWFSogWGM4gWGPwYWGM4gWGM4YWGKAgWGKAYWGHIgWGKAYWHHIYWGHIYWHHIYWGHIYWHOYYWHYgQWHEQYWHEQQWIEQQWHEQQWINAQWIRYIWIOgQWIHQIWJBYIWJAFI=")) \ No newline at end of file diff --git a/apps/astrocalc/new-icon.js b/apps/astrocalc/new-icon.js index 91f7034e7..5d610fbe1 100644 --- a/apps/astrocalc/new-icon.js +++ b/apps/astrocalc/new-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AAcP///BRQLHg4KC/wKFgIKC//4BYt/BYfgBQkfBQf/wAsHFw4HCBwXwBQc/AwYLB4AhEIARIBEQn//gECgYiEIYJ2FIoQQBE4YzBDgd/NoguBNAUPKoo/BB4YhEEQIdCAYYiECQMHUwwHDEIweBLgMPWIwiBAQSlENwQTBDIQAFFQMDHAw5BOYN/HAwfB8ANCAAofCHA45B+EPHA4UBKQQAGMgMfUYQAFv+DJ45QCn5PHKAPDJ45QB/hmICwPnT4yhC/1/Mw5nBCxZmIM4P/PpB+BC34WEVZCsB/7CIYYIWWOX4WbfiwWL/gKHgf+n/ABY8/4YWJ/k/VhF/4LDIg/4j5nI/+APxEP+EPM48BCgN/KA5CBg5QHMwINCJ4/AgY5Hh4fBj45GHAKeBAQSfFMgIZCHAoqCv45GA4QOBEQsfDwQDDEIgSC/4iFv6dCg4iFj60Dn4iEEIKRCL4K5E/5uDh4QDDgKFEv4uDj4/EE4IRCDYIzEAwIvBAQKnFEQIADMIhFBAAayFNAIACMoZtDBYa9GFwbrHBQR2EBYoKEA=")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgIGDh///4RHBQQLHg4KC/wKFgIKC//4BYt/BYfgBQkfBQf/wAsHFw4HCBwXwBQc/AwYLB4AhEIARIBEQn//gECgYiEIYJ2FIoQQBE4YzBDgd/NoguBNAUPKoo/BB4YhEEQIdCAYYiECQMHUwwHDEIweBLgMPWIwiBAQSlENwQTBDIQAFFQMDHAw5BOYN/HAwfB8ANCAAofCHA45B+EPHA4UBKQQAGMgMfUYQAFv+DJ45QCn5PHKAPDJ45QB/hmICwPnT4yhC/1/Mw5nBCxZmIM4P/PpB+BC34WEVZCsB/7CIYYIWWOX4WbfiwWL/gKHgf+n/ABY8/4YWJ/k/VhF/4LDIg/4j5nI/+APxEP+EPM48BCgN/KA5CBg5QHMwINCJ4/AgY5Hh4fBj45GHAKeBAQSfFMgIZCHAoqCv45GA4QOBEQsfDwQDDEIgSC/4iFv6dCg4iFj60Dn4iEEIKRCL4K5E/5uDh4QDDgKFEv4uDj4/EE4IRCDYIzEAwIvBAQKnFEQIADMIhFBAAayFNAIACMoZtDBYa9GFwbrHBQR2EBYoKEA==")) \ No newline at end of file diff --git a/apps/astrocalc/waning-crescent-icon.js b/apps/astrocalc/waning-crescent-icon.js index e142dc47b..8ff83ab1f 100644 --- a/apps/astrocalc/waning-crescent-icon.js +++ b/apps/astrocalc/waning-crescent-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AH4AI8AKJvgKJj+ABREH/AWJ/4KJn44Jg/+BREB//AEJP8EJP/J5P/J5MfCxMD//wCxF/MxI4BCxP/MxI4BPpA4BCxM/PpI4BPpJPBCxEPCxX/CxJPBShDBBShJPBCxBmBC0BmBCxB9BYRIWBYRAWLBQIWISgIW/C34WTW6wWNfhE/fhMDC0MfCxMBCxR+B/iVK4BnJCxJ+BCxBQBCxJnB8BQJ/wWIKAIWIHIQKIKALDIHIQKIHIKVIHISVIHIIWJOYJ+IYgJ+JEQJnJgZ+JIoJnJEQJQJgJQJRYJQJgEOBRIA/AAIA=")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgJC/ABHgBRN8BRMfwAKIg/4CxP/BRM/HBMH/wKIgP/4AhJ/ghJ/5PJ/5PJj4WJgf/+AWIv5mJHAIWJ/5mJHAJ9IHAIWJn59JHAJ9JJ4IWIh4WK/4WJJ4KUIYIKUJJ4IWIMwIWgMwIWIPoLCJCwLCICxYKBCxCUBC34W/Cya3WCxr8In78JgYWhj4WJgIWKPwP8SpXAM5IWJPwIWIKAIWJM4PgKBP+CxBQBCxA5CBRBQBYZA5CBRA5BSpA5CSpA5BCxJzBPxDEBPxIiBM5MDPxJFBM5IiBKBMBKBKLBKBMAhwKJAH4ABA=")) \ No newline at end of file diff --git a/apps/astrocalc/waning-gibbous-icon.js b/apps/astrocalc/waning-gibbous-icon.js index 0939d0454..2373475f4 100644 --- a/apps/astrocalc/waning-gibbous-icon.js +++ b/apps/astrocalc/waning-gibbous-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AGMB/+ABRP+CxH///wBQ9/BYIiHj4KB/gKGg4KB//gG44ACCw0/BQQ5GEIY5GEIn4EJP/4AhJKAwKDKAxlCAAKKFgYWEKAqHCM444ECwwKEM4o4EM4o4FCwpxEM4o4FPwpPFCwkPCxQKFPwhPFSojBECyJmGCwhmFCxasEMwqsEPowWbBQwWDSgwW/C34WOZ1s/CxMDBQv+CxXgBQUBCxUfCyp+GCwaVG/gKDM43APxIWEM4wWEM4v4BQZQGwBQJCwg5GBQhQFVYY5HBQg5FPog5FSgg5GMwhzFPojEGMwgiFMwoiFBQoiEMwoiEJ4xoEJ4qLEHAx0EBQ46CRAoLEEJAAqA=")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgI0xgP/wAKJ/wWI///+AKHv4LBEQ8fBQP8BQ0HBQP/8A3HAAQWGn4KCHIwhDHIwhE/AhJ//AEJJQGBQZQGMoQABRQsDCwhQFQ4RnHHAgWGBQhnFHAhnFHAoWFOIhnFHAp+FJ4oWEh4WKBQp+EJ4qVEYIgWRMwwWEMwoWLVghmFVgh9GCzYKGCwaUGC34W/CxzOtn4WJgYKF/wWK8AKCgIWKj4WVPwwWDSo38BQZnG4B+JCwhnGCwhnF/AKDKA2AKBIWEHIwKEKAqrDHI4KEHIp9EHIqUEHIxmEOYp9EYgxmEEQpmFEQoKFEQhmFEQhPGNAhPFRYg4GOggKHHQSIFBYghIAFQ=")) \ No newline at end of file diff --git a/apps/astrocalc/waxing-crescent-icon.js b/apps/astrocalc/waxing-crescent-icon.js index 0756408c6..d89525c88 100644 --- a/apps/astrocalc/waxing-crescent-icon.js +++ b/apps/astrocalc/waxing-crescent-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AH4ALuAKJgfgBZMfwAKIgP+CxMP+ALJv/ABREHERU//BQJ/4iJj4iJgP/IpMf/5oJ//8M5P/RZP/HJMHHJV/HJIWBHJM/OZJ+BHJIWBKBAWB/4WKKBCVBKBKVBYhAWCKBAWBM5AWZM5DDBPxIWYPxAWBSpIW/C34WLZz78BCxD8C4AWJ/gWV/AWKwAWSgZ9Kn6UJCwJ9JCwJ9Ig5mJgF/MxIWBJ5AWCBRDCBHBDCBJ5IWBHBCUBHBKUBBRAWBHBM/OJKUBYJBmBEJMHEJJmBBRMfEJMBMpMAnwKJg4KJAHw=")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgJC/ABdwBRMD8ALJj+ABREB/wWJh/wBZN/4AKIg4iKn/4KBP/ERMfERMB/5FJj//NBP//hnJ/6LJ/45Jg45Kv45JCwI5Jn5zJPwI5JCwJQICwP/CxRQISoJQJSoLEICwRQICwJnICzJnIYYJ+JCzB+ICwKVJC34W/CxbOffgIWIfgXACxP8Cyv4CxWACyUDPpU/ShIWBPpIWBPpEHMxMAv5mJCwJPICwQKIYQI4IYQJPJCwI4ISgI4JSgIKICwI4Jn5xJSgLBIMwIhJg4hJMwIKJj4hJgJlJgE+BRMHBRIA+A")) \ No newline at end of file diff --git a/apps/astrocalc/waxing-gibbous-icon.js b/apps/astrocalc/waxing-gibbous-icon.js index 2f6a665e2..90ccd6f37 100644 --- a/apps/astrocalc/waxing-gibbous-icon.js +++ b/apps/astrocalc/waxing-gibbous-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AGUf/AKIg//BZP/BZMfBYPgBQ0DBQP/wALGv4KB/wKGh4WCFw4KC//AHBAiHgIWDEQ0/ERJPDEQ5PCAAPwRAwADNAoKE/hmIRY4KEHIpmDHIxmEHIwWFHIhmEOYx9EHIoWGKAgWF/59JKAqUEKAoWGYggWRM4gWGM4arFCwoKFM4gWGPwgWdPwYWGSogW/C34WQZ1vABQUHBQv8Cwd/CyH4CxWACyoKDgaUJgE/MxIWGPoYWGMwkBMxMAj5PJCwwKDVgw4EVgpPECww4EPwo4ESowKEM4o4FPwhxEM4zBDM4whFKAghGM4ghFKAghGKAZlFHIiHFHIosGRQYKIEQIKIAFIA=")); \ No newline at end of file +require("heatshrink").decompress(atob("rlcgI1yj/4BREH/4LJ/4LJj4LB8AKGgYKB/+ABY1/BQP+BQ0PCwQuHBQX/4A4IEQ8BCwYiGn4iJJ4YiHJ4QAB+CIGAAZoFBQn8MxCLHBQg5FMwY5GMwg5GCwo5EMwhzGPog5FCwxQECwv/PpJQFSghQFCwzEECyJnECwxnDVYoWFBQpnECwx+ECzp+DCwyVEC34W/CyDOt4AKCg4KF/gWDv4WQ/AWKwAWVBQcDShMAn5mJCwx9DCwxmEgJmJgEfJ5IWGBQasGHAisFJ4gWGHAh+FHAiVGBQhnFHAp+EOIhnGYIZnGEIpQEEIxnEEIpQEEIxQDMoo5EQ4o5FFgyKDBRAiBBRAApA=")) \ No newline at end of file From 8d84d4d0dd331f17fdd6cb0e30f2647f77b64a3b Mon Sep 17 00:00:00 2001 From: Christian Hemker Date: Thu, 2 Apr 2020 19:30:12 +0200 Subject: [PATCH 09/40] refactor, btn2 for today --- apps/moonphase/app.js | 570 +++++++++++++++++++++--------------------- 1 file changed, 288 insertions(+), 282 deletions(-) diff --git a/apps/moonphase/app.js b/apps/moonphase/app.js index ecd4be05d..0049bf91d 100644 --- a/apps/moonphase/app.js +++ b/apps/moonphase/app.js @@ -3,294 +3,300 @@ //pictures function getImg(i) { - var data = { - "NewMoon": "AD8AAH/4AHwPgDwA8BwADg4AAcMAADHAAA5gAAGYAABsAAAPAAADwAAA8AAAPAAADwAAA2AAAZgAAGcAADjAAAw4AAcHAAOA8APAHwPgAf/gAA/AAA==", - "WaxingCrescentNorth" : "AD8AAH/4AHw/gDwH8BwA/g4AH8MAB/HAAf5gAD+YAA/sAAP/AAD/wAA/8AAP/AAD/wAA/2AAP5gAD+cAB/jAAfw4AH8HAD+A8B/AHw/gAf/gAA/AAA==", - "WaningCrescentSouth" : "AD8AAH/4AHw/gDwH8BwA/g4AH8MAB/HAAf5gAD+YAA/sAAP/AAD/wAA/8AAP/AAD/wAA/2AAP5gAD+cAB/jAAfw4AH8HAD+A8B/AHw/gAf/gAA/AAA==", - "FirstQuarterNorth" : "AD8AAH/4AHx/gDwf8BwH/g4B/8MAf/HAH/5gB/+YAf/sAH//AB//wAf/8AH//AB//wAf/2AH/5gB/+cAf/jAH/w4B/8HAf+A8H/AHx/gAf/gAA/AAA==", - "FirstQuarterSouth" : "AD8AAH/4AH+PgD/g8B/4Dg/+AcP/gDH/4A5/+AGf/gBv/4AP/+AD//gA//4AP/+AD//gA3/4AZ/+AGf/gDj/4Aw/+AcH/gOA/4PAH+PgAf/gAA/AAA==", - "WaxingGibbousNorth" : "AD8AAH/4AH3/gDz/8Bw//g4f/8MH//HB//5g//+YP//sD///A///wP//8D///A///wP//2D//5g//+cH//jB//w4f/8HD/+A8//AH3/gAf/gAA/AAA==", - "WaxingGibbousSouth" : "AD8AAH/4AH/vgD/88B//Dg//4cP/+DH//g5//8Gf//Bv//wP//8D///A///wP//8D///A3//wZ//8Gf/+Dj//gw//4cH/8OA//PAH/vgAf/gAA/AAA==", - "FullMoon" : "AD8AAH/4AH//gD//8B///g///8P///H///5///+f///v/////////////////////////3///5///+f///j///w///8H//+A///AH//gAf/gAA/AAA==", - "WaningGibbousNorth" : "AD8AAH/4AH/vgD/88B//Dg//4cP/+DH//g5//8Gf//Bv//wP//8D///A///wP//8D///A3//wZ//8Gf/+Dj//gw//4cH/8OA//PAH/vgAf/gAA/AAA==", - "WaningGibbousSouth" : "AD8AAH/4AH3/gDz/8Bw//g4f/8MH//HB//5g//+YP//sD///A///wP//8D///A///wP//2D//5g//+cH//jB//w4f/8HD/+A8//AH3/gAf/gAA/AAA==", - "LastQuarterNorth" : "AD8AAH/4AH+PgD/g8B/4Dg/+AcP/gDH/4A5/+AGf/gBv/4AP/+AD//gA//4AP/+AD//gA3/4AZ/+AGf/gDj/4Aw/+AcH/gOA/4PAH+PgAf/gAA/AAA==", - "LastQuarterSouth" : "AD8AAH/4AHx/gDwf8BwH/g4B/8MAf/HAH/5gB/+YAf/sAH//AB//wAf/8AH//AB//wAf/2AH/5gB/+cAf/jAH/w4B/8HAf+A8H/AHx/gAf/gAA/AAA==", - "WaningCrescentNorth" : "AD8AAH/4AH8PgD+A8B/ADg/gAcP4ADH+AA5/AAGfwABv8AAP/AAD/wAA/8AAP/AAD/wAA38AAZ/AAGf4ADj+AAw/gAcH8AOA/gPAH8PgAf/gAA/AAA==", - "WaxingCrescentSouth" : "AD8AAH/4AH8PgD+A8B/ADg/gAcP4ADH+AA5/AAGfwABv8AAP/AAD/wAA/8AAP/AAD/wAA38AAZ/AAGf4ADj+AAw/gAcH8AOA/gPAH8PgAf/gAA/AAA==" -}; - return { - width : 26, height : 26, bpp : 1, - transparent : 0, - buffer : E.toArrayBuffer(atob(data[i])) + var data = { + "NewMoon": "AD8AAH/4AHwPgDwA8BwADg4AAcMAADHAAA5gAAGYAABsAAAPAAADwAAA8AAAPAAADwAAA2AAAZgAAGcAADjAAAw4AAcHAAOA8APAHwPgAf/gAA/AAA==", + "WaxingCrescentNorth" : "AD8AAH/4AHw/gDwH8BwA/g4AH8MAB/HAAf5gAD+YAA/sAAP/AAD/wAA/8AAP/AAD/wAA/2AAP5gAD+cAB/jAAfw4AH8HAD+A8B/AHw/gAf/gAA/AAA==", + "WaningCrescentSouth" : "AD8AAH/4AHw/gDwH8BwA/g4AH8MAB/HAAf5gAD+YAA/sAAP/AAD/wAA/8AAP/AAD/wAA/2AAP5gAD+cAB/jAAfw4AH8HAD+A8B/AHw/gAf/gAA/AAA==", + "FirstQuarterNorth" : "AD8AAH/4AHx/gDwf8BwH/g4B/8MAf/HAH/5gB/+YAf/sAH//AB//wAf/8AH//AB//wAf/2AH/5gB/+cAf/jAH/w4B/8HAf+A8H/AHx/gAf/gAA/AAA==", + "FirstQuarterSouth" : "AD8AAH/4AH+PgD/g8B/4Dg/+AcP/gDH/4A5/+AGf/gBv/4AP/+AD//gA//4AP/+AD//gA3/4AZ/+AGf/gDj/4Aw/+AcH/gOA/4PAH+PgAf/gAA/AAA==", + "WaxingGibbousNorth" : "AD8AAH/4AH3/gDz/8Bw//g4f/8MH//HB//5g//+YP//sD///A///wP//8D///A///wP//2D//5g//+cH//jB//w4f/8HD/+A8//AH3/gAf/gAA/AAA==", + "WaxingGibbousSouth" : "AD8AAH/4AH/vgD/88B//Dg//4cP/+DH//g5//8Gf//Bv//wP//8D///A///wP//8D///A3//wZ//8Gf/+Dj//gw//4cH/8OA//PAH/vgAf/gAA/AAA==", + "FullMoon" : "AD8AAH/4AH//gD//8B///g///8P///H///5///+f///v/////////////////////////3///5///+f///j///w///8H//+A///AH//gAf/gAA/AAA==", + "WaningGibbousNorth" : "AD8AAH/4AH/vgD/88B//Dg//4cP/+DH//g5//8Gf//Bv//wP//8D///A///wP//8D///A3//wZ//8Gf/+Dj//gw//4cH/8OA//PAH/vgAf/gAA/AAA==", + "WaningGibbousSouth" : "AD8AAH/4AH3/gDz/8Bw//g4f/8MH//HB//5g//+YP//sD///A///wP//8D///A///wP//2D//5g//+cH//jB//w4f/8HD/+A8//AH3/gAf/gAA/AAA==", + "LastQuarterNorth" : "AD8AAH/4AH+PgD/g8B/4Dg/+AcP/gDH/4A5/+AGf/gBv/4AP/+AD//gA//4AP/+AD//gA3/4AZ/+AGf/gDj/4Aw/+AcH/gOA/4PAH+PgAf/gAA/AAA==", + "LastQuarterSouth" : "AD8AAH/4AHx/gDwf8BwH/g4B/8MAf/HAH/5gB/+YAf/sAH//AB//wAf/8AH//AB//wAf/2AH/5gB/+cAf/jAH/w4B/8HAf+A8H/AHx/gAf/gAA/AAA==", + "WaningCrescentNorth" : "AD8AAH/4AH8PgD+A8B/ADg/gAcP4ADH+AA5/AAGfwABv8AAP/AAD/wAA/8AAP/AAD/wAA38AAZ/AAGf4ADj+AAw/gAcH8AOA/gPAH8PgAf/gAA/AAA==", + "WaxingCrescentSouth" : "AD8AAH/4AH8PgD+A8B/ADg/gAcP4ADH+AA5/AAGfwABv8AAP/AAD/wAA/8AAP/AAD/wAA38AAZ/AAGf4ADj+AAw/gAcH8AOA/gPAH8PgAf/gAA/AAA==" }; -} - - //coordinates (will get from GPS later on real device) - var lat = 52.96236, - lon = 7.62571; - - var PI = Math.PI, - sin = Math.sin, - cos = Math.cos, - tan = Math.tan, - asin = Math.asin, - atan = Math.atan2, - acos = Math.acos, - rad = PI / 180; - - // sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas - // date/time constants and conversions - var dayMs = 1000 * 60 * 60 * 24, - J1970 = 2440588, - J2000 = 2451545; - - function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } - function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } - function toDays(date) { return toJulian(date) - J2000; } - - // general calculations for position - var e = rad * 23.4397; // obliquity of the Earth - function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } - function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } - function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } - function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } - function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } - function astroRefraction(h) { - if (h < 0) // the following formula works for positive altitudes only. - h = 0; // if h = -0.08901179 a div/0 would occur. - - // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: - return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); + return { + width : 26, height : 26, bpp : 1, + transparent : 0, + buffer : E.toArrayBuffer(atob(data[i])) + }; } - - // general sun calculations - function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } - function eclipticLongitude(M) { - - var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center - P = rad * 102.9372; // perihelion of the Earth - - return M + C + P + PI; - } - - function sunCoords(d) { - - var M = solarMeanAnomaly(d), - L = eclipticLongitude(M); - - return { - dec: declination(L, 0), - ra: rightAscension(L, 0) - }; - } - - var SunCalc = {}; - - // adds a custom time to the times config - SunCalc.addTime = function (angle, riseName, setName) { - times.push([angle, riseName, setName]); - }; - - // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas - function moonCoords(d) { // geocentric ecliptic coordinates of the moon - var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude - M = rad * (134.963 + 13.064993 * d), // mean anomaly - F = rad * (93.272 + 13.229350 * d), // mean distance - l = L + rad * 6.289 * sin(M), // longitude - b = rad * 5.128 * sin(F), // latitude - dt = 385001 - 20905 * cos(M); // distance to the moon in km - - return { - ra: rightAscension(l, b), - dec: declination(l, b), - dist: dt - }; - } - - SunCalc.getMoonPosition = function (date, lat, lng) { - - var lw = rad * -lng, - phi = rad * lat, - d = toDays(date), - c = moonCoords(d), - H = siderealTime(d, lw) - c.ra, - h = altitude(H, phi, c.dec), - // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); - h = h + astroRefraction(h); // altitude correction for refraction - return { - azimuth: azimuth(H, phi, c.dec), - altitude: h, - distance: c.dist, - parallacticAngle: pa - }; - }; - - // calculations for illumination parameters of the moon, - // based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and - // Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - - SunCalc.getMoonIllumination = function (date) { - var year = date.getFullYear(); - var month = date.getMonth(); - var day = date.getDate(); - var Moon = { - phases: ['new', 'waxing-crescent', 'first-quarter', 'waxing-gibbous', 'full', 'waning-gibbous', 'last-quarter', 'waning-crescent'], - phase: function (year, month, day) { - let c = 0; - let e = 0; - let jd = 0; - let b = 0; - if (month < 3) { - year--; - month += 12; - } - ++month; - c = 365.25 * year; - e = 30.6 * month; - jd = c + e + day - 694039.09; // jd is total days elapsed - jd /= 29.5305882; // divide by the moon cycle - b = parseInt(jd); // int(jd) -> b, take integer part of jd - jd -= b; // subtract integer part to leave fractional part of original jd - b = Math.round(jd * 8); // scale fraction from 0-8 and round - if (b >= 8) b = 0; // 0 and 8 are the same so turn 8 into 0 - //print ({phase: b, name: Moon.phases[b]}); - return {phase: b, name: Moon.phases[b]}; + + //coordinates (will get from GPS later on real device) + var lat = 52.96236, + lon = 7.62571; + + var PI = Math.PI, + sin = Math.sin, + cos = Math.cos, + tan = Math.tan, + asin = Math.asin, + atan = Math.atan2, + acos = Math.acos, + rad = PI / 180; + + // sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas + // date/time constants and conversions + var dayMs = 1000 * 60 * 60 * 24, + J1970 = 2440588, + J2000 = 2451545; + + function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } + function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } + function toDays(date) { return toJulian(date) - J2000; } + + // general calculations for position + var e = rad * 23.4397; // obliquity of the Earth + function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } + function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } + function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } + function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } + function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } + function astroRefraction(h) { + if (h < 0) // the following formula works for positive altitudes only. + h = 0; // if h = -0.08901179 a div/0 would occur. + + // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: + return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); } - }; - return (Moon.phase(year, month, day)); - }; - - function hoursLater(date, h) { - return new Date(date.valueOf() + h * dayMs / 24); - } - - // calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article - - SunCalc.getMoonTimes = function (date, lat, lng, inUTC) { - var t = new Date(date); - if (inUTC) t.setUTCHours(0, 0, 0, 0); - else t.setHours(0, 0, 0, 0); - var hc = 0.133 * rad, - h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, - h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; - - // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) - for (var i = 1; i <= 24; i += 2) { - h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; - h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; - a = (h0 + h2) / 2 - h1; - b = (h2 - h0) / 2; - xe = -b / (2 * a); - ye = (a * xe + b) * xe + h1; - d = b * b - 4 * a * h1; - roots = 0; - if (d >= 0) { - dx = Math.sqrt(d) / (Math.abs(a) * 2); - x1 = xe - dx; - x2 = xe + dx; - if (Math.abs(x1) <= 1) roots++; - if (Math.abs(x2) <= 1) roots++; - if (x1 < -1) x1 = x2; - } - if (roots === 1) { - if (h0 < 0) rise = i + x1; - else set = i + x1; - } else if (roots === 2) { - rise = i + (ye < 0 ? x2 : x1); - set = i + (ye < 0 ? x1 : x2); - } - if (rise && set) break; - h0 = h2; - } - var result = {}; - if (rise) result.rise = hoursLater(t, rise); - if (set) result.set = hoursLater(t, set); - if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; - return result; - }; - - function getMPhaseComp (offset) { - var date = new Date(); - date.setDate(date.getDate() + offset); - var dd = String(date.getDate()); - if(dd<10){dd='0'+dd;} - var mm = String(date.getMonth() + 1); - if(mm<10){mm='0'+mm;} - var yyyy = date.getFullYear(); - var phase = SunCalc.getMoonIllumination(date); - return dd + "." + mm + "." + yyyy + ": "+ phase.name; - } - - function getMPhaseSim (offset) { - var date = new Date(); - date.setDate(date.getDate() + offset); - var dd = String(date.getDate()); - if(dd<10){dd='0'+dd;} - var mm = String(date.getMonth() + 1); - if(mm<10){mm='0'+mm;} - var yyyy = date.getFullYear(); - var phase = SunCalc.getMoonIllumination(date); - return phase.name; - } - - function drawMoonPhase(offset, x, y){ - if (lat >= 0 && lat <= 90){ //Northern hemisphere - if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} - if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentNorth"), x, y);} - if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterNorth"), x, y);} - if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousNorth"), x, y);} - if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} - if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousNorth"), x, y);} - if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterNorth"), x, y);} - if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentNorth"), x, y);} - } - else { //Southern hemisphere - if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} - if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentSouth"), x, y);} - if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterSouth"), x, y);} - if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousSouth"), x, y);} - if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} - if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousSouth"), x, y);} - if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterSouth"), x, y);} - if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentSouth"), x, y);} + + // general sun calculations + function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } + function eclipticLongitude(M) { + + var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center + P = rad * 102.9372; // perihelion of the Earth + + return M + C + P + PI; } - } - - function drawMoon(offset, x, y) { - g.setFont("6x8"); - g.clear(); - g.drawString("Key1: increase day, Key3:decrease day",10,10); - g.drawString(getMPhaseComp(offset),x,y-10); - drawMoonPhase(offset, x, y); - g.drawString(getMPhaseComp(offset+2),x,y+40); - drawMoonPhase(offset+2, x, y+50); + function sunCoords(d) { - g.drawString(getMPhaseComp(offset+4),x,y+90); - drawMoonPhase(offset+4, x, y+100); + var M = solarMeanAnomaly(d), + L = eclipticLongitude(M); - g.drawString(getMPhaseComp(offset+6),x,y+140); - drawMoonPhase(offset+6, x, y+150); - } + return { + dec: declination(L, 0), + ra: rightAscension(L, 0) + }; + } + + var SunCalc = {}; + + // adds a custom time to the times config + SunCalc.addTime = function (angle, riseName, setName) { + times.push([angle, riseName, setName]); + }; + + // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas + function moonCoords(d) { // geocentric ecliptic coordinates of the moon + var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude + M = rad * (134.963 + 13.064993 * d), // mean anomaly + F = rad * (93.272 + 13.229350 * d), // mean distance + l = L + rad * 6.289 * sin(M), // longitude + b = rad * 5.128 * sin(F), // latitude + dt = 385001 - 20905 * cos(M); // distance to the moon in km + + return { + ra: rightAscension(l, b), + dec: declination(l, b), + dist: dt + }; + } + + SunCalc.getMoonPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + c = moonCoords(d), + H = siderealTime(d, lw) - c.ra, + h = altitude(H, phi, c.dec), + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); + h = h + astroRefraction(h); // altitude correction for refraction + return { + azimuth: azimuth(H, phi, c.dec), + altitude: h, + distance: c.dist, + parallacticAngle: pa + }; + }; + + // calculations for illumination parameters of the moon, + // based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and + // Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + + SunCalc.getMoonIllumination = function (date) { + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var Moon = { + phases: ['new', 'waxing-crescent', 'first-quarter', 'waxing-gibbous', 'full', 'waning-gibbous', 'last-quarter', 'waning-crescent'], + phase: function (year, month, day) { + let c = 0; + let e = 0; + let jd = 0; + let b = 0; + if (month < 3) { + year--; + month += 12; + } + ++month; + c = 365.25 * year; + e = 30.6 * month; + jd = c + e + day - 694039.09; // jd is total days elapsed + jd /= 29.5305882; // divide by the moon cycle + b = parseInt(jd); // int(jd) -> b, take integer part of jd + jd -= b; // subtract integer part to leave fractional part of original jd + b = Math.round(jd * 8); // scale fraction from 0-8 and round + if (b >= 8) b = 0; // 0 and 8 are the same so turn 8 into 0 + //print ({phase: b, name: Moon.phases[b]}); + return {phase: b, name: Moon.phases[b]}; + } + }; + return (Moon.phase(year, month, day)); + }; + + function hoursLater(date, h) { + return new Date(date.valueOf() + h * dayMs / 24); + } + + // calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article + + SunCalc.getMoonTimes = function (date, lat, lng, inUTC) { + var t = new Date(date); + if (inUTC) t.setUTCHours(0, 0, 0, 0); + else t.setHours(0, 0, 0, 0); + var hc = 0.133 * rad, + h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, + h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (var i = 1; i <= 24; i += 2) { + h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; + h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; + a = (h0 + h2) / 2 - h1; + b = (h2 - h0) / 2; + xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) roots++; + if (Math.abs(x2) <= 1) roots++; + if (x1 < -1) x1 = x2; + } + if (roots === 1) { + if (h0 < 0) rise = i + x1; + else set = i + x1; + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + if (rise && set) break; + h0 = h2; + } + var result = {}; + if (rise) result.rise = hoursLater(t, rise); + if (set) result.set = hoursLater(t, set); + if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; + return result; + }; + + function getMPhaseComp (offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + var dd = String(date.getDate()); + if(dd<10){dd='0'+dd;} + var mm = String(date.getMonth() + 1); + if(mm<10){mm='0'+mm;} + var yyyy = date.getFullYear(); + var phase = SunCalc.getMoonIllumination(date); + return dd + "." + mm + "." + yyyy + ": "+ phase.name; + } + + function getMPhaseSim (offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + var dd = String(date.getDate()); + if(dd<10){dd='0'+dd;} + var mm = String(date.getMonth() + 1); + if(mm<10){mm='0'+mm;} + var yyyy = date.getFullYear(); + var phase = SunCalc.getMoonIllumination(date); + return phase.name; + } + + function drawMoonPhase(offset, x, y){ + if (lat >= 0 && lat <= 90){ //Northern hemisphere + if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} + if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentNorth"), x, y);} + if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterNorth"), x, y);} + if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousNorth"), x, y);} + if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} + if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousNorth"), x, y);} + if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterNorth"), x, y);} + if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentNorth"), x, y);} + } + else { //Southern hemisphere + if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} + if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentSouth"), x, y);} + if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterSouth"), x, y);} + if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousSouth"), x, y);} + if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} + if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousSouth"), x, y);} + if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterSouth"), x, y);} + if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentSouth"), x, y);} + } + } + + function drawMoon(offset, x, y) { + g.setFont("6x8"); + g.clear(); + g.drawString("Key1: day+, Key2:today, Key3:day-",x,y-30); + g.drawString(getMPhaseComp(offset),x,y+30); + drawMoonPhase(offset, x+35, y+40); + + g.drawString(getMPhaseComp(offset+2),x,y+70); + drawMoonPhase(offset+2, x+35, y+80); + + g.drawString(getMPhaseComp(offset+4),x,y+110); + drawMoonPhase(offset+4, x+35, y+120); + + g.drawString(getMPhaseComp(offset+6),x,y+150); + drawMoonPhase(offset+6, x+35, y+160); + } + + function start() { + var x = 10; + var y = 50; + var offsetMoon = 0; + drawMoon(offsetMoon, x, y); //offset, x, y + + //define button functions + setWatch(function() { + offsetMoon++; //jump to next day + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN1, {edge:"rising", debounce:50, repeat:true}); - function start() { - var x = 10; - var y = 40; - var offsetMoon = 0; - drawMoon(offsetMoon, x, y); //offset, x, y + setWatch(function() { + offsetMoon = 0; //jump to today + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN2, {edge:"rising", debounce:50, repeat:true}); - //define button functions - setWatch(function() { - offsetMoon++; //jump to next day - drawMoon(offsetMoon, x, y); //offset, x, y - }, BTN1, {edge:"rising", debounce:50, repeat:true}); - setWatch(function() { - offsetMoon--; //jump to next day - drawMoon(offsetMoon, x, y); //offset, x, y - }, BTN3, {edge:"rising", debounce:50, repeat:true}); - } - - start(); \ No newline at end of file + setWatch(function() { + offsetMoon--; //jump to next day + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN3, {edge:"rising", debounce:50, repeat:true}); + } + + start(); \ No newline at end of file From ea0cefaa64499fbb3708245bccf7c0da232b1b9a Mon Sep 17 00:00:00 2001 From: ArPhil <32819299+ArPhil@users.noreply.github.com> Date: Thu, 2 Apr 2020 19:56:44 +0200 Subject: [PATCH 10/40] Create app.js --- apps/balltastic/app.js | 186 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 apps/balltastic/app.js diff --git a/apps/balltastic/app.js b/apps/balltastic/app.js new file mode 100644 index 000000000..6c1de940c --- /dev/null +++ b/apps/balltastic/app.js @@ -0,0 +1,186 @@ +Bangle.setLCDBrightness(1); +Bangle.setLCDMode("doublebuffered"); + +let points = 0; +let level = 1; +let levelSpeedStart = 0.8; +let nextLevelPoints = 20; +let levelSpeedFactor = 0.2; +let counterWidth = 10; +let gWidth = g.getWidth() - counterWidth; +let gHeight = g.getHeight(); +let counter = 160; +let counterMax = 160; +let ballDims = 20; +let ballx = g.getWidth() / 2 - ballDims; +let bally = g.getHeight() / 2 - ballDims; +let dotx = g.getWidth() / 2; +let doty = g.getWidth() / 2; +let ballBuzzTime = 5; +let ballSpeedFactor = 40; +let redrawspeed = 5; +let dotwidth = 5; +let running = false; +let drawInterval; +let xBuzzed = false; +let yBuzzed = false; + +let BALL = require("heatshrink").decompress( + atob( + "ikUyAROvkQ3v4405AIYHBGq9KpMhktz1/W7feAJAtBEZ9jhkhs0ZgkQ8lKxW+jAdB516627E4X8AIPWzelmolKlpJBjMFEYIpC4kQ0YBBqWKynTFYPe7gpE3ec6gnHkNFrXL7372u2E4WjhGCAIliqWrUIPeKoIpB7h9HoUoqWq999///FIJ3BhGDEIIBBgFBAoWCoUI3vY62aQIW7ymSJooLBEoIADwkQEYVhEoInEGIOjR4O1y/OrIrBUYdr198iH/74nF88cE4gpCA4MY8k59CzBAINrx2164nBtduufPWYIlF++/xkxNoMAAIJPBoSdB52a30ZkNGE4IvBoUpwkxLIOMyWEmAmE7+MqKbEsLLBH4P3zw1BAYJFBFIMY8sQ4cx44nB0tVHYITBEoO967lDgDDC1tVQ4QBD37xBjMmJ4I3BE4IxBPoOMuSrBHYL1BJYbrDvfPLoYBD889jMlEoMhkpJBwkRE4O+jB7B405LoJPEYYUx0xPG7/3vxvBmOnrXsdIOc6jxBE4JfBvfwHIafDFoMRgh3H99+zsUDIOMqWU2YlBAAO1/AnBToN76EhgpTBFYKPBGIIhBEovOrWliuc2YlBE4oABE4etu2UyVrpqJBMoKvBEIPnjvWze97ATBE4YPBEopRC64BC27nBzn0znTAIOlimtq21y4BCEoM1HYOMqIVBE44AB0tVCYIBEigVBE4U1GYIFBymywkwEoJzHABIRBMIIXBWoIDCqOEmOEiABCmIjPAA51BFoVSEoUwAIIZNA" + ) +); + +function reset() { + g.clear(); + level = 1; + points = 0; + ballx = g.getWidth() / 2 - ballDims; + bally = g.getHeight() / 2 - ballDims; + counter = counterMax; + createRandomDot(); + drawInterval = setInterval(play, redrawspeed); + running = true; +} + +function collide() { + try { + Bangle.buzz(ballBuzzTime, 0.8); + } catch (e) {} +} + +function createRandomDot() { + dotx = Math.floor( + Math.random() * Math.floor(gWidth - dotwidth / 2) + dotwidth / 2 + ); + doty = Math.floor( + Math.random() * Math.floor(gHeight - dotwidth / 2) + dotwidth / 2 + ); +} + +function checkIfDotEaten() { + if ( + ballx + ballDims > dotx && + ballx <= dotx + dotwidth && + bally + ballDims > doty && + bally <= doty + dotwidth + ) { + collide(); + createRandomDot(); + counter = counterMax; + points++; + + if (points % nextLevelPoints == 0) { + level++; + } + } +} + +function drawLevelText() { + g.setColor("#26b6c7"); + g.setFontAlign(0, 0); + g.setFont("4x6", 5); + g.drawString("Level " + level, 120, 80); +} + +function draw() { + //bg + g.setColor("#71c6cf"); + g.fillRect(0, 0, g.getWidth(), g.getHeight()); + + //counter + drawCounter(); + + //draw level + drawLevelText(); + + //dot + g.setColor("#ff0000"); + g.fillCircle(dotx, doty, dotwidth); + + //ball + g.drawImage(BALL, ballx, bally); + + g.flip(); +} + +function drawCounter() { + g.setColor("#000000"); + g.fillRect(g.getWidth() - counterWidth, 0, g.getWidth(), gHeight); + + if(counter < 40 ) g.setColor("#fc0303"); + else if (counter < 80 ) g.setColor("#fc9803"); + else g.setColor("#0318fc"); + + g.fillRect( + g.getWidth() - counterWidth, + gHeight, + g.getWidth(), + gHeight - counter + ); +} + +function checkCollision() { + if (ballx < 0) { + ballx = 0; + if (!xBuzzed) collide(); + xBuzzed = true; + } else if (ballx > gWidth - ballDims) { + ballx = gWidth - ballDims; + if (!xBuzzed) collide(); + xBuzzed = true; + } else { + xBuzzed = false; + } + + if (bally < 0) { + bally = 0; + if (!yBuzzed) collide(); + yBuzzed = true; + } else if (bally > gHeight - ballDims) { + bally = gHeight - ballDims; + if (!yBuzzed) collide(); + yBuzzed = true; + } else { + yBuzzed = false; + } +} + +function count() { + counter -= levelSpeedStart + level * levelSpeedFactor; + if (counter <= 0) { + running = false; + clearInterval(drawInterval); + setTimeout(function(){ E.showMessage("Press Button 1\nto restart.", "Gameover!");},50); + } +} + +function accel(values) { + ballx -= values.x * ballSpeedFactor; + bally -= values.y * ballSpeedFactor; +} + +function play() { + if (running) { + accel(Bangle.getAccel()); + checkCollision(); + checkIfDotEaten(); + count(); + draw(); + } +} + +setTimeout(() => { + reset(); + drawInterval = setInterval(play, redrawspeed); + + setWatch( + () => { + if(!running) reset(); + }, + BTN1, + { repeat: true } + ); + + running = true; +}, 10); From b5a1909dbc3e774e4783a4c1f11644d1f180feeb Mon Sep 17 00:00:00 2001 From: ArPhil <32819299+ArPhil@users.noreply.github.com> Date: Thu, 2 Apr 2020 19:57:08 +0200 Subject: [PATCH 11/40] Add files via upload --- apps/balltastic/app.png | Bin 0 -> 14809 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/balltastic/app.png diff --git a/apps/balltastic/app.png b/apps/balltastic/app.png new file mode 100644 index 0000000000000000000000000000000000000000..0f95e056f0285e8d1413cb6fa63ce6770265b853 GIT binary patch literal 14809 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 literal 0 HcmV?d00001 From 05a14ed2a2e2da1a3f12c7c5a1ec6f30dffec132 Mon Sep 17 00:00:00 2001 From: ArPhil <32819299+ArPhil@users.noreply.github.com> Date: Thu, 2 Apr 2020 19:58:29 +0200 Subject: [PATCH 12/40] Create app-icon.js --- apps/balltastic/app-icon.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/balltastic/app-icon.js diff --git a/apps/balltastic/app-icon.js b/apps/balltastic/app-icon.js new file mode 100644 index 000000000..f25b6e067 --- /dev/null +++ b/apps/balltastic/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkEogAIkUzmciBpIVIkYWBAAUyCx0hiIXFAAMkCxhUBC4fzDAYWLiAXFAAP//5KKoMRC4UTC4k/DAPzJJERiKcCC5H/GA4uBWwp6DC4YwHCwMBDI0SMAYwHoIWBiXxdIwYCMJCjBiM/C46VDC4M/GAkRgMf//ySAQAEgKrDC4lBgMCHIQXHSwxICIwIuBAAIXIGAYABiQXBkEBTYcgC473FkQXBiETTQZ4IgECC4cholCiJGDMAIXIWgIXCmMkC4JGDJBbEDC4UACwn/mAtGSYsxilCgIXFSAqDBkMRiIFBkcxiUiC4sxXowIBC4QGBkIXBiJ2EFwsDBIPyC4ILBgMRiUyiCmJgSCC+YXDgAXDR4YuEcAn/MAIXEmcgBoXyFwjIEMAQXFkIOCUgoXF+J3CC4cxBwR1IQQx3BkUzmUSBQKkFC5IuBkVDJAJeGRwLhHFwUkC4Mxl6lFC48gFwYXCmcTOwomBC4swYIMikU0C4UxkJ3FC40xFoIXCogXBmaxDC5MyCwUiogXDmIXTJASSBC4kRU4oXDkgXFmQwDNwIWEBoIXFJAYKBZggWFC4YWCC4g7BkIWBkYWBBYYXCkYXDJAYjDkQUEEYZGEGA4XIIwwwGDAQuOGAomCFo4uGGARoBE4ZOGFxAABBwgAICxAABCyxJBGJJFJJRgVNPggsMA=")) From 3ae9750152d0bf31c039970824a4913e26152db8 Mon Sep 17 00:00:00 2001 From: ArPhil <32819299+ArPhil@users.noreply.github.com> Date: Thu, 2 Apr 2020 20:05:02 +0200 Subject: [PATCH 13/40] Update apps.json --- apps.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps.json b/apps.json index b088865d2..467f6dd28 100644 --- a/apps.json +++ b/apps.json @@ -973,5 +973,18 @@ "storage": [ {"name":"widhwt.wid.js","url":"widget.js"} ] + }, + { + "id": "balltastic", + "name": "Balltastic", + "icon": "app.png", + "version": "0.01", + "description": "Simple but fun ball eats dots game.", + "tags": "game,fun", + "type": "app", + "storage": [ + {"name":"balltastic.app.js","url":"app.js"}, + {"name":"balltastic.img","url":"app-icon.js","evaluate":true} + ] } ] From 8ff0ba0c598ff1d434bf2871df5bfb00088b034e Mon Sep 17 00:00:00 2001 From: ArPhil <32819299+ArPhil@users.noreply.github.com> Date: Thu, 2 Apr 2020 20:09:24 +0200 Subject: [PATCH 14/40] Create ChangeLog --- apps/balltastic/ChangeLog | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/balltastic/ChangeLog diff --git a/apps/balltastic/ChangeLog b/apps/balltastic/ChangeLog new file mode 100644 index 000000000..5a62086c2 --- /dev/null +++ b/apps/balltastic/ChangeLog @@ -0,0 +1 @@ +0.01: Initial version of Balltastic released! Happy! From 44a94c80587851e9825d35f91fe10c7805310e5a Mon Sep 17 00:00:00 2001 From: Dimitri Gigot Date: Thu, 2 Apr 2020 18:36:19 +0000 Subject: [PATCH 15/40] App: Toucher - Touch enabled launcher --- apps.json | 13 ++++ apps/toucher/ChangeLog | 1 + apps/toucher/app.js | 150 +++++++++++++++++++++++++++++++++++++++++ apps/toucher/app.png | Bin 0 -> 899 bytes 4 files changed, 164 insertions(+) create mode 100644 apps/toucher/ChangeLog create mode 100644 apps/toucher/app.js create mode 100644 apps/toucher/app.png diff --git a/apps.json b/apps.json index b088865d2..723b7093f 100644 --- a/apps.json +++ b/apps.json @@ -973,5 +973,18 @@ "storage": [ {"name":"widhwt.wid.js","url":"widget.js"} ] + }, + { "id": "toucher", + "name": "Touch Launcher", + "shortName":"Menu", + "icon": "app.png", + "version":"0.01", + "description": "Touch enable left to right launcher.", + "tags": "tool,system,launcher", + "type":"launch", + "storage": [ + {"name":"toucher.app.js","url":"app.js"} + ], + "sortorder" : -10 } ] diff --git a/apps/toucher/ChangeLog b/apps/toucher/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/toucher/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/toucher/app.js b/apps/toucher/app.js new file mode 100644 index 000000000..abeb78092 --- /dev/null +++ b/apps/toucher/app.js @@ -0,0 +1,150 @@ +g.clear(); + +var Storage = require("Storage"); + + +function getApps(){ + return Storage.list(/\.info$/).filter(app => app.endsWith('.info')).map(app => Storage.readJSON(app,1) || { name: "DEAD: "+app.substr(1) }) + .filter(app=>app.type=="app" || app.type=="clock" || !app.type) + .sort((a,b)=>{ + var n=(0|a.sortorder)-(0|b.sortorder); + if (n) return n; // do sortorder first + if (a.nameb.name) return 1; + return 0; + }); +} + +var selected = 0; +var menuScroll = 0; +var menuShowing = false; +var apps = getApps(); +var displayBack = false; + +function prev(){ + if (selected>=0) { + selected--; + } + drawMenu(); +} + +function next() { + if (selected+1=n+menuScroll) menuScroll = 1+selected-n; + if (selectedn+menuScroll) g.fillPoly([120,239,100,219,140,219]); + else g.clearRect(100,219,140,239); + for (var i=0;iEZi8a;XNfX0KK)EG5t2&jmXSR!CR3JOvSF^w(VT@Oq6O4&}^ElAua zX|l63`{wtXcjoO3*x1-CYovub^cOBY?cfcO1>;+VoWa;>Pk;!SG_WW*Es5gZ1-`R@ z4t>oKYJ)f#xYia)IV)#&xZ*BHYck+F1K9eED0&f|F;Lm{VL;rb?(c)W{8d& zzrH4`vJJ?>K!fln4K4LjpAx19%+|UjgL{LF?3gt^suTXG8HN@KQv>tY{cLjA%Q)j? zIX0ma_G_?6n>drF(WulAaitj}A^+b$v5k$*zr})O^uogfX;+bphn_9#OZ}pdk^!&c zqrUVgUd3m%o}@|o!cmyJiIaP-*BlK-1F7-kNoN^X5h5L~tnN`_& zI3(jUhvcNZU>jbg3|-gg8hTDln@m+>N(dP^vh_T*8x8>Qb*ytvU&Y#;lz3_Z*tBLl zEghFFmS~QUfhzCr>E}|Fxr&yKOBoXz4vqjF zlzKwtw{idZM95W(Tbb#rO0Wkqaj6$F@MWZp>h=2o=nr;LTzCQiH!$$4i=w<5W87%F zs6NXOGI0OH6?#VB0k9%#HO2Wg(|z6FTj}`r1kmXWJk5wmGlUFGsuA7}JOW^&y9!O$ zkR=&S*XaHEp1^o_Mn#&D^ig6k6`9rJMEHEc@fMjg8GR Z=Px>?AD_-bQjY)t002ovPDHLkV1j2qp7Q_z literal 0 HcmV?d00001 From 63d4dbb101298caf4b25585c67a407af6f9e806b Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Thu, 2 Apr 2020 20:36:58 +0200 Subject: [PATCH 16/40] Inline variables and restore notification code --- apps.json | 6 +- apps/gbridge/gbridge-call-ico.js | 1 - apps/gbridge/gbridge-music-ico.js | 1 - apps/gbridge/gbridge-off-ico.js | 1 - apps/gbridge/gbridge-on-ico.js | 1 - apps/gbridge/widget.js | 271 ++++++++++++------------------ 6 files changed, 111 insertions(+), 170 deletions(-) delete mode 100644 apps/gbridge/gbridge-call-ico.js delete mode 100644 apps/gbridge/gbridge-music-ico.js delete mode 100644 apps/gbridge/gbridge-off-ico.js delete mode 100644 apps/gbridge/gbridge-on-ico.js diff --git a/apps.json b/apps.json index b5350d349..51a8f5af5 100644 --- a/apps.json +++ b/apps.json @@ -72,11 +72,7 @@ "storage": [ {"name":"gbridge.app.js","url":"app.js"}, {"name":"gbridge.img","url":"app-icon.js","evaluate":true}, - {"name":"gbridge.wid.js","url":"widget.js"}, - {"name":"gbridge-music-ico.img","url":"gbridge-music-ico.js","evaluate":true}, - {"name":"gbridge-off-ico.img","url":"gbridge-off-ico.js","evaluate":true}, - {"name":"gbridge-on-ico.img","url":"gbridge-on-ico.js","evaluate":true}, - {"name":"gbridge-call-ico.img","url":"gbridge-call-ico.js","evaluate":true} + {"name":"gbridge.wid.js","url":"widget.js"} ] }, { "id": "mclock", diff --git a/apps/gbridge/gbridge-call-ico.js b/apps/gbridge/gbridge-call-ico.js deleted file mode 100644 index ce722a9a8..000000000 --- a/apps/gbridge/gbridge-call-ico.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw=")) \ No newline at end of file diff --git a/apps/gbridge/gbridge-music-ico.js b/apps/gbridge/gbridge-music-ico.js deleted file mode 100644 index ff8f80883..000000000 --- a/apps/gbridge/gbridge-music-ico.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")) \ No newline at end of file diff --git a/apps/gbridge/gbridge-off-ico.js b/apps/gbridge/gbridge-off-ico.js deleted file mode 100644 index 1a722f372..000000000 --- a/apps/gbridge/gbridge-off-ico.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")) \ No newline at end of file diff --git a/apps/gbridge/gbridge-on-ico.js b/apps/gbridge/gbridge-on-ico.js deleted file mode 100644 index f40c4149f..000000000 --- a/apps/gbridge/gbridge-on-ico.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")) \ No newline at end of file diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index 0b68d3e1b..d60492c20 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -1,130 +1,64 @@ (() => { - const gb = { - musicState: { - STOP: "stop", - PLAY: "play", - PAUSE: "pause" - }, - - muiscControl: { - NEXT: "next", - PREV: "previous" - }, - - callCommands: { - UNDEFINED: "undefined", - ACCEPT: "accept", - INCOMING: "incoming", - OUTGOING: "outgoing", - REJECT: "reject", - START: "start", - END: "end" - }, - - send: (message) => { - Bluetooth.println(JSON.stringify(message)); - }, - - controlMusic: (operation) => { - gb.send({ t: "music", n: operation }); - }, - - reportBatteryLevel: () => { - gb.send({ t: "status", bat: E.getBattery() }); - }, - }; - const state = { - music: gb.musicState.STOP, + music: "stop", musicInfo: { artist: "", album: "", track: "" }, - debug: false, + + scrollPos: 0 }; - const notification = { + function gbSend(message) { + Bluetooth.println(JSON.stringify(message)); + } - backgroundColor: "#222222", - frameColor: "#ffffff", - titleColor: "#40d040", - contentColor: "#ffffff", - scrollPos: 0, + function showNotification(size, render) { + var oldMode = Bangle.getLCDMode(); - show: (size, content) => { - var oldMode = Bangle.getLCDMode(); - Bangle.setLCDMode("direct"); + Bangle.setLCDMode("direct"); + g.setClipRect(0, 240, 239, 319); + g.setColor("#222222"); + g.fillRect(1, 241, 238, 318); - g.setClipRect(0, 240, 239, 319); - g.setColor(notification.backgroundColor); - g.fillRect(1, 241, 238, 318); + render(320 - size); - notification.drawContent(320 - size, content); + g.setColor("#ffffff"); + g.fillRect(0, 240, 1, 319); + g.fillRect(238, 240, 239, 319); + g.fillRect(2, 318, 238, 319); - g.setColor(notification.frameColor); - g.fillRect(0, 240, 1, 319); - g.fillRect(238, 240, 239, 319); - g.fillRect(2, 318, 238, 319); + Bangle.setLCDPower(1); // light up + Bangle.setLCDMode(oldMode); // clears cliprect - Bangle.setLCDPower(1); // light up - Bangle.setLCDMode(oldMode); // clears cliprect - - function anim() { - notification.scrollPos -= 2; - if (notification.scrollPos < -size) notification.scrollPos = -size; - Bangle.setLCDOffset(notification.scrollPos); - if (notification.scrollPos > -size) setTimeout(anim, 10); + function anim() { + state.scrollPos -= 2; + if (state.scrollPos < -size) { + state.scrollPos = -size; } - anim(); - }, - - drawContent: (y, content) => { - - if (content.icon !== undefined) { - g.setColor(notification.contentColor); - const icon = require("Storage").read(content.icon); - g.drawImage(icon, 8, y + 8); - } - - var x = 120; - g.setFontAlign(0, 0); - - g.setFont("6x8", 1); - g.setColor(notification.titleColor); - g.drawString(content.title, x, y + 7); - - g.setColor(notification.contentColor); - g.setFont("6x8", 2); - g.drawString(content.header, x, y + 25); - - g.setFont("6x8", 1); - g.setColor(notification.contentColor); - g.setFontAlign(-1, -1); - g.drawString(content.body, 10, y + 40); - }, - - hide: () => { - function anim() { - notification.scrollPos += 4; - if (notification.scrollPos > 0) notification.scrollPos = 0; - Bangle.setLCDOffset(notification.scrollPos); - if (notification.scrollPos < 0) setTimeout(anim, 10); - } - anim(); - }, - - isVisible: () => { - return notification.scrollPos != 0; + Bangle.setLCDOffset(state.scrollPos); + if (state.scrollPos > -size) setTimeout(anim, 10); } - }; + anim(); + } - function showNotification(src, title, body) { + function hideNotification() { + function anim() { + state.scrollPos += 4; + if (state.scrollPos > 0) state.scrollPos = 0; + Bangle.setLCDOffset(state.scrollPos); + if (state.scrollPos < 0) setTimeout(anim, 10); + } + anim(); + } + + function handleNotificationEvent(event) { // split text up at word boundaries - var txt = body.split("\n"); + var txt = event.body.split("\n"); var MAXCHARS = 38; for (var i = 0; i < txt.length; i++) { txt[i] = txt[i].trim(); @@ -139,98 +73,113 @@ } } - var content = { - title: src, - header: title, - body: txt.join("\n") - }; + showNotification(80, (y) => { + + // TODO: icon based on src? + var x = 120; + g.setFontAlign(0, 0); + g.setFont("6x8", 1); + g.setColor("#40d040"); + g.drawString(event.src, x, y + 7); + + g.setColor("#ffffff"); + g.setFont("6x8", 2); + g.drawString(event.title, x, y + 25); + + g.setFont("6x8", 1); + g.setColor("#ffffff"); + g.setFontAlign(-1, -1); + g.drawString(txt.join("\n"), 10, y + 40); + }); - notification.show(80, content); Bangle.buzz(); } - function updateMusicInfo() { - if (state.music == gb.musicState.PLAY) { + function handleMusicStateUpdate(event) { + state.music = event.state - var content = { - title: state.musicInfo.artist, - icon: "gbridge-music-ico.img", - header: state.musicInfo.track, - body:"" - }; + if (state.music == "play") { + showNotification(40, (y) => { + g.setColor("#ffffff"); + g.drawImage(require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")), 8, y + 8); - notification.show(40, content); - } else { - notification.hide(); + g.setFontAlign(-1, -1); + var x = 40; + g.setFont("4x6", 2); + g.setColor("#ffffff"); + g.drawString(state.musicInfo.artist, x, y + 8); + + g.setFont("6x8", 1); + g.setColor("#ffffff"); + g.drawString(state.musicInfo.track, x, y + 22); + }); + } + + if (state.music == "pause") { + hideNotification(); } } - function handleCall(cmd, name, number) { - switch(cmd) { + function handleCallEvent(event) { - case gb.callCommands.ACCEPT: - notification.show(80, { - title: "Call incoming", - icon: "gbridge-call-ico.img", - header: name, - body: number - }); - Bangle.buzz(); + if (event.cmd == "accept") { + showNotification(40, (y) => { + g.setColor("#ffffff"); + g.drawImage(require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw=")), 8, y + 8); + + g.setFontAlign(-1, -1); + var x = 40; + g.setFont("4x6", 2); + g.setColor("#ffffff"); + g.drawString(event.name, x, y + 8); + + g.setFont("6x8", 1); + g.setColor("#ffffff"); + g.drawString(event.number, x, y + 22); + }); + + Bangle.buzz(); break; - - default: - if (state.debug) { - showNotification(cmd, name, number); - } } } global.GB = (event) => { switch (event.t) { case "notify": - showNotification(event.src, event.title, event.body); + handleNotificationEvent(event); break; case "musicinfo": state.musicInfo = event; break; case "musicstate": - state.music = event.state; - updateMusicInfo(); + handleMusicStateUpdate(event); break; case "call": - handleCall(event.cmd, event.name, event.number); + handleCallEvent(event); break; - default: - if (state.debug) { - showNotification("Gadgetbridge", event.t, JSON.stringify(event)); - } } }; // Touch control Bangle.on("touch", () => { - if (notification.isVisible()) { - notification.hide(); + if (state.scrollPos) { + hideNotification(); } }); Bangle.on("swipe", (dir) => { - if (state.music == gb.musicState.PLAY) { - gb.controlMusic(dir > 0 ? gb.muiscControl.NEXT : gb.muiscControl.PREV); + if (state.music == "play") { + const command = dir > 0 ? "next" : "previous" + gbSend({ t: "music", n: command }); } }); - function drawIcon() { + function draw() { g.setColor(-1); - - let icon; - if (NRF.getSecurityStatus().connected) { - icon = require("Storage").read("gbridge-on-ico.img"); - } else { - icon = require("Storage").read("gbridge-off-ico.img"); - } - - g.drawImage(icon, this.x + 1, this.y + 1); + if (NRF.getSecurityStatus().connected) + g.drawImage(require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")), this.x + 1, this.y + 1); + else + g.drawImage(require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")), this.x + 1, this.y + 1); } function changedConnectionState() { @@ -241,7 +190,7 @@ NRF.on("connected", changedConnectionState); NRF.on("disconnected", changedConnectionState); - WIDGETS["gbridgew"] = { area: "tl", width: 24, draw: drawIcon }; + WIDGETS["gbridgew"] = { area: "tl", width: 24, draw: draw }; - gb.reportBatteryLevel(); + gbSend({ t: "status", bat: E.getBattery() }); })(); From 0db4324e7b1acb151ea2582d1ec99cb834eaede7 Mon Sep 17 00:00:00 2001 From: Dimitri Gigot Date: Thu, 2 Apr 2020 18:38:12 +0000 Subject: [PATCH 17/40] clean up --- apps/toucher/app.js | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/apps/toucher/app.js b/apps/toucher/app.js index abeb78092..1b5a9186c 100644 --- a/apps/toucher/app.js +++ b/apps/toucher/app.js @@ -1,7 +1,6 @@ g.clear(); -var Storage = require("Storage"); - +const Storage = require("Storage"); function getApps(){ return Storage.list(/\.info$/).filter(app => app.endsWith('.info')).map(app => Storage.readJSON(app,1) || { name: "DEAD: "+app.substr(1) }) @@ -15,11 +14,8 @@ function getApps(){ }); } -var selected = 0; -var menuScroll = 0; -var menuShowing = false; -var apps = getApps(); -var displayBack = false; +const selected = 0; +const apps = getApps(); function prev(){ if (selected>=0) { @@ -35,35 +31,6 @@ function next() { drawMenu(); } -/* -function drawMenu() { - g.setFont("6x8",2); - g.setFontAlign(-1,0); - var n = 3; - if (selected>=n+menuScroll) menuScroll = 1+selected-n; - if (selectedn+menuScroll) g.fillPoly([120,239,100,219,140,219]); - else g.clearRect(100,219,140,239); - for (var i=0;i Date: Thu, 2 Apr 2020 20:40:56 +0200 Subject: [PATCH 18/40] Remove lost break --- apps/gbridge/widget.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index d60492c20..2042ea7a0 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -139,7 +139,6 @@ }); Bangle.buzz(); - break; } } From dd93f2be15c378d85a617b48f48af6b38228838e Mon Sep 17 00:00:00 2001 From: Dimitri Gigot Date: Thu, 2 Apr 2020 18:49:06 +0000 Subject: [PATCH 19/40] add Toucher icon --- apps/toucher/app.png | Bin 899 -> 695 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/toucher/app.png b/apps/toucher/app.png index 8b4e6caa2fe4720a32f492bd00c6e68751fabfbc..f1509dedb3db2744c9f8f16d1e38dc021832495c 100644 GIT binary patch delta 671 zcmV;Q0$}}v2e$=~B!3BTNLh0L01FcU01FcV0GgZ_00001b5ch_0Itp)=>Px#1ZP1_ zK>z@;j|==^1poj7Oi4sRRCodH*iBCpK^O<{|L(T5v5jDj!Hc^1675O$uF;br(StD% zukI&MKY@7Cn=#>P%GJaJ9*o8l351Y9QjQQpSResyd{N%DB$F-w6-IfxG8dq@Rf}~ zfg6uqq#kwh8Gp7iJAj3uuTVuv0nK+iFhBGKW>b=11(oWxkR3!Qez5>aY}Co)umV^g zexi{@0|iKA)0jpjrvr1OwuRo&2)#@`eEWxgvewyg_104)-Z-$LN#v*gA~)KDkh+8X zKGW#k*;rjbCO9CpiJvd|s&nVX_uXx4xh!%cJt&t7GJo32MkUW@G+ef!x`o^zJT#!R zwz%(mnI_G)NtzRR?B&VT&VgPU4l0-FJYk8moZ3*;K1}3@gS%@FJVPETgX$f&Gjhy{ z=Ydymr_x7>)1agPHesumQl|M{2U6)y$fl$Kwsa<19h3@-h^HPPe(gT4r@Q!yB%6`~ zmd1Y}d4Kx}9q}$S(fdN%s~2^U~&SuVc{veDl({o)4*5-uR^_IrkJ(mNyRgABYS zNx$J2opgxTDw?KgXDig(=>UOz!ueeI{{xz)X&Bn&whad_r#An&Sxo={002ovPDHLk FV1gg!H)a3; delta 876 zcmV-y1C#u>1%n5WB!32COGiWi{{a60|De66lK=n%I7vi7RA_W+MNKpwjDLU^J*Wpy9@Ur_hzBnkJ$dke#)F>J7&U4LsECqS zB49uY3Q`L(jV;|>4@>w;*-qOnNZcoBva>V$=J%U-=IspF*nik8Yovub^cOBY?cfcO z1>;+VoWa;>Pk;!SG_WW*Es5gZ1-`R@4t>oKYJ)f#xYia)IV)#&xZ*BHYck+F1K9eED0&f|F;Lm{VL;rb?(c)W{8d&zrH4`v=Yc(ki11mCgAi%s^z;OwY%W1?mqSG(dy!Ck-w2QlAp02F%vFB!hc{ zjO>^-KB^P`s2PS92~z{}*ZpjA*ULELWH~mW6ZUJcNPn9+lc&+B)DUr{86zS8-#@X9 zjm^Ksf@So=!vkqok&B0(E$d7DqL7jSui~S=^eA4%XSJTBN&3Q3n7fISd#>Yfkq`Y7oUZo6O*XSC0PE4CjRbxsB z891`_Jb$$t4gm0VtaDCZ#oC>ecxnOIv}QXk9hWVZXpWG9D(~*;=TgsBg26ZB$NCv_ z6jS1v>8o5s3^(92ez~L?D*iyLPXgC^Ss0rh= zETODzL{X|K%Bv>Fxr&yKOBoXz4vqjFlzKwtw{idZM95W(Tbb#rO0Wkqaj6$F z@MWZp>h=2o=nr;LTzCQiH!$$4i=w<5W87%Fs6NXOGI0OH6?#VB0k9%#HO2Wg(|z6F zTV?6^_5{%B3_Q((i7K)Fmx`$;#+_0- Date: Thu, 2 Apr 2020 21:11:30 +0200 Subject: [PATCH 20/40] Gbridge: change animation timeout a litte bit slower animation looks more fluent --- apps/gbridge/widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index a787d7e0b..349797baa 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -22,7 +22,7 @@ scrollPos-=2; if (scrollPos<-size) scrollPos=-size; Bangle.setLCDOffset(scrollPos); - if (scrollPos>-size) setTimeout(anim,10); + if (scrollPos>-size) setTimeout(anim,15); } anim(); } From 8468899a80a0ca3bd10c372e9b50434bf51f80bc Mon Sep 17 00:00:00 2001 From: Michael Werner Date: Thu, 2 Apr 2020 21:12:57 +0200 Subject: [PATCH 21/40] Fix indention --- apps/gbridge/widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index 349797baa..655c7fa4c 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -89,7 +89,7 @@ if (musicState=="play") show(40,function(y) { g.setColor("#ffffff"); - g.drawImage( require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")),8,y+8); + g.drawImage(require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")),8,y+8); g.setFontAlign(-1,-1); g.setFont("6x8",1); var x = 40; From 792e79c3a8156b3030f8fda6715476331acb2eb5 Mon Sep 17 00:00:00 2001 From: Michael Werner Date: Thu, 2 Apr 2020 21:14:05 +0200 Subject: [PATCH 22/40] Gbridge: Limit title length Some group chats like in whatsapp have really long title as they consist of group chat name + person name --- apps/gbridge/widget.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index 655c7fa4c..e167c9a50 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -58,7 +58,8 @@ g.drawString(j.src,x,y+7); g.setColor("#ffffff"); g.setFont("6x8",2); - g.drawString(j.title,x,y+25); + if (j.title === undefined) g.drawString(j.title,x,y+25); + else g.drawString(j.title.slice(0,17),x,y+25); g.setFont("6x8",1); g.setColor("#ffffff"); // split text up a word boundaries From 9692204ce0acb61b7cbf5de491fe66a743bad05e Mon Sep 17 00:00:00 2001 From: Michael Werner Date: Thu, 2 Apr 2020 21:15:54 +0200 Subject: [PATCH 23/40] Bump Gadgetbridge version to 0.05 --- apps.json | 2 +- apps/gbridge/ChangeLog | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps.json b/apps.json index b088865d2..67fbe4b0a 100644 --- a/apps.json +++ b/apps.json @@ -91,7 +91,7 @@ { "id": "gbridge", "name": "Gadgetbridge", "icon": "app.png", - "version":"0.04", + "version":"0.05", "description": "The default notification handler for Gadgetbridge notifications from Android", "tags": "tool,system,android,widget", "storage": [ diff --git a/apps/gbridge/ChangeLog b/apps/gbridge/ChangeLog index 28789ec04..c10eed16b 100644 --- a/apps/gbridge/ChangeLog +++ b/apps/gbridge/ChangeLog @@ -2,3 +2,4 @@ 0.02: Increase contrast (darker notification background, white text) 0.03: Gadgetbridge widget now shows connection state 0.04: Tweaks for variable size widget system +0.05: Optimize animation, limit title length From 5ad34face08be6bef1986dda9399c132d04b752b Mon Sep 17 00:00:00 2001 From: Christian Hemker Date: Thu, 2 Apr 2020 21:23:04 +0200 Subject: [PATCH 24/40] added gps --- apps/moonphase/app.js | 535 +++++++++++++++++++++++------------------- 1 file changed, 293 insertions(+), 242 deletions(-) diff --git a/apps/moonphase/app.js b/apps/moonphase/app.js index 0049bf91d..480a0e144 100644 --- a/apps/moonphase/app.js +++ b/apps/moonphase/app.js @@ -1,6 +1,26 @@ //Icons from https://icons8.com //Sun and Moon calculations from https://github.com/mourner/suncalc and https://gist.github.com/endel/dfe6bb2fbe679781948c +//varibales +const storage = require('Storage'); +let coords; +var timer; +var fix; + +var PI = Math.PI, + sin = Math.sin, + cos = Math.cos, + tan = Math.tan, + asin = Math.asin, + atan = Math.atan2, + acos = Math.acos, + rad = PI / 180, + dayMs = 1000 * 60 * 60 * 24, + J1970 = 2440588, + J2000 = 2451545; + +var SunCalc = {}; + //pictures function getImg(i) { var data = { @@ -18,134 +38,113 @@ function getImg(i) { "LastQuarterSouth" : "AD8AAH/4AHx/gDwf8BwH/g4B/8MAf/HAH/5gB/+YAf/sAH//AB//wAf/8AH//AB//wAf/2AH/5gB/+cAf/jAH/w4B/8HAf+A8H/AHx/gAf/gAA/AAA==", "WaningCrescentNorth" : "AD8AAH/4AH8PgD+A8B/ADg/gAcP4ADH+AA5/AAGfwABv8AAP/AAD/wAA/8AAP/AAD/wAA38AAZ/AAGf4ADj+AAw/gAcH8AOA/gPAH8PgAf/gAA/AAA==", "WaxingCrescentSouth" : "AD8AAH/4AH8PgD+A8B/ADg/gAcP4ADH+AA5/AAGfwABv8AAP/AAD/wAA/8AAP/AAD/wAA38AAZ/AAGf4ADj+AAw/gAcH8AOA/gPAH8PgAf/gAA/AAA==" - }; - return { + }; + return { width : 26, height : 26, bpp : 1, transparent : 0, buffer : E.toArrayBuffer(atob(data[i])) }; - } +} +// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas +// date/time constants and conversions +function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } +function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } +function toDays(date) { return toJulian(date) - J2000; } - //coordinates (will get from GPS later on real device) - var lat = 52.96236, - lon = 7.62571; +// general calculations for position +var e = rad * 23.4397; // obliquity of the Earth +function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } +function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } +function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } +function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } +function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } +function astroRefraction(h) { + if (h < 0) // the following formula works for positive altitudes only. + h = 0; // if h = -0.08901179 a div/0 would occur. + + // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: + return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); +} - var PI = Math.PI, - sin = Math.sin, - cos = Math.cos, - tan = Math.tan, - asin = Math.asin, - atan = Math.atan2, - acos = Math.acos, - rad = PI / 180; +// general sun calculations +function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } +function eclipticLongitude(M) { + + var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center + P = rad * 102.9372; // perihelion of the Earth + + return M + C + P + PI; +} - // sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas - // date/time constants and conversions - var dayMs = 1000 * 60 * 60 * 24, - J1970 = 2440588, - J2000 = 2451545; - - function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } - function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } - function toDays(date) { return toJulian(date) - J2000; } - - // general calculations for position - var e = rad * 23.4397; // obliquity of the Earth - function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } - function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } - function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } - function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } - function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } - function astroRefraction(h) { - if (h < 0) // the following formula works for positive altitudes only. - h = 0; // if h = -0.08901179 a div/0 would occur. - - // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: - return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); - } - - // general sun calculations - function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } - function eclipticLongitude(M) { - - var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center - P = rad * 102.9372; // perihelion of the Earth - - return M + C + P + PI; - } - - function sunCoords(d) { - - var M = solarMeanAnomaly(d), - L = eclipticLongitude(M); - - return { - dec: declination(L, 0), - ra: rightAscension(L, 0) - }; - } - - var SunCalc = {}; - - // adds a custom time to the times config - SunCalc.addTime = function (angle, riseName, setName) { - times.push([angle, riseName, setName]); +function sunCoords(d) { + var M = solarMeanAnomaly(d), + L = eclipticLongitude(M); + return { + dec: declination(L, 0), + ra: rightAscension(L, 0) }; +} - // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas - function moonCoords(d) { // geocentric ecliptic coordinates of the moon - var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude - M = rad * (134.963 + 13.064993 * d), // mean anomaly - F = rad * (93.272 + 13.229350 * d), // mean distance - l = L + rad * 6.289 * sin(M), // longitude - b = rad * 5.128 * sin(F), // latitude - dt = 385001 - 20905 * cos(M); // distance to the moon in km - return { - ra: rightAscension(l, b), - dec: declination(l, b), - dist: dt - }; - } - SunCalc.getMoonPosition = function (date, lat, lng) { +// adds a custom time to the times config +SunCalc.addTime = function (angle, riseName, setName) { + times.push([angle, riseName, setName]); +}; - var lw = rad * -lng, - phi = rad * lat, - d = toDays(date), - c = moonCoords(d), - H = siderealTime(d, lw) - c.ra, - h = altitude(H, phi, c.dec), - // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); - h = h + astroRefraction(h); // altitude correction for refraction - return { - azimuth: azimuth(H, phi, c.dec), - altitude: h, - distance: c.dist, - parallacticAngle: pa - }; +// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas +function moonCoords(d) { // geocentric ecliptic coordinates of the moon + var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude + M = rad * (134.963 + 13.064993 * d), // mean anomaly + F = rad * (93.272 + 13.229350 * d), // mean distance + l = L + rad * 6.289 * sin(M), // longitude + b = rad * 5.128 * sin(F), // latitude + dt = 385001 - 20905 * cos(M); // distance to the moon in km + + return { + ra: rightAscension(l, b), + dec: declination(l, b), + dist: dt }; +} - // calculations for illumination parameters of the moon, - // based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and - // Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. +SunCalc.getMoonPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + c = moonCoords(d), + H = siderealTime(d, lw) - c.ra, + h = altitude(H, phi, c.dec), + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); + h = h + astroRefraction(h); // altitude correction for refraction + return { + azimuth: azimuth(H, phi, c.dec), + altitude: h, + distance: c.dist, + parallacticAngle: pa + }; +}; - SunCalc.getMoonIllumination = function (date) { - var year = date.getFullYear(); - var month = date.getMonth(); - var day = date.getDate(); - var Moon = { - phases: ['new', 'waxing-crescent', 'first-quarter', 'waxing-gibbous', 'full', 'waning-gibbous', 'last-quarter', 'waning-crescent'], - phase: function (year, month, day) { +// calculations for illumination parameters of the moon, +// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and +// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. +SunCalc.getMoonIllumination = function (date) { + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var Moon = { + phases: ['new', 'waxing-crescent', 'first-quarter', 'waxing-gibbous', 'full', 'waning-gibbous', 'last-quarter', 'waning-crescent'], + phase: function (year, month, day) { let c = 0; let e = 0; let jd = 0; let b = 0; if (month < 3) { - year--; - month += 12; + year--; + month += 12; } ++month; c = 365.25 * year; @@ -156,147 +155,199 @@ function getImg(i) { jd -= b; // subtract integer part to leave fractional part of original jd b = Math.round(jd * 8); // scale fraction from 0-8 and round if (b >= 8) b = 0; // 0 and 8 are the same so turn 8 into 0 - //print ({phase: b, name: Moon.phases[b]}); return {phase: b, name: Moon.phases[b]}; - } + } }; return (Moon.phase(year, month, day)); - }; +}; - function hoursLater(date, h) { - return new Date(date.valueOf() + h * dayMs / 24); - } +function hoursLater(date, h) { + return new Date(date.valueOf() + h * dayMs / 24); +} - // calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article - - SunCalc.getMoonTimes = function (date, lat, lng, inUTC) { - var t = new Date(date); - if (inUTC) t.setUTCHours(0, 0, 0, 0); - else t.setHours(0, 0, 0, 0); - var hc = 0.133 * rad, - h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, - h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; - - // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) - for (var i = 1; i <= 24; i += 2) { - h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; - h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; - a = (h0 + h2) / 2 - h1; - b = (h2 - h0) / 2; - xe = -b / (2 * a); - ye = (a * xe + b) * xe + h1; - d = b * b - 4 * a * h1; - roots = 0; - if (d >= 0) { - dx = Math.sqrt(d) / (Math.abs(a) * 2); - x1 = xe - dx; - x2 = xe + dx; - if (Math.abs(x1) <= 1) roots++; - if (Math.abs(x2) <= 1) roots++; - if (x1 < -1) x1 = x2; - } - if (roots === 1) { - if (h0 < 0) rise = i + x1; - else set = i + x1; - } else if (roots === 2) { - rise = i + (ye < 0 ? x2 : x1); - set = i + (ye < 0 ? x1 : x2); - } - if (rise && set) break; - h0 = h2; +// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article +SunCalc.getMoonTimes = function (date, lat, lng, inUTC) { + var t = new Date(date); + if (inUTC) t.setUTCHours(0, 0, 0, 0); + else t.setHours(0, 0, 0, 0); + var hc = 0.133 * rad, + h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, + h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (var i = 1; i <= 24; i += 2) { + h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; + h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; + a = (h0 + h2) / 2 - h1; + b = (h2 - h0) / 2; + xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) roots++; + if (Math.abs(x2) <= 1) roots++; + if (x1 < -1) x1 = x2; } - var result = {}; - if (rise) result.rise = hoursLater(t, rise); - if (set) result.set = hoursLater(t, set); - if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; - return result; + if (roots === 1) { + if (h0 < 0) rise = i + x1; + else set = i + x1; + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + if (rise && set) break; + h0 = h2; + } + var result = {}; + if (rise) result.rise = hoursLater(t, rise); + if (set) result.set = hoursLater(t, set); + if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; + return result; +}; + +function getMPhaseComp (offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + var dd = String(date.getDate()); + if(dd<10){dd='0'+dd;} + var mm = String(date.getMonth() + 1); + if(mm<10){mm='0'+mm;} + var yyyy = date.getFullYear(); + var phase = SunCalc.getMoonIllumination(date); + return dd + "." + mm + "." + yyyy + ": "+ phase.name; +} + +function getMPhaseSim (offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + var dd = String(date.getDate()); + if(dd<10){dd='0'+dd;} + var mm = String(date.getMonth() + 1); + if(mm<10){mm='0'+mm;} + var yyyy = date.getFullYear(); + var phase = SunCalc.getMoonIllumination(date); + return phase.name; +} + +function drawMoonPhase(offset, x, y){ + if (coords.lat >= 0 && coords.lat <= 90){ //Northern hemisphere + if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} + if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentNorth"), x, y);} + if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterNorth"), x, y);} + if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousNorth"), x, y);} + if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} + if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousNorth"), x, y);} + if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterNorth"), x, y);} + if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentNorth"), x, y);} +} + else { //Southern hemisphere + if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} + if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentSouth"), x, y);} + if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterSouth"), x, y);} + if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousSouth"), x, y);} + if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} + if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousSouth"), x, y);} + if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterSouth"), x, y);} + if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentSouth"), x, y);} + } +} + +function drawMoon(offset, x, y) { + g.setFont("6x8"); + g.clear(); + g.drawString("Key1: day+, Key2:today, Key3:day-",x,y-30); + g.drawString("Last known coordinates: " + coords.lat.toFixed(4) + " " + coords.lon.toFixed(4), x, y-20); + g.drawString("Press BTN4 to update",x, y-10); + + g.drawString(getMPhaseComp(offset),x,y+30); + drawMoonPhase(offset, x+35, y+40); + + g.drawString(getMPhaseComp(offset+2),x,y+70); + drawMoonPhase(offset+2, x+35, y+80); + + g.drawString(getMPhaseComp(offset+4),x,y+110); + drawMoonPhase(offset+4, x+35, y+120); + + g.drawString(getMPhaseComp(offset+6),x,y+150); + drawMoonPhase(offset+6, x+35, y+160); +} + +//Write coordinates to file +function updateCoords() { + storage.write('coords.json', coords); +} + +//set coordinates to default (city where I live) +function resetCoords() { + coords = { + lat : 52.96236, + lon : 7.62571, }; + updateCoords(); +} - function getMPhaseComp (offset) { - var date = new Date(); - date.setDate(date.getDate() + offset); - var dd = String(date.getDate()); - if(dd<10){dd='0'+dd;} - var mm = String(date.getMonth() + 1); - if(mm<10){mm='0'+mm;} - var yyyy = date.getFullYear(); - var phase = SunCalc.getMoonIllumination(date); - return dd + "." + mm + "." + yyyy + ": "+ phase.name; - } +function getGpsFix() { + Bangle.on('GPS', function(fix) { + g.clear(); + + if (fix.fix == 1) { + var gpsString = "lat: " + fix.lat.toFixed(4) + " lon: " + fix.lon.toFixed(4); + coords.lat = fix.lat; + coords.lon = fix.lon; + updateCoords(); + g.drawString("Got GPS fix and wrote coords to file",10,20); + g.drawString(gpsString,10,30); + g.drawString("Press BTN5 to return to app",10,40); + clearInterval(timer); + timer = undefined; + } + else { + g.drawString("Searching satellites...",10,20); + g.drawString("Press BTN5 to stop GPS",10, 30); + } + }); +} - function getMPhaseSim (offset) { - var date = new Date(); - date.setDate(date.getDate() + offset); - var dd = String(date.getDate()); - if(dd<10){dd='0'+dd;} - var mm = String(date.getMonth() + 1); - if(mm<10){mm='0'+mm;} - var yyyy = date.getFullYear(); - var phase = SunCalc.getMoonIllumination(date); - return phase.name; - } +function start() { + var x = 10; + var y = 50; + var offsetMoon = 0; + coords = storage.readJSON('coords.json',1); //read coordinates from file + if (!coords) resetCoords(); //if coordinates could not be read, reset them + drawMoon(offsetMoon, x, y); //offset, x, y + + //define button functions + setWatch(function() { //BTN1 + offsetMoon++; //jump to next day + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN1, {edge:"rising", debounce:50, repeat:true}); + + setWatch(function() { //BTN2 + offsetMoon = 0; //jump to today + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN2, {edge:"rising", debounce:50, repeat:true}); + + setWatch(function() { //BTN3 + offsetMoon--; //jump to next day + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN3, {edge:"rising", debounce:50, repeat:true}); + + setWatch(function() { //BTN4 + g.drawString("--- Getting GPS signal ---",x, y); + Bangle.setGPSPower(1); + timer = setInterval(getGpsFix, 10000); + }, BTN4, {edge:"rising", debounce:50, repeat:true}); + + setWatch(function() { //BTN5 + if (timer) clearInterval(timer); + timer = undefined; + Bangle.setGPSPower(0); + drawMoon(offsetMoon, x, y); //offset, x, y + }, BTN5, {edge:"rising", debounce:50, repeat:true}); +} - function drawMoonPhase(offset, x, y){ - if (lat >= 0 && lat <= 90){ //Northern hemisphere - if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} - if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentNorth"), x, y);} - if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterNorth"), x, y);} - if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousNorth"), x, y);} - if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} - if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousNorth"), x, y);} - if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterNorth"), x, y);} - if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentNorth"), x, y);} - } - else { //Southern hemisphere - if (getMPhaseSim(offset) == "new") {g.drawImage(getImg("NewMoon"), x, y);} - if (getMPhaseSim(offset) == "waxing-crescent") {g.drawImage(getImg("WaxingCrescentSouth"), x, y);} - if (getMPhaseSim(offset) == "first-quarter") {g.drawImage(getImg("FirstQuarterSouth"), x, y);} - if (getMPhaseSim(offset) == "waxing-gibbous") {g.drawImage(getImg("WaxingGibbousSouth"), x, y);} - if (getMPhaseSim(offset) == "full") {g.drawImage(getImg("FullMoon"), x, y);} - if (getMPhaseSim(offset) == "waning-gibbous") {g.drawImage(getImg("WaningGibbousSouth"), x, y);} - if (getMPhaseSim(offset) == "last-quarter") {g.drawImage(getImg("LastQuarterSouth"), x, y);} - if (getMPhaseSim(offset) == "waning-crescent") {g.drawImage(getImg("WaningCrescentSouth"), x, y);} - } - } - - function drawMoon(offset, x, y) { - g.setFont("6x8"); - g.clear(); - g.drawString("Key1: day+, Key2:today, Key3:day-",x,y-30); - g.drawString(getMPhaseComp(offset),x,y+30); - drawMoonPhase(offset, x+35, y+40); - - g.drawString(getMPhaseComp(offset+2),x,y+70); - drawMoonPhase(offset+2, x+35, y+80); - - g.drawString(getMPhaseComp(offset+4),x,y+110); - drawMoonPhase(offset+4, x+35, y+120); - - g.drawString(getMPhaseComp(offset+6),x,y+150); - drawMoonPhase(offset+6, x+35, y+160); - } - - function start() { - var x = 10; - var y = 50; - var offsetMoon = 0; - drawMoon(offsetMoon, x, y); //offset, x, y - - //define button functions - setWatch(function() { - offsetMoon++; //jump to next day - drawMoon(offsetMoon, x, y); //offset, x, y - }, BTN1, {edge:"rising", debounce:50, repeat:true}); - - setWatch(function() { - offsetMoon = 0; //jump to today - drawMoon(offsetMoon, x, y); //offset, x, y - }, BTN2, {edge:"rising", debounce:50, repeat:true}); - - setWatch(function() { - offsetMoon--; //jump to next day - drawMoon(offsetMoon, x, y); //offset, x, y - }, BTN3, {edge:"rising", debounce:50, repeat:true}); - } - - start(); \ No newline at end of file +start(); \ No newline at end of file From c7b5e3e58628bd2664241578f8a741b839dc8b5d Mon Sep 17 00:00:00 2001 From: Christian Hemker Date: Thu, 2 Apr 2020 21:27:05 +0200 Subject: [PATCH 25/40] added version information --- apps.json | 4 ++-- apps/moonphase/ChangeLog | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps.json b/apps.json index a84a8010c..21829c457 100644 --- a/apps.json +++ b/apps.json @@ -15,8 +15,8 @@ { "id": "moonphase", "name": "Moonphase", "icon": "app.png", - "version":"0.01", - "description": "Shows current moon phase. Currently only with fixed coordinates (northern hemisphere).", + "version":"0.02", + "description": "Shows current moon phase. Now with GPS function.", "tags": "", "allow_emulator":true, "storage": [ diff --git a/apps/moonphase/ChangeLog b/apps/moonphase/ChangeLog index 5560f00bc..baa668c3c 100644 --- a/apps/moonphase/ChangeLog +++ b/apps/moonphase/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Added GPS to obtain coordinates, added buttons \ No newline at end of file From e7c8ea9813eba5813d2b374cb0da834d35950f31 Mon Sep 17 00:00:00 2001 From: Dimitri Gigot Date: Thu, 2 Apr 2020 20:29:37 +0000 Subject: [PATCH 26/40] add touch and swipe support --- apps/toucher/app.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/toucher/app.js b/apps/toucher/app.js index 1b5a9186c..2b80198c9 100644 --- a/apps/toucher/app.js +++ b/apps/toucher/app.js @@ -103,10 +103,28 @@ function drawMenu(){ } drawMenu(); + +// Physical buttons setWatch(prev, BTN1, {repeat:true}); -setWatch(prev, BTN4, {repeat:true}); - setWatch(next, BTN3, {repeat:true}); -setWatch(next, BTN5, {repeat:true}); - setWatch(run, BTN2, {repeat:true,edge:"falling"}); + +// Screen event +Bangle.on('touch', function(button){ + switch(button){ + case 1: + prev(); + break; + case 2: + next(); + break; + case 3: + run(); + break; + } +}); + +Bangle.on('swipe', dir => { + if(dir == 1) prev(); + else next(); +}); \ No newline at end of file From f143f3eac50da3dc1ef6fe4616bd0f0cf690b15d Mon Sep 17 00:00:00 2001 From: Dimitri Gigot Date: Thu, 2 Apr 2020 20:31:21 +0000 Subject: [PATCH 27/40] Increase version --- apps.json | 2 +- apps/toucher/ChangeLog | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 2bd8ef7bf..e0f5142af 100644 --- a/apps.json +++ b/apps.json @@ -999,7 +999,7 @@ "name": "Touch Launcher", "shortName":"Menu", "icon": "app.png", - "version":"0.01", + "version":"0.02", "description": "Touch enable left to right launcher.", "tags": "tool,system,launcher", "type":"launch", diff --git a/apps/toucher/ChangeLog b/apps/toucher/ChangeLog index 5560f00bc..bd3d5d225 100644 --- a/apps/toucher/ChangeLog +++ b/apps/toucher/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Add swipe support and doucle tap to run application \ No newline at end of file From 2ed00456bf12366f373f0d5a261d0ed36caa4238 Mon Sep 17 00:00:00 2001 From: nic Date: Fri, 3 Apr 2020 00:34:18 -0300 Subject: [PATCH 28/40] Addded Portuguese from Brazil --- apps/locale/locales.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/locale/locales.js b/apps/locale/locales.js index 42a2225e4..7f6fbaef9 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -370,4 +370,21 @@ var locales = { abday: "Vas,Hét,Ke,Szer,Csüt,Pén,Szom", day: "Vasárnap,Hétfő,Kedd,Szerda,Csütörtök,Péntek,Szombat", trans: { yes: "igen", Yes: "Igen", no: "nem", No: "Nem", ok: "ok", on: "be", off: "ki" }}, + "pt_BR": { + lang: "pt_BR", + decimal_point: ",", + thousands_sep: ".", + currency_symbol: "R$", currency_first:true, + int_curr_symbol: "BRL", + speed: "kmh", + distance: { 0: "m", 1: "km" }, + temperature: "°C", + ampm: {0:"am",1:"pm"}, + timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" }, + datePattern: { 0: "", 1: "%d/%m/%y" }, + abmonth: "Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez", + month: "Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro", + abday: "Dom,Seg,Ter,Qua,Qui,Sex,Sab", + day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado", + trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "certo", on: "ligado", off: "desligado" }}, }; From b135c097cf28f5ada86c5acdae019197ede1f6b7 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 3 Apr 2020 08:16:42 +0100 Subject: [PATCH 29/40] Add unicode flags for Language --- apps/locale/locale.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/locale/locale.html b/apps/locale/locale.html index 5d7882e00..5cb4b4598 100644 --- a/apps/locale/locale.html +++ b/apps/locale/locale.html @@ -56,7 +56,14 @@ exports = { name : "en_GB", currencySym:"£", }); var languageSelector = document.getElementById("languages"); - languageSelector.innerHTML = Object.keys(locales).map(l=>``).join("\n"); + languageSelector.innerHTML = Object.keys(locales).map(l=>{ + var localeParts = l.split("_"); // en_GB -> ["en","GB"] + var icon = ""; + // If we have a 2 char ISO country code, use it to get the unicode flag + if (localeParts[1] && localeParts[1].length==2) + icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" "; + return `` + }).join("\n"); document.getElementById("upload").addEventListener("click", function() { From 4f60a450227c840f7f7faa9e358eda14f485bf5f Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Fri, 3 Apr 2020 09:16:22 +0100 Subject: [PATCH 30/40] Bug fix - use 12 / 24 hr clock based on user settings --- apps.json | 2 +- apps/marioclock/ChangeLog | 1 + apps/marioclock/marioclock-app.js | 15 +++++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/apps.json b/apps.json index e0f5142af..3bdc55f07 100644 --- a/apps.json +++ b/apps.json @@ -892,7 +892,7 @@ { "id": "marioclock", "name": "Mario Clock", "icon": "marioclock.png", - "version":"0.04", + "version":"0.05", "description": "Animated Mario clock, jumps to change the time!", "tags": "clock,mario,retro", "type": "clock", diff --git a/apps/marioclock/ChangeLog b/apps/marioclock/ChangeLog index 4334ad92c..74db9bc18 100644 --- a/apps/marioclock/ChangeLog +++ b/apps/marioclock/ChangeLog @@ -2,3 +2,4 @@ 0.02: Fix day of the week and add padding 0.03: use short date format from locale, take timeout from settings 0.04: modify date to display to be more at the original idea but still localized +0.05: use 12/24 hour clock from settings diff --git a/apps/marioclock/marioclock-app.js b/apps/marioclock/marioclock-app.js index ecbaba38a..49ae20b76 100644 --- a/apps/marioclock/marioclock-app.js +++ b/apps/marioclock/marioclock-app.js @@ -1,14 +1,15 @@ /********************************** - BangleJS MARIO CLOCK V0.1.0 + BangleJS MARIO CLOCK + Based on Espruino Mario Clock V3 https://github.com/paulcockrell/espruino-mario-clock + Converting images to 1bit BMP: Image > Mode > Indexed and tick the "Use black and white (1-bit) palette", Then export as BMP. + Online Image convertor: https://www.espruino.com/Image+Converter **********************************/ -var locale = require("locale"); +const locale = require("locale"); const storage = require('Storage'); -const settings = (storage.readJSON('setting.json',1)||{}); -const timeout = settings.timeout||10; +const settings = (storage.readJSON('setting.json',1) || {}); +const timeout = settings.timeout || 10; +const is12Hour = settings["12hour"] || false; // Screen dimensions let W, H; @@ -273,7 +274,8 @@ function drawTime() { drawBrick(42, 25); const t = new Date(); - const hours = ("0" + t.getHours()).substr(-2); + const h = t.getHours(); + const hours = ("0" + ((is12Hour && h > 12) ? h - 12 : h)).substr(-2); const mins = ("0" + t.getMinutes()).substr(-2); g.setFont("6x8"); @@ -374,8 +376,9 @@ function init() { Bangle.setLCDPower(true); } }); + + startTimers(); } // Initialise! init(); -startTimers(); From 50322a0f05b4d152b66740fed12f3018ddc83593 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Fri, 3 Apr 2020 09:18:05 +0100 Subject: [PATCH 31/40] Lint --- apps/marioclock/marioclock-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/marioclock/marioclock-app.js b/apps/marioclock/marioclock-app.js index 49ae20b76..2eeb21c97 100644 --- a/apps/marioclock/marioclock-app.js +++ b/apps/marioclock/marioclock-app.js @@ -7,7 +7,7 @@ const locale = require("locale"); const storage = require('Storage'); -const settings = (storage.readJSON('setting.json',1) || {}); +const settings = (storage.readJSON('setting.json', 1) || {}); const timeout = settings.timeout || 10; const is12Hour = settings["12hour"] || false; From 6bac8e982b1b116e6b9f5e5ffd18a8557a7b5f11 Mon Sep 17 00:00:00 2001 From: Stefano Baldan Date: Fri, 3 Apr 2020 10:22:14 +0200 Subject: [PATCH 32/40] RPG dice app --- apps.json | 14 +++++++ apps/rpgdice/ChangeLog | 1 + apps/rpgdice/app-icon.js | 1 + apps/rpgdice/app.js | 86 +++++++++++++++++++++++++++++++++++++++ apps/rpgdice/rpgdice.png | Bin 0 -> 6491 bytes 5 files changed, 102 insertions(+) create mode 100755 apps/rpgdice/ChangeLog create mode 100755 apps/rpgdice/app-icon.js create mode 100755 apps/rpgdice/app.js create mode 100755 apps/rpgdice/rpgdice.png diff --git a/apps.json b/apps.json index 3bdc55f07..15f6a0cda 100644 --- a/apps.json +++ b/apps.json @@ -1020,5 +1020,19 @@ {"name":"balltastic.app.js","url":"app.js"}, {"name":"balltastic.img","url":"app-icon.js","evaluate":true} ] + }, + { + "id": "rpgdice", + "name": "RPG dice", + "icon": "rpgdice.png", + "version": "0.01", + "description": "Simple RPG dice rolling app.", + "tags": "game,fun", + "type": "app", + "allow_emulator": true, + "storage": [ + {"name":"rpgdice.app.js","url": "app.js"}, + {"name":"rpgdice.img","url": "app-icon.js","evaluate":true} + ] } ] diff --git a/apps/rpgdice/ChangeLog b/apps/rpgdice/ChangeLog new file mode 100755 index 000000000..7b83706bf --- /dev/null +++ b/apps/rpgdice/ChangeLog @@ -0,0 +1 @@ +0.01: First release diff --git a/apps/rpgdice/app-icon.js b/apps/rpgdice/app-icon.js new file mode 100755 index 000000000..d6fd1fda5 --- /dev/null +++ b/apps/rpgdice/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwgMJgMQgMZzOREaERiERzIACiIVOAIIUCz///ORgIXNIQIAC/4ABJJYsBCogYEAYMQiAWGLAoAJJI8JLAYoCAAgJBJIIwGBohxBJI4YBJIwOFC4w5EC4hdOzIgCyLFDC45hHAAZJDgJAKMQwyBSYSOBxIXGPRTdChOfxChHbpRhBC4P5GAgAOgEZFAKjIBAz1EC5YYJxAvBJ4IXQzGIxEQB4RbPCoOIwEAOKAsCC4QvCFiAXDdwwsMC5eebogVGAALWBC42f/AWLC4zwCUgIEBCxK+DE4bsFC5+f/IrBC4RzHXwkZzATEDgP/RZAXFz5ECf4oXMCYKICC6hABMAQXOgAXBLgLrHRxZfCC6sBCo4XLLwIXBbAgXRMIQAGRxgwChIXVgEQIYimOGAZ6CSgOJC6CrCC4TZBC6IwCC4QWQPQYXKOggAFPQOfC5AWKPQgXGCpR6FOwoWOPQQXDIZYwHC4QVRAAQXBBxgA=")) \ No newline at end of file diff --git a/apps/rpgdice/app.js b/apps/rpgdice/app.js new file mode 100755 index 000000000..2007d6ab0 --- /dev/null +++ b/apps/rpgdice/app.js @@ -0,0 +1,86 @@ +const dice = [4, 6, 8, 10, 12, 20, 100]; +const nFlips = 20; +const delay = 500; + +let dieIndex = 1; +let face = 0; +let rolling = false; + +let bgColor; +let fgColor; + +function getDie() { + return dice[dieIndex]; +} + +function setColors(lastBounce) { + if (lastBounce) { + bgColor = 0xFFFF; + fgColor = 0x0000; + } else { + bgColor = 0x0000 + fgColor = 0xFFFF; + } +} + +function flipFace() { + while(true) { + let newFace = Math.floor(Math.random() * getDie()) + 1; + if (newFace !== face) { + face = newFace; + break; + } + } +} + +function draw() { + g.setColor(bgColor); + g.fillRect(0, 0, g.getWidth(), g.getHeight()); + g.setColor(fgColor); + g.setFontAlign(0, 0); + g.setFontVector(40); + g.drawString('d' + getDie(), 180, 30); + g.setFontVector(100); + g.drawString(face, 120, 120); +} + +function roll(bounces) { + flipFace(); + setColors(bounces === 0); + draw(); + if (bounces > 0) { + setTimeout(() => roll(bounces - 1), delay / bounces); + } else { + rolling = false; + } +} + +function startRolling() { + if (rolling) return; + rolling = true; + roll(nFlips); +} + +function changeDie() { + if (rolling) return; + dieIndex = (dieIndex + 1) % dice.length; + draw(); +} + +Bangle.on('lcdPower',function(on) { + if (on) { + startRolling(); + } +}); + +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +startRolling(); + +// Top button rolls the die, bottom button changes it +setWatch(startRolling, BTN1, {repeat:true}); +setWatch(changeDie, BTN3, {repeat:true}); + +// Show launcher when middle button pressed +setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); diff --git a/apps/rpgdice/rpgdice.png b/apps/rpgdice/rpgdice.png new file mode 100755 index 0000000000000000000000000000000000000000..d14b9c8368ff88c94648cdde8487a412985850b7 GIT binary patch literal 6491 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 Bk5&Kx literal 0 HcmV?d00001 From 7b79074cb7e0d70639bde8c7cbacd123b7d4d636 Mon Sep 17 00:00:00 2001 From: MaBecker Date: Fri, 3 Apr 2020 13:13:14 +0200 Subject: [PATCH 33/40] add wigdet moon phase --- apps.json | 13 ++++++++++++- apps/widmp/ChangeLog | 1 + apps/widmp/widget.js | 33 +++++++++++++++++++++++++++++++++ apps/widmp/widget.png | Bin 0 -> 2075 bytes 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 apps/widmp/ChangeLog create mode 100644 apps/widmp/widget.js create mode 100644 apps/widmp/widget.png diff --git a/apps.json b/apps.json index 15f6a0cda..27115f068 100644 --- a/apps.json +++ b/apps.json @@ -1034,5 +1034,16 @@ {"name":"rpgdice.app.js","url": "app.js"}, {"name":"rpgdice.img","url": "app-icon.js","evaluate":true} ] - } + }, + { "id": "widmp", + "name": "Moon Phase Widget", + "icon": "widget.png", + "version":"0.01", + "description": "Display the current moon phase in blueish for the northern hemisphere in eight phases", + "tags": "widget,tools", + "type":"widget", + "storage": [ + {"name":"widmp.wid.js","url":"widget.js"} + ] + }, ] diff --git a/apps/widmp/ChangeLog b/apps/widmp/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/widmp/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/widmp/widget.js b/apps/widmp/widget.js new file mode 100644 index 000000000..be4c2bb39 --- /dev/null +++ b/apps/widmp/widget.js @@ -0,0 +1,33 @@ +/* jshint esversion: 6 */ +(() => { + + const BLACK = 0, MOON = 0x41f, MC = 29.5305882, NM = 694039.09; + var r = 12, mx = 0, my = 0; + + var moon = { + 0: () => { g.reset().setColor(BLACK).fillRect(mx - r, my - r, mx + r, my + r);}, + 1: () => { moon[0](); g.setColor(MOON).drawCircle(mx, my, r);}, + 2: () => { moon[3](); g.setColor(BLACK).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);}, + 3: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r).setColor(BLACK).fillRect(mx - r, my - r, mx, my + r);}, + 4: () => { moon[3](); g.setColor(MOON).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);}, + 5: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r);}, + 6: () => { moon[7](); g.setColor(MOON).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);}, + 7: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r).setColor(BLACK).fillRect(mx, my - r, mx + r + r, my + r);}, + 8: () => { moon[7](); g.setColor(BLACK).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);} + }; + + function moonPhase(d) { + var tmp, month = d.getMonth(), year = d.getFullYear(), day = d.getDate(); + if (month < 3) {year--; month += 12;} + tmp = ((365.25 * year + 30.6 * ++month + day - NM) / MC); + return Math.round(((tmp - (tmp | 0)) * 7)+1); + } + + function draw() { + mx = this.x; my = this.y + 12; + moon[moonPhase(Date())](); + } + + WIDGETS["widmoon"] = { area: "tr", width: 24, draw: draw }; + +})(); diff --git a/apps/widmp/widget.png b/apps/widmp/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..32803f4741583805fdf3d3812c7e6ac30d66748b GIT binary patch literal 2075 zcmY*adpy(YAKzx=(hhTpST=Ku*ael#=6=8Bw}hP(Yc__>vN0m&Qj<$!Zb$N)N^%sH zlT&EX*Ygb5=5gO-eFnu=Kx) z{8z`7!Xn4hm~2`c10mHV9f(U{?I(U0fbIoUMoPbUWJds~tQ(b5VUi^8CPXiK)@ zq*>ct@idCWS*mZ3#eHS|U+%jP4lOnRpTm6X^lMhqs=Xo({o}LQEBbaD69E7@buU+^ zz$n=_M7r<^!_x{~hT>T(B^`$Lx#K}FHK=-yeC^a3k++gLy>Gmsf!4QXMo;(R&yIhn znmauH!DjAx$|L-Snwg1sCESQPS52?;V)^+jr(vBQb+=NlyVDfy5INc3JEx^tpw7HCT4l{Y_T^u~KRCA&QOx3r>Dr z=$or@g(ORkHAb$(OM&g31Nn4dod9a$WL7!`O% zwaM_>B7H^=4M|zWpr<4m^K}dG{-u+d{>~Yq`oij7ZR&qk;rwz!Rb^9D*t1O<%+)8a z^|Y2h>3!6FhSk+sao!afo!A}{bwZ%`cPZ~}P+kUD_K$I<=E-64aMrR3Hq%xhgrq+}f1i zuf&&r(UD(ryoYs~(d4K%oj9B6QQOi+VQzr(F`ew<#$9UVf3w zlr@NLN_ha+u%cZ^%vH3DSY;YPjsvvpgw<^kv!=>D&`U3rBZMqJSaezu&X8X)&pT z^?~`TWzDW-TJ3En*f(YE^=cR+Pn7C&nYCJ%+Mfn$R=JJY((jmU4U((snNq91qf84| zeM3*TJD;GxUN`Gm-Y#;V>w=0RdNRfp!mu0lg=k1+tjYTa&X>}{E5$%vhoD- z`*hxNG>>AevGL5AU~TVkR>h|&ja%J++O#*dYGyiQ$bW$fXL9SaXO7eifb0@NJLhu- zz(;;zIu&|@W!v_bCDf7c4L~lr>&;pB?u5Us7M8x`&c>*WzT1-veQmby`rAI}F7zKk z4rVFo8n=508&Z8I&b}Rtb72t}4_j^^`{`S{bmc7z?M0;tcC(#4L0@+7%l7VZ_E2QG z(UW5iZ7Y#kxNcOvcRtr z*T*9^;W^>^3}i0}ZsZQuH;|iUPwDF_83aJq=kMmr3Rg1VH`~)5KE~@GW$d5S8Z(KE ztTa4kQ#5cbG_1S&9O=kWTJ{`LZ0%*X&2#@}ZkE8STfjNngWIA{1)62dh+X?{z{Z3Z zudl9o8%A%?Yx6)&hM#DA3gs(TE}r+ET%Y!)*FUXssz8Zv7ywB}r&>mqw!7cyBJf*e z{EgDNOL{kg5y>?!9iK2yc3rZ(ATrQ6uAf-o<5*EK_Hdg&=TEG7XlUdRMK?Bojem}U z`&Fa4{V)@?zU;$C#bJ`*k8a5!Xxnc7p4y6$T%*eO}+UOjf`43%$b7TMj literal 0 HcmV?d00001 From 04cdb224474e50f1b22960d68550bbb808c8b769 Mon Sep 17 00:00:00 2001 From: MaBecker Date: Fri, 3 Apr 2020 13:20:52 +0200 Subject: [PATCH 34/40] add widget moon phase --- apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 27115f068..487e3f835 100644 --- a/apps.json +++ b/apps.json @@ -1045,5 +1045,5 @@ "storage": [ {"name":"widmp.wid.js","url":"widget.js"} ] - }, + } ] From 86ab3706eaea81d99b08611957a700ac06946253 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 3 Apr 2020 13:42:31 +0100 Subject: [PATCH 35/40] Gadgetbridge App 'Connected' state is no longer toggleable --- apps.json | 2 +- apps/gbridge/ChangeLog | 1 + apps/gbridge/app.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps.json b/apps.json index 487e3f835..d502b37c4 100644 --- a/apps.json +++ b/apps.json @@ -91,7 +91,7 @@ { "id": "gbridge", "name": "Gadgetbridge", "icon": "app.png", - "version":"0.05", + "version":"0.06", "description": "The default notification handler for Gadgetbridge notifications from Android", "tags": "tool,system,android,widget", "storage": [ diff --git a/apps/gbridge/ChangeLog b/apps/gbridge/ChangeLog index ad6b01d6a..0bcf94e25 100644 --- a/apps/gbridge/ChangeLog +++ b/apps/gbridge/ChangeLog @@ -4,3 +4,4 @@ 0.04: Tweaks for variable size widget system 0.05: Show incoming call notification Optimize animation, limit title length +0.06: Gadgetbridge App 'Connected' state is no longer toggleable diff --git a/apps/gbridge/app.js b/apps/gbridge/app.js index 45dc0e33d..d12f0f768 100644 --- a/apps/gbridge/app.js +++ b/apps/gbridge/app.js @@ -4,7 +4,7 @@ function gb(j) { var mainmenu = { "" : { "title" : "Gadgetbridge" }, - "Connected" : { value : NRF.getSecurityStatus().connected }, + "Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" }, "Find Phone" : function() { E.showMenu(findPhone); }, "Exit" : ()=> {load();}, }; From 9b918055da339385d56ca08bbd328ee6c696c58e Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 3 Apr 2020 14:27:45 +0100 Subject: [PATCH 36/40] Fix progress bar - now goes smoothly up over the course of the app upload. Also tidy it up significantly and reduce duplication --- index.html | 1 + js/comms.js | 41 ++++++++++++--- js/index.js | 144 ++++------------------------------------------------ js/ui.js | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 140 deletions(-) create mode 100644 js/ui.js diff --git a/index.html b/index.html index efaf84a61..6e1a9b554 100644 --- a/index.html +++ b/index.html @@ -129,6 +129,7 @@ + diff --git a/js/comms.js b/js/comms.js index e2cbf0cdd..91ae54b68 100644 --- a/js/comms.js +++ b/js/comms.js @@ -9,14 +9,19 @@ reset : (opt) => new Promise((resolve,reject) => { }); }), uploadApp : (app,skipReset) => { + Progress.show({title:`Uploading ${app.name}`,sticky:true}); return AppInfo.getFiles(app, httpGet).then(fileContents => { return new Promise((resolve,reject) => { console.log("uploadApp",fileContents.map(f=>f.name).join(", ")); + var maxBytes = fileContents.reduce((b,f)=>b+f.content.length, 0)||1; + var currentBytes = 0; + // Upload each file one at a time function doUploadFiles() { // No files left - print 'reboot' message if (fileContents.length==0) { Puck.write(`\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => { + Progress.hide({sticky:true}); if (result===null) return reject(""); resolve(app); }); @@ -24,17 +29,27 @@ uploadApp : (app,skipReset) => { } var f = fileContents.shift(); console.log(`Upload ${f.name} => ${JSON.stringify(f.content)}`); + Progress.show({ + min:currentBytes / maxBytes, + max:(currentBytes+f.content.length) / maxBytes}); + currentBytes += f.content.length; // Chould check CRC here if needed instead of returning 'OK'... // E.CRC32(require("Storage").read(${JSON.stringify(app.name)})) Puck.write(`\x10${f.cmd};Bluetooth.println("OK")\n`,(result) => { - if (!result || result.trim()!="OK") return reject("Unexpected response "+(result||"")); + if (!result || result.trim()!="OK") { + Progress.hide({sticky:true}); + return reject("Unexpected response "+(result||"")); + } doUploadFiles(); }, true); // wait for a newline } // Start the upload function doUpload() { Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n`,(result) => { - if (result===null) return reject(""); + if (result===null) { + Progress.hide({sticky:true}); + return reject(""); + } doUploadFiles(); }); } @@ -48,10 +63,15 @@ uploadApp : (app,skipReset) => { }); }, getInstalledApps : () => { + Progress.show({title:`Getting app list...`,sticky:true}); return new Promise((resolve,reject) => { Puck.write("\x03",(result) => { - if (result===null) return reject(""); + if (result===null) { + Progress.hide({sticky:true}); + return reject(""); + } Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);return j})', (appList,err) => { + Progress.hide({sticky:true}); if (appList===null) return reject(err || ""); console.log("getInstalledApps", appList); resolve(appList); @@ -60,6 +80,7 @@ getInstalledApps : () => { }); }, removeApp : app => { // expects an app structure + Progress.show({title:`Removing ${app.name}`,sticky:true}); var storage = [{name:app.id+".info"}].concat(app.storage); var cmds = storage.map(file=>{ return `\x10require("Storage").erase(${toJS(file.name)});\n`; @@ -67,15 +88,21 @@ removeApp : app => { // expects an app structure console.log("removeApp", cmds); return Comms.reset().then(new Promise((resolve,reject) => { Puck.write(`\x03\x10E.showMessage('Erasing\\n${app.id}...')${cmds}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => { + Progress.hide({sticky:true}); if (result===null) return reject(""); resolve(); }); - })); + })).catch(function(reason) { + Progress.hide({sticky:true}); + return Promise.reject(reason); + }); }, removeAllApps : () => { + Progress.show({title:"Removing all apps",progess:"animate",sticky:true}); return new Promise((resolve,reject) => { // Use write with newline here so we wait for it to finish Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK");reset()\n', (result,err) => { + Progress.hide({sticky:true}); if (!result || result.trim()!="OK") return reject(err || ""); resolve(); }, true /* wait for newline */); @@ -171,10 +198,10 @@ readStorageFile : (filename) => { // StorageFiles are different to normal storag fileContent = fileContent.substr(newLineIdx+1); } } else { - showProgress(undefined,100*fileContent.length / (fileSize||1000000)); + Progress.show({percent:100*fileContent.length / (fileSize||1000000)}); } if (finished) { - hideProgress(); + Progress.hide(); connection.received = ""; connection.cb = undefined; resolve(fileContent); @@ -188,7 +215,7 @@ readStorageFile : (filename) => { // StorageFiles are different to normal storag while (l!==undefined) { Bluetooth.print(l); l = f.readLine(); } Bluetooth.print("\xFF"); })()\n`,() => { - showProgress(`Reading ${JSON.stringify(filename)}`,0); + Progress.show({title:`Reading ${JSON.stringify(filename)}`,percent:0}); console.log(`StorageFile read started...`); }); }); diff --git a/js/index.js b/js/index.js index b21fc907d..60b66436a 100644 --- a/js/index.js +++ b/js/index.js @@ -14,119 +14,7 @@ httpGet("apps.json").then(apps=>{ refreshFilter(); }); -// Status // =========================================== Top Navigation -function showToast(message, type) { - // toast-primary, toast-success, toast-warning or toast-error - var style = "toast-primary"; - if (type=="success") style = "toast-success"; - else if (type=="error") style = "toast-error"; - else if (type!==undefined) console.log("showToast: unknown toast "+type); - var toastcontainer = document.getElementById("toastcontainer"); - var msgDiv = htmlElement(`

`); - msgDiv.innerHTML = message; - toastcontainer.append(msgDiv); - setTimeout(function() { - msgDiv.remove(); - }, 5000); -} -var progressToast; // the DOM element -var progressSticky; // showProgress(,,"sticky") don't remove until hideProgress("sticky") -var progressInterval; // the interval used if showProgress(..., "animate") -var progressPercent; // the current progress percentage -function showProgress(text, percent, sticky) { - if (sticky=="sticky") - progressSticky = true; - if (!progressToast) { - if (progressInterval) { - clearInterval(progressInterval); - progressInterval = undefined; - } - if (percent == "animate") { - progressInterval = setInterval(function() { - progressPercent += 2; - if (progressPercent>100) progressPercent=0; - showProgress(undefined, progressPercent); - }, 100); - percent = 0; - } - progressPercent = percent; - - var toastcontainer = document.getElementById("toastcontainer"); - progressToast = htmlElement(`
- ${text ? `
${text}
`:``} -
-
-
-
`); - toastcontainer.append(progressToast); - } else { - var pt=document.getElementById("progressToast"); - pt.setAttribute("aria-valuenow",percent); - pt.style.width = percent+"%"; - } -} -function hideProgress(sticky) { - if (progressSticky && sticky!="sticky") - return; - progressSticky = false; - if (progressInterval) { - clearInterval(progressInterval); - progressInterval = undefined; - } - if (progressToast) progressToast.remove(); - progressToast = undefined; -} - -Puck.writeProgress = function(charsSent, charsTotal) { - if (charsSent===undefined) { - hideProgress(); - return; - } - var percent = Math.round(charsSent*100/charsTotal); - showProgress(undefined, percent); -} -function showPrompt(title, text, buttons) { - if (!buttons) buttons={yes:1,no:1}; - return new Promise((resolve,reject) => { - var modal = htmlElement(``); - document.body.append(modal); - modal.querySelector("a[href='#close']").addEventListener("click",event => { - event.preventDefault(); - reject("User cancelled"); - modal.remove(); - }); - htmlToArray(modal.getElementsByTagName("button")).forEach(button => { - button.addEventListener("click",event => { - event.preventDefault(); - var isYes = event.target.getAttribute("isyes")=="1"; - if (isYes) resolve(); - else reject("User cancelled"); - modal.remove(); - }); - }); - }); -} function showChangeLog(appid) { var app = appNameToApp(appid); function show(contents) { @@ -170,12 +58,11 @@ function handleCustomApp(appTemplate) { Object.keys(appFiles).forEach(k => app[k] = appFiles[k]); console.log("Received custom app", app); modal.remove(); - showProgress(`Uploading ${app.name}`,undefined,"sticky"); Comms.uploadApp(app).then(()=>{ - hideProgress("sticky"); + Progress.hide({sticky:true}); resolve(); }).catch(e => { - hideProgress("sticky"); + Progress.hide({sticky:true}); reject(e); }); }, false); @@ -334,9 +221,8 @@ function refreshLibrary() { // upload icon.classList.remove("icon-upload"); icon.classList.add("loading"); - showProgress(`Uploading ${app.name}`,undefined,"sticky"); Comms.uploadApp(app).then((appJSON) => { - hideProgress("sticky"); + Progress.hide({sticky:true}); if (appJSON) appsInstalled.push(appJSON); showToast(app.name+" Uploaded!", "success"); icon.classList.remove("loading"); @@ -344,7 +230,7 @@ function refreshLibrary() { refreshMyApps(); refreshLibrary(); }).catch(err => { - hideProgress("sticky"); + Progress.hide({sticky:true}); showToast("Upload failed, "+err, "error"); icon.classList.remove("loading"); icon.classList.add("icon-upload"); @@ -403,19 +289,16 @@ function customApp(app) { function updateApp(app) { if (app.custom) return customApp(app); - showProgress(`Upgrading ${app.name}`,undefined,"sticky"); return Comms.removeApp(app).then(()=>{ showToast(app.name+" removed successfully. Updating...",); appsInstalled = appsInstalled.filter(a=>a.id!=app.id); return Comms.uploadApp(app); }).then((appJSON) => { - hideProgress("sticky"); if (appJSON) appsInstalled.push(appJSON); showToast(app.name+" Updated!", "success"); refreshMyApps(); refreshLibrary(); }, err=>{ - hideProgress("sticky"); showToast(app.name+" update failed, "+err,"error"); refreshMyApps(); refreshLibrary(); @@ -488,18 +371,15 @@ return `
function getInstalledApps() { showLoadingIndicator("myappscontainer"); - showProgress(`Getting app list...`,undefined,"sticky"); // Get apps and files return Comms.getInstalledApps() .then(appJSON => { - hideProgress("sticky"); appsInstalled = appJSON; refreshMyApps(); refreshLibrary(); }) .then(() => handleConnectionChange(true)) .catch(err=>{ - hideProgress("sticky"); return Promise.reject(); }); } @@ -555,15 +435,14 @@ document.getElementById("settime").addEventListener("click",event=>{ }); document.getElementById("removeall").addEventListener("click",event=>{ showPrompt("Remove All","Really remove all apps?").then(() => { - showProgress("Removing all apps","animate", "sticky"); return Comms.removeAllApps(); }).then(()=>{ - hideProgress("sticky"); + Progress.hide({sticky:true}); appsInstalled = []; showToast("All apps removed","success"); return getInstalledApps(); }).catch(err=>{ - hideProgress("sticky"); + Progress.hide({sticky:true}); showToast("App removal failed, "+err,"error"); }); }); @@ -578,24 +457,23 @@ document.getElementById("installdefault").addEventListener("click",event=>{ appCount = defaultApps.length; return showPrompt("Install Defaults","Remove everything and install default apps?"); }).then(() => { - showProgress("Removing all apps","animate", "sticky"); return Comms.removeAllApps(); }).then(()=>{ - hideProgress("sticky"); + Progress.hide({sticky:true}); appsInstalled = []; showToast(`Existing apps removed. Installing ${appCount} apps...`); return new Promise((resolve,reject) => { function upload() { var app = defaultApps.shift(); if (app===undefined) return resolve(); - showProgress(`${app.name} (${appCount-defaultApps.length}/${appCount})`,undefined,"sticky"); + Progress.show({title:`${app.name} (${appCount-defaultApps.length}/${appCount})`,sticky:true}); Comms.uploadApp(app,"skip_reset").then((appJSON) => { - hideProgress("sticky"); + Progress.hide({sticky:true}); if (appJSON) appsInstalled.push(appJSON); showToast(`(${appCount-defaultApps.length}/${appCount}) ${app.name} Uploaded`); upload(); }).catch(function() { - hideProgress("sticky"); + Progress.hide({sticky:true}); reject() }); } @@ -607,7 +485,7 @@ document.getElementById("installdefault").addEventListener("click",event=>{ showToast("Default apps successfully installed!","success"); return getInstalledApps(); }).catch(err=>{ - hideProgress("sticky"); + Progress.hide({sticky:true}); showToast("App Install failed, "+err,"error"); }); }); diff --git a/js/ui.js b/js/ui.js new file mode 100644 index 000000000..c88091872 --- /dev/null +++ b/js/ui.js @@ -0,0 +1,140 @@ +// General UI tools (progress bar, toast, prompt) + +/// Handle progress bars +var Progress = { + domElement : null, // the DOM element + sticky : false, // Progress.show({..., sticky:true}) don't remove until Progress.hide({sticky:true}) + interval : undefined, // the interval used if Progress.show({progress:"animate"}) + percent : undefined, // the current progress percentage + min : 0, // scaling for percentage + max : 1, // scaling for percentage + + /* Show a Progress message + Progress.show({ + sticky : bool // keep showing text even when Progress.hide is called (unless Progress.hide({sticky:true})) + percent : number | "animate" + min : // minimum scale for percentage (default 0) + max : // maximum scale for percentage (default 1) + }) */ + show : function(options) { + options = options||{}; + var text = options.title; + if (options.sticky) Progress.sticky = true; + if (options.min!==undefined) Progress.min = options.min; + if (options.max!==undefined) Progress.max = options.max; + var percent = options.percent; + if (percent!==undefined) + percent = Progress.min*100 + (Progress.max-Progress.min)*percent; + if (!Progress.domElement) { + if (Progress.interval) { + clearInterval(Progress.interval); + Progress.interval = undefined; + } + if (percent == "animate") { + Progress.interval = setInterval(function() { + Progress.percent += 2; + if (Progress.percent>100) Progress.percent=0; + Progress.show({percent:Progress.percent}); + }, 100); + percent = 0; + } + + var toastcontainer = document.getElementById("toastcontainer"); + Progress.domElement = htmlElement(`
+ ${text ? `
${text}
`:``} +
+
+
+
`); + toastcontainer.append(Progress.domElement); + } else { + var pt=document.getElementById("Progress.domElement"); + pt.setAttribute("aria-valuenow",percent); + pt.style.width = percent+"%"; + } + }, + // Progress.hide({sticky:true}) undoes Progress.show({title:"title", sticky:true}) + hide : function(options) { + options = options||{}; + if (Progress.sticky && !options.sticky) + return; + Progress.sticky = false; + Progress.min = 0; + Progress.max = 1; + if (Progress.interval) { + clearInterval(Progress.interval); + Progress.interval = undefined; + } + if (Progress.domElement) Progress.domElement.remove(); + Progress.domElement = undefined; + } +}; + +/// Add progress handler so we get nice uploads +Puck.writeProgress = function(charsSent, charsTotal) { + if (charsSent===undefined) { + Progress.hide(); + return; + } + var percent = Math.round(charsSent*100/charsTotal); + Progress.show({percent: percent}); +} + +/// Show a 'toast' message for status +function showToast(message, type) { + // toast-primary, toast-success, toast-warning or toast-error + var style = "toast-primary"; + if (type=="success") style = "toast-success"; + else if (type=="error") style = "toast-error"; + else if (type!==undefined) console.log("showToast: unknown toast "+type); + var toastcontainer = document.getElementById("toastcontainer"); + var msgDiv = htmlElement(`
`); + msgDiv.innerHTML = message; + toastcontainer.append(msgDiv); + setTimeout(function() { + msgDiv.remove(); + }, 5000); +} + +/// Show a yes/no prompt +function showPrompt(title, text, buttons) { + if (!buttons) buttons={yes:1,no:1}; + return new Promise((resolve,reject) => { + var modal = htmlElement(``); + document.body.append(modal); + modal.querySelector("a[href='#close']").addEventListener("click",event => { + event.preventDefault(); + reject("User cancelled"); + modal.remove(); + }); + htmlToArray(modal.getElementsByTagName("button")).forEach(button => { + button.addEventListener("click",event => { + event.preventDefault(); + var isYes = event.target.getAttribute("isyes")=="1"; + if (isYes) resolve(); + else reject("User cancelled"); + modal.remove(); + }); + }); + }); +} From f66aab5823c6b1974c38ebf729c223017740773b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 3 Apr 2020 15:15:06 +0100 Subject: [PATCH 37/40] Add page to export files from Stopwatch, save files in better format and with better filename --- apps.json | 3 +- apps/heart/interface.html | 12 +---- apps/swatch/ChangeLog | 1 + apps/swatch/interface.html | 90 ++++++++++++++++++++++++++++++++++++++ apps/swatch/stopwatch.js | 32 +++++++------- lib/interface.js | 18 +++++++- 6 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 apps/swatch/interface.html diff --git a/apps.json b/apps.json index d502b37c4..18d20b9da 100644 --- a/apps.json +++ b/apps.json @@ -392,7 +392,8 @@ { "id": "swatch", "name": "Stopwatch", "icon": "stopwatch.png", - "version":"0.03", + "version":"0.04", + "interface": "interface.html", "description": "Simple stopwatch with Lap Time logging to a JSON file", "tags": "health", "allow_emulator":true, diff --git a/apps/heart/interface.html b/apps/heart/interface.html index 177e2cdfb..4a21d2e27 100644 --- a/apps/heart/interface.html +++ b/apps/heart/interface.html @@ -11,17 +11,7 @@ var domRecords = document.getElementById("records"); function saveRecord(record,name) { var csv = `${record.map(rec=>[rec.time, rec.bpm, rec.confidence].join(",")).join("\n")}`; - var a = document.createElement("a"), - file = new Blob([csv], {type: "Comma-separated value file"}); - var url = URL.createObjectURL(file); - a.href = url; - a.download = name+".csv"; - document.body.appendChild(a); - a.click(); - setTimeout(function() { - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - }, 0); + Util.saveCSV(name, csv); } diff --git a/apps/swatch/ChangeLog b/apps/swatch/ChangeLog index 86a782585..2900c9aa1 100644 --- a/apps/swatch/ChangeLog +++ b/apps/swatch/ChangeLog @@ -3,3 +3,4 @@ Lap log now scrolls into 2nd column after 18th entry, able to display 36 entries before going off screen 0.03: Added ability to save Lap log as a date named JSON file into memory Fixed bug from 0.01 where BN1 (reset) could clear the lap log when timer is running +0.04: Changed save file filename, add interface.html to allow laps to be loaded diff --git a/apps/swatch/interface.html b/apps/swatch/interface.html new file mode 100644 index 000000000..928c5fe39 --- /dev/null +++ b/apps/swatch/interface.html @@ -0,0 +1,90 @@ + + + + + +
+ + + + + diff --git a/apps/swatch/stopwatch.js b/apps/swatch/stopwatch.js index d4136d8ed..2ebd8198b 100644 --- a/apps/swatch/stopwatch.js +++ b/apps/swatch/stopwatch.js @@ -4,7 +4,6 @@ var started = false; var timeY = 60; var hsXPos = 0; var lapTimes = []; -var saveTimes = []; var displayInterval; function timeToText(t) { @@ -25,7 +24,7 @@ function updateLabels() { for (var i in lapTimes) { if (i<18) {g.drawString(lapTimes.length-i+": "+timeToText(lapTimes[i]),35,timeY + 30 + i*8);} - else + else {g.drawString(lapTimes.length-i+": "+timeToText(lapTimes[i]),125,timeY + 30 + (i-18)*8);} } drawsecs(); @@ -51,10 +50,8 @@ function drawms() { g.clearRect(hsXPos,timeY,220,timeY+20); g.drawString("."+("0"+hs).substr(-2),hsXPos,timeY+10); } -function saveconvert() { - for (var v in lapTimes){ - saveTimes[v]=v+1+"-"+timeToText(lapTimes[(lapTimes.length-1)-v]); - } +function getLapTimesArray() { + return lapTimes.map(timeToText).reverse(); } setWatch(function() { // Start/stop @@ -80,16 +77,21 @@ setWatch(function() { // Start/stop }, BTN2, {repeat:true}); setWatch(function() { // Lap Bangle.beep(); - if (started) tCurrent = Date.now(); - lapTimes.unshift(tCurrent-tStart); - tStart = tCurrent; - if (!started) - { - var timenow= Date(); - saveconvert(); - require("Storage").writeJSON("StpWch-"+timenow.toString(), saveTimes); + if (started) { + tCurrent = Date.now(); + lapTimes.unshift(tCurrent-tStart); + } + tStart = tCurrent; + if (!started) { // save + var timenow= Date(); + var filename = "swatch-"+(new Date()).toISOString().substr(0,16).replace("T","_")+".json"; + // this maxes out the 28 char maximum + require("Storage").writeJSON(filename, getLapTimesArray()); + E.showMessage("Laps Saved","Stopwatch"); + setTimeout(updateLabels, 1000); + } else { + updateLabels(); } - updateLabels(); }, BTN1, {repeat:true}); setWatch(function() { // Reset if (!started) { diff --git a/lib/interface.js b/lib/interface.js index 414c9d7fb..7e8be4fd9 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -39,7 +39,10 @@ var Util = { window.postMessage({type:"readstoragefile",data:filename,id:__id}); }, eraseStorageFile : function(filename,callback) { - Puck.write(`\x10require("Storage").open(${JSON.stringify(filename)}","r").erase()\n`,callback); + Puck.write(`\x10require("Storage").open(${JSON.stringify(filename)},"r").erase()\n`,callback); + }, + eraseStorage : function(filename,callback) { + Puck.write(`\x10require("Storage").erase(${JSON.stringify(filename)})\n`,callback); }, showModal : function(title) { if (!Util.domModal) { @@ -66,6 +69,19 @@ var Util = { hideModal : function() { if (!Util.domModal) return; Util.domModal.classList.remove("active"); + }, + saveCSV : function(filename, csvData) { + var a = document.createElement("a"), + file = new Blob([csvData], {type: "Comma-separated value file"}); + var url = URL.createObjectURL(file); + a.href = url; + a.download = filename+".csv"; + document.body.appendChild(a); + a.click(); + setTimeout(function() { + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }, 0); } }; window.addEventListener("message", function(event) { From fbce9aaa7cd1bddaefa71ee8a9b15fb54912d7f1 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 3 Apr 2020 15:24:09 +0100 Subject: [PATCH 38/40] stopwatch - Added widgets --- apps.json | 2 +- apps/swatch/ChangeLog | 1 + apps/swatch/stopwatch.js | 14 +++++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps.json b/apps.json index 18d20b9da..dcb27d1d1 100644 --- a/apps.json +++ b/apps.json @@ -392,7 +392,7 @@ { "id": "swatch", "name": "Stopwatch", "icon": "stopwatch.png", - "version":"0.04", + "version":"0.05", "interface": "interface.html", "description": "Simple stopwatch with Lap Time logging to a JSON file", "tags": "health", diff --git a/apps/swatch/ChangeLog b/apps/swatch/ChangeLog index 2900c9aa1..3246eeced 100644 --- a/apps/swatch/ChangeLog +++ b/apps/swatch/ChangeLog @@ -4,3 +4,4 @@ 0.03: Added ability to save Lap log as a date named JSON file into memory Fixed bug from 0.01 where BN1 (reset) could clear the lap log when timer is running 0.04: Changed save file filename, add interface.html to allow laps to be loaded +0.05: Added widgets diff --git a/apps/swatch/stopwatch.js b/apps/swatch/stopwatch.js index 2ebd8198b..6f8ad9e34 100644 --- a/apps/swatch/stopwatch.js +++ b/apps/swatch/stopwatch.js @@ -13,24 +13,26 @@ function timeToText(t) { return mins+":"+("0"+secs).substr(-2)+"."+("0"+hs).substr(-2); } function updateLabels() { - g.clear(); + g.reset(1); + g.clearRect(0,23,g.getWidth()-1,g.getHeight()-24); g.setFont("6x8",2); g.setFontAlign(0,0,3); g.drawString(started?"STOP":"GO",230,120); - if (!started) g.drawString("RESET",230,190); + if (!started) g.drawString("RESET",230,180); g.drawString(started?"LAP":"SAVE",230,50); g.setFont("6x8",1); g.setFontAlign(-1,-1); for (var i in lapTimes) { - if (i<18) + if (i<16) {g.drawString(lapTimes.length-i+": "+timeToText(lapTimes[i]),35,timeY + 30 + i*8);} - else - {g.drawString(lapTimes.length-i+": "+timeToText(lapTimes[i]),125,timeY + 30 + (i-18)*8);} + else if (i<32) + {g.drawString(lapTimes.length-i+": "+timeToText(lapTimes[i]),125,timeY + 30 + (i-16)*8);} } drawsecs(); } function drawsecs() { var t = tCurrent-tStart; + g.reset(1); g.setFont("Vector",48); g.setFontAlign(0,0); var secs = Math.floor(t/1000)%60; @@ -103,3 +105,5 @@ setWatch(function() { // Reset }, BTN3, {repeat:true}); updateLabels(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); From c60be707d82f9a3de6d0073afd8ba2dade9ecaa7 Mon Sep 17 00:00:00 2001 From: OmegaRogue Date: Fri, 3 Apr 2020 17:37:54 +0200 Subject: [PATCH 39/40] Added PWA and Browser Theme Support Add more favicon resolutions Add Progressive Web App Support Add Theme and Icon support for Safari Add Theme and Icon Support for Windows Metro Add Theme and Icon Support for Android Chrome Signed-off-by: OmegaRogue --- Bangle.js.svg | 478 +++++++++++++++++++++++++++++++++ browserconfig.xml | 9 + favicon.ico | Bin 1150 -> 0 bytes img/android-chrome-192x192.png | Bin 0 -> 4534 bytes img/android-chrome-512x512.png | Bin 0 -> 13713 bytes img/apple-touch-icon.png | Bin 0 -> 2905 bytes img/favicon-16x16.png | Bin 0 -> 586 bytes img/favicon-32x32.png | Bin 0 -> 860 bytes img/favicon.ico | Bin 0 -> 15086 bytes img/mstile-150x150.png | Bin 0 -> 3364 bytes img/safari-pinned-tab.svg | 100 +++++++ index.html | 13 +- site.webmanifest | 19 ++ 13 files changed, 618 insertions(+), 1 deletion(-) create mode 100644 Bangle.js.svg create mode 100644 browserconfig.xml delete mode 100644 favicon.ico create mode 100644 img/android-chrome-192x192.png create mode 100644 img/android-chrome-512x512.png create mode 100644 img/apple-touch-icon.png create mode 100644 img/favicon-16x16.png create mode 100644 img/favicon-32x32.png create mode 100644 img/favicon.ico create mode 100644 img/mstile-150x150.png create mode 100644 img/safari-pinned-tab.svg create mode 100644 site.webmanifest diff --git a/Bangle.js.svg b/Bangle.js.svg new file mode 100644 index 000000000..90c908c9b --- /dev/null +++ b/Bangle.js.svg @@ -0,0 +1,478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/browserconfig.xml b/browserconfig.xml new file mode 100644 index 000000000..0d56ca0d6 --- /dev/null +++ b/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #5755d9 + + + diff --git a/favicon.ico b/favicon.ico deleted file mode 100644 index 24ae659663f56ad85ce80aa656cd909b5977d7ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 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 COGRY> literal 0 HcmV?d00001 diff --git a/img/android-chrome-512x512.png b/img/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..6a147322241d7824b7d762c9e8d74c3c449c6769 GIT binary patch literal 13713 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 literal 0 HcmV?d00001 diff --git a/img/apple-touch-icon.png b/img/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5d62c1241c45e59abbca778b349fe648104d78 GIT binary patch literal 2905 zcmV-f3#RmmP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 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 JnJ45o{6FF@5n=!U literal 0 HcmV?d00001 diff --git a/img/mstile-150x150.png b/img/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..aae99969123d2a6c4fdfb081b2ff8ac17c6288cf GIT binary patch literal 3364 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 + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + + + + diff --git a/index.html b/index.html index efaf84a61..385e41a4b 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,17 @@ + + + + + + + + + + + Bangle.js App Loader