Merge branch 'espruino:master' into master

master
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.05: Added adjustment for Bangle.js magnetometer heading fix
0.06: optimized to update much faster 0.06: optimized to update much faster
0.07: added support for bangle.js 2 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); g.fillRect(0, 0, 175, 175);
current_moonphase = getMoonPhase(); current_moonphase = getMoonPhase();
Bangle.setUI("clock");
// Load widgets // Load widgets
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
@ -865,8 +867,6 @@ Bangle.setGPSPower(1);
var secondInterval; var secondInterval;
Bangle.setUI("clock");
autoUpdate(); autoUpdate();
setWatch(SwitchSensorState, BTN1, { repeat: true }); setWatch(SwitchSensorState, BTN1, { repeat: true });

View File

@ -1,7 +1,7 @@
{ {
"id": "astral", "id": "astral",
"name": "Astral Clock", "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.", "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", "icon": "app-icon.png",
"type": "clock", "type": "clock",

View File

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

View File

@ -1,7 +1,7 @@
{ {
"id": "cards", "id": "cards",
"name": "Cards", "name": "Cards",
"version": "0.01", "version": "0.02",
"description": "Display loyalty cards", "description": "Display loyalty cards",
"icon": "app.png", "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"}], "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

@ -38,3 +38,4 @@
0.30: Add clock info for showing and toggling recording state 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 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> <trk>
<name>${title}</name> <name>${title}</name>
<trkseg>`; <trkseg>`;
let lastTime = 0;
track.forEach(pt=>{ 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 += ` gpx += `
<trkpt lat="${pt.Latitude}" lon="${pt.Longitude}"> <trkpt lat="${pt.Latitude}" lon="${pt.Longitude}">
<ele>${pt.Altitude}</ele> <ele>${pt.Altitude}</ele>
<time>${pt.Time.toISOString()}</time> <time>${pt.Time.toISOString()}</time>
<extensions> <extensions>
<gpxtpx:TrackPointExtension> <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> </gpxtpx:TrackPointExtension>
</extensions> </extensions>
</trkpt>`; </trkpt>`;
}); });
// https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd // https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
gpx += ` gpx += `

View File

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

View File

