Merge branch 'espruino:master' into master

pull/3080/head
jamespsteinberg 2023-11-05 13:28:04 -05:00 committed by GitHub
commit 02489ac0f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1123 additions and 218 deletions

1
apps/Tyreid/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Change log created

18
apps/Tyreid/README.md Normal file
View File

@ -0,0 +1,18 @@
Tyreid
Tyreid is a Bluetooth war-driving app for the Bangle.js 2.
Menu options:
- Start: This turns on the Bluetooth and starts logging Bluetooth packets with time, latitude, and longitude information to a CSV file.
- Pause/Continue: These functions pause the capture and then allow it to resume.
- Devices: When paused this menu option will display the MAC addresses of discovered Bluetooth devices. Selecting a device will then display the MAC, Manufacturer code, the time it was first seen, and the RSSI of the first sighting.
- Marker: This command adds a 'marker' to the CSV log, which consists of the time and location information, but the Bluetooth packet information is replaced with the word MARKER. Markers can also be added by pressing the watch's button.
- Exit: This exits the app.
The current number of discovered devices is displayed in the top left corner.
This value is displayed in green when the GPS has a fix, or red otherwise.
To retrieve the CSV file, connect to the watch through the Espruino web IDE (https://www.espruino.com/ide/). From there the files stored on the watch can be downloaded by clicking the storage icon in the IDE's central column.

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

@ -0,0 +1 @@
E.toArrayBuffer(atob("MDACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAtAAAAAAAAAAAAAAB/QAAAAAAAAAAAAAD/0AAAAAAAAAAAAAH+9AAAAAAAAAAAAAPtfQAAAAAAAAAAAAudH0AAAAAAAAAAAB8tB9AAAAAAAAAAAD0tAfQAAAAAAAAAAHgtAH0AAAAAAAAAAPAtAB9AAAAAAAAAAtAtAAfQAAAAAAAAB8AtAAH0AAAAAAAAD0AtAAB9AAAAAAAALgAtAAAfQAAAAAAAfAAtAAAH0AAAAAAA9AAtAAAB9AAAAAAC4AAtAAAAfQAAAAADwAAtAAAALwAAAAAAQAAtAAAAvgAAAAAAAAAsAAAC9AAAAAAAAAAsAAAP0AAAAAAAAAAsAAB/AAAAAAAAAAAsAAH4AAAAAAAAAAAsAAvQAAAAAAAAAAAsAD9AAAAAAAAAAAAsAD0AAAAAAAAAAAAsAB8AAAAAAAAAAAAsAAtAAAAAAAAAAAAsAAPQAAAAAAAAAAAsAAHwAAAAAAAAAAAsAAC4AAAAAAAAAAAsAAA9AAAAAAAAAAAsAAAPAAAAAAAAAAAsAAAHgAAAAAAAAAA8AAAC0AAAAAAAAAA8AAAA8AAAAAAAAAA8AAAAEAAAAAAAAAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))

272
apps/Tyreid/app.js Normal file

File diff suppressed because one or more lines are too long

14
apps/Tyreid/metadata.json Normal file
View File

@ -0,0 +1,14 @@
{ "id": "Tyreid",
"name": "Tyreid",
"shortName":"Tyreid",
"version":"0.01",
"description": "Bluetooth war-driving app for Bangle.js 2",
"icon": "small_logo.png",
"tags": "",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"Tyreid.app.js","url":"app.js"},
{"name":"Tyreid.img","url":"app-icon.js","evaluate":true}
]
}

BIN
apps/Tyreid/small_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -0,0 +1,4 @@
0.1: Initial release
0.2: Added more descriptive approximations
0.2f: Bug fixes: Incorrect hour drawn after 50 mins, incorrect quarter minute drawn after 50 mins
0.3: Added touch interaction to display exact time and date.

View File

@ -0,0 +1 @@
atob("MDAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArgVYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABW19cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrNcrAACBVoGsVgAAgVaBrFYAAKzXK4GsKwAAgayBAAAAgVYAVoEAAAAAAAAAAADXVqyBAACs14Gs1ysArNeBrNcrAIHX14HXgQCB14HXrAAAVtdW11YAAAAAAAAAAFbXK4GsAACs1wAr11YArNcAK9dWAADXrABWVgDXgQCB1wAAAKzXgQAAAAAAAAAAAKzXrNfXKwCs1wAA14EArKwAK9dWAADXVgAAAADXgQBW1ysAAIHXVgAAAAAAAAAAANfXgYHXVgCs11aB11YArNcrgddWACvXgSsAAACsrCus1wAAK9es1ysAAAAAAAAAK9dWAACsrACsrKzXrAAArKzX16wAVtfX16wAAAAr19fXKwAArKwArKwAAAAAAAAAAAAAAAAAAACsrAArAAAArIEAKwAAAAAAAAAAAAAAACsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrAAAAAAArIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABWVgAAAAAAVlYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArVoErAAAAAAAAAAAAAAAAAAAAAAArVgAAAAAAAAAAAAAAAAAAAAAAAAArrNfXgQCBrNdWAAAAAAAAAAAAAAAAAAAAAABW1wAAAAAAAAAAAAAAAAAAAAAAACvXrCtWgQAAANdWAAAAAAArKwAAAAAAACsAAABW1wAAAAAAAAAAAAAAAAAAAAAAAFbXKwAAAAAAANdWAAAAAIHX16wAAACB19fXVgBW1wAA14EAAAAAAAAAAAAAAAAAAIGsAAAAAAAAANdWAAAAK9eBVteBACvXgSuBVgBW1wBW1ysAAAAAAAAAAAAAAAAAAIHXAAAAAAAAANdWAAAAVtcrAKyBAFbXKwAAAABW16zX1wAAAAAAAAAAAAAAAAAAAFbXVgAAAAAAANeBAAAAVtcrANeBAFbXKwAAAABW14HXrAAAAAAAAAAAAAAAAAAAAACs14GBrAAAAKzXrIEAK9esrNdWACvX14GBVgBW1wAr11YAAAAAAAAAAAAAAAAAAAAAVoGBgQAAACusrIEAACusrFYAAAArgaysVgBWgQAAgYEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

156
apps/approxclock/app.js Normal file
View File

@ -0,0 +1,156 @@
//load fonts
require("FontSinclair").add(Graphics);
require("FontTeletext5x9Ascii").add(Graphics);
//const
const numbers = {
"0": "Twelve",
"1": "One",
"2": "Two",
"3": "Three",
"4": "Four",
"5": "Five",
"6": "Six",
"7": "Seven",
"8": "Eight",
"9": "Nine",
"10": "Ten",
"11": "Eleven",
"12": "Twelve",
"13": "One",
"14": "Two",
"15": "Three",
"16": "Four",
"17": "Five",
"18": "Six",
"19": "Seven",
"20": "Eight",
"21": "Nine",
"22": "Ten",
"23": "Eleven",
"24": "Twelve",
};
const minutesByQuarterString = {
0: "O'Clock",
15: "Fifteen",
30: "Thirty",
45: "Fourty-Five"
};
const width = g.getWidth();
const height = g.getHeight();
let drawTimeout;
const getNearestHour = (hours, minutes) => {
if (minutes > 54) {
return hours + 1;
}
return hours;
};
const getApproximatePrefix = (minutes, minutesByQuarter) => {
if (minutes === minutesByQuarter) {
return " exactly";
} else if (minutesByQuarter - minutes < -54) {
return " nearly";
} else if (minutesByQuarter - minutes < -5) {
return " after";
} else if (minutesByQuarter - minutes < 0) {
return " just after";
} else if (minutesByQuarter - minutes > 5) {
return " before";
} else {
return " nearly";
}
};
const getMinutesByQuarter = minutes => {
if (minutes < 10) {
return 0;
} else if (minutes < 20) {
return 15;
} else if (minutes < 40) {
return 30;
} else if (minutes < 55) {
return 45;
} else {
return 0;
}
};
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function () {
drawTimeout = undefined;
drawTime();
}, 60000 - (Date.now() % 60000));
}
const drawTimeExact = () => {
var dateTime = Date();
var hours = dateTime.getHours();
var minutes = dateTime.getMinutes().toString().padStart(2,0);
var day = dateTime.getDay();
var date = dateTime.getDate();
var month = dateTime.getMonth();
var year = dateTime.getFullYear();
g.clear();
g.setBgColor(0,0,0);
g.clearRect(0,0,width, height);
g.setColor(1,1,1);
g.setFont("Vector", 30);
g.drawString(hours + ":" + minutes, (width - g.stringWidth(hours + ":" + minutes))/2, height * 0.3, false);
g.setFont("Vector", 26);
g.drawString(month + 1 + "/" + date + "/" + year, (width - g.stringWidth(month + 1 + "/" + date + "/" + year))/2, height * 0.6, false);
};
const drawTime = () => {
//Grab time vars
var date = Date();
var hour = date.getHours();
var minutes = date.getMinutes();
var minutesByQuarter = getMinutesByQuarter(minutes);
//reset graphics
g.clear();
g.reset();
//Build watch face
g.setBgColor(0, 0, 0);
g.clearRect(0, 0, width, height);
g.setFont("Vector", 22);
g.setColor(1, 1, 1);
g.drawString("It's" + getApproximatePrefix(minutes, minutesByQuarter), (width - g.stringWidth("It's" + getApproximatePrefix(minutes, minutesByQuarter))) / 2, height * 0.25, false);
g.setFont("Vector", 30);
g.drawString(numbers[getNearestHour(hour, minutes)], (width - g.stringWidth(numbers[getNearestHour(hour, minutes)])) / 2, height * 0.45, false);
g.setFont("Vector", 22);
g.drawString(minutesByQuarterString[minutesByQuarter], (width - g.stringWidth(minutesByQuarterString[minutesByQuarter])) / 2, height * 0.7, false);
queueDraw();
};
g.clear();
drawTime();
Bangle.on('lcdPower', function (on) {
if (on) {
drawTime();
} else {
if (idTimeout) {
clearTimeout(idTimeout);
}
}
});
Bangle.on('touch', function(button, xy){
drawTimeExact();
setTimeout(drawTime, 7000);
});
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();

