Merge branch 'espruino:master' into master

pull/3099/head
nxdefiant 2023-11-16 19:12:39 +01:00 committed by GitHub
commit 6a0236aa44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 381 additions and 19 deletions

1
apps/forge/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: attempt to import

8
apps/forge/README.md Normal file
View File

@ -0,0 +1,8 @@
# App Forge
This should help with your hacks. Sometimes, you want to work on an
application, you'd want to use the stable version, but you'd also want
to use latest development version.
Well, this makes it easy. Just save your development version as
a.name.js, and you should be able to run it from the menu system.

1
apps/forge/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgIhe/AEDgOKAocDwgFDgUEAokKAokTAohDEg0hgEgAoMEoMIoAFCgME4AFCwUCwAFBgoeChEAg8GAoMYEYMECIM4AoMMgFAuEAhv4gkg+EAhPghExAoIACg4FEh4FEj4FEn56Ev/8iAFC///CQUBAoPgQoQFBLYUHAoJbCh4FBFwf//wuD//8Fwf/GoYuNAoUGGggMCGgQeCbIl+Aol8Aol4Aoh2EgFgf5kAA"))

BIN
apps/forge/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

32
apps/forge/forge.app.js Normal file
View File

@ -0,0 +1,32 @@
// App Forge
st = require('Storage');
l = /^a\..*\.js$/;
//l = /.*\.js/;
l = st.list(l, {sf:false});
print(l);
function on_load(x) {
print("Loading", x);
Bangle.buzz(50, 1); // Won't happen because load() is quicker
g.reset().clear()
.setFont("Vector", 40)
.drawString("Loading", 0, 30)
.drawString(x, 0, 80);
g.flip();
load(x);
}
var menu = {
"< Back" : Bangle.load
};
if (l.length==0) Object.assign(menu, {"No apps":""});
else for (let id in l) {
let i = id;
menu[l[id]]=()=>{ on_load(l[i]); };
}
g.clear();
E.showMenu(menu);

13
apps/forge/metadata.json Normal file
View File