@ -1 +1,2 @@
0.01: attempt to import 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: Useful gestures:
F -- disable GPS. 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. 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 When application detects watch is being worn, it will use vibrations
to communicate back to the user. to communicate back to the user.
@ -49,3 +50,9 @@ night.
I'd like to make display nicer, and likely more dynamic, displaying I'd like to make display nicer, and likely more dynamic, displaying
whatever application believes is most important at the time (and 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", { "id": "sixths",
"name": "Sixth sense", "name": "Sixth sense",
"version":"0.01", "version":"0.02",
"description": "Clock for outdoor use with GPS support", "description": "Clock for outdoor use with GPS support",
"icon": "app.png", "icon": "app.png",
"readme": "README.md", "readme": "README.md",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"tags": "", "allow_emulator": true,
"type": "clock",
"tags": "clock",
"storage": [ "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} {"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 W = g.getWidth();
const H = g.getHeight(); const H = g.getHeight();
var cx = 100; cy = 105; sc = 70; var cx = 100; cy = 105; sc = 70;
var buzz = "", msg = "";
temp = 0; alt = 0; bpm = 0; temp = 0; alt = 0; bpm = 0;
var buzz = "", msg = "", inm = "", l = "", note = "(NOTEHERE)"; var buzz = "", /* Set this to transmit morse via vibrations */
var mode = 0, mode_time = 0; // 0 .. normal, 1 .. note 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 // GPS handling
var gps_needed = 0, gps_limit = 0; // seconds 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 prev_fix = null;
var gps_dist = 0; var gps_dist = 0;
var is_active = false; var mark_heading = -1;
var cur_altitude = 0, cur_temperature = 0, alt_adjust = 0;
const rest_altitude = 354; // 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) { function toMorse(x) {
r = ""; r = "";
@ -28,12 +61,10 @@ function toMorse(x) {
} }
return r; return r;
} }
function aload(s) { function aload(s) {
buzz += toMorse(' E'); buzz += toMorse(' E');
load(s); load(s);
} }
function gpsRestart() { function gpsRestart() {
print("gpsRestart"); print("gpsRestart");
Bangle.setGPSPower(1, "sixths"); Bangle.setGPSPower(1, "sixths");
@ -41,33 +72,155 @@ function gpsRestart() {
last_pause = 0; last_pause = 0;
last_fstart = 0; last_fstart = 0;
} }
function gpsPause() { function gpsPause() {
print("gpsPause"); print("gpsPause");
Bangle.setGPSPower(0, "sixths"); Bangle.setGPSPower(0, "sixths");
last_restart = 0; last_restart = 0;
last_pause = getTime(); last_pause = getTime();
} }
function gpsOn() { function gpsOn() {
gps_on = getTime(); gps_on = getTime();
gps_needed = 1000; gps_needed = 1000;
gps_limit = 60*60*4;
last_fix = 0; last_fix = 0;
prev_fix = null; prev_fix = null;
gps_dist = 0; gps_dist = 0;
gpsRestart(); gpsRestart();
} }
function gpsOff() { function gpsOff() {
Bangle.setGPSPower(0, "sixths"); Bangle.setGPSPower(0, "sixths");
gps_on = 0; 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) { function inputHandler(s) {
print("Ascii: ", s); print("Ascii: ", s, s[0], s[1]);
if (mode == 1) { if (s[0] == '^') {
note = note + s; 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(); mode_time = getTime();
return; return;
} }
@ -80,12 +233,21 @@ function inputHandler(s) {
else else
s = s+(bat/5); s = s+(bat/5);
buzz += toMorse(s); 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; break;
case 'F': gpsOff(); break;
case 'G': gpsOn(); break;
case 'L': aload("altimeter.app.js"); 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 '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': case 'T':
s = ' T'; s = ' T';
d = new Date(); d = new Date();
@ -94,9 +256,9 @@ function inputHandler(s) {
buzz += toMorse(s); buzz += toMorse(s);
break; break;
case 'R': aload("run.app.js"); break; case 'R': aload("run.app.js"); break;
case 'Y': buzz += " ."; Bangle.resetCompass(); break;
} }
} }
const morseDict = { const morseDict = {
'.-': 'A', '.-': 'A',
'-...': 'B', '-...': 'B',
@ -135,37 +297,46 @@ const morseDict = {
'-....': '6', '-....': '6',
'-----': '0', '-----': '0',
}; };
let asciiDict = {}; let asciiDict = {};
for (let k in morseDict) { for (let k in morseDict) {
print(k, morseDict[k]); print(k, morseDict[k]);
asciiDict[morseDict[k]] = k; asciiDict[morseDict[k]] = k;
} }
function morseToAscii(morse) { function morseToAscii(morse) {
return morseDict[morse]; return morseDict[morse];
} }
function asciiToMorse(char) { function asciiToMorse(char) {
return asciiDict[char]; return asciiDict[char];
} }
function morseHandler() { function morseHandler() {
if (inm[0] == "^") {
inputHandler("^"+morseToAscii(inm.substr(1)));
} else {
inputHandler(morseToAscii(inm)); inputHandler(morseToAscii(inm));
}
inm = ""; inm = "";
l = ""; l = "";
} }
function touchHandler(d) { function touchHandler(d) {
let x = Math.floor(d.x); let x = Math.floor(d.x);
let y = Math.floor(d.y); let y = Math.floor(d.y);
if (1) { /* Just a debugging feature */
g.setColor(0.25, 0, 0); g.setColor(0.25, 0, 0);
if (0)
g.fillCircle(W-x, W-y, 5); g.fillCircle(W-x, W-y, 5);
else
if (d.b) { 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") { if (x < W/2 && y < H/2 && l != ".u") {
inm = inm + "."; inm = inm + ".";
l = ".u"; l = ".u";
@ -183,12 +354,8 @@ function touchHandler(d) {
l = "-d"; l = "-d";
} }
} else //print(inm, "drag:", d);
morseHandler();
print(inm, "drag:", d);
} }
function add0(i) { function add0(i) {
if (i > 9) { if (i > 9) {
return ""+i; return ""+i;
@ -196,18 +363,14 @@ function add0(i) {
return "0"+i; return "0"+i;
} }
} }
var lastHour = -1, lastMin = -1; var lastHour = -1, lastMin = -1;
function logstamp(s) { function logstamp(s) {
logfile.write("utime=" + getTime() + " " + s + "\n"); logfile.write("utime=" + getTime() + " " + s + "\n");
} }
function loggps(fix) { function loggps(fix) {
logfile.write(fix.lat + " " + fix.lon + " "); logfile.write(fix.lat + " " + fix.lon + " ");
logstamp(""); logstamp("");
} }
function hourly() { function hourly() {
print("hourly"); print("hourly");
s = ' T'; s = ' T';
@ -215,31 +378,35 @@ function hourly() {
buzz += toMorse(s); buzz += toMorse(s);
logstamp(""); logstamp("");
} }
function show(msg, timeout) {
note = msg;
}
function fivemin() { function fivemin() {
print("fivemin"); print("fivemin");
s = ' B'; s = ' B';
bat = E.getBattery(); bat = E.getBattery();
if (bat < 45) { if (bat < 25) {
s = s+(bat/5);
if (is_active) if (is_active)
buzz += toMorse(s); buzz += toMorse(s);
show("Bat "+bat+"%", 60);
} }
if (0) try {
Bangle.getPressure().then((x) => { cur_altitude = x.altitude; Bangle.getPressure().then((x) => { cur_altitude = x.altitude;
cur_temperature = x.temperature; }, cur_temperature = x.temperature; },
print) print);
.catch(print); } catch (e) {
print("Altimeter error", e);
} }
}
function every(now) { function every(now) {
if ((mode > 0) && (mode_time - getTime() > 60)) { if ((mode > 0) && (getTime() - mode_time > 10)) {
if (mode == 1) { if (mode == 1) {
logstamp(">" + note); entryDone();
} }
mode = 0; mode = 0;
} }
if (gps_on && getTime() - gps_on > gps_limit) { if (gps_on && getTime() > gps_limit && getTime() > gps_speed_limit) {
Bangle.setGPSPower(0, "sixths"); Bangle.setGPSPower(0, "sixths");
gps_on = 0; 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 // distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km
// https://www.movable-type.co.uk/scripts/latlong.html // https://www.movable-type.co.uk/scripts/latlong.html
// (Equirectangular approximation) // (Equirectangular approximation)
function calcDistance(a,b) { 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 x = radians(b.lon-a.lon) * Math.cos(radians((a.lat+b.lat)/2));
var y = radians(b.lat-a.lat); var y = radians(b.lat-a.lat);
return Math.sqrt(x*x + y*y) * 6371000; 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 draw() { function radA(p) { return p*(Math.PI*2); }
g.setColor(1, 1, 1); function radD(d) { return d*(H/2); }
g.fillRect(0, 25, W, H); 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); 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, 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(); let now = new Date();
g.drawString(now.getHours() + ":" + add0(now.getMinutes()), 10, 90); g.setColor(0, 0, 0);
drawTime(now);
every(now); every(now);
let km = 0.001 * 0.719 * Bangle.getHealthStatus("day").steps; let km = 0.001 * 0.719 * Bangle.getHealthStatus("day").steps;
g.setFontAlign(-1, 1);
g.setFont('Vector', 26); g.setFont('Vector', 26);
const weekday = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]; 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); g.drawString(msg, 10, 145);
if (is_active) {
g.drawString("act " + (cur_altitude - alt_adjust).toFixed(0), 10, 175); if (getTime() - last_active > 15*60) {
} else {
alt_adjust = cur_altitude - rest_altitude; 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(); queueDraw();
} }
function draw_all() { function draw_all() {
g.setColor(0, 0, 0); g.setColor(0, 0, 0);
g.fillRect(0, 0, W, H); g.fillRect(0, 0, W, H);
@ -394,14 +601,13 @@ function draw_all() {
g.setFont('Vector', 22); g.setFont('Vector', 22);
g.drawString(now.getDate()+"."+(now.getMonth()+1)+" "+now.getDay(), 3, 60); 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("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("A" + Math.round(alt) + " T" + Math.round(temp), 3, 120);
g.drawString("C" + Math.round(co.heading) + " B" + bpm, 3, 140); g.drawString("C" + Math.round(co.heading) + " B" + bpm, 3, 140);
queueDraw(); queueDraw();
} }
function accelTask() { function accelTask() {
tm = 100; tm = 100;
acc = Bangle.getAccel(); acc = Bangle.getAccel();
@ -424,7 +630,6 @@ function accelTask() {
setTimeout(accelTask, tm); setTimeout(accelTask, tm);
} }
function buzzTask() { function buzzTask() {
if (buzz != "") { if (buzz != "") {
now = buzz[0]; now = buzz[0];
@ -442,9 +647,8 @@ function buzzTask() {
setTimeout(buzzTask, 6*dot); setTimeout(buzzTask, 6*dot);
} else print("Unknown character -- ", now, buzz); } else print("Unknown character -- ", now, buzz);
} else } else
setTimeout(buzzTask, 60000); setTimeout(buzzTask, 1000);
} }
function aliveTask() { function aliveTask() {
function cmp(s) { function cmp(s) {
let d = acc[s] - last_acc[s]; let d = acc[s] - last_acc[s];
@ -456,6 +660,7 @@ function aliveTask() {
if (cmp("x") || cmp("y") || cmp("z")) { if (cmp("x") || cmp("y") || cmp("z")) {
print("active"); print("active");
is_active = true; is_active = true;
last_active = getTime();
} }
last_acc = acc; last_acc = acc;
@ -476,14 +681,15 @@ function queueDraw() {
}, next - (Date.now() % next)); }, next - (Date.now() % next));
} }
function start() { function start() {
Bangle.on("drag", touchHandler); Bangle.on("drag", touchHandler);
if (0) if (0)
Bangle.on("accel", accelHandler); Bangle.on("accel", accelHandler);
if (0) { if (1) {
Bangle.setCompassPower(1, "sixths"); Bangle.setCompassPower(1, "sixths");
Bangle.setBarometerPower(1, "sixths"); Bangle.setBarometerPower(1, "sixths");
}
if (0) {
Bangle.setHRMPower(1, "sixths"); Bangle.setHRMPower(1, "sixths");
Bangle.setGPSPower(1, "sixths"); Bangle.setGPSPower(1, "sixths");
Bangle.on("HRM", (hrm) => { bpm = hrm.bpm; } ); Bangle.on("HRM", (hrm) => { bpm = hrm.bpm; } );
@ -500,9 +706,14 @@ function start() {
} }
g.reset(); g.reset();
Bangle.setUI(); Bangle.setUI({
mode : "clock"
});
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
let logfile = require("Storage").open("sixths.egt", "a"); let logfile = require("Storage").open("sixths.egt", "a");
if (0) {
testBearing();
} else
start(); start();

View File

@ -5,7 +5,8 @@ A simple game of stacking cubes.
## Usage ## 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 ## Creator

View File

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

View File

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

View File

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

View File

@ -36,11 +36,27 @@ const tiles = [
const ox = 176/2 - 5*8; const ox = 176/2 - 5*8;
const oy = 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 /* 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[20].fill(1);
pf[21].fill(1); pf[21].fill(1);
pf[22].fill(1); pf[22].fill(1);
pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; }); pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; });
}
function rotateTile(t, r) { function rotateTile(t, r) {
var nt = JSON.parse(JSON.stringify(t)); var nt = JSON.parse(JSON.stringify(t));
@ -98,6 +114,8 @@ function redrawPF(ly) {
function gameOver() { function gameOver() {
g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22) g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22)
.drawString("Game Over", 176/2, 76); .drawString("Game Over", 176/2, 76);
state = 0;
E.showAlert("Game Over").then(selectGame, print);
} }
function insertAndCheck() { function insertAndCheck() {
@ -138,6 +156,8 @@ function moveOk(t, dx, dy) {
} }
function gameStep() { function gameStep() {
if (state != 1)
return;
if (Date.now()-time > dropInterval) { // drop one step if (Date.now()-time > dropInterval) { // drop one step
time = Date.now(); time = Date.now();
if (moveOk(ct, 0, 1)) { if (moveOk(ct, 0, 1)) {
@ -169,11 +189,49 @@ function move(x, y) {
} }
} }
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(); 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) => { Bangle.on("drag", (e) => {
let h = 176/2; let h = 176/2;
if (state == 2) {
if (e.b)
selectGame();
return;
}
if (!e.b) if (!e.b)
return; return;
if (state == 0) return;
if (e.y < h) { if (e.y < h) {
if (e.x < h) if (e.x < h)
rotate(); rotate();
@ -185,6 +243,10 @@ Bangle.on("drag", (e) => {
} }
} }
} else { } else {
if (control == 1)
linear((e.x - 20) / 156);
if (control != 0)
return;
if (e.x < h) if (e.x < h)
move(-1, 0); move(-1, 0);
else else
@ -192,13 +254,48 @@ Bangle.on("drag", (e) => {
} }
}); });
Bangle.on("swipe", (x,y) => { initGame();
if (y<0) y = 0; drawGame();
move(x, y); state = 1;
}); var step = 450 - 50*level;
if (control == 3)
step = step*2;
dropInterval = step;
var gi = setInterval(gameStep, 50);
}
function drawGame() {
drawBoundingBox(); drawBoundingBox();
g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("6x15", 1).drawString("Lines", 22, 30).drawString("Next", 176-22, 30); g.setColor(1, 1, 1).setFontAlign(0, 1, 0)
.setFont("6x15", 1).drawString("Lines", 22, 30)
.drawString("Next", 176-22, 30);
showNext(ntn, ntr); showNext(ntn, ntr);
g.setColor(0).fillRect(5, 30, 41, 80).setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); g.setColor(0).fillRect(5, 30, 41, 80)
var gi = setInterval(gameStep, 20); .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.01: New App!
0.02: Display waypoint name instead of its index in remove menu and fix icon 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" 2 .. DD MM'ss"
*/ */
var mode = 1; var mode = 1;
var key; /* Shared between functions, typically wp name */
var fix; /* GPS fix */
var cancel_gps;
var gps_start;
function writeWP() { function writeWP() {
require('Storage').writeJSON("waypoints.json", wp); require('Storage').writeJSON("waypoints.json", wp);
@ -22,20 +26,86 @@ function mainMenu() {
var menu = { var menu = {
"< Back" : Bangle.load "< Back" : Bangle.load
}; };
if (Object.keys(wp).length==0) Object.assign(menu, {"NO WPs":""}); if (Object.keys(wp).length==0) {
else for (let id in wp) { //Object.assign(menu, {"NO WPs":""});
print("(no waypoints)");
} else for (let id in wp) {
let i = id; let i = id;
menu[wp[id]["name"]]=()=>{ decode(i); }; menu[wp[id]["name"]]=()=>{ show(i); };
} }
menu["Add"]=addCard; menu["Add"]=addCard;
menu["Remove"]=removeCard; menu["Remove"]=removeCard;
menu["Format"]=setFormat; menu["Format"]=setFormat;
menu["Mark GPS"]=markGps;
g.clear(); g.clear();
E.showMenu(menu); E.showMenu(menu);
} }
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();
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() { function setFormat() {
var confirmRemove = new Layout ( var la = new Layout (
{type:"v", c: [ {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:"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.dddd", cb:l=>{ mode = 0; mainMenu(); }},
@ -43,7 +113,7 @@ function setFormat() {
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD MM'ss"+'"', cb:l=>{ mode = 2; mainMenu(); }}, {type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: "DD MM'ss"+'"', cb:l=>{ mode = 2; mainMenu(); }},
], lazy:true}); ], lazy:true});
g.clear(); g.clear();
confirmRemove.render(); la.render();
} }
function format(x) { function format(x) {
@ -65,7 +135,6 @@ function format(x) {
return "" + d + " " + mf + "'" + s + '"'; return "" + d + " " + mf + "'" + s + '"';
} }
} }
function lat(x) { function lat(x) {
c = "N"; c = "N";
if (x<0) { if (x<0) {
@ -74,7 +143,6 @@ function lat(x) {
} }
return c+format(x); return c+format(x);
} }
function lon(x) { function lon(x) {
c = "E"; c = "E";
if (x<0) { if (x<0) {
@ -84,17 +152,17 @@ function lon(x) {
return c+format(x); return c+format(x);
} }
function decode(pin) { function show(pin) {
print(pin); print(pin);
var i = wp[pin]; var i = wp[pin];
var pinDecrypted=i["name"] + "\n" + lat(i["lat"]) + "\n" + lon(i["lon"]); var l = i["name"] + "\n" + lat(i["lat"]) + "\n" + lon(i["lon"]);
var showPin = new Layout ({ var la = new Layout ({
type:"v", c: [ type:"v", c: [
{type:"txt", font:"10%", pad:1, fillx:1, filly:1, label: pinDecrypted}, {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();}} {type:"btn", font:"10%", pad:1, fillx:1, filly:1, label:"OK", cb:l=>{mainMenu();}}
], lazy:true}); ], lazy:true});
g.clear(); g.clear();
showPin.render(); la.render();
} }
function showNumpad(text, key_, callback) { function showNumpad(text, key_, callback) {
@ -155,10 +223,10 @@ function showNumpad(text, key_, callback) {
function removeCard() { function removeCard() {
var menu = { var menu = {
"" : {title : "select card"}, "" : {title : "Select WP"},
"< Back" : mainMenu "< 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 { else {
wp.forEach((val, card) => { wp.forEach((val, card) => {
const name = wp[card].name; const name = wp[card].name;
@ -186,17 +254,16 @@ function removeCard() {
} }
function ask01(t, cb) { function ask01(t, cb) {
var confirmRemove = new Layout ( var la = new Layout (
{type:"v", c: [ {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[0], cb:l=>{ cb(1); }},
{type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: t[1], cb:l=>{ cb(-1); }}, {type:"btn", font:"15%", pad:1, fillx:1, filly:1, label: t[1], cb:l=>{ cb(-1); }},
], lazy:true}); ], lazy:true});
g.clear(); g.clear();
confirmRemove.render(); la.render();
} }
function askCoordinate(t1, t2, callback) { function askCoordinate(t1, t2, callback) {
let sign = 1; let sign = 1;
ask01(t1, function(sign) { 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() { function addCard() {
showNumpad("wpXX", "wp", function() { require("textinput").input({text:"wp"}).then(key => {
result = key; result = key;
if (wp[result]!=undefined) { if (wp[result]!=undefined) {
E.showMenu(); 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: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:"txt", font:"12%", pad:1, fillx:1, filly:1, label:"already exists."},
{type:"h", c: [ {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();}} {type:"btn", font:"10%", pad:1, fillx:1, filly:1, label: "CANCEL", cb:l=>{mainMenu();}}
]} ]}
], lazy:true}); ], lazy:true});
g.clear(); g.clear();
alreadyExists.render(); alreadyExists.render();
} }
g.clear(); addCardName(result);
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();
});
}); });
} }
g.reset(); g.reset();
Bangle.setUI(); Bangle.setUI();
mainMenu(); mainMenu();

View File

@ -1,11 +1,13 @@
{ "id": "waypoint_editor", { "id": "waypoint_editor",
"name": "Waypoint editor", "name": "Waypoint editor",
"version":"0.02", "version":"0.03",
"description": "Allows editing waypoints on device", "description": "Allows editing waypoints on device",
"icon": "app.png", "icon": "app.png",
"readme": "README.md", "readme": "README.md",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"allow_emulator": true,
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",
"dependencies": {"textinput":"type"},
"storage": [ "storage": [
{"name":"waypoint_editor.app.js","url":"app.js"}, {"name":"waypoint_editor.app.js","url":"app.js"},
{"name":"waypoint_editor.img","url":"app-icon.js","evaluate":true} {"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.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.06: Use 7 segment font, update *on* the minute, use less memory
0.07: allow turning on/off when quick-switching apps 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", "id": "widclk",
"name": "Digital clock widget", "name": "Digital clock widget",
"version": "0.07", "version": "0.08",
"description": "A simple digital clock widget that appears when not showing a fullscreen clock", "description": "A simple digital clock widget that appears when not showing a fullscreen clock",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",

View File

@ -7,9 +7,9 @@ WIDGETS["wdclk"]={area:"tl",width:Bangle.CLOCK?0:52/* g.stringWidth("00:00") */,
return setTimeout(Bangle.drawWidgets,1); // widget changed size - redraw return setTimeout(Bangle.drawWidgets,1); // widget changed size - redraw
} }
if (!this.width) return; // if not visible, return 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); 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 // queue draw in one minute
if (this.drawTimeout) clearTimeout(this.drawTimeout); if (this.drawTimeout) clearTimeout(this.drawTimeout);
this.drawTimeout = setTimeout(()=>{ this.drawTimeout = setTimeout(()=>{

View File

@ -3,4 +3,4 @@
0.03: based in widclk v0.05 compatible at same time, bottom area and color 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.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.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", "id": "widclkbttm",
"name": "Digital clock (Bottom) widget", "name": "Digital clock (Bottom) widget",
"shortName": "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)", "description": "Displays time HH:mm in the bottom of the screen (may not be compatible with some apps)",
"icon": "widclkbttm.png", "icon": "widclkbttm.png",
"type": "widget", "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 return setTimeout(Bangle.drawWidgets,1); // widget changed size - redraw
} }
if (!this.width) return; // if not visible, return 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); var time = require("locale").time(new Date(),1);
g.drawString(time, this.x, this.y+11, true); // 5 * 6*2 = 60 g.drawString(time, this.x, this.y+11, true); // 5 * 6*2 = 60
// queue draw in one minute // queue draw in one minute