BIN
apps/approxclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,18 @@
{ "id": "approxclock",
"name": "Approximate Clock",
"shortName" : "Approx Clock",
"version": "0.3",
"icon": "app.png",
"description": "A really basic spelled out time display for people looking for the vague time at a glance.",
"readme": "readme.md",
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"approxclock.app.js","url":"app.js"},
{"name":"approxclock.img","url":"app-icon.js","evaluate":true}
],
"screenshots": [
{"url": "screenshot.png"}
]
}

View File

@ -0,0 +1,7 @@
## Approximate Clock
### Description
Get a rough idea of the time at a quick glance, mostly made for myself based on a similar watchface on pebble. I find this keeps me from checking my watch too often and also saves me from moments of severe brainfart staring at these mysterious symbols we call numbers.
Exact time and date can be viewed temporarily by touching the screen.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -5,3 +5,4 @@
0.05: Added adjustment for Bangle.js magnetometer heading fix
0.06: optimized to update much faster
0.07: added support for bangle.js 2
0.08: call setUI before loading widgets to indicate we're a clock

View File

@ -850,6 +850,8 @@ g.setBgColor(0, 0, 0);
g.fillRect(0, 0, 175, 175);
current_moonphase = getMoonPhase();
Bangle.setUI("clock");
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -865,8 +867,6 @@ Bangle.setGPSPower(1);
var secondInterval;
Bangle.setUI("clock");
autoUpdate();
setWatch(SwitchSensorState, BTN1, { repeat: true });

View File

