1
0
Fork 0
BangleApps/apps/kitchen/kitchen.app.js

665 lines
18 KiB
JavaScript

// read in the faces
var FACES = [];
var STOR = require("Storage");
STOR.list(/\.kit\.js$/).forEach(face=>FACES.push(eval(require("Storage").read(face))));
var iface = STOR.list(/\.kit\.js$/).indexOf("stepo.kit.js");
var face = FACES[iface]();
var firstPress
var pressTimer;
function stopdraw() {
face.stopTimer();
}
function startdraw() {
Bangle.drawWidgets();
face.startTimer();
}
function nextFace(){
stopdraw();
face.freeResources();
iface += 1
iface = iface % FACES.length;
face = FACES[iface]();
g.clear();
g.reset();
face.init(gpsObj, swObj);
startdraw();
}
// when you feel the buzzer you know you have done a long press
function longPressCheck() {
Bangle.buzz();
if (pressTimer) {
clearInterval(pressTimer);
pressTimer = undefined;
}
}
// start a timer and buzz when held long enough
function buttonPressed(btn) {
if (btn === 3) {
nextFace();
} else {
firstPress = getTime();
pressTimer = setInterval(longPressCheck, 1500);
}
}
// if you release too soon there is no buzz as timer is cleared
function buttonReleased(btn) {
var dur = getTime() - firstPress;
if (pressTimer) {
clearInterval(pressTimer);
pressTimer = undefined;
}
if ( dur >= 1.5 ) {
switch(btn) {
case 1:
face.onButtonLong(btn);
break;
case 2:
Bangle.showLauncher();
break;
case 3:
// do nothing
break;
}
return;
}
if (btn !== 3) face.onButtonShort(btn);
}
function setButtons(){
setWatch(buttonPressed.bind(null,1), BTN1, {repeat:true,edge:"rising"});
setWatch(buttonPressed.bind(null,2), BTN2, {repeat:true,edge:"rising"});
setWatch(nextFace, BTN3, {repeat:true,edge:"rising"});
setWatch(buttonReleased.bind(null,1), BTN1, {repeat:true,edge:"falling"});
setWatch(buttonReleased.bind(null,2), BTN2, {repeat:true,edge:"falling"});
// BTN 3 long press should always reset the bangle
}
Bangle.on('kill',()=>{
Bangle.setCompassPower(0);
Bangle.setGPSPower(0);
});
Bangle.on('lcdPower',function(on) {
if (on) {
startdraw();
} else {
stopdraw();
}
});
/*****************************************************************************
Start of GPS object code so we can share it between faces
******************************************************************************/
function log_debug(o) {
//console.log(o);
}
function radians(a) {
return a*Math.PI/180;
}
function degrees(a) {
var d = a*180/Math.PI;
return (d+360)%360;
}
function GPS() {
this.wp;
this.wp_index = 0;
this.wp_current = undefined;
this.GPS_OFF = 0;
this.GPS_TIME = 1;
this.GPS_SATS = 2;
this.GPS_RUNNING = 3;
this.gpsState = this.GPS_OFF;
this.gpsPowerState = false;
this.last_fix = {
fix: 0,
alt: 0,
lat: 0,
lon: 0,
speed: 0,
time: 0,
satellites: 0
};
this.listenerCount = 0;
this.loadFirstWaypoint();
}
GPS.prototype.log_debug = function(o) {
//console.log(o);
};
GPS.prototype.getState = function() {
return this.gpsState;
}
GPS.prototype.getLastFix = function() {
return this.last_fix;
}
GPS.prototype.determineGPSState = function() {
this.log_debug("determineGPSState");
gpsPowerState = Bangle.isGPSOn();
//this.log_debug("last_fix.fix " + this.last_fix.fix);
//this.log_debug("gpsPowerState " + this.gpsPowerState);
//this.log_debug("last_fix.satellites " + this.last_fix.satellites);
if (!gpsPowerState) {
this.gpsState = this.GPS_OFF;
this.resetLastFix();
} else if (this.last_fix.fix && this.gpsPowerState && this.last_fix.satellites > 0) {
this.gpsState = this.GPS_RUNNING;
} else {
this.gpsState = this.GPS_SATS;
}
this.log_debug("gpsState=" + this.gpsState);
if (this.gpsState !== this.GPS_OFF) {
if (this.listenerCount === 0) {
Bangle.on('GPS', processFix);
this.listenerCount++;
this.log_debug("listener added " + this.listenerCount);
}
} else {
if (this.listenerCount > 0) {
Bangle.removeListener("GPS", this.processFix);
this.listenerCount--;
this.log_debug("listener removed " + this.listenerCount);
}
}
};
GPS.prototype.getGPSTime = function() {
var time;
if (this.last_fix !== undefined && this.last_fix.time !== undefined && this.last_fix.time.toUTCString !== undefined &&
(this.gpsState == this.GPS_SATS || this.gpsState == this.GPS_RUNNING)) {
time = this.last_fix.time.toUTCString().split(" ");
return time[4];
} else {
var d = new Date();
var da = d.toString().split(" ");
time = da[4].substr(0,5);
return time;
}
};
GPS.prototype.toggleGPSPower = function() {
this.log_debug("toggleGPSPower()");
this.gpsPowerState = Bangle.isGPSOn();
this.gpsPowerState = !this.gpsPowerState;
Bangle.setGPSPower(this.gpsPowerState ? 1 : 0);
this.resetLastFix();
this.determineGPSState();
// poke the gps widget indicator to change
if (WIDGETS.gps !== undefined) {
WIDGETS.gps.draw();
}
};
GPS.prototype.resetLastFix = function() {
this.last_fix = {
fix: 0,
alt: 0,
lat: 0,
lon: 0,
speed: 0,
time: 0,
satellites: 0
};
};
function processFix(fix) {
//log_debug("processFix()");
gpsObj.processFix(fix);
}
GPS.prototype.processFix = function(fix) {
//this.log_debug("GPS:processFix()");
//this.log_debug(fix);
this.last_fix.time = fix.time;
if (this.gpsState == this.GPS_TIME) {
this.gpsState = this.GPS_SATS;
}
if (fix.fix) {
//this.log_debug("Got fix - setting state to GPS_RUNNING");
this.gpsState = this.GPS_RUNNING;
if (!this.last_fix.fix && !(require("Storage").readJSON("setting.json", 1) || {}).quiet) {
Bangle.buzz(); // buzz on first position
}
this.last_fix = fix;
}
};
GPS.prototype.formatTime = function(now) {
var fd = now.toUTCString().split(" ");
return fd[4];
};
GPS.prototype.timeSince = function(t) {
var hms = t.split(":");
var now = new Date();
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
return (sn - st);
};
GPS.prototype.getOsRef = function() {
let os = OsGridRef.latLongToOsGrid(this.last_fix);
let ref = to_map_ref(6, os.easting, os.northing);
return ref;
};
GPS.prototype.calcBearing = function(a,b) {
var delta = radians(b.lon-a.lon);
var alat = radians(a.lat);
var blat = radians(b.lat);
var y = Math.sin(delta) * Math.cos(blat);
var x = Math.cos(alat)*Math.sin(blat) -
Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
return Math.round(degrees(Math.atan2(y, x)));
}
GPS.prototype.calcDistance = function(a,b) {
var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
var y = radians(b.lat-a.lat);
return Math.round(Math.sqrt(x*x + y*y) * 6371000);
}
GPS.prototype.getWPdistance = function() {
//log_debug(this.last_fix);
//log_debug(this.wp_current);
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return 0;
else
return this.calcDistance(this.last_fix, this.wp_current);
}
GPS.prototype.getWPbearing = function() {
//log_debug(this.last_fix);
//log_debug(this.wp_current);
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return 0;
else
return this.calcBearing(this.last_fix, this.wp_current);
}
GPS.prototype.loadFirstWaypoint = function() {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
this.wp_index = 0;
this.wp_current = waypoints[this.wp_index];
log_debug(this.wp_current);
return this.wp_current;
}
GPS.prototype.getCurrentWaypoint = function() {
return this.wp_current;
}
GPS.prototype.waypointHasLocation = function() {
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return false;
else
return true;
}
GPS.prototype.markWaypoint = function() {
if(this.wp_current.name === "NONE")
return;
log_debug("GPS::markWaypoint()");
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
this.wp_current = waypoints[this.wp_index];
if (this.waypointHasLocation()) {
waypoints[this.wp_index] = {name:this.wp_current.name, lat:0, lon:0};
} else {
waypoints[this.wp_index] = {name:this.wp_current.name, lat:this.last_fix.lat, lon:this.last_fix.lon};
}
this.wp_current = waypoints[this.wp_index];
require("Storage").writeJSON("waypoints.json", waypoints);
log_debug("GPS::markWaypoint() written");
}
GPS.prototype.nextWaypoint = function(inc) {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
this.wp_index+=inc;
if (this.wp_index>=waypoints.length) this.wp_index=0;
if (this.wp_index<0) this.wp_index = waypoints.length-1;
this.wp_current = waypoints[this.wp_index];
log_debug(this.wp_current);
return this.wp_current;
}
var gpsObj = new GPS();
/*****************************************************************************
Start of OS lat lon to grid ref code
******************************************************************************/
Number.prototype.toRad = function() { return this*Math.PI/180; };
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2014 */
/* - www.movable-type.co.uk/scripts/gridref.js */
/* - www.movable-type.co.uk/scripts/latlon-gridref.html */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function OsGridRef(easting, northing) {
this.easting = 0|easting;
this.northing = 0|northing;
}
OsGridRef.latLongToOsGrid = function(point) {
var lat = point.lat.toRad();
var lon = point.lon.toRad();
var a = 6377563.396, b = 6356256.909; // Airy 1830 major & minor semi-axes
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
var lat0 = (49).toRad(), lon0 = (-2).toRad(); // NatGrid true origin is 49�N,2�W
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
var eta2 = nu/rho-1;
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
var M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
var cos3lat = cosLat*cosLat*cosLat;
var cos5lat = cos3lat*cosLat*cosLat;
var tan2lat = Math.tan(lat)*Math.tan(lat);
var tan4lat = tan2lat*tan2lat;
var I = M + N0;
var II = (nu/2)*sinLat*cosLat;
var III = (nu/24)*sinLat*cos3lat*(5-tan2lat+9*eta2);
var IIIA = (nu/720)*sinLat*cos5lat*(61-58*tan2lat+tan4lat);
var IV = nu*cosLat;
var V = (nu/6)*cos3lat*(nu/rho-tan2lat);
var VI = (nu/120) * cos5lat * (5 - 18*tan2lat + tan4lat + 14*eta2 - 58*tan2lat*eta2);
var dLon = lon-lon0;
var dLon2 = dLon*dLon, dLon3 = dLon2*dLon, dLon4 = dLon3*dLon, dLon5 = dLon4*dLon, dLon6 = dLon5*dLon;
var N = I + II*dLon2 + III*dLon4 + IIIA*dLon6;
var E = E0 + IV*dLon + V*dLon3 + VI*dLon5;
return new OsGridRef(E, N);
};
/*
* converts northing, easting to standard OS grid reference.
*
* [digits=10] - precision (10 digits = metres)
* to_map_ref(8, 651409, 313177); => 'TG 5140 1317'
* to_map_ref(0, 651409, 313177); => '651409,313177'
*
*/
function to_map_ref(digits, easting, northing) {
if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision '${digits}'`); // eslint-disable-line comma-spacing
let e = easting;
let n = northing;
// use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7
if (digits == 0) {
const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };
const ePad = e.toLocaleString('en', format);
const nPad = n.toLocaleString('en', format);
return `${ePad},${nPad}`;
}
// get the 100km-grid indices
const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);
// translate those into numeric equivalents of the grid letters
let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);
let l2 = (19 - n100km) * 5 % 25 + e100km % 5;
// compensate for skipped 'I' and build grid letter-pairs
if (l1 > 7) l1++;
if (l2 > 7) l2++;
const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));
// strip 100km-grid indices from easting & northing, and reduce precision
e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));
n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));
// pad eastings & northings with leading zeros
e = e.toString().padStart(digits/2, '0');
n = n.toString().padStart(digits/2, '0');
return `${letterPair} ${e} ${n}`;
}
/*****************************************************************************
Stopwatch Class Code
******************************************************************************/
function STOPWATCH() {
this.tTotal = Date.now();
this.tStart = Date.now();
this.tCurrent = Date.now();
this.running = false;
this.timeY = 45;
this.TtimeY = 75;
this.lapTimes = [];
this.displayInterval;
this.redrawButtons = true;
this.redrawLaps = true;
this.redrawTime = true;
}
STOPWATCH.prototype.log_debug = function(o) {
//console.log(o);
}
STOPWATCH.prototype.timeToText = function(t) {
let hrs = Math.floor(t/3600000);
let mins = Math.floor(t/60000)%60;
let secs = Math.floor(t/1000)%60;
let text;
if (hrs === 0)
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
else
text = (""+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
this.log_debug(text);
return text;
}
STOPWATCH.prototype.getLapTimesArray = function() {
this.lapTimes.push(tCurrent-tTotal);
return this.lapTimes.map(timeToText).reverse();
}
STOPWATCH.prototype.stopStart = function() {
this.log_debug("stopStart()");
this.running = !this.running;
if (this.running)
this.tStart = Date.now() + this.tStart - this.tCurrent;
this.tTotal = Date.now() + this.tTotal - this.tCurrent;
this.tCurrent = Date.now();
this.redrawButtons = true;
this.redrawLaps = true;
this.redrawTime = true;
this.draw();
}
STOPWATCH.prototype.lap = function() {
this.log_debug("lap()");
if (this.running) {
this.tCurrent = Date.now();
this.lapTimes.unshift(this.tCurrent - this.tStart);
log_debug(this.tCurrent - this.tStart);
}
this.tStart = this.tCurrent;
this.redrawButtons = true;
this.redrawLaps = true;
this.draw();
}
STOPWATCH.prototype.reset = function() {
this.log_debug("reset()");
if (this.running === false) {
this.tStart = this.tCurrent = this.tTotal = Date.now();
this.lapTimes = [];
}
g.clear();
this.draw();
}
// lap or reset
STOPWATCH.prototype.lapOrReset = function() {
this.redrawButtons = true;
this.redrawLaptimes = true;
this.redrawTime = true;
this.log_debug("lapReset()");
if (this.running)
this.lap()
else
this.reset();
}
STOPWATCH.prototype.draw = function() {
if (this.running) this.tCurrent = Date.now();
this.log_debug("draw()" + getTime());
g.setColor(1,1,1);
if (this.redrawButtons) this.drawButtons();
if (this.running || this.redrawTime) this.drawTime();
if (this.redrawLaps) this.drawLaptimes();
}
STOPWATCH.prototype.drawButtons = function() {
this.log_debug("drawButtons()");
g.clearRect(0,23,g.getWidth()-1,g.getHeight()-24);
g.setColor(1,1,1);
g.setFont("Vector", 20);
g.setFontAlign(0,0,3);
g.drawString(this.running? "STOP" : "GO", 230, 50); // BTN1
g.drawString(this.running? "LAP" : "RESET", 230, 120); // BTN2
this.redrawButtons = false;
}
STOPWATCH.prototype.drawLaptimes = function() {
g.setFont("Vector",24);
g.setFontAlign(-1,-1);
g.clearRect(4, 205, 239, 229); // clear the last line of the lap times
let laps = 0;
for (let i in this.lapTimes) {
g.drawString(this.lapTimes.length-i + ": " + this.timeToText(this.lapTimes[i]), 4, this.timeY + 40 + i*24);
if (++laps > 5) break;
}
this.redrawLaps = false;
}
STOPWATCH.prototype.drawTime = function() {
this.log_debug("drawTime()");
let tLap = this.tCurrent - this.tStart;
let tTotal = this.tCurrent - this.tTotal;
let txtLap = this.timeToText(tLap);
let txtTotal = this.timeToText(tTotal);
let xTotal = 100;
let xLap = 125;
// total time
g.setFont("Vector",38);
g.setFontAlign(0,0);
g.clearRect(0, this.timeY-21, 200, this.timeY+21);
g.setColor(0xFFC0);
g.drawString(txtTotal, xTotal, this.timeY);
// current lap time
g.setFont("Vector", 20);
g.clearRect(0, this.TtimeY-7, 200, this.TtimeY+7);
g.setColor(1,1,1);
g.drawString(txtLap, xLap, this.TtimeY);
this.redrawTime = false;
}
STOPWATCH.prototype.startTimer = function() {
this.log_debug("startTimer()");
this.redrawButtons = true;
this.redrawLaps = true;
this.redrawTime = true;
this.draw();
this.displayInterval = setInterval(stopwatchDraw, 1000);
}
STOPWATCH.prototype.stopTimer = function() {
this.log_debug("stopTimer()");
if (this.displayInterval) {
clearInterval(this.displayInterval);
this.displayInterval = undefined;
}
}
let swObj = new STOPWATCH();
function stopwatchDraw() {
swObj.draw();
}
/*****************************************************************************
Start App
******************************************************************************/
g.clear();
Bangle.loadWidgets();
face.init(gpsObj,swObj);
startdraw();
setButtons();