@ -0,0 +1,13 @@
{ "id": "forge",
"name": "App Forge",
"version":"0.01",
"description": "Easy way to run development versions of your apps",
"icon": "app.png",
"readme": "README.md",
"supports" : ["BANGLEJS2"],
"tags": "tool",
"storage": [
{"name":"forge.app.js","url":"forge.app.js"},
{"name":"forge.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -39,4 +39,6 @@
0.31: Ensure that background-drawn tracks can get cancelled, and draw less at a time to make updates smoother
plotTrack now draws the current track even if you're not actively recording
0.32: Add cadence data to output files
0.33: Ensure that a new file is always created if the stuff that's being recorded has changed (fix #3081)
0.33: Ensure that a new file is always created if the stuff that's being recorded has changed (fix #3081)
0.34: Avoid prompting when creating a new file (#3081)
0.35: Handle loading without a settings file (default record setting)

View File

@ -2,7 +2,7 @@
"id": "recorder",
"name": "Recorder",
"shortName": "Recorder",
"version": "0.33",
"version": "0.35",
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
"icon": "app.png",
"tags": "tool,outdoors,gps,widget,clkinfo",

View File

@ -9,6 +9,8 @@
settings.period = settings.period||10;
if (!settings.file || !settings.file.startsWith("recorder.log"))
settings.recording = false;
if (!settings.record)
settings.record = ["gps"];
return settings;
}
@ -159,7 +161,7 @@
return recorders;
}
let getActiveRecorders = function() {
let getActiveRecorders = function(settings) {
let activeRecorders = [];
let recorders = getRecorders();
settings.record.forEach(r => {
@ -204,7 +206,7 @@
if (settings.recording) {
// set up recorders
activeRecorders = getActiveRecorders();
activeRecorders = getActiveRecorders(settings);
activeRecorders.forEach(activeRecorder => {
activeRecorder.start();
});
@ -252,7 +254,11 @@
settings.file = getTrackFilename();
}
var headers = require("Storage").open(settings.file,"r").readLine();
if (headers && headers.trim()==getCSVHeaders(getActiveRecorders()).join(",")){ // if file exists AND the headers match (#3081)
if (headers){ // if file exists
if(headers.trim()!==getCSVHeaders(getActiveRecorders(settings)).join(",")){
// headers don't match, reset (#3081)
options.force = "new";
}
if (!options.force) { // if not forced, ask the question
g.reset(); // work around bug in 2v17 and earlier where bg color wasn't reset
return E.showPrompt(

View File

@ -75,4 +75,5 @@ of 'Select Clock'
0.65: Prepend 'LCD->Calibration' touch listener and stop event propagation.
0.66: Fix LCD calibration bug where it would come on again after the
calibration was done.
0.67: Rename 'Wake on BTN1/Touch' to 'Wake on Button/Tap' on Bangle.js 2

View File

@ -32,7 +32,7 @@ This is Bangle.js's settings menu
* **Rotation** allows you to rotate (or mirror) what's displayed on the screen, eg. for left-handed wearers (needs 2v16 or 2v15 cutting edge firmware to work reliably)
* **Wake on X** should the given activity wake up the Bangle.js LCD?
* On Bangle.js 2 when locked the touchscreen is turned off to save power. Because of this,
`Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js.
`Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js (we recently renamed the menu item to `Wake on Tap`).
* **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement.
* **Calibrate** on Bangle.js 2, pop up a screen allowing you to calibrate the touchscreen (calibration only works on 2v16 or 2v15 cutting edge builds)

View File

@ -1,7 +1,7 @@
{
"id": "setting",
"name": "Settings",
"version": "0.66",
"version": "0.67",
"description": "A menu for setting up Bangle.js",
"icon": "settings.png",
"tags": "tool,system",

View File

@ -448,17 +448,35 @@ function showLCDMenu() {
g.setRotation(settings.rotate&3,settings.rotate>>2).clear();
Bangle.drawWidgets();
}
}
});
if (BANGLEJS2)
Object.assign(lcdMenu, {
/*LANG*/'Wake on Button': {
value: settings.options.wakeOnBTN1,
onchange: () => {
settings.options.wakeOnBTN1 = !settings.options.wakeOnBTN1;
updateOptions();
}
}
},
/*LANG*/'Wake on BTN1': {
/*LANG*/'Wake on Tap': {
value: settings.options.wakeOnTouch,
onchange: () => {
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
updateOptions();
}
});
else
Object.assign(lcdMenu, {
/*LANG*/'Wake on BTN1': {
value: settings.options.wakeOnBTN1,
onchange: () => {
settings.options.wakeOnBTN1 = !settings.options.wakeOnBTN1;
updateOptions();
}
}
});
if (!BANGLEJS2)
Object.assign(lcdMenu, {
},
/*LANG*/'Wake on BTN2': {
value: settings.options.wakeOnBTN2,
onchange: () => {
@ -472,6 +490,13 @@ function showLCDMenu() {
settings.options.wakeOnBTN3 = !settings.options.wakeOnBTN3;
updateOptions();
}
},
/*LANG*/'Wake on Touch': {
value: settings.options.wakeOnTouch,
onchange: () => {
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
updateOptions();
}
}});
Object.assign(lcdMenu, {
/*LANG*/'Wake on FaceUp': {
@ -481,13 +506,6 @@ function showLCDMenu() {
updateOptions();
}
},
/*LANG*/'Wake on Touch': {
value: settings.options.wakeOnTouch,
onchange: () => {
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
updateOptions();
}
},
/*LANG*/'Wake on Twist': {
value: settings.options.wakeOnTwist,
onchange: () => {

1
apps/skyspy/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: attempt to import

4
apps/skyspy/README.md Normal file
View File

@ -0,0 +1,4 @@
# Sky Spy
Application to view GPS satellite positions and whether watch can
receive data from them.

2
apps/skyspy/app-icon.js Normal file
View File

@ -0,0 +1,2 @@
require("heatshrink").decompress(atob("mEwgIQNgQFEj/gAof+jgECgeAAIIFBgwCBuACBhgCEjAOEAoQ6CmAhCDwItDoEB4AFCsEBFgUEkEDG4XEJYcL8gFCgUP+gxCAoP8DIIFBhfsiEIAoMJAogCBAoYlBiBMBAoUwrA0B////ALECI0QAocgAolgApVADolAHYnAAomAAoqdBAoKVBMoRvCOIQDCRIIFBYwKVBAoKqC4AFBVQVggTRDn0CYgQcBN4LpDV4T7IAooAJA="))

BIN
apps/skyspy/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

13
apps/skyspy/metadata.json Normal file
View File

@ -0,0 +1,13 @@
{ "id": "skyspy",
"name": "Sky Spy",
"version":"0.01",
"description": "Application for debugging GPS problems",
"icon": "app.png",
"readme": "README.md",
"supports" : ["BANGLEJS2"],
"tags": "tool",
"storage": [
{"name":"skyspy.app.js","url":"skyspy.app.js"},
{"name":"skyspy.img","url":"app-icon.js","evaluate":true}
]
}

260
apps/skyspy/skyspy.app.js Normal file
View File

@ -0,0 +1,260 @@
/* Sky spy */
/* 0 .. DD.ddddd
1 .. DD MM.mmm'
2 .. DD MM'ss"
*/
var mode = 1;
var display = 0;
var debug = 0;
var cancel_gps, gps_start;
var cur_altitude;
var wi = 24;
var h = 176-wi, w = 176;
var fix;
function radA(p) { return p*(Math.PI*2); }
function radD(d) { return d*(h/2); }
function radX(p, d) {
let a = radA(p);
return w/2 + Math.sin(a)*radD(d);
}
function radY(p, d) {
let a = radA(p);
return h/2 - Math.cos(a)*radD(d) + wi;
}
function format(x) {
switch (mode) {
case 0:
return "" + x;
case 1:
d = Math.floor(x);
m = x - d;
m = m*60;
return "" + d + " " + m.toFixed(3) + "'";
case 2:
d = Math.floor(x);
m = x - d;
m = m*60;
mf = Math.floor(m);
s = m - mf;
s = s*60;
return "" + d + " " + mf + "'" + s.toFixed(0) + '"';
}
}
var qalt = -1;
function resetAlt() {
min_dalt = 9999; max_dalt = -9999; step = 0;
}
resetAlt();
function calcAlt(alt, cur_altitude) {
let dalt = alt - cur_altitude;
if (min_dalt > dalt)
min_dalt = dalt;
if (max_dalt < dalt)
max_dalt = dalt;
let ddalt = max_dalt - min_dalt;
return ddalt;
}
function updateGps() {
let have = false, lat = "lat", lon = "lon", alt = "alt",
speed = "speed", hdop = "hdop", balt = "balt";
if (cancel_gps)
return;
fix = Bangle.getGPSFix();
try {
Bangle.getPressure().then((x) => {
cur_altitude = x.altitude;
}, print);
} catch (e) {
print("Altimeter error", e);
}
speed = getTime() - gps_start;
if (fix && fix.fix && fix.lat) {
lat = "" + format(fix.lat);
lon = "" + format(fix.lon);
alt = "" + fix.alt.toFixed(1);
speed = "" + fix.speed.toFixed(1);
hdop = "" + fix.hdop.toFixed(1);
have = true;
}
let ddalt = calcAlt(alt, cur_altitude);
if (display == 1)
g.reset().setFont("Vector", 20)
.setColor(1,1,1)
.fillRect(0, wi, 176, 176)
.setColor(0,0,0)
.drawString("Acquiring GPS", 0, 30)
.drawString(lat, 0, 50)
.drawString(lon, 0, 70)
.drawString("alt "+alt, 0, 90)
.drawString("speed "+speed, 0, 110)
.drawString("hdop "+hdop, 0, 130)
.drawString("balt" + cur_altitude, 0, 150);
if (display == 2) {
g.reset().setFont("Vector", 20)
.setColor(1,1,1)
.fillRect(0, wi, 176, 176)
.setColor(0,0,0)
.drawString("GPS status", 0, 30)
.drawString("speed "+speed, 0, 50)
.drawString("hdop "+hdop, 0, 70)
.drawString("dd "+qalt.toFixed(0) + " (" + ddalt.toFixed(0) + ")", 0, 90)
.drawString("alt "+alt, 0, 110)
.drawString("balt " + cur_altitude, 0, 130)
.drawString(step, 0, 150);
step++;
if (step == 10) {
qalt = max_dalt - min_dalt;
resetAlt();
}
}
if (debug > 0)
print(fix);
setTimeout(updateGps, 1000);
}
function radLine(a1, d1, a2, d2) {
g.drawLine(radX(a1, d1), radY(a1, d1), radX(a2, d2), radY(a2, d2));
}
function radCircle(d) {
let step = 0.05;
for (let i=0; i<1; i+=0.05) {
radLine(i-step, d, i, d);
}
//g.flip();
}
function drawGrid() {
g.setColor(0,0,0);
radLine(0, 1, 0.5, 1);
radLine(0.25, 1, 0.75, 1);
radCircle(0.5);
radCircle(1.0);
}
function drawSat(s) {
let a = s.azi / 360;
let e = ((90 - s.ele) / 90);
let x = radX(a, e);
let y = radY(a, e);
if (s.snr == "")
g.setColor(1, 0.25, 0.25);
else {
let snr = 1*s.snr;
g.setColor(0, 0, 0);
sats_receiving ++;
}
g.drawString(s.id, x, y);
}
// Should correspond to view from below.
// https://in-the-sky.org//satmap_radar.php?year=2023&month=10&day=24&skin=1
function drawSats(sats) {
sats_receiving = 0;
g.reset().setFont("Vector", 20)
.setColor(1,1,1)
.fillRect(0, 30, 176, 176);
drawGrid();
for (var s of sats) {
if (debug > 1)
print(s.ele, s.azi, s.snr);
drawSat(s);
}
if (fix && fix.fix && fix.lat) {
g.setColor(0, 0, 0);
g.drawString(fix.satellites + "/" + fix.hdop, 10, 150);
}
}
var sats = [];
var snum = 0;
var sats_receiving = 0;
function parseRaw(msg, lost) {
if (lost)
print("## data lost");
let s = msg.split(",");
if (s[0] != "$GPGSV")
return;
//print("Message", s[2], s[1]);
if (debug > 0)
print(msg);
if (s[2] == "1") {
snum = 0;
sats = [];
}
let view = 1 * s[3];
// s[3] -- sats in view.
// id, ele, azi, snr
if (debug > 0)
print("in view:", view);
let i = 4;
let k = 4;
if (view - snum < k)
k = view - snum;
for (let j=0; j<k; j++) {
let sat = {};
sat.id = s[i++];
sat.ele = 1*s[i++];
sat.azi = 1*s[i++];
sat.snr = s[i++];
if (debug > 0)
print(" ", sat);
sats[snum++] = sat;
}
if (debug > 1)
print("Checksum:", s[i]);
if (s[1] == s[2]) {
print("Complete...");
//print(sats);
if (display == 0)
drawSats(sats);
}
}
function stopGps() {
cancel_gps=true;
Bangle.setGPSPower(0, "skyspy");
}
function markGps() {
cancel_gps = false;
Bangle.setGPSPower(1, "skyspy");
Bangle.on('GPS-raw', parseRaw);
gps_start = getTime();
updateGps();
}
function onSwipe(dir) {
display = display + 1;
if (display == 3)
display = 0;
}
Bangle.setUI({
mode : "custom",
swipe : onSwipe,
clock : 0
});
Bangle.loadWidgets();
Bangle.drawWidgets();
markGps();