@ -1,7 +1,7 @@
{
"id": "astral",
"name": "Astral Clock",
"version": "0.07",
"version": "0.08",
"description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.",
"icon": "app-icon.png",
"type": "clock",

View File

@ -1 +1,2 @@
0.01: Simple app to display loyalty cards
0.02: Hiding widgets while showing the code

View File

@ -18,9 +18,8 @@ Bangle.drawWidgets();
const WHITE=-1
const BLACK=0
var FILE = "android.cards.json";
var Locale = require("locale");
const Locale = require("locale");
const widget_utils = require('widget_utils');
var fontSmall = "6x8";
var fontMedium = g.getFonts().includes("6x15")?"6x15":"6x8:2";
@ -90,6 +89,7 @@ function printLinearCode(binary) {
}
function showCode(card) {
widget_utils.hide();
E.showScroller();
// keeping it on rising edge would come back twice..
setWatch(()=>showCard(card), BTN, {edge:"falling"});
@ -151,6 +151,7 @@ function showCard(card) {
var titleColor = g.theme.fg2;
if (card.color)
titleColor = isLight(titleBgColor) ? BLACK : WHITE;
widget_utils.show();
E.showScroller({
h : g.getFontHeight(), // height of each menu item in pixels
c : lines.length, // number of menu items

View File

@ -1,7 +1,7 @@
{
"id": "cards",
"name": "Cards",
"version": "0.01",
"version": "0.02",
"description": "Display loyalty cards",
"icon": "app.png",
"screenshots": [{"url":"screenshot_cards_overview.png"}, {"url":"screenshot_cards_card1.png"}, {"url":"screenshot_cards_card2.png"}, {"url":"screenshot_cards_barcode.png"}, {"url":"screenshot_cards_qrcode.png"}],

View File

@ -37,4 +37,5 @@
0.29: When plotting with OpenStMap scale map to track width & height
0.30: Add clock info for showing and toggling recording state
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
plotTrack now draws the current track even if you're not actively recording
0.32: Add cadence data to output files

View File

@ -95,17 +95,27 @@ function saveGPX(track, title) {
<trk>
<name>${title}</name>
<trkseg>`;
let lastTime = 0;
track.forEach(pt=>{
let cadence;
if (pt.Steps && lastTime != 0){
cadence = pt.Steps * 60000 / (pt.Time.getTime() - lastTime);
cadence = cadence / 2; /*Convert from rpm to spm (one cycle is two steps), see https://github.com/espruino/BangleApps/pull/3068#issuecomment-1790041058*/
}
lastTime = pt.Time.getTime();
gpx += `
<trkpt lat="${pt.Latitude}" lon="${pt.Longitude}">
<ele>${pt.Altitude}</ele>
<time>${pt.Time.toISOString()}</time>
<extensions>
<gpxtpx:TrackPointExtension>
${pt.Heartrate ? `<gpxtpx:hr>${pt.Heartrate}</gpxtpx:hr>`:``}${""/*<gpxtpx:distance>...</gpxtpx:distance><gpxtpx:cad>65</gpxtpx:cad>*/}
${pt.Heartrate ? `<gpxtpx:hr>${pt.Heartrate}</gpxtpx:hr>`:``}
${cadence ? `<gpxtpx:cad>${cadence}</gpxtpx:cad>`:``} ${""/*<gpxtpx:distance>...</gpxtpx:distance><gpxtpx:cad>65</gpxtpx:cad>*/}
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>`;
});
// https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
gpx += `

View File

@ -2,7 +2,7 @@
"id": "recorder",
"name": "Recorder",
"shortName": "Recorder",
"version": "0.31",
"version": "0.32",
"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

@ -1 +1,2 @@
0.01: attempt to import
0.02: better GPS support, adding altitude and temperature support

View File

@ -25,8 +25,9 @@ minutes, real distance will be usually higher than approximation.
Useful gestures:
F -- disable GPS.
G -- enable GPS for 4 hours.
G -- enable GPS for 4 hours in low power mode.
N -- take a note and write it to the log.
S -- enable GPS for 30 minutes in high power mode.
When application detects watch is being worn, it will use vibrations
to communicate back to the user.
@ -48,4 +49,10 @@ night.
I'd like to make display nicer, and likely more dynamic, displaying
whatever application believes is most important at the time (and
possibly allowing scrolling).
possibly allowing scrolling).
Todo:
*) only turn on compass when needed
*) adjust draw timeouts to save power

View File

@ -1,13 +1,15 @@
{ "id": "sixths",
"name": "Sixth sense",
"version":"0.01",
"version":"0.02",
"description": "Clock for outdoor use with GPS support",
"icon": "app.png",
"readme": "README.md",
"supports" : ["BANGLEJS2"],
"tags": "",
"allow_emulator": true,
"type": "clock",
"tags": "clock",
"storage": [
{"name":"sixths.app.js","url":"app.js"},
{"name":"sixths.app.js","url":"sixths.app.js"},
{"name":"sixths.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -1,20 +1,53 @@
// Sixth sense
// Options you'll want to edit
const rest_altitude = 354;
const geoid_to_sea_level = 0; // Maybe BangleJS2 already compensates?
const W = g.getWidth();
const H = g.getHeight();
var cx = 100; cy = 105; sc = 70;
var buzz = "", msg = "";
temp = 0; alt = 0; bpm = 0;
var buzz = "", msg = "", inm = "", l = "", note = "(NOTEHERE)";
var mode = 0, mode_time = 0; // 0 .. normal, 1 .. note
var buzz = "", /* Set this to transmit morse via vibrations */
inm = "", l = "", /* For incoming morse handling */
in_str = "",
note = "(NOTEHERE)",
debug = "v930", debug2 = "(otherdb)", debug3 = "(short)";
var mode = 0, mode_time = 0; // 0 .. normal, 1 .. note, 2.. mark name
var disp_mode = 0; // 0 .. normal, 1 .. small time
var gps_on = 0, last_fix = 0, last_restart = 0, last_pause = 0, last_fstart = 0; // utime
var gps_needed = 0, gps_limit = 0; // seconds
// GPS handling
var gps_on = 0, // time GPS was turned on
last_fix = 0, // time of last fix
last_restart = 0, last_pause = 0, last_fstart = 0; // utime
var gps_needed = 0, // how long to wait for a fix
gps_limit = 0, // timeout -- when to stop recording
gps_speed_limit = 0;
var prev_fix = null;
var gps_dist = 0;
var is_active = false;
var cur_altitude = 0, cur_temperature = 0, alt_adjust = 0;
const rest_altitude = 354;
var mark_heading = -1;
// Is the human present?
var is_active = false, last_active = getTime();
var is_level = false;
// For altitude handling.
var cur_altitude = 0;
var cur_temperature = 0, alt_adjust = 0;
var alt_adjust_mode = "";
// Marks
var cur_mark = null;
// Icons
icon_alt = "\0\x08\x1a\1\x00\x00\x00\x20\x30\x78\x7C\xFE\xFF\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00";
icon_m = "\0\x08\x1a\1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00";
icon_km = "\0\x08\x1a\1\xC3\xC6\xCC\xD8\xF0\xD8\xCC\xC6\xC3\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00";
icon_kph = "\0\x08\x1a\1\xC3\xC6\xCC\xD8\xF0\xD8\xCC\xC6\xC3\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\xFF\x00\xC3\xC3\xFF\xC3\xC3";
icon_c = "\0\x08\x1a\1\x00\x00\x60\x90\x90\x60\x00\x7F\xFF\xC0\xC0\xC0\xC0\xC0\xFF\x7F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
function toMorse(x) {
r = "";
@ -28,12 +61,10 @@ function toMorse(x) {
}
return r;
}
function aload(s) {
buzz += toMorse(' E');
load(s);
}
function gpsRestart() {
print("gpsRestart");
Bangle.setGPSPower(1, "sixths");
@ -41,33 +72,155 @@ function gpsRestart() {
last_pause = 0;
last_fstart = 0;
}
function gpsPause() {
print("gpsPause");
Bangle.setGPSPower(0, "sixths");
last_restart = 0;
last_pause = getTime();
}
function gpsOn() {
gps_on = getTime();
gps_needed = 1000;
gps_limit = 60*60*4;
last_fix = 0;
prev_fix = null;
gps_dist = 0;
gpsRestart();
}
function gpsOff() {
Bangle.setGPSPower(0, "sixths");
gps_on = 0;
}
function fmtDist(km) { return km.toFixed(1) + icon_km; }
function fmtSteps(n) { return fmtDist(0.001 * 0.719 * n); }
function fmtAlt(m) { return m.toFixed(0) + icon_alt; }
function fmtTimeDiff(d) {
if (d < 180)
return ""+d.toFixed(0);
d = d/60;
return ""+d.toFixed(0)+"m";
}
function gpsHandleFix(fix) {
if (!prev_fix) {
show("GPS acquired", 10);
buzz += " .";
prev_fix = fix;
}
if (0) {
/* GPS altitude fluctuates a lot, not really usable */
alt_adjust = cur_altitude - (fix.alt + geoid_to_sea_level);
alt_adjust_mode = "g";
}
if (1) {
debug = ""+fix.alt+"m "+alt_adjust;
}
if (1) {
let now1 = Date();
let now2 = fix.time;
n1 = now1.getMinutes() * 60 + now1.getSeconds();
n2 = now2.getMinutes() * 60 + now2.getSeconds();
debug2 = "te "+(n2-n1)+"s";
}
loggps(fix);
d = calcDistance(fix, prev_fix);
if (d > 30) {
prev_fix = fix;
gps_dist += d/1000;
}
}
function gpsHandle() {
let msg = "";
if (!last_restart) {
d = (getTime()-last_pause);
if (last_fix)
msg = "PL"+ fmtTimeDiff(getTime()-last_fix);
else
msg = "PN"+ fmtTimeDiff(getTime()-gps_on);
print("gps on, paused ", d, gps_needed);
if (d > gps_needed * 2) {
gpsRestart();
}
} else {
fix = Bangle.getGPSFix();
if (fix && fix.fix && fix.lat) {
gpsHandleFix(fix);
msg = fix.speed.toFixed(1) + icon_kph;
print("GPS FIX", msg);
if (!last_fstart)
last_fstart = getTime();
last_fix = getTime();
gps_needed = 60;
} else {
if (last_fix)
msg = "L"+ fmtTimeDiff(getTime()-last_fix);
else {
msg = "N"+ fmtTimeDiff(getTime()-gps_on);
if (fix) {
msg += " " + fix.satellites + "sats";
}
}
}
d = (getTime()-last_restart);
d2 = (getTime()-last_fstart);
print("gps on, restarted ", d, gps_needed, d2, fix.lat);
if (getTime() > gps_speed_limit &&
(d > gps_needed || (last_fstart && d2 > 10))) {
gpsPause();
gps_needed = gps_needed * 1.5;
print("Pausing, next try", gps_needed);
}
}
msg += " "+gps_dist.toFixed(1)+icon_km;
return msg;
}
function markNew() {
let r = {};
r.time = getTime();
r.fix = prev_fix;
r.steps = Bangle.getHealthStatus("day").steps;
r.gps_dist = gps_dist;
r.altitude = cur_altitude;
r.name = "auto";
return r;
}
function markHandle() {
let m = cur_mark;
msg = m.name + ">" + fmtTimeDiff(getTime()- m.time);
if (m.fix && m.fix.fix) {
let s = fmtDist(calcDistance(m.fix, prev_fix)/1000) + icon_km;
msg += " " + s;
debug = "wp>" + s;
mark_heading = 180 + calcBearing(m.fix, prev_fix);
debug2 = "wp>" + mark_heading;
} else {
msg += " w" + fmtDist(gps_dist - m.gps_dist);
}
return msg;
}
function entryDone() {
show(":" + in_str);
buzz += " .";
switch (mode) {
case 1: logstamp(">" + in_str); break;
case 2: cur_mark.name = in_str; break;
}
in_str = 0;
mode = 0;
}
function inputHandler(s) {
print("Ascii: ", s);
if (mode == 1) {
note = note + s;
print("Ascii: ", s, s[0], s[1]);
if (s[0] == '^') {
switch (s[1]) {
case 'E': mode = 0; break;
case 'T': entryDone(); break;
}
return;
}
if ((mode == 1) || (mode == 2)){
in_str = in_str + s;
show(">"+in_str, 10);
mode_time = getTime();
return;
}
@ -80,12 +233,21 @@ function inputHandler(s) {
else
s = s+(bat/5);
buzz += toMorse(s);
show("Bat "+bat+"%", 60);
break;
case 'F': gpsOff(); show("GPS off", 3); break;
case 'G': gpsOn(); gps_limit = getTime() + 60*60*4; show("GPS on", 3); break;
case 'I':
disp_mode += 1;
if (disp_mode == 2) {
disp_mode = 0;
}
break;
case 'F': gpsOff(); break;
case 'G': gpsOn(); break;
case 'L': aload("altimeter.app.js"); break;
case 'N': mode = 1; note = ">"; mode_time = getTime(); break;
case 'M': mode = 2; show("M>", 10); cur_mark = markNew(); mode_time = getTime(); break;
case 'N': mode = 1; show(">", 10); mode_time = getTime(); break;
case 'O': aload("orloj.app.js"); break;
case 'S': gpsOn(); gps_limit = getTime() + 60*30; gps_speed_limit = gps_limit; show("GPS on", 3); break;
case 'T':
s = ' T';
d = new Date();
@ -94,9 +256,9 @@ function inputHandler(s) {
buzz += toMorse(s);
break;
case 'R': aload("run.app.js"); break;
case 'Y': buzz += " ."; Bangle.resetCompass(); break;
}
}
const morseDict = {
'.-': 'A',
'-...': 'B',
@ -135,37 +297,46 @@ const morseDict = {
'-....': '6',
'-----': '0',
};
let asciiDict = {};
for (let k in morseDict) {
print(k, morseDict[k]);
asciiDict[morseDict[k]] = k;
}
function morseToAscii(morse) {
return morseDict[morse];
}
function asciiToMorse(char) {
return asciiDict[char];
}
function morseHandler() {
inputHandler(morseToAscii(inm));
if (inm[0] == "^") {
inputHandler("^"+morseToAscii(inm.substr(1)));
} else {
inputHandler(morseToAscii(inm));
}
inm = "";
l = "";
}
function touchHandler(d) {
let x = Math.floor(d.x);
let y = Math.floor(d.y);
g.setColor(0.25, 0, 0);
g.fillCircle(W-x, W-y, 5);
if (d.b) {
if (1) { /* Just a debugging feature */
g.setColor(0.25, 0, 0);
if (0)
g.fillCircle(W-x, W-y, 5);
else
g.fillCircle(x, y, 5);
}
if (!d.b) {
morseHandler();
l = "";
return;
}
if (y > H/2 && l == "") {
inm = "^";
}
if (x < W/2 && y < H/2 && l != ".u") {
inm = inm + ".";
l = ".u";
@ -181,14 +352,10 @@ function touchHandler(d) {
if (x > W/2 && y > H/2 && l != "-d") {
inm = inm + "-";
l = "-d";
}
}
} else
morseHandler();
print(inm, "drag:", d);
//print(inm, "drag:", d);
}
function add0(i) {
if (i > 9) {
return ""+i;
@ -196,18 +363,14 @@ function add0(i) {
return "0"+i;
}
}
var lastHour = -1, lastMin = -1;
function logstamp(s) {
logfile.write("utime=" + getTime() + " " + s + "\n");
}
function loggps(fix) {
logfile.write(fix.lat + " " + fix.lon + " ");
logstamp("");
}
function hourly() {
print("hourly");
s = ' T';
@ -215,31 +378,35 @@ function hourly() {
buzz += toMorse(s);
logstamp("");
}
function show(msg, timeout) {
note = msg;
}
function fivemin() {
print("fivemin");
s = ' B';
bat = E.getBattery();
if (bat < 45) {
s = s+(bat/5);
if (bat < 25) {
if (is_active)
buzz += toMorse(s);
show("Bat "+bat+"%", 60);
}
if (0)
try {
Bangle.getPressure().then((x) => { cur_altitude = x.altitude;
cur_temperature = x.temperature; },
print)
.catch(print);
print);
} catch (e) {
print("Altimeter error", e);
}
}
function every(now) {
if ((mode > 0) && (mode_time - getTime() > 60)) {
if ((mode > 0) && (getTime() - mode_time > 10)) {
if (mode == 1) {
logstamp(">" + note);
entryDone();
}
mode = 0;
}
if (gps_on && getTime() - gps_on > gps_limit) {
if (gps_on && getTime() > gps_limit && getTime() > gps_speed_limit) {
Bangle.setGPSPower(0, "sixths");
gps_on = 0;
}
@ -255,96 +422,136 @@ function every(now) {
}
function radians(a) { return a*Math.PI/180; }
function degrees(a) { return a*180/Math.PI; }
// distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km
// https://www.movable-type.co.uk/scripts/latlong.html
// (Equirectangular approximation)
function calcDistance(a,b) {
function radians(a) { return a*Math.PI/180; }
var x = radians(b.lon-a.lon) * Math.cos(radians((a.lat+b.lat)/2));
var y = radians(b.lat-a.lat);
return Math.sqrt(x*x + y*y) * 6371000;
}
// thanks to waypointer
function calcBearing(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)));
}
function testBearing() {
let p1 = {}, p2 = {};
p1.lat = 40; p2.lat = 50;
p1.lon = 14; p2.lon = 14;
print("bearing = ", calcBearing(p1, p2));
}
function radA(p) { return p*(Math.PI*2); }
function radD(d) { return d*(H/2); }
function radX(p, d) {
let a = radA(p);
return H/2 + Math.sin(a)*radD(d);
}
function radY(p, d) {
let a = radA(p);
return W/2 - Math.cos(a)*radD(d);
}
function drawDot(h, d, s) {
let x = radX(h/360, d);
let y = radY(h/360, d);
g.fillCircle(x,y, 10);
}
function drawBackground() {
acc = Bangle.getAccel();
is_level = (acc.z < -0.95);
if (is_level) {
let obj = Bangle.getCompass();
if (obj) {
let h = 360-obj.heading;
print("Compass", h);
g.setColor(0.5, 0.5, 1);
drawDot(h, 0.7, 10);
}
}
if (prev_fix && prev_fix.fix) {
g.setColor(0.5, 1, 0.5);
drawDot(prev_fix.course, 0.5, 6);
}
if (mark_heading != -1) {
g.setColor(1, 0.5, 0.5);
drawDot(mark_heading, 0.6, 8);
}
}
function drawTime(now) {
if (disp_mode == 0)
g.setFont('Vector', 60);
else
g.setFont('Vector', 26);
g.setFontAlign(1, 1);
g.drawString(now.getHours() + ":" + add0(now.getMinutes()), W, 90);
}
function draw() {
if (disp_mode == 2) {
draw_all();
return;
}
g.setColor(1, 1, 1);
g.fillRect(0, 25, W, H);
g.setFont('Vector', 60);
g.fillRect(0, 24, W, H);
if (0) {
g.setColor(0.25, 1, 1);
g.fillPoly([ W/2, 24, W, 80, 0, 80 ]);
}
let msg = "";
if (gps_on) {
msg = gpsHandle();
} else {
msg = note;
}
drawBackground();
g.setColor(0, 0, 0);
g.setFontAlign(-1, 1);
let now = new Date();
g.drawString(now.getHours() + ":" + add0(now.getMinutes()), 10, 90);
g.setColor(0, 0, 0);
drawTime(now);
every(now);
let km = 0.001 * 0.719 * Bangle.getHealthStatus("day").steps;
g.setFontAlign(-1, 1);
g.setFont('Vector', 26);
const weekday = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
g.drawString(weekday[now.getDay()] + "" + now.getDate() + ". " + km.toFixed(1) + "km", 10, 115);
g.drawString(weekday[now.getDay()] + "" + now.getDate() + ". "
+ fmtSteps(Bangle.getHealthStatus("day").steps), 10, 115);
if (gps_on) {
if (!last_restart) {
d = (getTime()-last_pause);
if (last_fix)
msg = "PL"+ (getTime()-last_fix).toFixed(0);
else
msg = "PN"+ (getTime()-gps_on).toFixed(0);
print("gps on, paused ", d, gps_needed);
if (d > gps_needed * 2) {
gpsRestart();
}
} else {
fix = Bangle.getGPSFix();
if (fix.fix && fix.lat) {
if (!prev_fix) {
prev_fix = fix;
}
msg = fix.speed.toFixed(1) + " km/h";
if (!last_fstart)
last_fstart = getTime();
last_fix = getTime();
gps_needed = 60;
loggps(fix);
print("GPS FIX", msg);
d = calcDistance(fix, prev_fix);
if (d > 30) {
prev_fix = fix;
gps_dist += d/1000;
}
} else {
if (last_fix)
msg = "L"+ (getTime()-last_fix).toFixed(0);
else
msg = "N"+ (getTime()-gps_on).toFixed(0);
}
d = (getTime()-last_restart);
d2 = (getTime()-last_fstart);
print("gps on, restarted ", d, gps_needed, d2, fix.lat);
if (d > gps_needed || (last_fstart && d2 > 10)) {
gpsPause();
gps_needed = gps_needed * 1.5;
print("Pausing, next try", gps_needed);
}
}
msg += " "+gps_dist.toFixed(1)+"km";
} else {
msg = note;
}
g.drawString(msg, 10, 145);
if (is_active) {
g.drawString("act " + (cur_altitude - alt_adjust).toFixed(0), 10, 175);
} else {
if (getTime() - last_active > 15*60) {
alt_adjust = cur_altitude - rest_altitude;
g.drawString(alt_adjust.toFixed(0) + "m " + cur_temperature.toFixed(1)+"C", 10, 175);
alt_adjust_mode = "h";
msg = "H)" + fmtAlt(alt_adjust);
} else {
msg = alt_adjust_mode+")"+fmtAlt(cur_altitude - alt_adjust);
}
msg = msg + " " + cur_temperature.toFixed(1)+icon_c;
if (cur_mark) {
msg = markHandle();
}
g.drawString(msg, 10, 175);
if (disp_mode == 1) {
g.drawString(debug, 10, 45);
g.drawString(debug2, 10, 65);
g.drawString(debug3, 10, 85);
}
queueDraw();
}
function draw_all() {
g.setColor(0, 0, 0);
g.fillRect(0, 0, W, H);
@ -394,14 +601,13 @@ function draw_all() {
g.setFont('Vector', 22);
g.drawString(now.getDate()+"."+(now.getMonth()+1)+" "+now.getDay(), 3, 60);
g.drawString(msg, 3, 80);
g.drawString("(message here)", 3, 80);
g.drawString("S" + step + " B" + Math.round(bat/10) + (Bangle.isCharging()?"c":""), 3, 100);
g.drawString("A" + Math.round(alt) + " T" + Math.round(temp), 3, 120);
g.drawString("C" + Math.round(co.heading) + " B" + bpm, 3, 140);
queueDraw();
}
function accelTask() {
tm = 100;
acc = Bangle.getAccel();
@ -424,7 +630,6 @@ function accelTask() {
setTimeout(accelTask, tm);
}
function buzzTask() {
if (buzz != "") {
now = buzz[0];
@ -442,9 +647,8 @@ function buzzTask() {
setTimeout(buzzTask, 6*dot);
} else print("Unknown character -- ", now, buzz);
} else
setTimeout(buzzTask, 60000);
setTimeout(buzzTask, 1000);
}
function aliveTask() {
function cmp(s) {
let d = acc[s] - last_acc[s];
@ -456,6 +660,7 @@ function aliveTask() {
if (cmp("x") || cmp("y") || cmp("z")) {
print("active");
is_active = true;
last_active = getTime();
}
last_acc = acc;
@ -476,14 +681,15 @@ function queueDraw() {
}, next - (Date.now() % next));
}
function start() {
Bangle.on("drag", touchHandler);
if (0)
Bangle.on("accel", accelHandler);
if (0) {
if (1) {
Bangle.setCompassPower(1, "sixths");
Bangle.setBarometerPower(1, "sixths");
}
if (0) {
Bangle.setHRMPower(1, "sixths");
Bangle.setGPSPower(1, "sixths");
Bangle.on("HRM", (hrm) => { bpm = hrm.bpm; } );
@ -500,9 +706,14 @@ function start() {
}
g.reset();
Bangle.setUI();
Bangle.setUI({
mode : "clock"
});
Bangle.loadWidgets();
Bangle.drawWidgets();
let logfile = require("Storage").open("sixths.egt", "a");
start();
if (0) {
testBearing();
} else
start();

View File

@ -5,7 +5,8 @@ A simple game of stacking cubes.
## Usage
Press the button to stack!
Boxes move horizontally. Use button to stack them on top of existing
boxes. You win when you reach top of the screen.
## Creator

View File

@ -6,6 +6,7 @@
"icon": "app.png",
"tags": "game",
"supports" : ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{"name":"stacker.app.js","url":"app.js"},

View File

@ -1,2 +1,3 @@
0.01: New app!
0.02: Better controls, implement game over.
0.03: Implement mode and level selection screens.

View File

@ -1,7 +1,7 @@
{ "id": "tetris",
"name": "Tetris",
"shortName":"Tetris",
"version":"0.02",
"version":"0.03",
"description": "Tetris",
"icon": "tetris.png",
"readme": "README.md",

View File

@ -36,11 +36,27 @@ const tiles = [
const ox = 176/2 - 5*8;
const oy = 8;
var pf = Array(23).fill().map(()=>Array(12).fill(0)); // field is really 10x20, but adding a border for collision checks
pf[20].fill(1);
pf[21].fill(1);
pf[22].fill(1);
pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; });
/* 0 .. simulated arrows
1 .. drag piece
2 .. accelerometer. 12 lines record.
3 .. altimeter
*/
var control = 0, level = 0;
var alt_start = -9999; /* For altimeter control */
/* 0 .. menu
1 .. game
2 .. game over */
var state = 0;
var pf;
function initGame() {
pf = Array(23).fill().map(()=>Array(12).fill(0)); // field is really 10x20, but adding a border for collision checks
pf[20].fill(1);
pf[21].fill(1);
pf[22].fill(1);
pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; });
}
function rotateTile(t, r) {
var nt = JSON.parse(JSON.stringify(t));
@ -98,6 +114,8 @@ function redrawPF(ly) {
function gameOver() {
g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22)
.drawString("Game Over", 176/2, 76);
state = 0;
E.showAlert("Game Over").then(selectGame, print);
}
function insertAndCheck() {
@ -138,6 +156,8 @@ function moveOk(t, dx, dy) {
}
function gameStep() {
if (state != 1)
return;
if (Date.now()-time > dropInterval) { // drop one step
time = Date.now();
if (moveOk(ct, 0, 1)) {
@ -169,12 +189,50 @@ function move(x, y) {
}
}
Bangle.setUI();
Bangle.on("drag", (e) => {
let h = 176/2;
if (!e.b)
function linear(x) {
print("Linear: ", x);
let now = px / 10;
if (x < now-0.06)
move(-1, 0);
if (x > now+0.06)
move(1, 0);
}
function newGame() {
E.showMenu();
Bangle.setUI();
if (control == 2) {
Bangle.on("accel", (e) => {
if (state != 1) return;
if (control != 2) return;
print(e.x);
linear((0.2-e.x) * 2.5);
});
}
if (control == 3) {
Bangle.setBarometerPower(true);
Bangle.on("pressure", (e) => {
if (state != 1) return;
if (control != 3) return;
let a = e.altitude;
if (alt_start == -9999)
alt_start = a;
a = a - alt_start;
print(e.altitude, a);
linear(a);
});
}
Bangle.on("drag", (e) => {
let h = 176/2;
if (state == 2) {
if (e.b)
selectGame();
return;
}
if (!e.b)
return;
if (e.y < h) {
if (state == 0) return;
if (e.y < h) {
if (e.x < h)
rotate();
else {
@ -184,21 +242,60 @@ Bangle.on("drag", (e) => {
g.flip();
}
}
} else {
} else {
if (control == 1)
linear((e.x - 20) / 156);
if (control != 0)
return;
if (e.x < h)
move(-1, 0);
else
move(1, 0);
}
});
}
});
Bangle.on("swipe", (x,y) => {
if (y<0) y = 0;
move(x, y);
});
initGame();
drawGame();
state = 1;
var step = 450 - 50*level;
if (control == 3)
step = step*2;
dropInterval = step;
var gi = setInterval(gameStep, 50);
}
drawBoundingBox();
g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("6x15", 1).drawString("Lines", 22, 30).drawString("Next", 176-22, 30);
showNext(ntn, ntr);
g.setColor(0).fillRect(5, 30, 41, 80).setColor(1, 1, 1).drawString(nlines.toString(), 22, 50);
var gi = setInterval(gameStep, 20);
function drawGame() {
drawBoundingBox();
g.setColor(1, 1, 1).setFontAlign(0, 1, 0)
.setFont("6x15", 1).drawString("Lines", 22, 30)
.drawString("Next", 176-22, 30);
showNext(ntn, ntr);
g.setColor(0).fillRect(5, 30, 41, 80)
.setColor(1, 1, 1).drawString(nlines.toString(), 22, 50);
}
function selectLevel() {
print("Level selection menu");
var menu = {};
menu["Level 1"] = () => { level = 0; selectGame(); };
menu["Level 2"] = () => { level = 1; selectGame(); };
menu["Level 3"] = () => { level = 2; selectGame(); };
E.showMenu(menu);
}
function selectGame() {
state = 0;
print("Game selection menu");
//for (let i = 0; i < 100000; i++) ;
var menu = {};
menu["Normal"] = () => { control = 0; newGame(); };
menu["Drag"] = () => { control = 1; newGame(); };
menu["Tilt"] = () => { control = 2; newGame(); };
menu["Move"] = () => { control = 3; newGame(); };
menu["Level"] = () => { selectLevel(); };
E.showMenu(menu);
}
selectGame();

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Display waypoint name instead of its index in remove menu and fix icon
0.03: Use text input for waypoint names, allow marking waypoint with current GPS position

View File

@ -13,6 +13,10 @@ var wp = require('Storage').readJSON("waypoints.json", true) || [];
2 .. DD MM'ss"
*/
var mode = 1;
var key; /* Shared between functions, typically wp name */
var fix; /* GPS fix */
var cancel_gps;
var gps_start;
function writeWP() {
require('Storage').writeJSON("waypoints.json", wp);
@ -22,28 +26,94 @@ function mainMenu() {
var menu = {
"< Back" : Bangle.load
};
if (Object.keys(wp).length==0) Object.assign(menu, {"NO WPs":""});
else for (let id in wp) {
if (Object.keys(wp).length==0) {
//Object.assign(menu, {"NO WPs":""});
print("(no waypoints)");
} else for (let id in wp) {
let i = id;
menu[wp[id]["name"]]=()=>{ decode(i); };
menu[wp[id]["name"]]=()=>{ show(i); };
}
menu["Add"]=addCard;
menu["Remove"]=removeCard;
menu["Format"]=setFormat;
menu["Mark GPS"]=markGps;
g.clear();
E.showMenu(menu);
}
function setFormat() {
var confirmRemove = new Layout (
{type:"v", c: [
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:"Format"},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD.dddd", cb:l=>{ mode = 0; mainMenu(); }},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD MM.mmm'", cb:l=>{ mode = 1; mainMenu(); }},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD MM'ss"+'"', cb:l=>{ mode = 2; mainMenu(); }},
], lazy:true});
function updateGps() {
let have = false, lat = "lat", lon = "lon", alt = "alt", speed = "speed";
if (cancel_gps)
return;
fix = Bangle.getGPSFix();
speed = "no fix for " + (getTime() - gps_start).toFixed(0) + "s";
if (fix && fix.fix && fix.lat) {
lat = "" + lat(fix.lat);
lon = "" + lon(fix.lon);
alt = "alt " + fix.alt.toFixed(0) + "m";
speed = "speed " + fix.speed.toFixed(1) + "kt";
have = true;
}
g.reset().setFont("Vector", 20)
.setColor(1,1,1)
.fillRect(0, 0, 176, 120)
.setColor(0,0,0)
.drawString(key, 0, 0)
.drawString(lat, 0, 20)
.drawString(lon, 0, 40)
.drawString(alt, 0, 60)
.drawString(speed, 0, 80);
setTimeout(updateGps, 100);
}
function stopGps() {
cancel_gps=true;
Bangle.setGPSPower(0, "waypoint_editor");
}
function confirmGps(s) {
key = s;
var la = new Layout (
{type:"v", c: [
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:""},
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:""},
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:""},
{type:"h", c: [
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "YES", cb:l=>{
print("should mark", key, fix); createWP(fix.lat, fix.lon, key); cancel_gps=true; mainMenu();
}},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: " NO", cb:l=>{ cancel_gps=true; mainMenu(); }}
]}
], lazy:true});
g.clear();
confirmRemove.render();
la.render();
updateGps();
}
function markGps() {
cancel_gps = false;
Bangle.setGPSPower(1, "waypoint_editor");
gps_start = getTime();
require("textinput").input({text:"wp"}).then(key => {
confirmGps(key);
});
}
function setFormat() {
var la = new Layout (
{type:"v", c: [
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:"Format"},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD.dddd", cb:l=>{ mode = 0; mainMenu(); }},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD MM.mmm'", cb:l=>{ mode = 1; mainMenu(); }},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD MM'ss"+'"', cb:l=>{ mode = 2; mainMenu(); }},
], lazy:true});
g.clear();
la.render();
}
function format(x) {
@ -65,7 +135,6 @@ function format(x) {
return "" + d + " " + mf + "'" + s + '"';
}
}
function lat(x) {
c = "N";
if (x<0) {
@ -74,7 +143,6 @@ function lat(x) {
}
return c+format(x);
}
function lon(x) {
c = "E";
if (x<0) {
@ -84,17 +152,17 @@ function lon(x) {
return c+format(x);
}
function decode(pin) {
print(pin);
var i = wp[pin];
var pinDecrypted=i["name"] + "\n" + lat(i["lat"]) + "\n" + lon(i["lon"]);
var showPin = new Layout ({
type:"v", c: [
{type:"txt", font:"10%", pad:1, fillx:1, filly:1, label: pinDecrypted},
{type:"btn", font:"10%", pad:1, fillx:1, filly:1, label:"OK", cb:l=>{mainMenu();}}
], lazy:true});
g.clear();
showPin.render();
function show(pin) {
print(pin);
var i = wp[pin];
var l = i["name"] + "\n" + lat(i["lat"]) + "\n" + lon(i["lon"]);
var la = new Layout ({
type:"v", c: [
{type:"txt", font:"10%", pad:1, fillx:1, filly:1, label: l},
{type:"btn", font:"10%", pad:1, fillx:1, filly:1, label:"OK", cb:l=>{mainMenu();}}
], lazy:true});
g.clear();
la.render();
}
function showNumpad(text, key_, callback) {
@ -155,10 +223,10 @@ function showNumpad(text, key_, callback) {
function removeCard() {
var menu = {
"" : {title : "select card"},
"" : {title : "Select WP"},
"< Back" : mainMenu
};
if (Object.keys(wp).length==0) Object.assign(menu, {"NO CARDS":""});
if (Object.keys(wp).length==0) Object.assign(menu, {"No WPs":""});
else {
wp.forEach((val, card) => {
const name = wp[card].name;
@ -186,17 +254,16 @@ function removeCard() {
}
function ask01(t, cb) {
var confirmRemove = new Layout (
var la = new Layout (
{type:"v", c: [
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:"Format"},
{type:"txt", font:"15%", pad:1, fillx:1, filly:1, label:"Select"},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: t[0], cb:l=>{ cb(1); }},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: t[1], cb:l=>{ cb(-1); }},
], lazy:true});
g.clear();
confirmRemove.render();
la.render();
}
function askCoordinate(t1, t2, callback) {
let sign = 1;
ask01(t1, function(sign) {
@ -237,8 +304,27 @@ function askPosition(callback) {
});
}
function createWP(lat, lon, name) {
let n = {};
n["name"] = name;
n["lat"] = lat;
n["lon"] = lon;
wp.push(n);
print("add -- waypoints", wp);
writeWP();
}
function addCardName(name) {
g.clear();
askPosition(function(lat, lon) {
print("position -- ", lat, lon);
createWP(lat, lon, result);
mainMenu();
});
}
function addCard() {
showNumpad("wpXX", "wp", function() {
require("textinput").input({text:"wp"}).then(key => {
result = key;
if (wp[result]!=undefined) {
E.showMenu();
@ -247,29 +333,17 @@ function addCard() {
{type:"txt", font:Math.min(15,100/result.length)+"%", pad:1, fillx:1, filly:1, label:result},
{type:"txt", font:"12%", pad:1, fillx:1, filly:1, label:"already exists."},
{type:"h", c: [
{type:"btn", font:"10%", pad:1, fillx:1, filly:1, label: "REPLACE", cb:l=>{encodeCard(result);}},
{type:"btn", font:"10%", pad:1, fillx:1, filly:1, label: "REPLACE", cb:l=>{addCardName(result);}},
{type:"btn", font:"10%", pad:1, fillx:1, filly:1, label: "CANCEL", cb:l=>{mainMenu();}}
]}
], lazy:true});
g.clear();
alreadyExists.render();
}
g.clear();
askPosition(function(lat, lon) {
print("position -- ", lat, lon);
let n = {};
n["name"] = result;
n["lat"] = lat;
n["lon"] = lon;
wp.push(n);
print("add -- waypoints", wp);
writeWP();
mainMenu();
});
}
addCardName(result);
});
}
g.reset();
Bangle.setUI();
mainMenu();

View File

@ -1,11 +1,13 @@
{ "id": "waypoint_editor",
"name": "Waypoint editor",
"version":"0.02",
"version":"0.03",
"description": "Allows editing waypoints on device",
"icon": "app.png",
"readme": "README.md",
"supports" : ["BANGLEJS2"],
"allow_emulator": true,
"tags": "tool,outdoors,gps",
"dependencies": {"textinput":"type"},
"storage": [
{"name":"waypoint_editor.app.js","url":"app.js"},
{"name":"waypoint_editor.img","url":"app-icon.js","evaluate":true}

View File

@ -4,3 +4,4 @@
0.05: Don't show clock widget if already showing clock app
0.06: Use 7 segment font, update *on* the minute, use less memory
0.07: allow turning on/off when quick-switching apps
0.08: Ensure we clear the whole rect so we don't end up leaving old text when time changes (fix #3073)

View File

@ -1,7 +1,7 @@
{
"id": "widclk",
"name": "Digital clock widget",
"version": "0.07",
"version": "0.08",
"description": "A simple digital clock widget that appears when not showing a fullscreen clock",
"icon": "widget.png",
"type": "widget",

View File

@ -7,13 +7,13 @@ WIDGETS["wdclk"]={area:"tl",width:Bangle.CLOCK?0:52/* g.stringWidth("00:00") */,
return setTimeout(Bangle.drawWidgets,1); // widget changed size - redraw
}
if (!this.width) return; // if not visible, return
g.reset().setFontCustom(atob("AAAAAAAAAAIAAAQCAQAAAd0BgMBdwAAAAAAAdwAB0RiMRcAAAERiMRdwAcAQCAQdwAcERiMRBwAd0RiMRBwAAEAgEAdwAd0RiMRdwAcERiMRdwAFAAd0QiEQdwAdwRCIRBwAd0BgMBAAABwRCIRdwAd0RiMRAAAd0QiEQAAAAAAAAAA="), 32, atob("BgAAAAAAAAAAAAAAAAYCAAYGBgYGBgYGBgYCAAAAAAAABgYGBgYG"), 512+9);
g.reset().setFontCustom(atob("AAAAAAAAAAIAAAQCAQAAAd0BgMBdwAAAAAAAdwAB0RiMRcAAAERiMRdwAcAQCAQdwAcERiMRBwAd0RiMRBwAAEAgEAdwAd0RiMRdwAcERiMRdwAFAAd0QiEQdwAdwRCIRBwAd0BgMBAAABwRCIRdwAd0RiMRAAAd0QiEQAAAAAAAAAA="), 32, atob("BgAAAAAAAAAAAAAAAAYCAAYGBgYGBgYGBgYCAAAAAAAABgYGBgYG"), 512+9).setFontAlign(0,0);
var time = require("locale").time(new Date(),1);
g.drawString(time, this.x, this.y+3, true); // 5 * 6*2 = 60
g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23).drawString(time, this.x+this.width/2, this.y+12); // 5 * 6*2 = 60
// queue draw in one minute
if (this.drawTimeout) clearTimeout(this.drawTimeout);
this.drawTimeout = setTimeout(()=>{
this.drawTimeout = undefined;
this.draw();
}, 60000 - (Date.now() % 60000));
}};
}};

View File

@ -3,4 +3,4 @@
0.03: based in widclk v0.05 compatible at same time, bottom area and color
0.04: refactored to use less memory, and allow turning on/off when quick-switching apps
0.05: Remove cyan color, use theme foreground instead
0.06: Ensure we clear the whole rect so we don't end up leaving old text when time changes

View File

@ -2,7 +2,7 @@
"id": "widclkbttm",
"name": "Digital clock (Bottom) widget",
"shortName": "Digital clock Bottom Widget",
"version": "0.05",
"version": "0.06",
"description": "Displays time HH:mm in the bottom of the screen (may not be compatible with some apps)",
"icon": "widclkbttm.png",
"type": "widget",

View File

@ -4,7 +4,7 @@ WIDGETS["wdclkbttm"]={area:"br",width:Bangle.CLOCK?0:60,draw:function() {
return setTimeout(Bangle.drawWidgets,1); // widget changed size - redraw
}
if (!this.width) return; // if not visible, return
g.reset().setFont("6x8", 2).setFontAlign(-1, 0);
g.reset().setFont("6x8", 2).setFontAlign(-1, 0).clearRect(this.x, this.y, this.x+this.width-1, this.y+23);
var time = require("locale").time(new Date(),1);
g.drawString(time, this.x, this.y+11, true); // 5 * 6*2 = 60
// queue draw in one minute