Merge branch 'master' of github.com:espruino/BangleApps
After Width: | Height: | Size: 983 B |
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
|
@ -0,0 +1 @@
|
||||||
|
atob("MDCBAAAAAAAAAAAAAAAAAAAAH/AAAAAAf/4AAAAB4AeAAAAHgAHgAAAOAABwAAAcAAA4AAA4AMAcAABwAMAOAABgA/AGAADAAeADAAHAAMADgAGAAAABgAGAAAABgAMAAAAAwAMBAAAAwAMDAAAAwAMHwAAAwAMHwAAAwAMDAACAwAMBAADAwAMAAAPgwAMAAAPgwAMAAADAwAGAAACBgAGAAAABgAHAAAADgADAAAADAADgAAAHAAB////+AAB////+AABgAAAGAABgAAAGAABgAAAGAADAAAADAADAAAADAADAAAADAAGAAAABgAGAAAABgAH/////gAP/////wAYAAAAAYAYAAAAAYAf/////4AP/////wAAAAAAAAAAAAAAAAA==")
|
|
@ -0,0 +1,92 @@
|
||||||
|
var keyboard = "textinput";
|
||||||
|
var Name = "";
|
||||||
|
Bangle.setLCDTimeout(0);
|
||||||
|
var menuOpen = 1;
|
||||||
|
var answers = new Array("no", "yes","WHAT????","What do you think", "That was a bad question", "YES!!!", "NOOOOO!!", "nope","100%","yup","why should I answer that?","think for yourself","ask again later, I'm busy", "what Was that horrible question","how dare you?","you wanted to hear yes? okay, yes", "Don't get angry when I say no","you are 100% wrong","totally, for sure","hmmm... I'll ponder it and get back to you later","wow, you really have a lot of questions", "NOPE","is the sky blue, hmmm...","I don't have time to answer","How many more questions before you change my name?","theres this thing called wikipedia","hmm... I don't seem to be able to reach the internet right now","if you phrase it like that, yes","Huh, never thought so hard in my life","The winds of time say no");
|
||||||
|
var consonants = new Array("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z");
|
||||||
|
var vowels = new Array("a","e","i","o","u");
|
||||||
|
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
||||||
|
function generateName()
|
||||||
|
{
|
||||||
|
Name = "";
|
||||||
|
var nameLength = Math.round(Math.random()*5);
|
||||||
|
for(var i = 0; i < nameLength; i++){
|
||||||
|
var cosonant = consonants[Math.round(Math.random()*consonants.length/2)];
|
||||||
|
var vowel = vowels[Math.round(Math.random()*vowels.length/2)];
|
||||||
|
Name = Name + cosonant + vowel;
|
||||||
|
if(Name == "")
|
||||||
|
{
|
||||||
|
generateName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generateName();
|
||||||
|
function menu()
|
||||||
|
{
|
||||||
|
g.clear();
|
||||||
|
E.showMenu();
|
||||||
|
menuOpen = 1;
|
||||||
|
E.showMenu({
|
||||||
|
"" : { title : Name },
|
||||||
|
"< Back" : () => menu(),
|
||||||
|
"Start" : () => {
|
||||||
|
E.showMenu();
|
||||||
|
g.clear();
|
||||||
|
menuOpen = 0;
|
||||||
|
Drawtext("ask " + Name + " a yes or no question");
|
||||||
|
},
|
||||||
|
"regenerate name" : () => {
|
||||||
|
menu();
|
||||||
|
generateName();
|
||||||
|
},
|
||||||
|
"show answers" : () => {
|
||||||
|
var menu = new Array([]);
|
||||||
|
for(var i = 0; i < answers.length; i++){
|
||||||
|
menu.push({title : answers[i]});
|
||||||
|
}
|
||||||
|
E.showMenu(menu);
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"Add answer" : () => {
|
||||||
|
E.showMenu();
|
||||||
|
keyboard.input({}).then(result => {if(result != ""){answers.push(result);} menu();});
|
||||||
|
},
|
||||||
|
"Edit name" : () => {
|
||||||
|
E.showMenu();
|
||||||
|
keyboard.input({}).then(result => {if(result != ""){Name = result;} menu();});
|
||||||
|
|
||||||
|
},
|
||||||
|
"Exit" : () => load(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
menu();
|
||||||
|
|
||||||
|
var answer;
|
||||||
|
function Drawtext(text)
|
||||||
|
{
|
||||||
|
g.clear();
|
||||||
|
g.setFont("Vector", 20);
|
||||||
|
g.drawString(g.wrapString(text, g.getWidth(), -20).join("\n"));
|
||||||
|
}
|
||||||
|
function WriteAnswer()
|
||||||
|
{
|
||||||
|
if (menuOpen == 0)
|
||||||
|
{
|
||||||
|
var randomnumber = Math.round(Math.random()*answers.length);
|
||||||
|
answer = answers[randomnumber];
|
||||||
|
Drawtext(answer);
|
||||||
|
setTimeout(function() {
|
||||||
|
Drawtext("ask " + Name + " a yes or no question");
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
setWatch(function() {
|
||||||
|
menu();
|
||||||
|
|
||||||
|
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true, edge:"falling"});
|
||||||
|
|
||||||
|
Bangle.on('touch', function(button, xy) { WriteAnswer(); });
|
|
@ -0,0 +1,19 @@
|
||||||
|
{ "id": "8ball",
|
||||||
|
"name": "Magic 8 ball",
|
||||||
|
"shortName":"8ball",
|
||||||
|
"icon": "8ball.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"screenshots": [
|
||||||
|
{"url":"screenshot.png"},
|
||||||
|
{"url":"screenshot-1.png"},
|
||||||
|
{"url":"screenshot-2.png"}
|
||||||
|
],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"description": "A very sarcastic magic 8ball",
|
||||||
|
"tags": "game",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"8ball.app.js","url":"app.js"},
|
||||||
|
{"name":"8ball.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
0.10: New app introduced to the app loader!
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Binary LED Clock
|
||||||
|
|
||||||
|
A binary watch with LEDs, showing time and date.
|
||||||
|
|
||||||
|
From top to bottom the watch face shows four rows of leds:
|
||||||
|
|
||||||
|
* hours (red leds)
|
||||||
|
* minutes (green leds)
|
||||||
|
* day (yellow leds, top row)
|
||||||
|
* month (yellow leds, bottom row)
|
||||||
|
|
||||||
|
As usual, luminous leds represent a logical one, dark leds a logcal '0'.
|
||||||
|
Widgets aren't affected and are shown as normal.
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEw4n/AAIHBqut8FPgH4sspk1T885/feoMI74TB1Fc51Dmfg28gKmMCrNSAgMlyo5BgV7uQIKgEhiMRkECAYMSgErolLBBIXBqIKBqEFAYMVgF0olEuAIIC4ORBQOQhIDBjMA2gOB2AIIF7JfXR67X0lvdHwQII7vSa4/TmYKBBBEtmc9a40NmYKBBBIbBmfQa4oOEBBAXFF65fXR64A/AG8IvN4AgOG62ABAuHy4IGgEHiMXAgNu91gBAtxiNwBAsAhMRjIEB73ucIIIEyMRyAIFF7BfXAH6/IttoKxRoIgEG93mQxSYIgEN93tWxTIIF7BfXAH4AGw93u/A44IDhl8vQRFBogXB0ECuGoBAcKxRxBC53Hhlyk8ggVyuQGBvlwhgNBk98BAN6I4UgC4N4BwWgAwWsC4fAk4IB0AvBAgIQBBwUIkQOBAwQXCJIIEBI4UAkQXE48sAwgXJF40mgAvDvRtCC4pfEC4WCPYJdBDYNyC4wAX"))
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,136 @@
|
||||||
|
//Binary LED Clock (BLC) by aeMKai
|
||||||
|
|
||||||
|
{ // must be inside our own scope here so that when we are unloaded everything disappears
|
||||||
|
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
||||||
|
let drawTimeout;
|
||||||
|
|
||||||
|
// Actually draw the watch face
|
||||||
|
let draw = function()
|
||||||
|
{
|
||||||
|
// Bangle.js2 -> 176x176
|
||||||
|
var x_rgt = g.getWidth();
|
||||||
|
var y_bot = g.getHeight();
|
||||||
|
//var x_cntr = x_rgt / 2;
|
||||||
|
var y_cntr = y_bot / 18*7; // not to high because of widget-field (1/3 is to high)
|
||||||
|
g.reset().clearRect(Bangle.appRect); // clear whole background (w/o widgets)
|
||||||
|
|
||||||
|
let white = [1,1,1];
|
||||||
|
let red = [1,0,0];
|
||||||
|
let green = [0,1,0];
|
||||||
|
//let blue = [0,0,1];
|
||||||
|
let yellow = [1,1,0];
|
||||||
|
//let magenta = [1,0,1];
|
||||||
|
//let cyan = [0,1,1];
|
||||||
|
let black = [0,0,0];
|
||||||
|
let bord_col = white;
|
||||||
|
let col_off = black;
|
||||||
|
|
||||||
|
var col = new Array(red, green, yellow, yellow); // [R,G,B]
|
||||||
|
|
||||||
|
let pot_2 = [1, 2, 4, 8, 16, 32]; // array with powers of two, because power-op (**)
|
||||||
|
// doesn't work -> maybe also faster
|
||||||
|
|
||||||
|
|
||||||
|
var nr_lines = 4; // 4 rows: hour (hr), minute (min), day (day), month (mon)
|
||||||
|
|
||||||
|
// Arrays: [hr, min, day, mon]
|
||||||
|
//No of Bits: 5 6 5 4
|
||||||
|
let msbits = [4, 5, 4, 3]; // MSB = No bits - 1
|
||||||
|
let rad = [12, 12, 8, 8]; // radiuses for each row
|
||||||
|
var x_dist = 28;
|
||||||
|
let y_dist = [0, 30, 60, 85]; // y-position from y_centr for each row from top
|
||||||
|
// don't calc. automatic as for x, because of different spaces
|
||||||
|
var x_offs_rgt = 16; // distance from right border (layout)
|
||||||
|
|
||||||
|
// Date-Time-Array: 4x6 Bit
|
||||||
|
//var idx_hr = 0;
|
||||||
|
//var idx_min = 1;
|
||||||
|
//var idx_day = 2;
|
||||||
|
//var idx_mon = 3;
|
||||||
|
var dt_bit_arr = [[0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0]];
|
||||||
|
|
||||||
|
var date_time = new Date();
|
||||||
|
var hr = date_time.getHours(); // 0..23
|
||||||
|
var min = date_time.getMinutes(); // 0..59
|
||||||
|
var day = date_time.getDate(); // 1..31
|
||||||
|
var mon = date_time.getMonth() + 1; // GetMonth() -> 0..11
|
||||||
|
|
||||||
|
let dt_array = [hr, min, day, mon];
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// compute bit-pattern from time/date and draw leds
|
||||||
|
////////////////////////////////////////
|
||||||
|
var line_cnt = 0;
|
||||||
|
var cnt = 0;
|
||||||
|
var bit_cnt = 0;
|
||||||
|
|
||||||
|
while (line_cnt < nr_lines)
|
||||||
|
{
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// compute bit-pattern
|
||||||
|
bit_cnt = msbits[line_cnt];
|
||||||
|
|
||||||
|
while (bit_cnt >= 0)
|
||||||
|
{
|
||||||
|
if (dt_array[line_cnt] >= pot_2[bit_cnt])
|
||||||
|
{
|
||||||
|
dt_array[line_cnt] -= pot_2[bit_cnt];
|
||||||
|
dt_bit_arr[line_cnt][bit_cnt] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dt_bit_arr[line_cnt][bit_cnt] = 0;
|
||||||
|
}
|
||||||
|
bit_cnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// draw leds (first white border for black screen, then led itself)
|
||||||
|
cnt = 0;
|
||||||
|
|
||||||
|
while (cnt <= msbits[line_cnt])
|
||||||
|
{
|
||||||
|
g.setColor(bord_col[0], bord_col[1], bord_col[2]);
|
||||||
|
g.drawCircle(x_rgt-x_offs_rgt-cnt*x_dist, y_cntr-20+y_dist[line_cnt], rad[line_cnt]);
|
||||||
|
|
||||||
|
if (dt_bit_arr[line_cnt][cnt] == 1)
|
||||||
|
{
|
||||||
|
g.setColor(col[line_cnt][0], col[line_cnt][1], col[line_cnt][2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g.setColor(col_off[0], col_off[1], col_off[2]);
|
||||||
|
}
|
||||||
|
g.fillCircle(x_rgt-x_offs_rgt-cnt*x_dist, y_cntr-20+y_dist[line_cnt], rad[line_cnt]-1);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
line_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue next draw
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function()
|
||||||
|
{
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
Bangle.setUI(
|
||||||
|
{
|
||||||
|
mode : "clock",
|
||||||
|
remove : function()
|
||||||
|
{
|
||||||
|
// Called to unload all of the clock app
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Load widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
draw();
|
||||||
|
setTimeout(Bangle.drawWidgets,0);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"id":"blc",
|
||||||
|
"name":"Binary LED Clock",
|
||||||
|
"version": "0.10",
|
||||||
|
"description": "Binary LED Clock with date",
|
||||||
|
"icon":"blc-icon.png",
|
||||||
|
"screenshots": [{"url":"screenshot_blc.bmp"}],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"blc.app.js","url":"blc.js"},
|
||||||
|
{"name":"blc.img","url":"blc-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 15 KiB |
|
@ -3,3 +3,4 @@
|
||||||
0.03: Allows showing the month in short or long format by setting `"shortMonth"` to true or false
|
0.03: Allows showing the month in short or long format by setting `"shortMonth"` to true or false
|
||||||
0.04: Improves touchscreen drag handling for background apps such as Pattern Launcher
|
0.04: Improves touchscreen drag handling for background apps such as Pattern Launcher
|
||||||
0.05: Fixes step count not resetting after a new day starts
|
0.05: Fixes step count not resetting after a new day starts
|
||||||
|
0.06 Added clockbackground app functionality
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
* ---------------------------------------------------------------
|
* ---------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
let background = require("clockbg");
|
||||||
let storage = require("Storage");
|
let storage = require("Storage");
|
||||||
let locale = require("locale");
|
let locale = require("locale");
|
||||||
let widgets = require("widget_utils");
|
let widgets = require("widget_utils");
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
let bgImage;
|
|
||||||
let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0;
|
let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0;
|
||||||
let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json';
|
let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json';
|
||||||
// Add a condition to check if the file exists, if it does not, default to 'boxclk.json'
|
// Add a condition to check if the file exists, if it does not, default to 'boxclk.json'
|
||||||
|
@ -71,14 +71,6 @@
|
||||||
* ---------------------------------------------------------------
|
* ---------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (let key in boxesConfig) {
|
|
||||||
if (key === 'bg' && boxesConfig[key].img) {
|
|
||||||
bgImage = storage.read(boxesConfig[key].img);
|
|
||||||
} else if (key !== 'selectedConfig') {
|
|
||||||
boxes[key] = Object.assign({}, boxesConfig[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let boxKeys = Object.keys(boxes);
|
let boxKeys = Object.keys(boxes);
|
||||||
|
|
||||||
boxKeys.forEach((key) => {
|
boxKeys.forEach((key) => {
|
||||||
|
@ -224,9 +216,7 @@
|
||||||
return function(boxes) {
|
return function(boxes) {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
g.clear();
|
g.clear();
|
||||||
if (bgImage) {
|
background.fillRect(Bangle.appRect);
|
||||||
g.drawImage(bgImage, 0, 0);
|
|
||||||
}
|
|
||||||
if (boxes.time) {
|
if (boxes.time) {
|
||||||
boxes.time.string = modString(boxes.time, locale.time(date, isBool(boxes.time.short, true) ? 1 : 0));
|
boxes.time.string = modString(boxes.time, locale.time(date, isBool(boxes.time.short, true) ? 1 : 0));
|
||||||
updatePerMinute = isBool(boxes.time.short, true);
|
updatePerMinute = isBool(boxes.time.short, true);
|
||||||
|
@ -412,4 +402,4 @@
|
||||||
widgets.swipeOn();
|
widgets.swipeOn();
|
||||||
modSetColor();
|
modSetColor();
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"version": "0.05",
|
"version": "0.05",
|
||||||
"description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background",
|
"description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
"dependencies" : { "clockbg":"module" },
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{"url":"screenshot.png"},
|
{"url":"screenshot.png"},
|
||||||
{"url":"screenshot-1.png"},
|
{"url":"screenshot-1.png"},
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: attempt to import
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Ded Reckon
|
||||||
|
|
||||||
|
Dead Reckoning using compass and step counter.
|
||||||
|
|
||||||
|
This allows logging track using "dead reckoning" -- that's logging
|
||||||
|
angles from compass and distances from step counter. You need to mark
|
||||||
|
turns, and point watch to direction of the turn. Simultaneously, it
|
||||||
|
tries to log positions using GPS. You can use it to calibrate your
|
||||||
|
step length by comparing GPS and step counter data. It can also get
|
||||||
|
pretty accurate recording of track walked in right circumstances.
|
||||||
|
|
||||||
|
Tap bottom part of the screen to select display (text or map for
|
||||||
|
now). Point watch to new direction, then tap top left part of screen
|
||||||
|
to indicate a turn.
|
||||||
|
|
||||||
|
Map shows blue line for track from dead reckonging, and green line for
|
||||||
|
track from GPS.
|
||||||
|
|
||||||
|
You probably want magnav installed (and calibrated) for useful
|
||||||
|
results, as it provides library with better compass.
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwhHXAH4A/AH4A/AFsAFtoADF1wwqF4wwhEI5goGGIjFYN4wFF1KbHGUolIMc4lGSdIwJd9DstAH7FrBywwgad4veDwojJBIIvcFwIACGBYICGDYvEGBYvdFwqyLL8i+LF7oxFRxgveGAQ0EF5IwfMY4vpL5AFLAEYv/F8owoE44vrAY4vmAQIEEF85dGGE0AE4gvoFwpmHd0oINAH4A/AH4AvA"))
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,442 @@
|
||||||
|
/* Ded Reckon */
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
|
/* fmt library v0.1.3 */
|
||||||
|
let fmt = {
|
||||||
|
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",
|
||||||
|
|
||||||
|
/* 0 .. DD.ddddd
|
||||||
|
1 .. DD MM.mmm'
|
||||||
|
2 .. DD MM'ss"
|
||||||
|
*/
|
||||||
|
geo_mode : 1,
|
||||||
|
|
||||||
|
init: function() {},
|
||||||
|
fmtDist: function(km) {
|
||||||
|
if (km >= 1.0) return km.toFixed(1) + this.icon_km;
|
||||||
|
return (km*1000).toFixed(0) + this.icon_m;
|
||||||
|
},
|
||||||
|
fmtSteps: function(n) { return this.fmtDist(0.001 * 0.719 * n); },
|
||||||
|
fmtAlt: function(m) { return m.toFixed(0) + this.icon_alt; },
|
||||||
|
draw_dot : 1,
|
||||||
|
add0: function(i) {
|
||||||
|
if (i > 9) {
|
||||||
|
return ""+i;
|
||||||
|
} else {
|
||||||
|
return "0"+i;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fmtTOD: function(now) {
|
||||||
|
this.draw_dot = !this.draw_dot;
|
||||||
|
let dot = ":";
|
||||||
|
if (!this.draw_dot)
|
||||||
|
dot = ".";
|
||||||
|
return now.getHours() + dot + this.add0(now.getMinutes());
|
||||||
|
},
|
||||||
|
fmtNow: function() { return this.fmtTOD(new Date()); },
|
||||||
|
fmtTimeDiff: function(d) {
|
||||||
|
if (d < 180)
|
||||||
|
return ""+d.toFixed(0);
|
||||||
|
d = d/60;
|
||||||
|
return ""+d.toFixed(0)+"m";
|
||||||
|
},
|
||||||
|
fmtAngle: function(x) {
|
||||||
|
switch (this.geo_mode) {
|
||||||
|
case 0:
|
||||||
|
return "" + x;
|
||||||
|
case 1: {
|
||||||
|
let d = Math.floor(x);
|
||||||
|
let m = x - d;
|
||||||
|
m = m*60;
|
||||||
|
return "" + d + " " + m.toFixed(3) + "'";
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
let d = Math.floor(x);
|
||||||
|
let m = x - d;
|
||||||
|
m = m*60;
|
||||||
|
let mf = Math.floor(m);
|
||||||
|
let s = m - mf;
|
||||||
|
s = s*60;
|
||||||
|
return "" + d + " " + mf + "'" + s.toFixed(0) + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "bad mode?";
|
||||||
|
},
|
||||||
|
fmtPos: function(pos) {
|
||||||
|
let x = pos.lat;
|
||||||
|
let c = "N";
|
||||||
|
if (x<0) {
|
||||||
|
c = "S";
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
let s = c+this.fmtAngle(x) + "\n";
|
||||||
|
c = "E";
|
||||||
|
if (x<0) {
|
||||||
|
c = "W";
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
return s + c + this.fmtAngle(x);
|
||||||
|
},
|
||||||
|
fmtFix: function(fix, t) {
|
||||||
|
if (fix && fix.fix && fix.lat) {
|
||||||
|
return this.fmtSpeed(fix.speed) + " " +
|
||||||
|
this.fmtAlt(fix.alt);
|
||||||
|
} else {
|
||||||
|
return "N/FIX " + this.fmtTimeDiff(t);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fmtSpeed: function(kph) {
|
||||||
|
return kph.toFixed(1) + this.icon_kph;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* gps library v0.1.1 */
|
||||||
|
let gps = {
|
||||||
|
emulator: -1,
|
||||||
|
init: function(x) {
|
||||||
|
this.emulator = (process.env.BOARD=="EMSCRIPTEN"
|
||||||
|
|| process.env.BOARD=="EMSCRIPTEN2")?1:0;
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
on_gps: function(f) {
|
||||||
|
let fix = this.getGPSFix();
|
||||||
|
f(fix);
|
||||||
|
|
||||||
|
/*
|
||||||
|
"lat": number, // Latitude in degrees
|
||||||
|
"lon": number, // Longitude in degrees
|
||||||
|
"alt": number, // altitude in M
|
||||||
|
"speed": number, // Speed in kph
|
||||||
|
"course": number, // Course in degrees
|
||||||
|
"time": Date, // Current Time (or undefined if not known)
|
||||||
|
"satellites": 7, // Number of satellites
|
||||||
|
"fix": 1 // NMEA Fix state - 0 is no fix
|
||||||
|
"hdop": number, // Horizontal Dilution of Precision
|
||||||
|
*/
|
||||||
|
this.state.timeout = setTimeout(this.on_gps, 1000, f);
|
||||||
|
},
|
||||||
|
off_gps: function() {
|
||||||
|
clearTimeout(this.state.timeout);
|
||||||
|
},
|
||||||
|
getGPSFix: function() {
|
||||||
|
if (!this.emulator)
|
||||||
|
return Bangle.getGPSFix();
|
||||||
|
let fix = {};
|
||||||
|
fix.fix = 1;
|
||||||
|
fix.lat = 50;
|
||||||
|
fix.lon = 14-(getTime()-this.gps_start) / 1000; /* Go West! */
|
||||||
|
fix.alt = 200;
|
||||||
|
fix.speed = 5;
|
||||||
|
fix.course = 30;
|
||||||
|
fix.time = Date();
|
||||||
|
fix.satellites = 5;
|
||||||
|
fix.hdop = 12;
|
||||||
|
return fix;
|
||||||
|
},
|
||||||
|
gps_start : -1,
|
||||||
|
start_gps: function() {
|
||||||
|
Bangle.setGPSPower(1, "libgps");
|
||||||
|
this.gps_start = getTime();
|
||||||
|
},
|
||||||
|
stop_gps: function() {
|
||||||
|
Bangle.setGPSPower(0, "libgps");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ui library 0.1 */
|
||||||
|
let ui = {
|
||||||
|
display: 0,
|
||||||
|
numScreens: 2,
|
||||||
|
drawMsg: function(msg) {
|
||||||
|
g.reset().setFont("Vector", 35)
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.fillRect(0, this.wi, 176, 176)
|
||||||
|
.setColor(0,0,0)
|
||||||
|
.drawString(msg, 5, 30);
|
||||||
|
},
|
||||||
|
drawBusy: function() {
|
||||||
|
this.drawMsg("\n.oO busy");
|
||||||
|
},
|
||||||
|
nextScreen: function() {
|
||||||
|
print("nextS");
|
||||||
|
this.display = this.display + 1;
|
||||||
|
if (this.display == this.numScreens)
|
||||||
|
this.display = 0;
|
||||||
|
this.drawBusy();
|
||||||
|
},
|
||||||
|
prevScreen: function() {
|
||||||
|
print("prevS");
|
||||||
|
this.display = this.display - 1;
|
||||||
|
if (this.display < 0)
|
||||||
|
this.display = this.numScreens - 1;
|
||||||
|
this.drawBusy();
|
||||||
|
},
|
||||||
|
onSwipe: function(dir) {
|
||||||
|
this.nextScreen();
|
||||||
|
},
|
||||||
|
h: 176,
|
||||||
|
w: 176,
|
||||||
|
wi: 32,
|
||||||
|
last_b: 0,
|
||||||
|
touchHandler: function(d) {
|
||||||
|
let x = Math.floor(d.x);
|
||||||
|
let y = Math.floor(d.y);
|
||||||
|
|
||||||
|
if (d.b != 1 || this.last_b != 0) {
|
||||||
|
this.last_b = d.b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("touch", x, y, this.h, this.w);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if ((x<this.h/2) && (y<this.w/2)) {
|
||||||
|
}
|
||||||
|
if ((x>this.h/2) && (y<this.w/2)) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((x<this.h/2) && (y>this.w/2)) {
|
||||||
|
print("prev");
|
||||||
|
this.prevScreen();
|
||||||
|
}
|
||||||
|
if ((x>this.h/2) && (y>this.w/2)) {
|
||||||
|
print("next");
|
||||||
|
this.nextScreen();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: function() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var last_steps = Bangle.getStepCount(), last_time = getTime(), speed = 0, step_phase = 0;
|
||||||
|
|
||||||
|
var mpstep = 0.719 * 1.15;
|
||||||
|
|
||||||
|
function updateSteps() {
|
||||||
|
if (step_phase ++ > 9) {
|
||||||
|
step_phase =0;
|
||||||
|
let steps = Bangle.getStepCount();
|
||||||
|
let time = getTime();
|
||||||
|
|
||||||
|
speed = 3.6 * mpstep * ((steps-last_steps) / (time-last_time));
|
||||||
|
last_steps = steps;
|
||||||
|
last_time = time;
|
||||||
|
}
|
||||||
|
return "" + fmt.fmtSpeed(speed) + " " + step_phase + "\n" + fmt.fmtDist(log_dist/1000) + " " + fmt.fmtDist(log_last/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compensated compass */
|
||||||
|
var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
|
||||||
|
const tiltfixread = require("magnav").tiltfixread;
|
||||||
|
var heading;
|
||||||
|
|
||||||
|
|
||||||
|
var cancel_gps = false;
|
||||||
|
|
||||||
|
function drawStats() {
|
||||||
|
let fix = gps.getGPSFix();
|
||||||
|
|
||||||
|
let msg = fmt.fmtFix(fix, getTime() - gps.gps_start);
|
||||||
|
|
||||||
|
msg += "\n" + fmt.fmtDist(gps_dist/1000) + " " + fmt.fmtDist(gps_last/1000) + "\n" + updateSteps();
|
||||||
|
let c = Bangle.getCompass();
|
||||||
|
if (c) msg += "\n" + c.heading.toFixed(0) + "/" + heading.toFixed(0) + "deg " + log.length + "\n";
|
||||||
|
|
||||||
|
g.reset().clear().setFont("Vector", 31)
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.fillRect(0, 24, 176, 100)
|
||||||
|
.setColor(0,0,0)
|
||||||
|
.drawString(msg, 3, 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGps() {
|
||||||
|
if (cancel_gps)
|
||||||
|
return;
|
||||||
|
heading = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
|
||||||
|
if (ui.display == 0) {
|
||||||
|
setTimeout(updateGps, 1000);
|
||||||
|
drawLog();
|
||||||
|
drawStats();
|
||||||
|
}
|
||||||
|
if (ui.display == 1) {
|
||||||
|
setTimeout(updateGps, 1000);
|
||||||
|
drawLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopGps() {
|
||||||
|
cancel_gps=true;
|
||||||
|
gps.stop_gps();
|
||||||
|
}
|
||||||
|
|
||||||
|
var log = [], log_dist = 0, gps_dist = 0;
|
||||||
|
var log_last = 0, gps_last = 0;
|
||||||
|
|
||||||
|
function logEntry() {
|
||||||
|
let e = {};
|
||||||
|
e.time = getTime();
|
||||||
|
e.fix = gps.getGPSFix();
|
||||||
|
e.steps = Bangle.getStepCount();
|
||||||
|
if (0) {
|
||||||
|
let c = Bangle.getCompass();
|
||||||
|
if (c)
|
||||||
|
e.dir = c.heading;
|
||||||
|
else
|
||||||
|
e.dir = -1;
|
||||||
|
} else {
|
||||||
|
e.dir = heading;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTurn() {
|
||||||
|
let e = logEntry();
|
||||||
|
log.push(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dn, de;
|
||||||
|
function initConv(fix) {
|
||||||
|
let n = { lat: fix.lat+1, lon: fix.lon };
|
||||||
|
let e = { lat: fix.lat, lon: fix.lon+1 };
|
||||||
|
|
||||||
|
dn = calcDistance(fix, n);
|
||||||
|
de = calcDistance(fix, e);
|
||||||
|
print("conversion is ", dn, 108000, de, 50000);
|
||||||
|
}
|
||||||
|
function toM(start, fix) {
|
||||||
|
return { x: (fix.lon - start.lon) * de, y: (fix.lat - start.lat) * dn };
|
||||||
|
}
|
||||||
|
var mpp = 4;
|
||||||
|
function toPix(q) {
|
||||||
|
let p = { x: q.x, y: q.y };
|
||||||
|
p.x /= mpp; /* 10 m / pix */
|
||||||
|
p.y /= -mpp;
|
||||||
|
p.x += 85;
|
||||||
|
p.y += 85;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLog() {
|
||||||
|
let here = logEntry();
|
||||||
|
if (!here.fix.lat) {
|
||||||
|
here.fix.lat = 50;
|
||||||
|
here.fix.lon = 14;
|
||||||
|
}
|
||||||
|
initConv(here.fix);
|
||||||
|
log.push(here);
|
||||||
|
let l = log;
|
||||||
|
log_dist = 0;
|
||||||
|
log_last = -1;
|
||||||
|
gps_last = -1;
|
||||||
|
|
||||||
|
g.reset().clear();
|
||||||
|
g.setColor(0, 0, 1);
|
||||||
|
let last = { x: 0, y: 0 };
|
||||||
|
for (let i = l.length - 2; i >= 0; i--) {
|
||||||
|
let next = {};
|
||||||
|
let m = (l[i+1].steps - l[i].steps) * mpstep;
|
||||||
|
let dir = radians(180 + l[i].dir);
|
||||||
|
next.x = last.x + m * Math.sin(dir);
|
||||||
|
next.y = last.y + m * Math.cos(dir);
|
||||||
|
print(dir, m, last, next);
|
||||||
|
let lp = toPix(last);
|
||||||
|
let np = toPix(next);
|
||||||
|
g.drawLine(lp.x, lp.y, np.x, np.y);
|
||||||
|
g.drawCircle(np.x, np.y, 3);
|
||||||
|
last = next;
|
||||||
|
if (log_last == -1)
|
||||||
|
log_last = m;
|
||||||
|
log_dist += m;
|
||||||
|
}
|
||||||
|
g.setColor(0, 1, 0);
|
||||||
|
last = { x: 0, y: 0 };
|
||||||
|
gps_dist = 0;
|
||||||
|
for (let i = l.length - 2; i >= 0; i--) {
|
||||||
|
let fix = l[i].fix;
|
||||||
|
if (fix.fix && fix.lat) {
|
||||||
|
let next = toM(here.fix, fix);
|
||||||
|
let lp = toPix(last);
|
||||||
|
let np = toPix(next);
|
||||||
|
let d = Math.sqrt((next.x-last.x)*(next.x-last.x)+(next.y-last.y)*(next.y-last.y));
|
||||||
|
if (gps_last == -1)
|
||||||
|
gps_last = d;
|
||||||
|
gps_dist += d;
|
||||||
|
g.drawLine(lp.x, lp.y, np.x, np.y);
|
||||||
|
g.drawCircle(np.x, np.y, 3);
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testPaint() {
|
||||||
|
let pos = gps.getGPSFix();
|
||||||
|
log = [];
|
||||||
|
let e = { fix: pos, steps: 100, dir: 0 };
|
||||||
|
log.push(e);
|
||||||
|
e = { fix: pos, steps: 200, dir: 90 };
|
||||||
|
log.push(e);
|
||||||
|
e = { fix: pos, steps: 300, dir: 0 };
|
||||||
|
log.push(e);
|
||||||
|
print(log, log.length, log[0], log[1]);
|
||||||
|
drawLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
function touchHandler(d) {
|
||||||
|
let x = Math.floor(d.x);
|
||||||
|
let y = Math.floor(d.y);
|
||||||
|
|
||||||
|
if (d.b != 1 || ui.last_b != 0) {
|
||||||
|
ui.last_b = d.b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ((x<ui.h/2) && (y<ui.w/2)) {
|
||||||
|
ui.drawMsg("Turn");
|
||||||
|
onTurn();
|
||||||
|
}
|
||||||
|
if ((x>ui.h/2) && (y<ui.w/2)) {
|
||||||
|
ui.drawMsg("Writing");
|
||||||
|
require('Storage').writeJSON("speedstep."+getTime()+".json", log);
|
||||||
|
ui.drawMsg("Wrote");
|
||||||
|
}
|
||||||
|
ui.touchHandler(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fmt.init();
|
||||||
|
gps.init();
|
||||||
|
ui.init();
|
||||||
|
ui.drawBusy();
|
||||||
|
gps.start_gps();
|
||||||
|
Bangle.setCompassPower(1, "speedstep");
|
||||||
|
Bangle.on("drag", touchHandler);
|
||||||
|
Bangle.setUI({
|
||||||
|
mode : "custom",
|
||||||
|
swipe : (s) => ui.onSwipe(s),
|
||||||
|
clock : 0
|
||||||
|
});
|
||||||
|
|
||||||
|
if (0)
|
||||||
|
testPaint();
|
||||||
|
if (1) {
|
||||||
|
g.reset();
|
||||||
|
updateGps();
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{ "id": "dedreckon",
|
||||||
|
"name": "Ded Reckon",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "Dead Reckoning using compass and step counter",
|
||||||
|
"icon": "app.png",
|
||||||
|
"readme": "README.md",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"tags": "outdoors",
|
||||||
|
"storage": [
|
||||||
|
{"name":"dedreckon.app.js","url":"dedreckon.app.js"},
|
||||||
|
{"name":"dedreckon.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
|
@ -4,3 +4,4 @@
|
||||||
Also avoid polluting global scope.
|
Also avoid polluting global scope.
|
||||||
0.04: Enhance menu: enable bluetooth, visit settings & visit recovery
|
0.04: Enhance menu: enable bluetooth, visit settings & visit recovery
|
||||||
0.05: Enhance menu: permit toggling bluetooth
|
0.05: Enhance menu: permit toggling bluetooth
|
||||||
|
0.06: Display clock in green when charging, with "charging" text
|
||||||
|
|
|
@ -37,14 +37,19 @@ var draw = function () {
|
||||||
require("locale").dow(date, 0).toUpperCase();
|
require("locale").dow(date, 0).toUpperCase();
|
||||||
var x2 = x + 6;
|
var x2 = x + 6;
|
||||||
var y2 = y + 66;
|
var y2 = y + 66;
|
||||||
|
var charging = Bangle.isCharging();
|
||||||
g.reset()
|
g.reset()
|
||||||
.clearRect(Bangle.appRect)
|
.clearRect(Bangle.appRect)
|
||||||
.setFont("Vector", 55)
|
.setFont("Vector", 55)
|
||||||
.setFontAlign(0, 0)
|
.setFontAlign(0, 0)
|
||||||
|
.setColor(charging ? "#0f0" : g.theme.fg)
|
||||||
.drawString(timeStr, x, y)
|
.drawString(timeStr, x, y)
|
||||||
.setFont("Vector", 24)
|
.setFont("Vector", 24)
|
||||||
.drawString(dateStr, x2, y2)
|
.drawString(dateStr, x2, y2);
|
||||||
.drawString("".concat(E.getBattery(), "%"), x2, y2 + 48);
|
if (charging)
|
||||||
|
g.drawString("charging: ".concat(E.getBattery(), "%"), x2, y2 + 48);
|
||||||
|
else
|
||||||
|
g.drawString("".concat(E.getBattery(), "%"), x2, y2 + 48);
|
||||||
if (nextDraw)
|
if (nextDraw)
|
||||||
clearTimeout(nextDraw);
|
clearTimeout(nextDraw);
|
||||||
nextDraw = setTimeout(function () {
|
nextDraw = setTimeout(function () {
|
||||||
|
@ -96,8 +101,10 @@ function drainedRestore() {
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
var checkCharge = function () {
|
var checkCharge = function () {
|
||||||
if (E.getBattery() < restore)
|
if (E.getBattery() < restore) {
|
||||||
|
draw();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
drainedRestore();
|
drainedRestore();
|
||||||
};
|
};
|
||||||
if (Bangle.isCharging())
|
if (Bangle.isCharging())
|
||||||
|
|
|
@ -54,15 +54,21 @@ const draw = () => {
|
||||||
require("locale").dow(date, 0).toUpperCase();
|
require("locale").dow(date, 0).toUpperCase();
|
||||||
const x2 = x + 6;
|
const x2 = x + 6;
|
||||||
const y2 = y + 66;
|
const y2 = y + 66;
|
||||||
|
const charging = Bangle.isCharging();
|
||||||
|
|
||||||
g.reset()
|
g.reset()
|
||||||
.clearRect(Bangle.appRect)
|
.clearRect(Bangle.appRect)
|
||||||
.setFont("Vector", 55)
|
.setFont("Vector", 55)
|
||||||
.setFontAlign(0, 0)
|
.setFontAlign(0, 0)
|
||||||
|
.setColor(charging ? "#0f0" : g.theme.fg)
|
||||||
.drawString(timeStr, x, y)
|
.drawString(timeStr, x, y)
|
||||||
.setFont("Vector", 24)
|
.setFont("Vector", 24)
|
||||||
.drawString(dateStr, x2, y2)
|
.drawString(dateStr, x2, y2);
|
||||||
.drawString(`${E.getBattery()}%`, x2, y2 + 48);
|
|
||||||
|
if(charging)
|
||||||
|
g.drawString(`charging: ${E.getBattery()}%`, x2, y2 + 48);
|
||||||
|
else
|
||||||
|
g.drawString(`${E.getBattery()}%`, x2, y2 + 48);
|
||||||
|
|
||||||
if(nextDraw) clearTimeout(nextDraw);
|
if(nextDraw) clearTimeout(nextDraw);
|
||||||
nextDraw = setTimeout(() => {
|
nextDraw = setTimeout(() => {
|
||||||
|
@ -125,7 +131,10 @@ function drainedRestore() { // "public", to allow users to call
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkCharge = () => {
|
const checkCharge = () => {
|
||||||
if(E.getBattery() < restore) return;
|
if(E.getBattery() < restore) {
|
||||||
|
draw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
drainedRestore();
|
drainedRestore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "drained",
|
"id": "drained",
|
||||||
"name": "Drained",
|
"name": "Drained",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "Switches to displaying a simple clock when the battery percentage is low, and disables some peripherals",
|
"description": "Switches to displaying a simple clock when the battery percentage is low, and disables some peripherals",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: New Clock Nifty A ++ >> adding more information on the right side of the clock
|
0.01: New Clock Nifty A ++ >> adding more information on the right side of the clock
|
||||||
|
0.02: Fix weather icon for languages other than English
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const w = require("weather");
|
const w = require("weather");
|
||||||
//const locale = require("locale");
|
const locale = require("locale");
|
||||||
|
|
||||||
// Weather icons from https://icons8.com/icon/set/weather/color
|
// Weather icons from https://icons8.com/icon/set/weather/color
|
||||||
function getSun() {
|
function getSun() {
|
||||||
|
@ -67,6 +67,33 @@ function chooseIcon(condition) {
|
||||||
return getPartSun;
|
return getPartSun;
|
||||||
} else return getErr;
|
} else return getErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Choose weather icon to display based on weather conditition code
|
||||||
|
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
|
||||||
|
*/
|
||||||
|
function chooseIconByCode(code) {
|
||||||
|
const codeGroup = Math.round(code / 100);
|
||||||
|
switch (codeGroup) {
|
||||||
|
case 2: return getStorm;
|
||||||
|
case 3: return getRain;
|
||||||
|
case 5:
|
||||||
|
switch (code) {
|
||||||
|
case 511: return getSnow;
|
||||||
|
default: return getRain;
|
||||||
|
}
|
||||||
|
case 6: return getSnow;
|
||||||
|
case 7: return getPartSun;
|
||||||
|
case 8:
|
||||||
|
switch (code) {
|
||||||
|
case 800: return getSun;
|
||||||
|
case 804: return getCloud;
|
||||||
|
default: return getPartSun;
|
||||||
|
}
|
||||||
|
default: return getCloud;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*function condenseWeather(condition) {
|
/*function condenseWeather(condition) {
|
||||||
condition = condition.toLowerCase();
|
condition = condition.toLowerCase();
|
||||||
if (condition.includes("thunderstorm") ||
|
if (condition.includes("thunderstorm") ||
|
||||||
|
@ -143,8 +170,17 @@ const clock = new ClockFace({
|
||||||
//let cWea =(curr === "no data" ? "no data" : curr.txt);
|
//let cWea =(curr === "no data" ? "no data" : curr.txt);
|
||||||
let cTemp= (curr === "no data" ? 273 : curr.temp);
|
let cTemp= (curr === "no data" ? 273 : curr.temp);
|
||||||
// const temp = locale.temp(curr.temp - 273.15).match(/^(\D*\d*)(.*)$/);
|
// const temp = locale.temp(curr.temp - 273.15).match(/^(\D*\d*)(.*)$/);
|
||||||
let w_icon = chooseIcon(curr.txt === undefined ? "no data" : curr.txt );
|
|
||||||
//let w_icon = chooseIcon(curr.txt);
|
let w_icon = getErr;
|
||||||
|
if (locale.name === "en" || locale.name === "en_GB" || locale.name === "en_US") {
|
||||||
|
w_icon = chooseIcon(curr.txt === undefined ? "no data" : curr.txt);
|
||||||
|
} else {
|
||||||
|
// cannot use condition string to determine icon if language is not English; use weather code instead
|
||||||
|
const code = curr.code || -1;
|
||||||
|
if (code > 0) {
|
||||||
|
w_icon = chooseIconByCode(curr.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g.setFontAlign(1, 0).setFont("Vector", 90 * this.scale);
|
g.setFontAlign(1, 0).setFont("Vector", 90 * this.scale);
|
||||||
g.drawString(format(hour), this.centerTimeScaleX, this.center.y - 31 * this.scale);
|
g.drawString(format(hour), this.centerTimeScaleX, this.center.y - 31 * this.scale);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "ffcniftyapp",
|
"id": "ffcniftyapp",
|
||||||
"name": "Nifty-A Clock ++",
|
"name": "Nifty-A Clock ++",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "A nifty clock with time and date and more",
|
"description": "A nifty clock with time and date and more",
|
||||||
"dependencies": {"weather":"app"},
|
"dependencies": {"weather":"app"},
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -106,6 +106,7 @@ function onInit(device) {
|
||||||
else if (crcs[0] == 3816337552) version = "2v21";
|
else if (crcs[0] == 3816337552) version = "2v21";
|
||||||
else if (crcs[0] == 3329616485) version = "2v22";
|
else if (crcs[0] == 3329616485) version = "2v22";
|
||||||
else if (crcs[0] == 1569433504) version = "2v23";
|
else if (crcs[0] == 1569433504) version = "2v23";
|
||||||
|
else if (crcs[0] == 680675961) version = "2v24";
|
||||||
else { // for other versions all 7 pages are used, check those
|
else { // for other versions all 7 pages are used, check those
|
||||||
var crc = crcs[1];
|
var crc = crcs[1];
|
||||||
if (crc==1339551013) { version = "2v10.219"; ok = false; }
|
if (crc==1339551013) { version = "2v10.219"; ok = false; }
|
||||||
|
|
|
@ -138,4 +138,6 @@
|
||||||
|
|
||||||
0.25: Minor code improvements
|
0.25: Minor code improvements
|
||||||
|
|
||||||
0.26: Support for large paths (grid sizes > 65k)
|
0.26: Add option to plot openstmap if installed
|
||||||
|
|
||||||
|
0.27: Support for large paths (grid sizes > 65k)
|
||||||
|
|
|
@ -666,11 +666,11 @@ class Status {
|
||||||
towards = next_point;
|
towards = next_point;
|
||||||
}
|
}
|
||||||
let diff = towards.minus(this.projected_point);
|
let diff = towards.minus(this.projected_point);
|
||||||
direction = Math.atan2(diff.lat, diff.lon);
|
const direction = Math.atan2(diff.lat, diff.lon);
|
||||||
|
|
||||||
let full_angle = direction - this.angle;
|
let full_angle = direction - this.angle;
|
||||||
|
|
||||||
c = this.projected_point.coordinates(
|
const c = this.projected_point.coordinates(
|
||||||
this.displayed_position,
|
this.displayed_position,
|
||||||
this.adjusted_cos_direction,
|
this.adjusted_cos_direction,
|
||||||
this.adjusted_sin_direction,
|
this.adjusted_sin_direction,
|
||||||
|
@ -1400,7 +1400,7 @@ function ask_options(fn) {
|
||||||
g.flip();
|
g.flip();
|
||||||
|
|
||||||
function options_select(b, xy) {
|
function options_select(b, xy) {
|
||||||
end = false;
|
let end = false;
|
||||||
if (xy.y < height / 2 - 10) {
|
if (xy.y < height / 2 - 10) {
|
||||||
g.setColor(0, 0, 0).fillRect(10, 10, width - 10, height / 2 - 10);
|
g.setColor(0, 0, 0).fillRect(10, 10, width - 10, height / 2 - 10);
|
||||||
g.setColor(1, 1, 1).setFont("Vector:30").setFontAlign(0,0).drawString("Forward", width/2, height/4);
|
g.setColor(1, 1, 1).setFont("Vector:30").setFontAlign(0,0).drawString("Forward", width/2, height/4);
|
||||||
|
@ -1480,6 +1480,98 @@ function start_gipy(path, maps, interests, heights) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
|
// plot openstmap option if installed
|
||||||
|
const osm = require("openstmap");
|
||||||
|
menu[/*LANG*/"Plot OpenStMap"] = function() {
|
||||||
|
E.showMenu(); // remove menu
|
||||||
|
|
||||||
|
// compute min/max coordinates
|
||||||
|
const fix = Bangle.getGPSFix();
|
||||||
|
let minLat = fix.lat ? fix.lat : 90;
|
||||||
|
let maxLat = fix.lat ? fix.lat : -90;
|
||||||
|
let minLong = fix.lon ? fix.lon : 180;
|
||||||
|
let maxLong = fix.lon ? fix.lon : -180;
|
||||||
|
for(let i=0; i<path.len; i++) {
|
||||||
|
const point = path.point(i);
|
||||||
|
if(point.lat>maxLat) maxLat=point.lat; if(point.lat<minLat) minLat=point.lat;
|
||||||
|
if(point.lon>maxLong) maxLong=point.lon; if(point.lon<minLong) minLong=point.lon;
|
||||||
|
}
|
||||||
|
const max = Bangle.project({lat: maxLat, lon: maxLong});
|
||||||
|
const min = Bangle.project({lat: minLat, lon: minLong});
|
||||||
|
const scaleX = (max.x-min.x)/Bangle.appRect.w;
|
||||||
|
const scaleY = (max.y-min.y)/Bangle.appRect.h;
|
||||||
|
|
||||||
|
// openstmap initialization
|
||||||
|
osm.scale = Math.ceil((scaleX > scaleY ? scaleX : scaleY)*1.1); // add 10% margin
|
||||||
|
osm.lat = (minLat+maxLat)/2.0;
|
||||||
|
osm.lon = (minLong+maxLong)/2.0;
|
||||||
|
|
||||||
|
const drawOpenStmap = () => {
|
||||||
|
g.clearRect(Bangle.appRect);
|
||||||
|
osm.draw();
|
||||||
|
|
||||||
|
// draw track
|
||||||
|
g.setColor("#f09");
|
||||||
|
for(let i=0; i<path.len; i++) {
|
||||||
|
const point = path.point(i);
|
||||||
|
const mp = osm.latLonToXY(point.lat, point.lon);
|
||||||
|
if (i == 0) {
|
||||||
|
g.moveTo(mp.x,mp.y);
|
||||||
|
} else {
|
||||||
|
g.lineTo(mp.x,mp.y);
|
||||||
|
}
|
||||||
|
g.fillCircle(mp.x,mp.y,2); // make the track more visible
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw current position
|
||||||
|
g.setColor("#000");
|
||||||
|
if (fix.lat && fix.lon) {
|
||||||
|
const icon = require("heatshrink").decompress(atob("jEYwYPMyVJkgHEkgICyAHCgIIDyQIChIIEoAIDC4IIEBwOAgEEyVIBAY4DBD4sGHxBQIMRAIIPpAyCHAYILUJEAiVJkAIFgVJXo5fCABQA==")); // 24x24px
|
||||||
|
const mp = osm.latLonToXY(fix.lat, fix.lon);
|
||||||
|
g.drawImage(icon, mp.x, mp.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// labels
|
||||||
|
g.setFont("6x8",2);
|
||||||
|
g.setFontAlign(0,0,3);
|
||||||
|
g.drawString(/*LANG*/"Back", g.getWidth() - 10, g.getHeight()/2);
|
||||||
|
g.drawString("+", g.getWidth() - 10, g.getHeight()/4);
|
||||||
|
g.drawString("-", g.getWidth() - 10, g.getHeight()/4*3);
|
||||||
|
};
|
||||||
|
drawOpenStmap();
|
||||||
|
|
||||||
|
let startDrag = 0;
|
||||||
|
Bangle.setUI({
|
||||||
|
mode: "custom",
|
||||||
|
btn: (n) => { // back handling
|
||||||
|
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
|
E.showMenu(menu);
|
||||||
|
},
|
||||||
|
drag: (ev) => { // zoom, move
|
||||||
|
if (ev.b) {
|
||||||
|
osm.scroll(ev.dx, ev.dy);
|
||||||
|
if (!startDrag) {
|
||||||
|
startDrag = getTime();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (getTime() - startDrag < 0.2) {
|
||||||
|
// tap
|
||||||
|
if (ev.y > g.getHeight() / 2) {
|
||||||
|
osm.scale *= 2;
|
||||||
|
} else {
|
||||||
|
osm.scale /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDrag = 0;
|
||||||
|
drawOpenStmap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} catch (ex) {
|
||||||
|
// openstmap not available.
|
||||||
|
}
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
},
|
},
|
||||||
BTN1,
|
BTN1,
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: attempt to import
|
0.01: attempt to import
|
||||||
|
0.02: implement colors and lines
|
||||||
|
|
|
@ -3,5 +3,18 @@
|
||||||
Bitmap editor suitable for creating icons and fonts for BangleJS2.
|
Bitmap editor suitable for creating icons and fonts for BangleJS2.
|
||||||
|
|
||||||
You'll want to run a copy of this in simulator, and another one on
|
You'll want to run a copy of this in simulator, and another one on
|
||||||
watch to view the results. Draw using the provided tools, then press
|
watch to view the results.
|
||||||
the button, and you'll get result on the console.
|
|
||||||
|
Draw using the provided tools, then press the button, and you'll get
|
||||||
|
result on the console; you can also use "dump();" on command
|
||||||
|
line. show_icon() takes same parameter as is used in app-icon.js
|
||||||
|
files, you can just copy&paste it to get an icon. By using
|
||||||
|
"for_screen();" command, then taking a screenshot, you can easily
|
||||||
|
generate app.png file.
|
||||||
|
|
||||||
|
It is also possible to load existing icon into editor, using
|
||||||
|
"load_icon("");" command. At the end of iconbits.app.js file there are
|
||||||
|
more utility functions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.0 KiB |
|
@ -10,14 +10,16 @@
|
||||||
let kule = [0, 0, 0]; // R, G, B
|
let kule = [0, 0, 0]; // R, G, B
|
||||||
var font_height = 22, font_width = 8;
|
var font_height = 22, font_width = 8;
|
||||||
var zoom_x = 64, zoom_y = 24, zoom_f = 6;
|
var zoom_x = 64, zoom_y = 24, zoom_f = 6;
|
||||||
|
var color = true;
|
||||||
let oldLock = false;
|
let oldLock = false;
|
||||||
let sg = null;
|
let sg = null;
|
||||||
|
const top_bar = 20;
|
||||||
|
|
||||||
function clear(m) {
|
function clear(m) {
|
||||||
sg.setColor(1,1,1).fillRect(0,0, font_width, font_height);
|
sg.setColor(1,1,1).fillRect(0,0, font_width, font_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup(m) {
|
function __setup(m) {
|
||||||
mode = m;
|
mode = m;
|
||||||
switch (m) {
|
switch (m) {
|
||||||
case 'font':
|
case 'font':
|
||||||
|
@ -37,19 +39,32 @@
|
||||||
zoom_f = 2;
|
zoom_f = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
function setup(m) {
|
||||||
|
__setup(m);
|
||||||
sg = Graphics.createArrayBuffer(font_width, font_height, 8, {});
|
sg = Graphics.createArrayBuffer(font_width, font_height, 8, {});
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function icon_big() {
|
||||||
|
zoom_x = 16;
|
||||||
|
zoom_y = 25;
|
||||||
|
zoom_f = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function icon_small() {
|
||||||
|
__setup("icon");
|
||||||
|
}
|
||||||
|
|
||||||
function updateLock() {
|
function updateLock() {
|
||||||
if (oldLock) {
|
if (oldLock) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g.setColor('#fff');
|
g.setColor('#fff');
|
||||||
g.fillRect(0, 0, g.getWidth(), 20);
|
g.fillRect(0, 0, g.getWidth(), 20);
|
||||||
g.setFont('6x8', 2);
|
g.setFont('Vector', 22);
|
||||||
g.setColor('#000');
|
g.setColor('#000');
|
||||||
g.drawString('PLEASE UNLOCK', 10, 2);
|
g.drawString('PLEASE\nUNLOCK', 10, 2);
|
||||||
oldLock = true;
|
oldLock = true;
|
||||||
}
|
}
|
||||||
Bangle.on("lock", function() {
|
Bangle.on("lock", function() {
|
||||||
|
@ -60,17 +75,20 @@ Bangle.on("lock", function() {
|
||||||
drawUtil();
|
drawUtil();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
function nextColor () {
|
function nextColor() {
|
||||||
kule[0] = Math.random();
|
kule[0] = Math.random();
|
||||||
kule[1] = Math.random();
|
kule[1] = Math.random();
|
||||||
kule[2] = Math.random();
|
kule[2] = Math.random();
|
||||||
}
|
}
|
||||||
function selectColor (x) {
|
function selectColor(x) {
|
||||||
let c;
|
if (color) {
|
||||||
|
let i = Math.floor((x - 32) / 4);
|
||||||
|
kule = toColor(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let c = 255;
|
||||||
if (x < g.getWidth()/2) {
|
if (x < g.getWidth()/2) {
|
||||||
c = 0;
|
c = 0;
|
||||||
} else {
|
|
||||||
c = 255;
|
|
||||||
}
|
}
|
||||||
kule[0] = c;
|
kule[0] = c;
|
||||||
kule[1] = c;
|
kule[1] = c;
|
||||||
|
@ -79,8 +97,8 @@ Bangle.on("lock", function() {
|
||||||
function nextPen () {
|
function nextPen () {
|
||||||
switch (pen) {
|
switch (pen) {
|
||||||
case 'circle': pen = 'pixel'; break;
|
case 'circle': pen = 'pixel'; break;
|
||||||
case 'pixel': pen = 'crayon'; break;
|
case 'pixel': pen = 'line'; break;
|
||||||
case 'crayon': pen = 'square'; break;
|
case 'line': pen = 'square'; break;
|
||||||
case 'square': pen = 'circle'; break;
|
case 'square': pen = 'circle'; break;
|
||||||
default: pen = 'pixel'; break;
|
default: pen = 'pixel'; break;
|
||||||
}
|
}
|
||||||
|
@ -89,8 +107,8 @@ Bangle.on("lock", function() {
|
||||||
discard = setTimeout(function () { oldX = -1; oldY = -1; console.log('timeout'); discard = null; }, 500);
|
discard = setTimeout(function () { oldX = -1; oldY = -1; console.log('timeout'); discard = null; }, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldX = -1;
|
var oldX = -1, oldY = -1;
|
||||||
var oldY = -1;
|
var line_from = null;
|
||||||
|
|
||||||
function drawBrushIcon () {
|
function drawBrushIcon () {
|
||||||
const w = g.getWidth();
|
const w = g.getWidth();
|
||||||
|
@ -110,13 +128,17 @@ Bangle.on("lock", function() {
|
||||||
g.drawLine(w - 14, 6, w - 10, 12);
|
g.drawLine(w - 14, 6, w - 10, 12);
|
||||||
g.drawLine(w - 6, 6, w - 10, 12);
|
g.drawLine(w - 6, 6, w - 10, 12);
|
||||||
break;
|
break;
|
||||||
|
case 'line':
|
||||||
|
g.drawLine(w - 5, 5, w - 15, 15);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawArea () {
|
function drawArea() {
|
||||||
g.clear();
|
g.clear();
|
||||||
if (mode == "draw")
|
if (mode == "draw")
|
||||||
return;
|
return;
|
||||||
|
const w = g.getWidth;
|
||||||
g.setColor(0, 0, 0.5);
|
g.setColor(0, 0, 0.5);
|
||||||
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
g.setColor(1, 1, 1);
|
g.setColor(1, 1, 1);
|
||||||
|
@ -129,13 +151,28 @@ Bangle.on("lock", function() {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawUtil () {
|
function toColor(i) {
|
||||||
|
let r = [0, 0, 0];
|
||||||
|
r[0] = (i % 3) / 2;
|
||||||
|
i = Math.floor(i / 3);
|
||||||
|
r[1] = (i % 3) / 2;
|
||||||
|
i = Math.floor(i / 3);
|
||||||
|
r[2] = (i % 3) / 2;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawUtil() {
|
||||||
if (Bangle.isLocked()) {
|
if (Bangle.isLocked()) {
|
||||||
updateLock();
|
updateLock();
|
||||||
}
|
}
|
||||||
// titlebar
|
// titlebar
|
||||||
g.setColor(kule[0], kule[1], kule[2]);
|
g.setColor(kule[0], kule[1], kule[2]);
|
||||||
g.fillRect(0, 0, g.getWidth(), 20);
|
g.fillRect(0, 0, g.getWidth(), top_bar);
|
||||||
|
for (let i = 0; i < 3*3*3; i++) {
|
||||||
|
let r = toColor(i);
|
||||||
|
g.setColor(r[0], r[1], r[2]);
|
||||||
|
g.fillRect(32+4*i, 12, 32+4*i+3, top_bar);
|
||||||
|
}
|
||||||
// clear button
|
// clear button
|
||||||
g.setColor('#000'); // black
|
g.setColor('#000'); // black
|
||||||
g.fillCircle(10, 10, 8, 8);
|
g.fillCircle(10, 10, 8, 8);
|
||||||
|
@ -149,7 +186,7 @@ Bangle.on("lock", function() {
|
||||||
drawBrushIcon();
|
drawBrushIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
function transform (p) {
|
function transform(p) {
|
||||||
if (p.x < zoom_x || p.y < zoom_y)
|
if (p.x < zoom_x || p.y < zoom_y)
|
||||||
return p;
|
return p;
|
||||||
p.x = ((p.x - zoom_x) / zoom_f);
|
p.x = ((p.x - zoom_x) / zoom_f);
|
||||||
|
@ -159,8 +196,12 @@ Bangle.on("lock", function() {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
function __draw (g, from, to) {
|
function __draw(g, from, to) {
|
||||||
|
let XS = (to.x - from.x) / 32;
|
||||||
|
let YS = (to.y - from.y) / 32;
|
||||||
|
|
||||||
switch (pen) {
|
switch (pen) {
|
||||||
|
case 'line':
|
||||||
case 'pixel':
|
case 'pixel':
|
||||||
g.drawLine(from.x, from.y, to.x, to.y);
|
g.drawLine(from.x, from.y, to.x, to.y);
|
||||||
break;
|
break;
|
||||||
|
@ -170,27 +211,25 @@ Bangle.on("lock", function() {
|
||||||
g.drawLine(from.x + 2, from.y + 2, to.x, to.y + 2);
|
g.drawLine(from.x + 2, from.y + 2, to.x, to.y + 2);
|
||||||
break;
|
break;
|
||||||
case 'circle':
|
case 'circle':
|
||||||
var XS = (to.x - from.x) / 32;
|
|
||||||
var YS = (to.y - from.y) / 32;
|
|
||||||
for (let i = 0; i < 32; i++) {
|
for (let i = 0; i < 32; i++) {
|
||||||
g.fillCircle(from.x + (i * XS), from.y + (i * YS), 4, 4);
|
g.fillCircle(from.x + (i * XS), from.y + (i * YS), 2, 2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'square':
|
case 'square':
|
||||||
var XS = (to.x - from.x) / 32;
|
|
||||||
var YS = (to.y - from.y) / 32;
|
|
||||||
for (let i = 0; i < 32; i++) {
|
for (let i = 0; i < 32; i++) {
|
||||||
const posX = from.x + (i * XS);
|
const posX = from.x + (i * XS);
|
||||||
const posY = from.y + (i * YS);
|
const posY = from.y + (i * YS);
|
||||||
g.fillRect(posX - 10, posY - 10, posX + 10, posY + 10);
|
g.fillRect(posX - 4, posY - 4, posX + 4, posY + 4);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
print("Unkown pen ", pen);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
g.drawImage(sg, 0, 64, {});
|
if (zoom_f < 3)
|
||||||
|
g.drawImage(sg, 4, 64, {});
|
||||||
g.drawImage(sg, zoom_x, zoom_y, { scale: zoom_f });
|
g.drawImage(sg, zoom_x, zoom_y, { scale: zoom_f });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +266,7 @@ Bangle.on("lock", function() {
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
// tap and hold the clear button
|
// tap and hold the clear button
|
||||||
if (tap.x < 32 && tap.y < 32) {
|
if (tap.x < 32 && tap.y < top_bar) {
|
||||||
if (tap.b === 1) {
|
if (tap.b === 1) {
|
||||||
if (tapTimer === null) {
|
if (tapTimer === null) {
|
||||||
tapTimer = setTimeout(function () {
|
tapTimer = setTimeout(function () {
|
||||||
|
@ -244,7 +283,7 @@ Bangle.on("lock", function() {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tap.x > g.getWidth() - 32 && tap.y < 32) {
|
if (tap.x > g.getWidth() - 32 && tap.y < top_bar) {
|
||||||
if (tap.b === 1) {
|
if (tap.b === 1) {
|
||||||
if (tapTimer === null) {
|
if (tapTimer === null) {
|
||||||
tapTimer = setTimeout(function () {
|
tapTimer = setTimeout(function () {
|
||||||
|
@ -264,7 +303,7 @@ Bangle.on("lock", function() {
|
||||||
}
|
}
|
||||||
drawUtil();
|
drawUtil();
|
||||||
return;
|
return;
|
||||||
} else if (tap.y < 32) {
|
} else if (tap.y < top_bar) {
|
||||||
if (mode == "draw")
|
if (mode == "draw")
|
||||||
nextColor();
|
nextColor();
|
||||||
else
|
else
|
||||||
|
@ -272,20 +311,31 @@ Bangle.on("lock", function() {
|
||||||
drawUtil();
|
drawUtil();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
oldX = to.x;
|
|
||||||
oldY = to.y;
|
|
||||||
sg.setColor(kule[0], kule[1], kule[2]);
|
sg.setColor(kule[0], kule[1], kule[2]);
|
||||||
g.setColor(kule[0], kule[1], kule[2]);
|
g.setColor(kule[0], kule[1], kule[2]);
|
||||||
|
oldX = to.x;
|
||||||
|
oldY = to.y;
|
||||||
|
|
||||||
do_draw(from, to);
|
if (pen != "line") {
|
||||||
|
do_draw(from, to);
|
||||||
|
} else {
|
||||||
|
if (tap.b == 1) {
|
||||||
|
print(line_from);
|
||||||
|
if (!line_from) {
|
||||||
|
line_from = to;
|
||||||
|
} else {
|
||||||
|
do_draw(line_from, to);
|
||||||
|
line_from = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
drawUtil();
|
drawUtil();
|
||||||
}
|
}
|
||||||
function on_btn(n) {
|
|
||||||
|
function dump(n) {
|
||||||
function f(i) {
|
function f(i) {
|
||||||
return "\\x" + i.toString(16).padStart(2, '0');
|
return "\\x" + i.toString(16).padStart(2, '0');
|
||||||
}
|
}
|
||||||
print("on_btn", n);
|
|
||||||
print(g.getPixel(0, 0));
|
|
||||||
let s = f(0) + f(font_width) + f(font_height) + f(1);
|
let s = f(0) + f(font_width) + f(font_height) + f(1);
|
||||||
// 0..black, 65535..white
|
// 0..black, 65535..white
|
||||||
for (let y = 0; y < font_height; y++) {
|
for (let y = 0; y < font_height; y++) {
|
||||||
|
@ -296,41 +346,55 @@ Bangle.on("lock", function() {
|
||||||
}
|
}
|
||||||
s += f(v);
|
s += f(v);
|
||||||
}
|
}
|
||||||
print("Manual bitmap\n");
|
if (mode == "font")
|
||||||
print('ft("' + s + '");');
|
print('show_font("' + s + '");');
|
||||||
if (1) {
|
var im = sg.asImage("string");
|
||||||
s = "";
|
//print('show_unc_icon("'+btoa(im)+'");');
|
||||||
var im = sg.asImage("string");
|
print('show_icon("'+btoa(require('heatshrink').compress(im))+'");');
|
||||||
for (var v of im) {
|
|
||||||
//print("val", v, typeof v);
|
|
||||||
s += f(v);
|
|
||||||
}
|
|
||||||
//print("wh", im, typeof im, im[0], typeof im[0]);
|
|
||||||
//print("Image:", im.length, s);
|
|
||||||
print('fi("'+btoa(im)+'");');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setup("icon");
|
setup("icon");
|
||||||
drawArea();
|
drawArea();
|
||||||
Bangle.setUI({
|
Bangle.setUI({
|
||||||
"mode": "custom",
|
"mode": "custom",
|
||||||
"drag": on_drag,
|
"drag": on_drag,
|
||||||
"btn": on_btn,
|
"btn": dump,
|
||||||
});
|
});
|
||||||
drawUtil();
|
drawUtil();
|
||||||
|
|
||||||
|
function show_font(icon) {
|
||||||
function ft(icon) {
|
|
||||||
g.reset().clear();
|
g.reset().clear();
|
||||||
g.setFont("Vector", 26).drawString("Hellord" + icon, 0, 0);
|
g.setFont("Vector", 26).drawString("Hellord" + icon, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fi(icon) {
|
function show_bin_icon(icon) {
|
||||||
g.reset().clear();
|
g.reset().clear();
|
||||||
g.drawImage(atob(icon), 40, 40);
|
g.drawImage(icon, 40, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_unc_icon(icon) {
|
||||||
|
show_bin_icon(atob(icon));
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_icon(icon) {
|
||||||
|
let unc = require("heatshrink").decompress(atob(icon));
|
||||||
|
show_bin_icon(unc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function load_bin_icon(i) {
|
||||||
|
sg.reset().clear();
|
||||||
|
sg.drawImage(i, 0, 0);
|
||||||
|
drawArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
function load_icon(icon) {
|
||||||
|
let unc = require("heatshrink").decompress(atob(icon));
|
||||||
|
load_bin_icon(unc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function for_screen() {
|
||||||
|
g.reset().clear();
|
||||||
|
icon_big();
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
//ft(icon_10 + "23.1" + icon_hpa);
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "iconbits",
|
{ "id": "iconbits",
|
||||||
"name": "Icon bits",
|
"name": "Icon bits",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "Bitmap editor suitable for creating icons",
|
"description": "Bitmap editor suitable for creating icons",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: first release
|
0.01: first release
|
||||||
0.02: Use clock_info module as an app
|
0.02: Use clock_info module as an app
|
||||||
0.03: clock_info now uses app name to maintain settings specifically for this clock face
|
0.03: clock_info now uses app name to maintain settings specifically for this clock face
|
||||||
|
0.04: add optional date display, and a settings page to configure it
|
|
@ -5,6 +5,7 @@ A simple clock with the Lato font, with fast load and clock_info
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
This clock is a Lato version of Simplest++. Simplest++ provided the
|
This clock is a Lato version of Simplest++. Simplest++ provided the
|
||||||
smallest example of a clock that supports 'fast load' and 'clock
|
smallest example of a clock that supports 'fast load' and 'clock
|
||||||
|
@ -25,6 +26,8 @@ Pastel Clock.
|
||||||
|
|
||||||
* Settings are saved automatically and reloaded along with the clock.
|
* Settings are saved automatically and reloaded along with the clock.
|
||||||
|
|
||||||
|
* Date display can be enabled and disabled, along with format choice in the app settings
|
||||||
|
|
||||||
## About Clock Info's
|
## About Clock Info's
|
||||||
|
|
||||||
* The clock info modules enable all clocks to add the display of information to the clock face.
|
* The clock info modules enable all clocks to add the display of information to the clock face.
|
||||||
|
@ -52,3 +55,5 @@ Pastel Clock.
|
||||||
Written by: [Hugh Barney](https://github.com/hughbarney) For support
|
Written by: [Hugh Barney](https://github.com/hughbarney) For support
|
||||||
and discussion please post in the [Bangle JS
|
and discussion please post in the [Bangle JS
|
||||||
Forum](http://forum.espruino.com/microcosms/1424/)
|
Forum](http://forum.espruino.com/microcosms/1424/)
|
||||||
|
|
||||||
|
Date functionality added by [Septolum](https://github.com/Septolum)
|
||||||
|
|
|
@ -38,6 +38,11 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
|
||||||
// must be inside our own scope here so that when we are unloaded everything disappears
|
// must be inside our own scope here so that when we are unloaded everything disappears
|
||||||
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
||||||
|
|
||||||
|
let settings = Object.assign({
|
||||||
|
dateDisplay: false,
|
||||||
|
dateFormat: 0,
|
||||||
|
}, require("Storage").readJSON("lato.json", true) || {});
|
||||||
|
|
||||||
let draw = function() {
|
let draw = function() {
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
var timeStr = require("locale").time(date,1);
|
var timeStr = require("locale").time(date,1);
|
||||||
|
@ -53,6 +58,25 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
|
||||||
g.setFontAlign(0, 0);
|
g.setFontAlign(0, 0);
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.drawString(timeStr, w/2, h/2);
|
g.drawString(timeStr, w/2, h/2);
|
||||||
|
|
||||||
|
if (settings.dateDisplay) {
|
||||||
|
switch (settings.dateFormat) {
|
||||||
|
case 1:
|
||||||
|
var dateStr = require("locale").date(date,1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
var dateStr = require("locale").date(date);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var dateStr = require("locale").dow(date,1) + ', ' + date.getDate() + ' ' + require("locale").month(date,1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g.setFontVector(16);
|
||||||
|
g.drawString(dateStr, w/2, h/4 -4);
|
||||||
|
}
|
||||||
|
|
||||||
clockInfoMenu.redraw(); // clock_info_support
|
clockInfoMenu.redraw(); // clock_info_support
|
||||||
|
|
||||||
// schedule a draw for the next minute
|
// schedule a draw for the next minute
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "lato",
|
"id": "lato",
|
||||||
"name": "Lato",
|
"name": "Lato",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "A Lato Font clock with fast load and clock_info",
|
"description": "A Lato Font clock with fast load and clock_info",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
@ -12,6 +12,10 @@
|
||||||
"dependencies" : { "clock_info":"module" },
|
"dependencies" : { "clock_info":"module" },
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"lato.app.js","url":"app.js"},
|
{"name":"lato.app.js","url":"app.js"},
|
||||||
{"name":"lato.img","url":"icon.js","evaluate":true}
|
{"name":"lato.img","url":"icon.js","evaluate":true},
|
||||||
|
{"name":"lato.settings.js","url":"settings.js"}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"lato.json"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,24 @@
|
||||||
|
(function(back) {
|
||||||
|
let settings = require('Storage').readJSON('lato.json',1)||{};
|
||||||
|
if (typeof settings.dateDisplay !== "boolean") settings.dateDisplay = false; // default value
|
||||||
|
if (typeof settings.dateFormat !== "number") settings.dateFormat = 0; // default value
|
||||||
|
function save(key, value) {
|
||||||
|
settings[key] = value;
|
||||||
|
require('Storage').write('lato.json', settings);
|
||||||
|
}
|
||||||
|
const appMenu = {
|
||||||
|
'': {'title': 'Lato'},
|
||||||
|
'< Back': back,
|
||||||
|
'Display Date?': {
|
||||||
|
value: settings.dateDisplay,
|
||||||
|
onchange: (v) => {save('dateDisplay', v)}
|
||||||
|
},
|
||||||
|
"Date Format": {
|
||||||
|
value: settings.dateFormat,
|
||||||
|
min: 0, max: 2,
|
||||||
|
format: v => ["DoW, dd MMM","Locale Short","Locale Long"][v],
|
||||||
|
onchange: (v) => {save('dateFormat', v)}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
E.showMenu(appMenu)
|
||||||
|
})
|
|
@ -196,12 +196,6 @@ module.exports = {
|
||||||
"no-undef"
|
"no-undef"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"apps/sixths/sixths.app.js": {
|
|
||||||
"hash": "2a4676828bdf78df052df402de34e6f1abd1c847ebe0d193fc789cd6e9dd0e5c",
|
|
||||||
"rules": [
|
|
||||||
"no-undef"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"apps/scribble/app.js": {
|
"apps/scribble/app.js": {
|
||||||
"hash": "6d13abd27bab8009a6bdabe1df2df394bc14aac20c68f67e8f8b085fa6b427cd",
|
"hash": "6d13abd27bab8009a6bdabe1df2df394bc14aac20c68f67e8f8b085fa6b427cd",
|
||||||
"rules": [
|
"rules": [
|
||||||
|
@ -1021,12 +1015,6 @@ module.exports = {
|
||||||
"no-undef"
|
"no-undef"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"apps/gipy/app.js": {
|
|
||||||
"hash": "41f342e8ef6f2a87b3aea19b75ee45cfdfeff723b94281049e3ae0ec89cddba5",
|
|
||||||
"rules": [
|
|
||||||
"no-undef"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"apps/geissclk/precompute.js": {
|
"apps/geissclk/precompute.js": {
|
||||||
"hash": "2317812a9e348e7883e93a4be9e294ad7accd4dc3f0e31ee00343e2412030f98",
|
"hash": "2317812a9e348e7883e93a4be9e294ad7accd4dc3f0e31ee00343e2412030f98",
|
||||||
"rules": [
|
"rules": [
|
||||||
|
@ -1249,12 +1237,6 @@ module.exports = {
|
||||||
"no-undef"
|
"no-undef"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"apps/accelrec/app.js": {
|
|
||||||
"hash": "b5369a60afc8f360f0b33f71080eb3f5d09a1bf3703acfcf07cd80dd19f1997d",
|
|
||||||
"rules": [
|
|
||||||
"no-undef"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"apps/BLEcontroller/app-joy.js": {
|
"apps/BLEcontroller/app-joy.js": {
|
||||||
"hash": "e4f34bb1bc11b52c3d7a1c537a140b0e23ccef82694dcd602cb517a8ba342898",
|
"hash": "e4f34bb1bc11b52c3d7a1c537a140b0e23ccef82694dcd602cb517a8ba342898",
|
||||||
"rules": [
|
"rules": [
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<html>
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
<style>
|
<style>
|
||||||
table { width:100%;}
|
table {width:100%;margin-top:3%;}
|
||||||
.table_t {font-weight:bold;width:40%;};
|
.table_t {font-weight:bold;width:40%;}
|
||||||
|
.form-group > * {display:block;}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -15,18 +17,20 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input id="translations" type="checkbox" /> <label for="translations">Add common language translations like "Yes", "No", "On", "Off"<br/><i>(Not recommended. For translations use the option under <code>More...</code> in the app loader.</i></label>
|
<label><input id="translations" type="checkbox" /> Add common language translations like "Yes", "No", "On", "Off"<br/><i>(Not recommended. For translations use the option under <code>More...</code> in the app loader.</i></label>
|
||||||
|
<label><input id="customize" type="checkbox" /> Advanced: Customize the date and time formats.</label>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<table id="examples">
|
<span id="customize-warning"></span>
|
||||||
|
<table id="examples-short-long"></table>
|
||||||
|
<table id="examples"></table>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</table>
|
|
||||||
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p>
|
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||||
|
|
||||||
<script src="../../core/lib/customize.js"></script>
|
<script src="../../core/lib/customize.js"></script>
|
||||||
<script src="../../core/js/utils.js"></script>
|
<script src="../../core/js/utils.js"></script>
|
||||||
<script src="locales.js" charset="utf-8"></script>
|
<script src="locales.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/*
|
/*
|
||||||
|
@ -125,15 +129,14 @@ exports = { name : "system", currencySym:"£",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function createLocaleModule(lang) {
|
function createLocaleModule() {
|
||||||
console.log(`Language ${lang}`);
|
console.log(`Language ${lang}`);
|
||||||
|
|
||||||
const translations = document.getElementById('translations').checked;
|
const translations = document.getElementById('translations').checked;
|
||||||
console.log(`Translations: ${translations}`);
|
console.log(`Translations: ${translations}`);
|
||||||
|
|
||||||
const locale = locales[lang];
|
|
||||||
if (!locale) {
|
if (!locale) {
|
||||||
alert(`Language ${lang} not found!`);
|
alert(`Locale not set for language ${lang}!`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,16 +188,10 @@ exports = { name : "system", currencySym:"£",
|
||||||
"%P": `d.getHours()<12?${js(locale.ampm[0].toLowerCase())}:${js(locale.ampm[1].toLowerCase())}`
|
"%P": `d.getHours()<12?${js(locale.ampm[0].toLowerCase())}:${js(locale.ampm[1].toLowerCase())}`
|
||||||
};
|
};
|
||||||
|
|
||||||
var timeN = locale.timePattern[0];
|
var timeN = patternToCode(locale.timePattern[0]);
|
||||||
var timeS = locale.timePattern[1];
|
var timeS = patternToCode(locale.timePattern[1]);
|
||||||
var dateN = locale.datePattern[0];
|
var dateN = patternToCode(locale.datePattern[0]);
|
||||||
var dateS = locale.datePattern[1];
|
var dateS = patternToCode(locale.datePattern[1]);
|
||||||
Object.keys(replaceList).forEach(e => {
|
|
||||||
timeN = timeN.replace(e,"${"+replaceList[e]+"}");
|
|
||||||
timeS = timeS.replace(e,"${"+replaceList[e]+"}");
|
|
||||||
dateN = dateN.replace(e,"${"+replaceList[e]+"}");
|
|
||||||
dateS = dateS.replace(e,"${"+replaceList[e]+"}");
|
|
||||||
});
|
|
||||||
var temperature = locale.temperature=='°F' ? '(t*9/5)+32' : 't';
|
var temperature = locale.temperature=='°F' ? '(t*9/5)+32' : 't';
|
||||||
|
|
||||||
function getLocaleModule(isLocal) {
|
function getLocaleModule(isLocal) {
|
||||||
|
@ -246,46 +243,200 @@ exports = {
|
||||||
eval(getLocaleModule(true));
|
eval(getLocaleModule(true));
|
||||||
console.log("exports:",exports);
|
console.log("exports:",exports);
|
||||||
|
|
||||||
|
function patternToCode(pattern){
|
||||||
|
for(const symbol of Object.keys(replaceList)){
|
||||||
|
pattern = pattern.replaceAll(symbol,"${"+replaceList[symbol]+"}");
|
||||||
|
}
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
function patternToOutput(pattern){
|
||||||
|
const code = patternToCode(pattern);
|
||||||
|
const result = eval(`let d = new Date();\`${code}\``);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function dataList(id, options, formatter){
|
||||||
|
let output = `<datalist id="${id}">`;
|
||||||
|
for(const option of options){
|
||||||
|
const formatted = formatter?.(option) || option;
|
||||||
|
output+=`\n<option value="${option}">${formatted}</option>`
|
||||||
|
}
|
||||||
|
output += "\n</datalist>";
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
document.getElementById("examples").innerHTML = `
|
// TODO: This warning should have a link to an article explaining how the formats work, and how long they are allowed to be
|
||||||
|
document.getElementById("customize-warning").innerText = customizeLocale ? "⚠️ If you make the formats too long, some apps will not work!" : "";
|
||||||
|
document.getElementById("examples-short-long").innerHTML = `
|
||||||
<tr><td class="table_t"></td><td style="font-weight:bold">Short</td><td style="font-weight:bold">Long</td></tr>
|
<tr><td class="table_t"></td><td style="font-weight:bold">Short</td><td style="font-weight:bold">Long</td></tr>
|
||||||
<tr><td class="table_t">Day</td><td>${exports.dow(date,1)}</td><td>${exports.dow(date,0)}</td></tr>
|
<tr><td class="table_t">Day</td><td>${exports.dow(date,1)}</td><td>${exports.dow(date,0)}</td></tr>
|
||||||
<tr><td class="table_t">Month</td><td>${exports.month(date,1)}</td><td>${exports.month(date,0)}</td></tr>
|
<tr><td class="table_t">Month</td><td>${exports.month(date,1)}</td><td>${exports.month(date,0)}</td></tr>
|
||||||
<tr><td class="table_t">Date</td><td>${exports.date(date,1)}</td><td>${exports.date(date,0)}</td></tr>
|
<tr><td class="table_t">Date</td>
|
||||||
<tr><td class="table_t">Time</td><td>${exports.time(date,1)}</td><td>${exports.time(date,0)}</td></tr>
|
<td id="short-date-pattern-output">${exports.date(date,1)}</td>
|
||||||
<tr><td class="table_t">Number</td><td>${exports.number(12.3456789)}</td><td>${exports.number(12.3456789,4)}</td></tr>
|
<td id="long-date-pattern-output">${exports.date(date,0)}</td>
|
||||||
<tr><td class="table_t">Distance</td><td>${exports.distance(12.34,0)}</td><td>${exports.distance(12345.6,1)}</td></tr>
|
</tr>
|
||||||
<tr><td class="table_t">Speed</td><td></td><td>${exports.speed(123)}</td></tr>
|
${customizeLocale ? `<tr><td class="table_t">Date format</td>
|
||||||
<tr><td class="table_t">Temperature</td><td></td><td>${exports.temp(12,0)}</td></tr>
|
<td>
|
||||||
|
<input type=text id="short-date-pattern" list="short-date-patterns" value="${locale?.datePattern["1"]}"/>
|
||||||
|
${dataList("short-date-patterns", [locale?.datePattern["1"], "%-d.%-m.%y", "%-d/%-m/%y", "%d/%m/%Y"], patternToOutput)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type=text id="long-date-pattern" list="long-date-patterns" value="${locale?.datePattern["0"]}"/>
|
||||||
|
${dataList("long-date-patterns", [locale?.datePattern["0"], "%-d. %b %Y", "%b %d, %Y"], patternToOutput)}
|
||||||
|
</td>
|
||||||
|
</td>`
|
||||||
|
: ""}
|
||||||
|
<tr><td class="table_t">Time</td>
|
||||||
|
<td id="short-time-pattern-output">${exports.time(date,1)}</td>
|
||||||
|
<td id="long-time-pattern-output">${exports.time(date,0)}</td>
|
||||||
|
</tr>
|
||||||
|
${customizeLocale ? `<tr><td class="table_t">Time format</td>
|
||||||
|
<td>
|
||||||
|
<input type=text id="short-time-pattern" list="short-time-patterns" value="${locale?.timePattern["1"]}"/>
|
||||||
|
${dataList("short-time-patterns", [ "%HH.%MM", "%HH:%MM"], patternToOutput)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type=text id="long-time-pattern" list="long-time-patterns" value="${locale?.timePattern["0"]}"/>
|
||||||
|
${dataList("long-time-patterns", [locale?.timePattern["0"], "%HH.%MM.%SS", "%HH:%MM:%SS"], patternToOutput)}
|
||||||
|
</td>
|
||||||
|
</td>`
|
||||||
|
: ""}
|
||||||
|
<tr><td class="table_t">Number</td><td>${exports.number(12.3456789)}</td><td>${exports.number(12.3456789,4)}</td></tr>
|
||||||
|
<tr><td class="table_t">Distance</td><td>${exports.distance(12.34,0)}</td><td>${exports.distance(12345.6,1)}</td></tr>
|
||||||
`;
|
`;
|
||||||
|
document.getElementById("examples").innerHTML = `
|
||||||
|
<tr><td class="table_t">Meridian</td><td>
|
||||||
|
<span id="meridian-am-output">${exports.meridian(new Date(0))}</span> /
|
||||||
|
<span id="meridian-pm-output">${exports.meridian(new Date(43200000))}</span>
|
||||||
|
</td></tr>
|
||||||
|
${customizeLocale ? `<tr><td class="table_t">Meridian names</td>
|
||||||
|
<td>
|
||||||
|
<input type=text id="meridian-am" list="meridian-ams" value="${locale?.ampm["0"]}"/>
|
||||||
|
${dataList("meridian-ams", [locale?.ampm["0"], "AM"])}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type=text id="meridian-pm" list="meridian-pms" value="${locale?.ampm["1"]}"/>
|
||||||
|
${dataList("meridian-pms", [locale?.ampm["1"], "PM"])}
|
||||||
|
</td>
|
||||||
|
</tr>`
|
||||||
|
: ""}
|
||||||
|
<tr><td class="table_t">Speed</td><td>${exports.speed(123)}</td></tr>
|
||||||
|
<tr><td class="table_t">Temperature</td><td>${exports.temp(12,0)}</td></tr>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if(customizeLocale){
|
||||||
|
document.querySelector("input#short-date-pattern").addEventListener("input", event => {
|
||||||
|
locale.datePattern["1"] = event.target.value;
|
||||||
|
document.querySelector("td#short-date-pattern-output").innerText = patternToOutput(event.target.value);
|
||||||
|
});
|
||||||
|
document.querySelector("input#long-date-pattern").addEventListener("input", event => {
|
||||||
|
locale.datePattern["0"] = event.target.value;
|
||||||
|
document.querySelector("td#long-date-pattern-output").innerText = patternToOutput(event.target.value);
|
||||||
|
});
|
||||||
|
document.querySelector("input#short-time-pattern").addEventListener("input", event => {
|
||||||
|
locale.timePattern["1"] = event.target.value;
|
||||||
|
document.querySelector("td#short-time-pattern-output").innerText = patternToOutput(event.target.value);
|
||||||
|
});
|
||||||
|
document.querySelector("input#long-time-pattern").addEventListener("input", event => {
|
||||||
|
locale.timePattern["0"] = event.target.value;
|
||||||
|
document.querySelector("td#long-time-pattern-output").innerText = patternToOutput(event.target.value);
|
||||||
|
});
|
||||||
|
document.querySelector("input#meridian-am").addEventListener("input", event => {
|
||||||
|
locale.ampm["0"] = event.target.value;
|
||||||
|
document.querySelector("span#meridian-am-output").innerText = event.target.value;
|
||||||
|
});
|
||||||
|
document.querySelector("input#meridian-pm").addEventListener("input", event => {
|
||||||
|
locale.ampm["1"] = event.target.value;
|
||||||
|
document.querySelector("span#meridian-pm-output").innerText = event.target.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
return getLocaleModule(false);
|
return getLocaleModule(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lastUploadedLocaleID = "last-uploaded-locale";
|
||||||
|
let lastUploadedLocale;
|
||||||
|
try{
|
||||||
|
lastUploadedLocale = JSON.parse(localStorage?.getItem(lastUploadedLocaleID));
|
||||||
|
}catch(error){
|
||||||
|
console.warn("Unable to load last uploaded locale", error);
|
||||||
|
}
|
||||||
|
if(lastUploadedLocale){
|
||||||
|
if(!lastUploadedLocale.lang){
|
||||||
|
lastUploadedLocale = undefined;
|
||||||
|
console.warn("Unable to load last uploaded locale, it is missing the lang entry");
|
||||||
|
}else if(lastUploadedLocale.custom){
|
||||||
|
// Make sure to add any missing data from the original lang
|
||||||
|
// We don't know if fx a new entry has been added after the locale was last saved
|
||||||
|
const originalLocale = structuredClone(locales[lastUploadedLocale.lang]);
|
||||||
|
lastUploadedLocale = {...originalLocale, ...lastUploadedLocale};
|
||||||
|
|
||||||
|
// Add a special entry for the custom locale, put it first in the list
|
||||||
|
locales = {[lastUploadedLocaleID]: lastUploadedLocale, ...locales};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lang;
|
||||||
|
var locale;
|
||||||
|
var customizeLocale = false;
|
||||||
var languageSelector = document.getElementById("languages");
|
var languageSelector = document.getElementById("languages");
|
||||||
|
var customizeSelector = document.getElementById('customize');
|
||||||
languageSelector.innerHTML = Object.keys(locales).map(l=>{
|
languageSelector.innerHTML = Object.keys(locales).map(l=>{
|
||||||
var locale = locales[l];
|
var locale = locales[l];
|
||||||
var localeParts = l.split("_"); // en_GB -> ["en","GB"]
|
var name = l === lastUploadedLocaleID ? `Custom locale based on ${locale.lang}` : locale.lang;
|
||||||
|
var localeParts = locale.lang.split("_"); // en_GB -> ["en","GB"]
|
||||||
var icon = "";
|
var icon = "";
|
||||||
// If we have a 2 char ISO country code, use it to get the unicode flag
|
// If we have a 2 char ISO country code, use it to get the unicode flag
|
||||||
if (locale.icon)
|
if (locale.icon)
|
||||||
icon = locale.icon+" ";
|
icon = locale.icon+" ";
|
||||||
else if (localeParts[1] && localeParts[1].length==2)
|
else if (localeParts[1] && localeParts[1].length==2)
|
||||||
icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" ";
|
icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" ";
|
||||||
return `<option value="${l}">${icon}${l}${locale.notes?" - "+locale.notes:""}</option>`
|
return `<option value="${l}">${icon}${name}${locale.notes?" - "+locale.notes:""}</option>`
|
||||||
}).join("\n");
|
}).join("\n");
|
||||||
languageSelector.addEventListener('change', function() {
|
if(lastUploadedLocale){
|
||||||
const lang = languageSelector.options[languageSelector.selectedIndex].value;
|
if(lastUploadedLocale.custom){
|
||||||
createLocaleModule(lang);
|
// If the last uploaded locale was customized, choose the custom locale as default value
|
||||||
});
|
languageSelector.value = lastUploadedLocaleID;
|
||||||
// initial value
|
}else{
|
||||||
createLocaleModule(languageSelector.options[languageSelector.selectedIndex].value);
|
// If the last uploaded locale was not customized, choose the existing locale in the list as the default value
|
||||||
|
languageSelector.value = lastUploadedLocale.lang;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
languageSelector.addEventListener('change', handleLanguageChange);
|
||||||
|
function handleLanguageChange(){
|
||||||
|
lang = languageSelector.value;
|
||||||
|
locale = structuredClone(locales[lang]);
|
||||||
|
// If the locale is customized, make sure the customization option is activated. If not, disable it.
|
||||||
|
if(Boolean(customizeSelector.checked) !== Boolean(locale.custom)){
|
||||||
|
customizeSelector.checked = Boolean(locale.custom);
|
||||||
|
handleCustomizeChange();
|
||||||
|
}else{
|
||||||
|
createLocaleModule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customizeSelector.addEventListener('change', handleCustomizeChange);
|
||||||
|
function handleCustomizeChange(){
|
||||||
|
customizeLocale = customizeSelector.checked;
|
||||||
|
// If the user no longer wants to customize, make sure to return to the default lang entry
|
||||||
|
if(!customizeLocale){
|
||||||
|
languageSelector.value = locales[lang].lang;
|
||||||
|
handleLanguageChange();
|
||||||
|
}else{
|
||||||
|
createLocaleModule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set initial values
|
||||||
|
handleLanguageChange();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
document.getElementById("upload").addEventListener("click", function() {
|
document.getElementById("upload").addEventListener("click", function() {
|
||||||
|
|
||||||
const lang = languageSelector.options[languageSelector.selectedIndex].value;
|
var localeModule = createLocaleModule();
|
||||||
var localeModule = createLocaleModule(lang);
|
|
||||||
|
// Save the locale data to make it easier to upload the same locale next time.
|
||||||
|
// If the locale is not customized, only save the lang. The rest of the data will be added when the page loads next time.
|
||||||
|
const savedLocaleData = customizeLocale ? {...locale, custom: true} : {lang: locale.lang};
|
||||||
|
localStorage?.setItem(lastUploadedLocaleID, JSON.stringify(savedLocaleData));
|
||||||
|
|
||||||
console.log("Locale Module is:",localeModule);
|
console.log("Locale Module is:",localeModule);
|
||||||
sendCustomizedApp({
|
sendCustomizedApp({
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.1: Initial release
|
0.1: Initial release
|
||||||
0.2: Draw line for 3d effect, fix number alignment
|
0.2: Draw line for 3d effect, fix number alignment
|
||||||
|
0.3: Fix day-end overflowing hour calculation
|
||||||
|
|
|
@ -78,6 +78,19 @@
|
||||||
return lineEndFull - 5;
|
return lineEndFull - 5;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let drawHourString = function(hour, yLines) {
|
||||||
|
var hourForDrawing = 0;
|
||||||
|
if (hour < 0) {
|
||||||
|
// a negative hour => (+ and - = -)
|
||||||
|
hourForDrawing = 24 + hour;
|
||||||
|
} else if (hour >= 24) {
|
||||||
|
hourForDrawing = hour - 24;
|
||||||
|
} else {
|
||||||
|
hourForDrawing = hour;
|
||||||
|
}
|
||||||
|
g.drawString(hourForDrawing, hourStringXOffset(hourForDrawing), yLines, true);
|
||||||
|
};
|
||||||
|
|
||||||
let drawTime = function () {
|
let drawTime = function () {
|
||||||
g.clear();
|
g.clear();
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
|
@ -101,12 +114,12 @@
|
||||||
switch (yTopLines - 88 + mins) {
|
switch (yTopLines - 88 + mins) {
|
||||||
case -60:
|
case -60:
|
||||||
lineEnd = lineEndFull;
|
lineEnd = lineEndFull;
|
||||||
g.drawString(d.getHours()-1, hourStringXOffset(d.getHours()-1), yTopLines, true);
|
drawHourString(d.getHours() - 1, yTopLines);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
case 60:
|
case 60:
|
||||||
lineEnd = lineEndFull;
|
lineEnd = lineEndFull;
|
||||||
g.drawString(d.getHours(), hourStringXOffset(d.getHours()), yTopLines, true);
|
drawHourString(d.getHours(), yTopLines);
|
||||||
break;
|
break;
|
||||||
case 45:
|
case 45:
|
||||||
case -45:
|
case -45:
|
||||||
|
@ -136,11 +149,11 @@
|
||||||
case 0:
|
case 0:
|
||||||
case 60:
|
case 60:
|
||||||
lineEnd = lineEndFull;
|
lineEnd = lineEndFull;
|
||||||
g.drawString(d.getHours() + 1, hourStringXOffset(d.getHours()+1), yBottomLines, true);
|
drawHourString(d.getHours() + 1, yBottomLines);
|
||||||
break;
|
break;
|
||||||
case 120:
|
case 120:
|
||||||
lineEnd = lineEndFull;
|
lineEnd = lineEndFull;
|
||||||
g.drawString(d.getHours() + 2, hourStringXOffset(d.getHours()+2), yBottomLines, true);
|
drawHourString(d.getHours() + 2, yBottomLines);
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
case 75:
|
case 75:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "measuretime",
|
"id": "measuretime",
|
||||||
"name": "Measure Time",
|
"name": "Measure Time",
|
||||||
"version": "0.2",
|
"version": "0.3",
|
||||||
"description": "Measure Time in a fancy way.",
|
"description": "Measure Time in a fancy way.",
|
||||||
"icon": "measuretime_icon.png",
|
"icon": "measuretime_icon.png",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Pomodoro
|
||||||
|
|
||||||
|
> The Pomodoro Technique is a time management method developed by Francesco Cirillo in the late 1980s. It uses a kitchen timer to break work into intervals, typically 25 minutes in length, separated by short breaks. Each interval is known as a pomodoro, from the Italian word for tomato, after the tomato-shaped kitchen timer Cirillo used as a university student.
|
||||||
|
>
|
||||||
|
> The original technique has six steps:
|
||||||
|
>
|
||||||
|
> Decide on the task to be done.
|
||||||
|
> Set the Pomodoro timer (typically for 25 minutes).
|
||||||
|
> Work on the task.
|
||||||
|
> End work when the timer rings and take a short break (typically 5–10 minutes).
|
||||||
|
> Go back to Step 2 and repeat until you complete four pomodori.
|
||||||
|
> After four pomodori are done, take a long break (typically 20 to 30 minutes) instead of a short break. Once the long break is finished, return to step 2.
|
||||||
|
|
||||||
|
*Description gathered from https://en.wikipedia.org/wiki/Pomodoro_Technique*
|
|
@ -7,6 +7,7 @@
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "pomodoro,cooking,tools",
|
"tags": "pomodoro,cooking,tools",
|
||||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"screenshots": [{"url":"bangle2-pomodoro-screenshot.png"}],
|
"screenshots": [{"url":"bangle2-pomodoro-screenshot.png"}],
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
0.05: Fix display of final menu item when no options are given and
|
0.05: Fix display of final menu item when no options are given and
|
||||||
handling of E.showMenu() with no arguments
|
handling of E.showMenu() with no arguments
|
||||||
0.06: Fix lower bounding of numeric values
|
0.06: Fix lower bounding of numeric values
|
||||||
|
0.07: Fix bug with alarms app (scroller) and correctly show images
|
||||||
|
|
|
@ -28,6 +28,9 @@ E.showMenu = function (items) {
|
||||||
y += 22;
|
y += 22;
|
||||||
var lastIdx = 0;
|
var lastIdx = 0;
|
||||||
var selectEdit = undefined;
|
var selectEdit = undefined;
|
||||||
|
var scroller = {
|
||||||
|
scroll: selected,
|
||||||
|
};
|
||||||
var l = {
|
var l = {
|
||||||
draw: function (rowmin, rowmax) {
|
draw: function (rowmin, rowmax) {
|
||||||
var rows = 0 | Math.min((y2 - y) / fontHeight, menuItems.length);
|
var rows = 0 | Math.min((y2 - y) / fontHeight, menuItems.length);
|
||||||
|
@ -76,10 +79,11 @@ E.showMenu = function (items) {
|
||||||
v = "";
|
v = "";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
if (name.length >= 17 - v.length && typeof item === "object") {
|
var vplain = v.indexOf("\0") < 0;
|
||||||
|
if (vplain && name.length >= 17 - v.length && typeof item === "object") {
|
||||||
g.drawString(name.substring(0, 12 - v.length) + "...", x + 3.7, iy + 2.7);
|
g.drawString(name.substring(0, 12 - v.length) + "...", x + 3.7, iy + 2.7);
|
||||||
}
|
}
|
||||||
else if (name.length >= 15) {
|
else if (vplain && name.length >= 15) {
|
||||||
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
|
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -138,9 +142,11 @@ E.showMenu = function (items) {
|
||||||
else {
|
else {
|
||||||
var lastSelected = selected;
|
var lastSelected = selected;
|
||||||
selected = (selected + dir + menuItems.length) % menuItems.length;
|
selected = (selected + dir + menuItems.length) % menuItems.length;
|
||||||
|
scroller.scroll = selected;
|
||||||
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
|
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
scroller: scroller,
|
||||||
};
|
};
|
||||||
l.draw();
|
l.draw();
|
||||||
var back = options.back;
|
var back = options.back;
|
||||||
|
|
|
@ -35,6 +35,10 @@ E.showMenu = (items?: Menu): MenuInstance => {
|
||||||
let lastIdx = 0;
|
let lastIdx = 0;
|
||||||
let selectEdit: undefined | ActualMenuItem = undefined;
|
let selectEdit: undefined | ActualMenuItem = undefined;
|
||||||
|
|
||||||
|
const scroller = {
|
||||||
|
scroll: selected,
|
||||||
|
};
|
||||||
|
|
||||||
const l = {
|
const l = {
|
||||||
draw: (rowmin?: number, rowmax?: number) => {
|
draw: (rowmin?: number, rowmax?: number) => {
|
||||||
let rows = 0|Math.min((y2 - y) / fontHeight, menuItems.length);
|
let rows = 0|Math.min((y2 - y) / fontHeight, menuItems.length);
|
||||||
|
@ -83,9 +87,10 @@ E.showMenu = (items?: Menu): MenuInstance => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*???*/{
|
/*???*/{
|
||||||
if(name.length >= 17 - v.length && typeof item === "object"){
|
const vplain = v.indexOf("\0") < 0;
|
||||||
|
if(vplain && name.length >= 17 - v.length && typeof item === "object"){
|
||||||
g.drawString(name.substring(0, 12 - v.length) + "...", x + 3.7, iy + 2.7);
|
g.drawString(name.substring(0, 12 - v.length) + "...", x + 3.7, iy + 2.7);
|
||||||
}else if(name.length >= 15){
|
}else if(vplain && name.length >= 15){
|
||||||
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
|
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
|
||||||
}else{
|
}else{
|
||||||
g.drawString(name, x + 3.7, iy + 2.7);
|
g.drawString(name, x + 3.7, iy + 2.7);
|
||||||
|
@ -156,9 +161,11 @@ E.showMenu = (items?: Menu): MenuInstance => {
|
||||||
} else {
|
} else {
|
||||||
const lastSelected = selected;
|
const lastSelected = selected;
|
||||||
selected = (selected + dir + /*keep +ve*/menuItems.length) % menuItems.length;
|
selected = (selected + dir + /*keep +ve*/menuItems.length) % menuItems.length;
|
||||||
|
scroller.scroll = selected;
|
||||||
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
|
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
scroller,
|
||||||
};
|
};
|
||||||
|
|
||||||
l.draw();
|
l.draw();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "promenu",
|
"id": "promenu",
|
||||||
"name": "Pro Menu",
|
"name": "Pro Menu",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "Replace the built in menu function. Supports Bangle.js 1 and Bangle.js 2.",
|
"description": "Replace the built in menu function. Supports Bangle.js 1 and Bangle.js 2.",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEw4X/AwX48AFCqoAEC4oL/Bf4L/Bf4LTAH4A/ADGqAAIL/Bf4LD"))
|
|
@ -0,0 +1,142 @@
|
||||||
|
{
|
||||||
|
const minute_boxes = [
|
||||||
|
{x:0.5, y:0},
|
||||||
|
{x:0.5, y:0.5},
|
||||||
|
{x:0, y:0.5},
|
||||||
|
{x:0, y:0},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hour_boxes = [
|
||||||
|
{x:0.5, y:0},
|
||||||
|
{x:0.75, y:0},
|
||||||
|
{x:0.75, y:0.25},
|
||||||
|
{x:0.75, y:0.5},
|
||||||
|
{x:0.75, y:0.75},
|
||||||
|
{x:0.5, y:0.75},
|
||||||
|
{x:0.25, y:0.75},
|
||||||
|
{x:0, y:0.75},
|
||||||
|
{x:0, y:0.5},
|
||||||
|
{x:0, y:0.25},
|
||||||
|
{x:0, y:0},
|
||||||
|
{x:0.25, y:0},
|
||||||
|
];
|
||||||
|
|
||||||
|
let drawTimeout;
|
||||||
|
|
||||||
|
// schedule a draw for the next 15 minute period
|
||||||
|
let queueDraw = function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}, (60000 * 15) - (Date.now() % (60000 * 15)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main draw function
|
||||||
|
let draw = function draw() {
|
||||||
|
var d = new Date();
|
||||||
|
var h = d.getHours(), m = d.getMinutes();
|
||||||
|
|
||||||
|
g.setBgColor(settings.backgroundColour);
|
||||||
|
g.clearRect(Bangle.appRect);
|
||||||
|
|
||||||
|
if (settings.showBattery) {
|
||||||
|
drawBattery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw minute box
|
||||||
|
drawBox(Math.floor(m/15), minute_boxes, Bangle.appRect.h/2, settings.minuteColour);
|
||||||
|
|
||||||
|
// Draw an hour box or write the number
|
||||||
|
if (settings.digital) {
|
||||||
|
g.setColor(settings.hourColour);
|
||||||
|
g.setFont("Vector:60");
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.drawString(h, Bangle.appRect.x + Bangle.appRect.w/2, Bangle.appRect.y + Bangle.appRect.h/2);
|
||||||
|
} else {
|
||||||
|
drawBox(h % 12, hour_boxes, Bangle.appRect.h/4, settings.hourColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
queueDraw();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw battery box
|
||||||
|
let drawBattery = function drawBattery() {
|
||||||
|
// Round battery up to 10% interval
|
||||||
|
let battery = Math.min((Math.floor(E.getBattery()/10)+1)/10, 1);
|
||||||
|
|
||||||
|
// Maximum battery box
|
||||||
|
let batterySize = 30;
|
||||||
|
|
||||||
|
// Draw outer box at full brightness
|
||||||
|
g.setColor(settings.batteryColour);
|
||||||
|
g.drawRect(
|
||||||
|
(Bangle.appRect.w / 2) - batterySize,
|
||||||
|
(Bangle.appRect.h / 2) - batterySize + Bangle.appRect.y,
|
||||||
|
(Bangle.appRect.w / 2) + batterySize,
|
||||||
|
(Bangle.appRect.h / 2) + batterySize + Bangle.appRect.y
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fade battery colour and draw inner box
|
||||||
|
g.setColor(settings.batteryColour.split('').map((c) => {
|
||||||
|
return c=='f' ? Math.ceil(15 * battery).toString(16) : c;
|
||||||
|
}).join(''));
|
||||||
|
g.fillRect(
|
||||||
|
(Bangle.appRect.w / 2) - (batterySize * battery),
|
||||||
|
(Bangle.appRect.h / 2) - (batterySize * battery) + Bangle.appRect.y,
|
||||||
|
(Bangle.appRect.w / 2) + (batterySize * battery),
|
||||||
|
(Bangle.appRect.h / 2) + (batterySize * battery) + Bangle.appRect.y
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw hour or minute boxes
|
||||||
|
let drawBox = function drawBox(current, boxes, size, colour) {
|
||||||
|
let x1 = (boxes[current].x * Bangle.appRect.h) + (Bangle.appRect.y/2);
|
||||||
|
let y1 = (boxes[current].y * Bangle.appRect.h) + Bangle.appRect.y;
|
||||||
|
let x2 = x1 + size;
|
||||||
|
let y2 = y1 + size;
|
||||||
|
g.setColor(colour);
|
||||||
|
g.fillRect(x1, y1, x2, y2);
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = Object.assign({
|
||||||
|
// Default values
|
||||||
|
minuteColour: '#f00',
|
||||||
|
hourColour: '#ff0',
|
||||||
|
backgroundColour: 'theme',
|
||||||
|
showWidgets: true,
|
||||||
|
showBattery: true,
|
||||||
|
digital: false,
|
||||||
|
batteryColour: '#0f0'
|
||||||
|
}, require('Storage').readJSON('quarterclock.json', true) || {});
|
||||||
|
|
||||||
|
if (settings.backgroundColour == 'theme') {
|
||||||
|
settings.backgroundColour = g.theme.bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set minuteColour to a darker shade if same as hourColour
|
||||||
|
if (settings.minuteColour == settings.hourColour) {
|
||||||
|
settings.minuteColour = settings.minuteColour.split('').map((c) => {
|
||||||
|
return c=='f' ? '7' : c;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
// Remove handler to allow fast loading
|
||||||
|
Bangle.setUI({mode:"clock", remove:function() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
require("widget_utils").show();
|
||||||
|
}});
|
||||||
|
|
||||||
|
// Load and display widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
if (settings.showWidgets) {
|
||||||
|
require("widget_utils").show();
|
||||||
|
} else {
|
||||||
|
require("widget_utils").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw initial boxes and queue subsequent redraws
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
After Width: | Height: | Size: 252 B |
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"id": "quarterclock",
|
||||||
|
"name": "Quarter Clock",
|
||||||
|
"shortName":"Quarter Clock",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots" : [ { "url":"screenshot.png" } ],
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "For those lazy days when the exact time doesn't matter. Small square shows the hour, large square shows the fifteen minute period, and centre square shows the battery level.",
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"quarterclock.app.js","url":"app.js"},
|
||||||
|
{"name":"quarterclock.settings.js","url":"settings.js"},
|
||||||
|
{"name":"quarterclock.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"quarterclock.json"}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,66 @@
|
||||||
|
(function(back) {
|
||||||
|
var FILE = 'quarterclock.json';
|
||||||
|
// Load settings
|
||||||
|
var settings = Object.assign({
|
||||||
|
minuteColour: '#f00',
|
||||||
|
hourColour: '#ff0',
|
||||||
|
backgroundColour: 'theme',
|
||||||
|
showWidgets: true,
|
||||||
|
showBattery: true,
|
||||||
|
digital: false,
|
||||||
|
batteryColour: '#0f0',
|
||||||
|
}, require('Storage').readJSON(FILE, true) || {});
|
||||||
|
|
||||||
|
function setSetting(key,value) {
|
||||||
|
settings[key] = value;
|
||||||
|
require('Storage').writeJSON(FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method which uses int-based menu item for set of string values and their labels
|
||||||
|
function stringItems(key, startvalue, values, labels) {
|
||||||
|
return {
|
||||||
|
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
|
||||||
|
format: v => labels[v],
|
||||||
|
min: 0,
|
||||||
|
max: values.length - 1,
|
||||||
|
wrap: true,
|
||||||
|
step: 1,
|
||||||
|
onchange: v => {
|
||||||
|
setSetting(key,values[v]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method which breaks string set settings down to local settings object
|
||||||
|
function stringInSettings(name, values, labels) {
|
||||||
|
return stringItems(name,settings[name], values, labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the menu
|
||||||
|
E.showMenu({
|
||||||
|
'' : { 'title' : 'Quarter Clock' },
|
||||||
|
'< Back' : () => back(),
|
||||||
|
'Hour Colour': stringInSettings('hourColour', ['#f00', '#0f0', '#00f', '#ff0', '#0ff', '#f0f'], ['Red', 'Green', 'Blue', 'Yellow', 'Cyan', 'Magenta']),
|
||||||
|
'Minute Colour': stringInSettings('minuteColour', ['#f00', '#0f0', '#00f', '#ff0', '#0ff', '#f0f'], ['Red', 'Green', 'Blue', 'Yellow', 'Cyan', 'Magenta']),
|
||||||
|
'Background Colour': stringInSettings('backgroundColour', ['theme', '#000', '#fff'],['theme', 'Black', 'White']),
|
||||||
|
'Digital': {
|
||||||
|
value: !!settings.digital, // !! converts undefined to false
|
||||||
|
onchange: v => {
|
||||||
|
setSetting('digital', v);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Show Widgets': {
|
||||||
|
value: !!settings.showWidgets,
|
||||||
|
onchange: v => {
|
||||||
|
setSetting('showWidgets', v);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Show Battery': {
|
||||||
|
value: !!settings.showBattery,
|
||||||
|
onchange: v => {
|
||||||
|
setSetting('showBattery', v);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Battery Colour': stringInSettings('batteryColour', ['#f00', '#0f0', '#00f', '#ff0', '#0ff', '#f0f'], ['Red', 'Green', 'Blue', 'Yellow', 'Cyan', 'Magenta']),
|
||||||
|
});
|
||||||
|
})
|
|
@ -651,11 +651,11 @@ function showUtilMenu() {
|
||||||
E.showMessage(/*LANG*/'Flattening battery - this can take hours.\nLong-press button to cancel.');
|
E.showMessage(/*LANG*/'Flattening battery - this can take hours.\nLong-press button to cancel.');
|
||||||
Bangle.setLCDTimeout(0);
|
Bangle.setLCDTimeout(0);
|
||||||
Bangle.setLCDPower(1);
|
Bangle.setLCDPower(1);
|
||||||
|
Bangle.setLCDBrightness(1);
|
||||||
if (Bangle.setGPSPower) Bangle.setGPSPower(1,"flat");
|
if (Bangle.setGPSPower) Bangle.setGPSPower(1,"flat");
|
||||||
if (Bangle.setHRMPower) Bangle.setHRMPower(1,"flat");
|
if (Bangle.setHRMPower) Bangle.setHRMPower(1,"flat");
|
||||||
if (Bangle.setCompassPower) Bangle.setCompassPower(1,"flat");
|
if (Bangle.setCompassPower) Bangle.setCompassPower(1,"flat");
|
||||||
if (Bangle.setBarometerPower) Bangle.setBarometerPower(1,"flat");
|
if (Bangle.setBarometerPower) Bangle.setBarometerPower(1,"flat");
|
||||||
if (Bangle.setHRMPower) Bangle.setGPSPower(1,"flat");
|
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
var i=1000;while (i--);
|
var i=1000;while (i--);
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: attempt to import
|
0.01: attempt to import
|
||||||
0.02: Minor code improvements
|
0.02: Minor code improvements
|
||||||
|
0.03: big rewrite, adding time-adjust and altitude-adjust functionality
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "skyspy",
|
{ "id": "skyspy",
|
||||||
"name": "Sky Spy",
|
"name": "Sky Spy",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Application for debugging GPS problems",
|
"description": "Application for debugging GPS problems",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
|
@ -1,20 +1,121 @@
|
||||||
/* Sky spy */
|
/* Sky spy */
|
||||||
/* 0 .. DD.ddddd
|
|
||||||
1 .. DD MM.mmm'
|
/* fmt library v0.1 */
|
||||||
2 .. DD MM'ss"
|
let fmt = {
|
||||||
*/
|
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",
|
||||||
var mode = 1;
|
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",
|
||||||
|
|
||||||
|
/* 0 .. DD.ddddd
|
||||||
|
1 .. DD MM.mmm'
|
||||||
|
2 .. DD MM'ss"
|
||||||
|
*/
|
||||||
|
geo_mode : 1,
|
||||||
|
|
||||||
|
init: function() {},
|
||||||
|
fmtDist: function(km) { return km.toFixed(1) + this.icon_km; },
|
||||||
|
fmtSteps: function(n) { return this.fmtDist(0.001 * 0.719 * n); },
|
||||||
|
fmtAlt: function(m) { return m.toFixed(0) + this.icon_alt; },
|
||||||
|
fmtTimeDiff: function(d) {
|
||||||
|
if (d < 180)
|
||||||
|
return ""+d.toFixed(0);
|
||||||
|
d = d/60;
|
||||||
|
return ""+d.toFixed(0)+"m";
|
||||||
|
},
|
||||||
|
fmtAngle: function(x) {
|
||||||
|
switch (this.geo_mode) {
|
||||||
|
case 0:
|
||||||
|
return "" + x;
|
||||||
|
case 1: {
|
||||||
|
let d = Math.floor(x);
|
||||||
|
let m = x - d;
|
||||||
|
m = m*60;
|
||||||
|
return "" + d + " " + m.toFixed(3) + "'";
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
let d = Math.floor(x);
|
||||||
|
let m = x - d;
|
||||||
|
m = m*60;
|
||||||
|
let mf = Math.floor(m);
|
||||||
|
let s = m - mf;
|
||||||
|
s = s*60;
|
||||||
|
return "" + d + " " + mf + "'" + s.toFixed(0) + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "bad mode?";
|
||||||
|
},
|
||||||
|
fmtPos: function(pos) {
|
||||||
|
let x = pos.lat;
|
||||||
|
let c = "N";
|
||||||
|
if (x<0) {
|
||||||
|
c = "S";
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
let s = c+this.fmtAngle(pos.lat) + "\n";
|
||||||
|
c = "E";
|
||||||
|
if (x<0) {
|
||||||
|
c = "W";
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
return s + c + this.fmtAngle(pos.lon);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* gps library v0.1 */
|
||||||
|
let gps = {
|
||||||
|
emulator: -1,
|
||||||
|
init: function(x) {
|
||||||
|
this.emulator = (process.env.BOARD=="EMSCRIPTEN"
|
||||||
|
|| process.env.BOARD=="EMSCRIPTEN2")?1:0;
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
on_gps: function(f) {
|
||||||
|
let fix = this.getGPSFix();
|
||||||
|
f(fix);
|
||||||
|
|
||||||
|
/*
|
||||||
|
"lat": number, // Latitude in degrees
|
||||||
|
"lon": number, // Longitude in degrees
|
||||||
|
"alt": number, // altitude in M
|
||||||
|
"speed": number, // Speed in kph
|
||||||
|
"course": number, // Course in degrees
|
||||||
|
"time": Date, // Current Time (or undefined if not known)
|
||||||
|
"satellites": 7, // Number of satellites
|
||||||
|
"fix": 1 // NMEA Fix state - 0 is no fix
|
||||||
|
"hdop": number, // Horizontal Dilution of Precision
|
||||||
|
*/
|
||||||
|
this.state.timeout = setTimeout(this.on_gps, 1000, f);
|
||||||
|
},
|
||||||
|
off_gps: function() {
|
||||||
|
clearTimeout(this.state.timeout);
|
||||||
|
},
|
||||||
|
getGPSFix: function() {
|
||||||
|
if (!this.emulator)
|
||||||
|
return Bangle.getGPSFix();
|
||||||
|
let fix = {};
|
||||||
|
fix.fix = 1;
|
||||||
|
fix.lat = 50;
|
||||||
|
fix.lon = 14;
|
||||||
|
fix.alt = 200;
|
||||||
|
fix.speed = 5;
|
||||||
|
fix.course = 30;
|
||||||
|
fix.time = Date();
|
||||||
|
fix.satellites = 5;
|
||||||
|
fix.hdop = 12;
|
||||||
|
return fix;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var display = 0;
|
var display = 0;
|
||||||
|
|
||||||
var debug = 0;
|
var debug = 0;
|
||||||
|
var gps_start;
|
||||||
var cancel_gps, gps_start;
|
|
||||||
var cur_altitude;
|
var cur_altitude;
|
||||||
|
|
||||||
var wi = 24;
|
var wi = 24;
|
||||||
var h = 176-wi, w = 176;
|
var h = 176-wi, w = 176;
|
||||||
|
|
||||||
var fix;
|
var fix;
|
||||||
|
var adj_time = 0, adj_alt = 0;
|
||||||
|
|
||||||
function radA(p) { return p*(Math.PI*2); }
|
function radA(p) { return p*(Math.PI*2); }
|
||||||
function radD(d) { return d*(h/2); }
|
function radD(d) { return d*(h/2); }
|
||||||
|
@ -27,26 +128,7 @@ function radY(p, d) {
|
||||||
return h/2 - Math.cos(a)*radD(d) + wi;
|
return h/2 - Math.cos(a)*radD(d) + wi;
|
||||||
}
|
}
|
||||||
|
|
||||||
function format(x) {
|
var qalt = -1, min_dalt, max_dalt, step;
|
||||||
switch (mode) {
|
|
||||||
case 0:
|
|
||||||
return "" + x;
|
|
||||||
case 1:
|
|
||||||
d = Math.floor(x);
|
|
||||||
m = x - d;
|
|
||||||
m = m*60;
|
|
||||||
return "" + d + " " + m.toFixed(3) + "'";
|
|
||||||
case 2:
|
|
||||||
d = Math.floor(x);
|
|
||||||
m = x - d;
|
|
||||||
m = m*60;
|
|
||||||
mf = Math.floor(m);
|
|
||||||
s = m - mf;
|
|
||||||
s = s*60;
|
|
||||||
return "" + d + " " + mf + "'" + s.toFixed(0) + '"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var qalt = -1;
|
|
||||||
function resetAlt() {
|
function resetAlt() {
|
||||||
min_dalt = 9999; max_dalt = -9999; step = 0;
|
min_dalt = 9999; max_dalt = -9999; step = 0;
|
||||||
}
|
}
|
||||||
|
@ -64,65 +146,96 @@ function calcAlt(alt, cur_altitude) {
|
||||||
return ddalt;
|
return ddalt;
|
||||||
}
|
}
|
||||||
function updateGps() {
|
function updateGps() {
|
||||||
let /*have = false,*/ lat = "lat", lon = "lon", alt = "alt",
|
let lat = "lat ", alt = "?",
|
||||||
speed = "speed", hdop = "hdop"; // balt = "balt";
|
speed = "speed ", hdop = "?", adelta = "adelta ",
|
||||||
|
tdelta = "tdelta ";
|
||||||
|
|
||||||
if (cancel_gps)
|
fix = gps.getGPSFix();
|
||||||
return;
|
if (adj_time) {
|
||||||
fix = Bangle.getGPSFix();
|
print("Adjusting time");
|
||||||
|
setTime(fix.time.getTime()/1000);
|
||||||
|
adj_time = 0;
|
||||||
|
}
|
||||||
|
if (adj_alt) {
|
||||||
|
print("Adjust altitude");
|
||||||
|
if (qalt < 5) {
|
||||||
|
let rest_altitude = fix.alt;
|
||||||
|
let alt_adjust = cur_altitude - rest_altitude;
|
||||||
|
let abs = Math.abs(alt_adjust);
|
||||||
|
print("adj", alt_adjust);
|
||||||
|
let o = Bangle.getOptions();
|
||||||
|
if (abs > 10 && abs < 150) {
|
||||||
|
let a = 0.01;
|
||||||
|
// FIXME: draw is called often compared to alt reading
|
||||||
|
if (cur_altitude > rest_altitude)
|
||||||
|
a = -a;
|
||||||
|
o.seaLevelPressure = o.seaLevelPressure + a;
|
||||||
|
Bangle.setOptions(o);
|
||||||
|
}
|
||||||
|
msg = o.seaLevelPressure.toFixed(1) + "hPa";
|
||||||
|
print(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Bangle.getPressure().then((x) => {
|
Bangle.getPressure().then((x) => {
|
||||||
cur_altitude = x.altitude;
|
cur_altitude = x.altitude;
|
||||||
}, print);
|
}, print);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("Altimeter error", e);
|
//print("Altimeter error", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
speed = getTime() - gps_start;
|
|
||||||
|
|
||||||
|
//print(fix);
|
||||||
|
if (fix && fix.time) {
|
||||||
|
tdelta = "" + (getTime() - fix.time.getTime()/1000).toFixed(0);
|
||||||
|
}
|
||||||
if (fix && fix.fix && fix.lat) {
|
if (fix && fix.fix && fix.lat) {
|
||||||
lat = "" + format(fix.lat);
|
lat = "" + fmt.fmtPos(fix);
|
||||||
lon = "" + format(fix.lon);
|
alt = "" + fix.alt.toFixed(0);
|
||||||
alt = "" + fix.alt.toFixed(1);
|
adelta = "" + (cur_altitude - fix.alt).toFixed(0);
|
||||||
speed = "" + fix.speed.toFixed(1);
|
speed = "" + fix.speed.toFixed(1);
|
||||||
hdop = "" + fix.hdop.toFixed(1);
|
hdop = "" + fix.hdop.toFixed(0);
|
||||||
//have = true;
|
} else {
|
||||||
|
lat = "NO FIX\n"
|
||||||
|
+ "" + (getTime() - gps_start).toFixed(0) + "s "
|
||||||
|
+ sats_used + "/" + snum;
|
||||||
|
if (cur_altitude)
|
||||||
|
adelta = "" + cur_altitude.toFixed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ddalt = calcAlt(alt, cur_altitude);
|
let ddalt = calcAlt(alt, cur_altitude);
|
||||||
if (display == 1)
|
let msg = "";
|
||||||
g.reset().setFont("Vector", 20)
|
if (display == 1) {
|
||||||
.setColor(1,1,1)
|
msg = lat +
|
||||||
.fillRect(0, wi, 176, 176)
|
"\ne" + hdop + "m "+tdelta+"s\n" +
|
||||||
.setColor(0,0,0)
|
speed + "km/h\n"+ alt + "m+" + adelta + "\nmsghere";
|
||||||
.drawString("Acquiring GPS", 0, 30)
|
}
|
||||||
.drawString(lat, 0, 50)
|
|
||||||
.drawString(lon, 0, 70)
|
|
||||||
.drawString("alt "+alt, 0, 90)
|
|
||||||
.drawString("speed "+speed, 0, 110)
|
|
||||||
.drawString("hdop "+hdop, 0, 130)
|
|
||||||
.drawString("balt" + cur_altitude, 0, 150);
|
|
||||||
|
|
||||||
if (display == 2) {
|
if (display == 2) {
|
||||||
g.reset().setFont("Vector", 20)
|
/* qalt is altitude quality estimate -- over ten seconds,
|
||||||
.setColor(1,1,1)
|
computes differences between GPS and barometric altitude.
|
||||||
.fillRect(0, wi, 176, 176)
|
The lower the better.
|
||||||
.setColor(0,0,0)
|
|
||||||
.drawString("GPS status", 0, 30)
|
ddalt is just a debugging -- same estimate, but without
|
||||||
.drawString("speed "+speed, 0, 50)
|
waiting 10 seconds, so will be always optimistic at start
|
||||||
.drawString("hdop "+hdop, 0, 70)
|
of the cycle */
|
||||||
.drawString("dd "+qalt.toFixed(0) + " (" + ddalt.toFixed(0) + ")", 0, 90)
|
msg = speed + "km/h\n" +
|
||||||
.drawString("alt "+alt, 0, 110)
|
"e"+hdop + "m"
|
||||||
.drawString("balt " + cur_altitude, 0, 130)
|
+"\ndd "+qalt.toFixed(0) + "\n(" + step + "/" + ddalt.toFixed(0) + ")" +
|
||||||
.drawString(step, 0, 150);
|
"\n"+alt + "m+" + adelta;
|
||||||
|
}
|
||||||
step++;
|
step++;
|
||||||
if (step == 10) {
|
if (step == 10) {
|
||||||
qalt = max_dalt - min_dalt;
|
qalt = max_dalt - min_dalt;
|
||||||
resetAlt();
|
resetAlt();
|
||||||
}
|
}
|
||||||
|
if (display > 0) {
|
||||||
|
g.reset().setFont("Vector", 31)
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.fillRect(0, wi, 176, 176)
|
||||||
|
.setColor(0,0,0)
|
||||||
|
.drawString(msg, 3, 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug > 0)
|
if (debug > 0)
|
||||||
print(fix);
|
print(fix);
|
||||||
setTimeout(updateGps, 1000);
|
setTimeout(updateGps, 1000);
|
||||||
|
@ -184,7 +297,7 @@ function drawSats(sats) {
|
||||||
|
|
||||||
var sats = [];
|
var sats = [];
|
||||||
var snum = 0;
|
var snum = 0;
|
||||||
//var sats_receiving = 0;
|
var sats_used = 0;
|
||||||
|
|
||||||
function parseRaw(msg, lost) {
|
function parseRaw(msg, lost) {
|
||||||
if (lost)
|
if (lost)
|
||||||
|
@ -199,6 +312,7 @@ function parseRaw(msg, lost) {
|
||||||
if (s[2] == "1") {
|
if (s[2] == "1") {
|
||||||
snum = 0;
|
snum = 0;
|
||||||
sats = [];
|
sats = [];
|
||||||
|
sats_used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let view = 1 * s[3];
|
let view = 1 * s[3];
|
||||||
|
@ -217,6 +331,8 @@ function parseRaw(msg, lost) {
|
||||||
sat.ele = 1*s[i++];
|
sat.ele = 1*s[i++];
|
||||||
sat.azi = 1*s[i++];
|
sat.azi = 1*s[i++];
|
||||||
sat.snr = s[i++];
|
sat.snr = s[i++];
|
||||||
|
if (sat.snr != "")
|
||||||
|
sats_used++;
|
||||||
if (debug > 0)
|
if (debug > 0)
|
||||||
print(" ", sat);
|
print(" ", sat);
|
||||||
sats[snum++] = sat;
|
sats[snum++] = sat;
|
||||||
|
@ -231,30 +347,80 @@ function parseRaw(msg, lost) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopGps() {
|
|
||||||
cancel_gps=true;
|
|
||||||
Bangle.setGPSPower(0, "skyspy");
|
|
||||||
}
|
|
||||||
|
|
||||||
function markGps() {
|
function markGps() {
|
||||||
cancel_gps = false;
|
|
||||||
Bangle.setGPSPower(1, "skyspy");
|
Bangle.setGPSPower(1, "skyspy");
|
||||||
Bangle.on('GPS-raw', parseRaw);
|
Bangle.on('GPS-raw', parseRaw);
|
||||||
gps_start = getTime();
|
gps_start = getTime();
|
||||||
updateGps();
|
updateGps();
|
||||||
}
|
}
|
||||||
|
function drawMsg(msg) {
|
||||||
function onSwipe(dir) {
|
g.reset().setFont("Vector", 35)
|
||||||
display = display + 1;
|
.setColor(1,1,1)
|
||||||
if (display == 3)
|
.fillRect(0, wi, 176, 176)
|
||||||
display = 0;
|
.setColor(0,0,0)
|
||||||
|
.drawString(msg, 5, 30);
|
||||||
|
}
|
||||||
|
function drawBusy() {
|
||||||
|
drawMsg("\n.oO busy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var numScreens = 3;
|
||||||
|
|
||||||
|
function nextScreen() {
|
||||||
|
display = display + 1;
|
||||||
|
if (display == numScreens)
|
||||||
|
display = 0;
|
||||||
|
drawBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevScreen() {
|
||||||
|
display = display - 1;
|
||||||
|
if (display < 0)
|
||||||
|
display = numScreens - 1;
|
||||||
|
drawBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSwipe(dir) {
|
||||||
|
nextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
var last_b = 0;
|
||||||
|
function touchHandler(d) {
|
||||||
|
let x = Math.floor(d.x);
|
||||||
|
let y = Math.floor(d.y);
|
||||||
|
|
||||||
|
if (d.b != 1 || last_b != 0) {
|
||||||
|
last_b = d.b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_b = d.b;
|
||||||
|
|
||||||
|
if ((x<h/2) && (y<w/2)) {
|
||||||
|
drawMsg("Clock\nadjust");
|
||||||
|
adj_time = 1;
|
||||||
|
}
|
||||||
|
if ((x>h/2) && (y<w/2)) {
|
||||||
|
drawMsg("Alt\nadjust");
|
||||||
|
adj_alt = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((x<h/2) && (y>w/2))
|
||||||
|
prevScreen();
|
||||||
|
if ((x>h/2) && (y>w/2))
|
||||||
|
nextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
gps.init();
|
||||||
|
fmt.init();
|
||||||
|
|
||||||
|
Bangle.on("drag", touchHandler);
|
||||||
Bangle.setUI({
|
Bangle.setUI({
|
||||||
mode : "custom",
|
mode : "custom",
|
||||||
swipe : onSwipe,
|
swipe : onSwipe,
|
||||||
clock : 0
|
clock : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
drawBusy();
|
||||||
markGps();
|
markGps();
|
||||||
|
|
|
@ -10,4 +10,5 @@
|
||||||
0.08: Stability improvements - ensure we continue even if a flat string can't be allocated
|
0.08: Stability improvements - ensure we continue even if a flat string can't be allocated
|
||||||
Stop ClockInfo text drawing outside the allocated area
|
Stop ClockInfo text drawing outside the allocated area
|
||||||
0.09: Use clock_info module as an app
|
0.09: Use clock_info module as an app
|
||||||
0.10: Option to hide widgets, tweak top widget width to avoid overlap with hour text at 9am
|
0.10: Option to hide widgets, tweak top widget width to avoid overlap with hour text at 9am
|
||||||
|
0.11: Avoid rendering clkinfo in the same colour as the background
|
||||||
|
|
|
@ -86,6 +86,7 @@ let draw = function() {
|
||||||
|
|
||||||
let isAnimIn = true;
|
let isAnimIn = true;
|
||||||
let animInterval;
|
let animInterval;
|
||||||
|
let minuteX;
|
||||||
// Draw *just* the minute image
|
// Draw *just* the minute image
|
||||||
let drawMinute = function() {
|
let drawMinute = function() {
|
||||||
var yo = slopeBorder + offsy + y - 2*slope*minuteX/R.w;
|
var yo = slopeBorder + offsy + y - 2*slope*minuteX/R.w;
|
||||||
|
@ -128,9 +129,9 @@ let clockInfoDraw = (itm, info, options) => {
|
||||||
let texty = options.y+41;
|
let texty = options.y+41;
|
||||||
// set a cliprect to stop us drawing outside our box
|
// set a cliprect to stop us drawing outside our box
|
||||||
g.reset().setClipRect(options.x, options.y, options.x+options.w-1, options.y+options.h-1);
|
g.reset().setClipRect(options.x, options.y, options.x+options.w-1, options.y+options.h-1);
|
||||||
g.setFont("6x15").setBgColor(options.bg).setColor(options.fg).clearRect(options.x, texty-15, options.x+options.w-2, texty);
|
g.setFont("6x15").setBgColor(options.bg).clearRect(options.x, texty-15, options.x+options.w-2, texty);
|
||||||
|
|
||||||
if (options.focus) g.setColor(options.hl);
|
g.setColor(options.focus ? options.hl : options.fg);
|
||||||
if (options.x < g.getWidth()/2) { // left align
|
if (options.x < g.getWidth()/2) { // left align
|
||||||
let x = options.x+2;
|
let x = options.x+2;
|
||||||
if (info.img) g.clearRect(x, options.y, x+23, options.y+23).drawImage(info.img, x, options.y);
|
if (info.img) g.clearRect(x, options.y, x+23, options.y+23).drawImage(info.img, x, options.y);
|
||||||
|
@ -150,7 +151,7 @@ let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { // t
|
||||||
});
|
});
|
||||||
let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { // bottom left
|
let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { // bottom left
|
||||||
app:"slopeclockpp",x:0, y:115, w:50, h:40,
|
app:"slopeclockpp",x:0, y:115, w:50, h:40,
|
||||||
draw : clockInfoDraw, bg : bgColor, fg : g.theme.bg, hl : (bgColor=="#000")?"#f00"/*red*/:g.theme.fg
|
draw : clockInfoDraw, bg : bgColor, fg : g.theme.bg, hl : (g.theme.fg===g.toColor(bgColor))?"#f00"/*red*/:g.theme.fg
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
// Show launcher when middle button pressed
|
||||||
|
@ -175,4 +176,4 @@ Bangle.loadWidgets();
|
||||||
if (settings.hideWidgets) require("widget_utils").swipeOn();
|
if (settings.hideWidgets) require("widget_utils").swipeOn();
|
||||||
else setTimeout(Bangle.drawWidgets,0);
|
else setTimeout(Bangle.drawWidgets,0);
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "slopeclockpp",
|
{ "id": "slopeclockpp",
|
||||||
"name": "Slope Clock ++",
|
"name": "Slope Clock ++",
|
||||||
"version":"0.10",
|
"version":"0.11",
|
||||||
"description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows extra information and allows the colors to be selected.",
|
"description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows extra information and allows the colors to be selected.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
0.07: Update clock_info to avoid a redraw
|
0.07: Update clock_info to avoid a redraw
|
||||||
0.08: Timer ClockInfo now updates once a minute
|
0.08: Timer ClockInfo now updates once a minute
|
||||||
0.09: Timer ClockInfo resets to timer menu when blurred
|
0.09: Timer ClockInfo resets to timer menu when blurred
|
||||||
|
0.10: Timer ClockInfo now uses +- icons, and changes timer from 'T-5 min' to just '5 min' to aid readability
|
|
@ -28,7 +28,7 @@
|
||||||
var min = getAlarmMinutes();
|
var min = getAlarmMinutes();
|
||||||
if(min < 0)
|
if(min < 0)
|
||||||
return "OFF";
|
return "OFF";
|
||||||
return "T-" + String(min)+ " min";
|
return min + " min";
|
||||||
}
|
}
|
||||||
|
|
||||||
function increaseAlarm(t){
|
function increaseAlarm(t){
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
offsets.forEach((o, i) => {
|
offsets.forEach((o, i) => {
|
||||||
smpltmrItems.items = smpltmrItems.items.concat({
|
smpltmrItems.items = smpltmrItems.items.concat({
|
||||||
name: null,
|
name: null,
|
||||||
get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: smpltmrItems.img }),
|
get: () => ({ text: (o > 0 ? "+" : "") + o + " min", img: (o>0)?atob("GBiBAAB+AAB+AAAYAAAYAAB+AA3/sA+B8A4AcAwAMBgYGBgYGDAYDDAYDDH/jDH/jDAYDDAYDBgYGBgYGAwAMA4AcAeB4AH/gAB+AA=="):atob("GBiBAAB+AAB+AAAYAAAYAAB+AA3/sA+B8A4AcAwAMBgAGBgAGDAADDAADDH/jDH/jDAADDAADBgAGBgAGAwAMA4AcAeB4AH/gAB+AA==") }),
|
||||||
show: function() { },
|
show: function() { },
|
||||||
hide: function() { },
|
hide: function() { },
|
||||||
blur: restoreMainItem,
|
blur: restoreMainItem,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "smpltmr",
|
"id": "smpltmr",
|
||||||
"name": "Simple Timer",
|
"name": "Simple Timer",
|
||||||
"shortName": "Simple Timer",
|
"shortName": "Simple Timer",
|
||||||
"version": "0.09",
|
"version": "0.10",
|
||||||
"description": "A very simple app to start a timer.",
|
"description": "A very simple app to start a timer.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,timer,clkinfo",
|
"tags": "tool,alarm,timer,clkinfo",
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Adjusted main font size to fit nicely even at 8pm, minor tweaks
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{ // must be inside our own scope here so that when we are unloaded everything disappears
|
{ // must be inside our own scope here so that when we are unloaded everything disappears
|
||||||
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are globalj
|
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
||||||
let removeHasNotRun = true;
|
let removeHasNotRun = true;
|
||||||
let drawTimeout;
|
let drawTimeout;
|
||||||
|
|
||||||
|
@ -37,19 +37,19 @@ let drawSplashScreen = function (frame, total) {
|
||||||
|
|
||||||
// for fast startup-feeling, draw the splash screen once directly once
|
// for fast startup-feeling, draw the splash screen once directly once
|
||||||
g.clear()
|
g.clear()
|
||||||
drawSplashScreen(0, 20);
|
drawSplashScreen(0, 15);
|
||||||
|
|
||||||
let splashScreen = function () {
|
let splashScreen = function () {
|
||||||
g.clearRect(R.x,R.y, R.x2, R.y2);
|
g.clearRect(R.x,R.y, R.x2, R.y2);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let frame = 0;
|
let frame = 0;
|
||||||
function tick() {
|
function tick() {
|
||||||
if (removeHasNotRun) drawSplashScreen(frame, 20);
|
if (removeHasNotRun) drawSplashScreen(frame, 15);
|
||||||
frame += 1;
|
frame += 1;
|
||||||
if (!removeHasNotRun) {
|
if (!removeHasNotRun) {
|
||||||
reject();
|
reject();
|
||||||
} else if (frame < 20) {
|
} else if (frame < 15) {
|
||||||
setTimeout(tick, 50);
|
setTimeout(tick, 30);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
|
@ -62,13 +62,13 @@ let splashScreen = function () {
|
||||||
Graphics.prototype.setFontPlayfairDisplay = function() {
|
Graphics.prototype.setFontPlayfairDisplay = function() {
|
||||||
// https://www.espruino.com/Font+Converter
|
// https://www.espruino.com/Font+Converter
|
||||||
// <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap" rel="stylesheet">
|
// <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap" rel="stylesheet">
|
||||||
// 60pt, 2bpp, Numeric
|
// Actual height 58 (61 - 4)
|
||||||
// Actual height 62 (67 - 6)
|
// 1 BPP
|
||||||
return this.setFontCustom(
|
return this.setFontCustom(
|
||||||
E.toString(require('heatshrink').decompress(atob('AD8/A40B/4IGh/8DI/4BA3/8AHFg//wAIFj/+ES8DEQ5FIj4ZGBAKdzhwIHNA8f4BoGUpBwGg/wRQ7HHPA0BFJA6HJY9/FI46GgLWHMiF/Mi8Dbo8MbuYALv6VGg//BA1/BAwQB/51Fn4IBNokBA4P/UAkPBASYEFQIABDI7DEDIY9EDIY9DDIY0EJoICDJoYNCFYgnDAYcDJQUBMAbKDBgcAuADCgw8DBgcYBg0AkAMGgAVDsADCgQiDLYYVDg4ZDCocOBgYiDnwDGNocHNAh/CRYy3Gj6uLcYgZHW4f8BAYrDDIjaDA4Y0DGYjJBbIoIDFQhGDCAoRBCAwAqcYcEBAbNDiDNHoCLDZoUDEQ8MBAccAYVwOAigCOQbaBToylDPYicC//waI4iVegYiDdYYiEdYYiEHgYiECAZFEDobbGRYoADRYgADVwgADVwYAjcYUDaoQAB8ACBjxcEAYX4BAc4DIQUCSgJtCj4iDhwDC/wZGg4iDgIeCn4iDGYS6BEQc8agQiEVQV/EQcDHgMDv4iDh4CBnIiEnwqB84iDgIeBj8fEQcH+AKBEQkf+E/8IiEv7qBg4iEfYQiFBAPgEQoIBNAs/DIIiEgAZCEQkDBAI3BRYn//AiFFYPAEQs/AoIiED4KVBEQg0BwAiFdASuFCYYiEJAYiEPwYHGAFsDAYUOBAcIAYVwBAdgCgRtDgIECjgQDgwDCMgkYVwSHEEQQZBCwQnDUgM4IIkD4AkDvACBh4WDgINBgE8gEMBoYLBEQMwBoY8B4AWCBoTfBwEPEQICCb4MAZ4V+EQX/h/cGwLSCg///98gE/HgUf//9/gMBNYU///nCYLsDAoOfAQJiCeIP8v4IBCAUP//wA4P8BAQrBwYZEJwIyC/6hDDIITBDIZXBwA/BDIZbCGYgRBCYRNDCYYqEDgoAXgYIHd4IAGXwQAEZgIIGYYIqGOAYADj4iH/4iGVAIQGuYiGgePEQ0cdQaVD4AiGhxFHuEPEQsDYAIiFjBOBFQwiGhxXBEQtwgAiFZ4IACCQbyBAAQSCdIIADQAgACGod/EQ0HDIgiCFQgiCFQoiCDIp7GaI5oGH4TRGFwIZHEQ9/EQwZBEQwAcggIHiAIHoA7EwBzBVwn+AgMMaAfAmAFBAQRlCDIMAVwfwgyiCEQQZBjAEBjgiDgHgAoNwEQcDHgQlCEQMOAgMeEQk4AgN4EQ0DEoQiCIQMfDIQiBh5rBXAYiBn7bEEQX/PgYiDfgJ8CEQYIBaQYiCBAJ5CEQYZEEQIpB//4EQkHDIjxCWAhaBEQIrBGYd/LYN/JoYAD/5nDbYiBCAAgQGAFReBHYp4CLwZ6CBAysCDQp6BRQghDAAJ5DXoQABDIaIBWwoqDRYgqDbIoADGgTFCGgoZBD4MHFYYeBKgMBcQQMBgZSCMAUf4EYBAQiCDoNgCwRNC8AZCgEMDIQEDgEgAQN4gE4EQkDKIIwChwCDEgIFBIoQXBh5uBj5RCDIK3CNAQ/CRYpUBSoaLCDgKEDHgSeEQQRUCcYpZCaIzbDTgbKEfog0DA4gICM4QIFFQgAih4pHn6ICAAn/VwReFEQ4ZHn5uFEQTCBESIMEEQd/TwYiCQgIVGYQIVCEQSwCLYQiCaYR1CEQLKFEQSFBEQzkCLYQiBaQRFFfwoiBAIPwgxoEj7iFEQJ7G//HV4ogBnzRHYA0/IIbRMCA8PZA8/A40ACA4AUvDlKAAl/BAcHAgKlBRgYNCcIiBBX4a+Cj4WBX4ThCv4WBDILhEQQICBGgQZBVwLQEDIP/z/gv6XBgLwCBwMfFYK1BAAIWBjySCFAk4AQPgIwQFBsA8BCQQoCEQMMBARdBgQTBkA+CBwMIBAINBGgYOBEQJHBMwQiDQgcGH4aBBBAMYBAJCBGgIDBH4MD/h7C/EPEQKZCMQQtCLwSFCRYnhPgS3CAgKoCVwi/DPgQFB8CXCDIQFBXQbrCj4VBSwiyDRoYAEv4zCBApnBAAp4CAExZCAApeECAgIGSwJWFSYTJBAAbADDIyXBMQYZCQQVgDIg0BgKRBDIY0BhwHBgIQCFYMwJogrBgKnCn4DBv+Ag48CIQU+gE8HgQuCnEAWAUcCgXgg4NCJARDBDIRIDg0B+D+CDIUwh48CEQfARod8DIUPQgaRCnL+DQQJrCDIZoDTwk/BAYZCRYYZERYf/JoSuE/5bCFYjSEW4aBCGgraHcZAqDBAYQFEYQHFAHh2Cj5XDg5UBS4JVETIMHUocARAU/NIcHO4SUEj4WBWILIECwKxBZAgiCW4YiIh4iDJwZFIv4NBh7rDNAiRkA=='))),
|
E.toString(require('heatshrink').decompress(atob('ADv8AwsB/4PG/+AA43gA4t/+AHFn/4A4sfGA0P/wHFg44GIBF/IA0fPL8OA40/PI5IGMA0HPA0f4BXNO40DR40PU40/Ew5FWEw5FNgJFGgAmGAEbpBJYr5BMYoHB/5UEh4HBDAgHCKogHCMogHCEAgnCEAgHDNwYHDIIcDA4QoDA4OALQK6GeggkCgYwDuADGmADCjAHGhgLGgQLGgIDCgxtDNIUDA4ZACgJEDsBAKngDCTQZdCfAkPOwMfQJZ+BWQ1/BAQHDn4HGYQYHDFAbKDFAbzEEAQGDEAYHEDAI/EDARXDADKiDhAvDIoUEVwzKDVwa+EnAbFgEeB4TGDj5xC8CJG+AHCg4HCYQaRDNQa6IA4SKEA4ZADZQZADZQZADA4ZADKAZADAAccA40GA40BUw7jEAC8gAQMMA4cwAQMOA40PA444DYQUPPIYHDPIc8A4R5DNoUPPIY0Ch66DGgUPXQcHGgLdBaQY0Bhy6DgI0Bj1/IAUBFgM+n5ADEgM/j5ADv/B/5AEZQUHIAbKCwZADHoP/wJAEfIRAEA4RADgAHB4BAEv4HBIAgwB4BAEGAPAIAgwB4EDIAYgB4AzBA4aSBA4L7FDQQHEB4JADACkCeYoiBdYoHBHIQPDgA5CB4cAsAHGCgQHEjACBjkAhgEDcALqBAgICCOAMHAgYFBwEDHoIKCgaIBA4QCBgfggJFBg4CBgPwAIIMCDAP8gK4BA4d/wfuC4LLCn//++Ag7LCaQP7/EfA4UH//5doLTCW4P8A4i3B/giBbYYEBEQIHDn/+eoIHDj/+EQLrDg/+EQIHDgIUBv77EnhLCbApLBA4oaCABqjCA4rhCA4iQCA4iQCHAiICA4iACA4hAGgxQGh1/IAsOn5AFh0fIAsMh5AFjhAGjkDIAscQI0YYoIHEnCqBIAgHBIArhBdYgHEFIbIBAAQHHFIQHEFIQGD/5qCA4hqCC4hqFQI0AQIzCIQIzCIg7CGgJXDACsBBA4hDgZXCoAGCMwdgPIX8YYMAmB5C4EIZwaxB8EMewR+C+EGAgMOPwX4g5jCAQX8gY9BA4UD/0BW4MHBQJuBgBIBgbCDwFwKYhABngXBA4RABj4HBWYRABh/gXYZACaQhACA4K7CIAQHEIAQHEIAU//7LDIAMfA4vgGAIHD//wGAIPEHgIHEAAU/eY43DAAcgeS4AMJ4KHCAARfFIwR4BXARZCTAhpCAAIYEA4SMBA4zJCU4QABHIQHEVIgoGA4YoDIwQbBJIX/+IEBfQbBBBgQwCv8AA4UYM4UAoAEBhgCBn0AsAEBgQCBj0AmD/CA4ccKoRABh0Ah4PCFYIFBHoQCDBgJxEEQJyDA4QiBA4SiBFQSyCVQQqBYQKJDJwLSBA4b+BFATTFA44oBA4ogBWIYYDcQj8CUAQARH4IWGdAYADv4PGn4HGj4XGh5GGg5WGgZmDBYRABBYX/UAJABAYMPJgd/Z4RzDIASsBEARACSYhACYgRAEA4QsBIAV/A4ZACA4Q0BIATsCJARABRYpABBoZADaIpABQQsH/qSFOoKiFIAI8CYQguEIATbGn4HGjz4TACsPdo0fOQYMCYIJTCBgQHBNYQEB4ACBQYQEB+DZFAgLpBaIQECAQT+D//PZIcHAgP38EHHgKgBCod4A4glBjioBaAIdCgwKBIwQdCA4NgDIJOCEQMgXQJvCoEAjCyBGAQNBhhKCN4MQFQQgBM4IVBgYUBIIQVBgPwdYIYBhzpC//vDALeCFwPzLYhPCNYg2B+ASBMYRXCRgQHBFAP4PgS6Cv5nBA4kPA4IgBA4UDP4JiDAAZSBA4ojBF4QASg6iCAAZjBL4IADH4I7BA4qaBA4qACA4pAEA4RQBmAvDbgS7BA4YoBXYJvCFAS7CA4eAg5XC/+DGAIHCgYtBg/gcIUBHoXwgYEBA4c4A4UAAQUMgKbCsACBgwkCKYcCW4UAjgzCA4cPGYMDZ4TLDge/A4TICKYKTDMAQHEv4HGQIQHEPIYHDgYHC/yqDB4yyDA4ggCA4ggCegpBBdYpXBbQgAon50CToIHCGwMfIIc/AgMfKIYECh55Dj5mBKQJwDBgJrBFAQMCXoJiCBgbFBTIYMBv44Dv4MBIAkfA4MPA4cHA4MBSQoAE'))),
|
||||||
46,
|
46,
|
||||||
atob("ExouGyYjJiEoISgoFQ=="),
|
atob("EBYpGCEfIh4lHyQjEQ=="),
|
||||||
70|65536
|
65|65536
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,11 +95,10 @@ let draw = function() {
|
||||||
g.setColor(g.theme.fg)
|
g.setColor(g.theme.fg)
|
||||||
// Time
|
// Time
|
||||||
const yt = R.y + 92 - 20 - 30 + 6 + 10;
|
const yt = R.y + 92 - 20 - 30 + 6 + 10;
|
||||||
const xt = R.w/2 - 5;
|
const xt = R.w/2;
|
||||||
let hours = date.getHours()+'';
|
let hours = date.getHours()+'';
|
||||||
g.setFontAlign(1, 0).setFontPlayfairDisplay().drawString(hours, xt - 8, yt);
|
|
||||||
g.setFontAlign(0, 0).setFontPlayfairDisplay().drawString(':', xt, yt);
|
g.setFontAlign(0, 0).setFontPlayfairDisplay().drawString(hours + ':' + minutes, xt, yt);
|
||||||
g.setFontAlign(-1, 0).setFontPlayfairDisplay().drawString(minutes, xt + 8, yt);
|
|
||||||
// logo
|
// logo
|
||||||
g.drawImage(supaClockImg, R.x2 - supaClockImg.width - 2, R.y + 2);
|
g.drawImage(supaClockImg, R.x2 - supaClockImg.width - 2, R.y + 2);
|
||||||
// dow + date
|
// dow + date
|
||||||
|
@ -174,16 +173,15 @@ splashScreen().then(() => {
|
||||||
draw();
|
draw();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
// Allocate and draw clockinfos
|
// Allocate and draw clockinfos
|
||||||
g.setFontAlign(1, 1).setFont('6x8').drawString('Loading Clock Info Modules...', R.x + 10, upperCI);
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// delay loading of clock info, so that the clock face appears quicker
|
// delay loading of clock info, so that the clock face appears quicker
|
||||||
g.clearRect(R.x, upperCI, R.x2, upperCI+10); // clear loading text
|
g.clearRect(R.x, upperCI, R.x2, upperCI+10); // clear loading text
|
||||||
try {
|
try {
|
||||||
clockInfoItems = require("clock_info").load();
|
clockInfoItems = require("clock_info").load();
|
||||||
clockInfoMenu1 = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:R.x+1, y:upperCI, w:midX-2, h:28, draw : clockInfoDraw});
|
clockInfoMenu1 = require("clock_info").addInteractive(clockInfoItems, { app:"supaclk", x:R.x+1, y:upperCI, w:midX-2, h:28, draw : clockInfoDraw});
|
||||||
clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:midX+1, y:upperCI, w:midX-2, h:28, draw : clockInfoDrawR});
|
clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { app:"supaclk", x:midX+1, y:upperCI, w:midX-2, h:28, draw : clockInfoDrawR});
|
||||||
clockInfoMenu3 = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:R.x+1, y:lowerCI, w:midX-2, h:28, draw : clockInfoDraw});
|
clockInfoMenu3 = require("clock_info").addInteractive(clockInfoItems, { app:"supaclk", x:R.x+1, y:lowerCI, w:midX-2, h:28, draw : clockInfoDraw});
|
||||||
clockInfoMenu4 = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:midX+1, y:lowerCI, w:midX-2, h:28, draw : clockInfoDrawR});
|
clockInfoMenu4 = require("clock_info").addInteractive(clockInfoItems, { app:"supaclk", x:midX+1, y:lowerCI, w:midX-2, h:28, draw : clockInfoDrawR});
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
if ((err + '').includes('Module "clock_info" not found' )) {
|
if ((err + '').includes('Module "clock_info" not found' )) {
|
||||||
g.setFont('6x8').drawString('Please install\nclockinfo module!', R.x + 10, upperCI);
|
g.setFont('6x8').drawString('Please install\nclockinfo module!', R.x + 10, upperCI);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "supaclk",
|
{ "id": "supaclk",
|
||||||
"name": "SUPACLOCK Pro ULTRA",
|
"name": "SUPACLOCK Pro ULTRA",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "SUPACLOCK Pro ULTRA, with four ClockInfo areas at the bottom. Tap them and swipe up/down and left/right to toggle between different information.",
|
"description": "SUPACLOCK Pro ULTRA, with four ClockInfo areas at the bottom. Tap them and swipe up/down and left/right to toggle between different information.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"}],
|
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"}],
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: New Clock!
|
0.01: New Clock!
|
||||||
|
0.02: Clockinfos now save under correct name, and wrap correctly to >1 line
|
|
@ -134,8 +134,8 @@ for (var i=0;i<10;i++)
|
||||||
if (g.stringWidth(txt) > options.w) // if too big, smaller font
|
if (g.stringWidth(txt) > options.w) // if too big, smaller font
|
||||||
g.setFont("LECO1976Regular14");
|
g.setFont("LECO1976Regular14");
|
||||||
if (g.stringWidth(txt) > options.w) {// if still too big, split to 2 lines
|
if (g.stringWidth(txt) > options.w) {// if still too big, split to 2 lines
|
||||||
var l = g.wrapString(txt, options.w);
|
var l = g.wrapString(txt, options.w-4);
|
||||||
txt = l.slice(0,2).join("\n") + (l.length>2)?"...":"";
|
txt = l.slice(0,2).join("\n") + ((l.length>2)?"...":"");
|
||||||
}
|
}
|
||||||
var x = options.x+options.w/2, y = options.y+54;
|
var x = options.x+options.w/2, y = options.y+54;
|
||||||
g.setColor(g.theme.bg).drawString(txt, x-2, y). // draw the text background
|
g.setColor(g.theme.bg).drawString(txt, x-2, y). // draw the text background
|
||||||
|
@ -147,12 +147,12 @@ for (var i=0;i<10;i++)
|
||||||
};
|
};
|
||||||
|
|
||||||
clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, {
|
clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, {
|
||||||
app:"pebblepp",
|
app:"twotwoclock",
|
||||||
x : g.getWidth()-clockInfoW, y: 0, w: clockInfoW, h:clockInfoH,
|
x : g.getWidth()-clockInfoW, y: 0, w: clockInfoW, h:clockInfoH,
|
||||||
draw : clockInfoDraw
|
draw : clockInfoDraw
|
||||||
});
|
});
|
||||||
clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
|
clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
|
||||||
app:"pebblepp",
|
app:"twotwoclock",
|
||||||
x : g.getWidth()-clockInfoW, y: clockInfoH, w: clockInfoW, h:clockInfoH,
|
x : g.getWidth()-clockInfoW, y: clockInfoH, w: clockInfoW, h:clockInfoH,
|
||||||
draw : clockInfoDraw
|
draw : clockInfoDraw
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "twotwoclock",
|
{ "id": "twotwoclock",
|
||||||
"name": "TwoTwo Clock",
|
"name": "TwoTwo Clock",
|
||||||
"shortName":"22 Clock",
|
"shortName":"22 Clock",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "A clock with the time split over two lines, with custom backgrounds and two ClockInfos",
|
"description": "A clock with the time split over two lines, with custom backgrounds and two ClockInfos",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
2
core
|
@ -1 +1 @@
|
||||||
Subproject commit 1cdcb3405f78ef35f231b9c3df501721bda75525
|
Subproject commit 4f07b72ce2bdac4a8da6bfa3da3e2152370446fc
|
|
@ -16,7 +16,7 @@ if (window.location.host=="banglejs.com") {
|
||||||
'This is not the official Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.';
|
'This is not the official Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.';
|
||||||
}
|
}
|
||||||
|
|
||||||
var RECOMMENDED_VERSION = "2v23";
|
var RECOMMENDED_VERSION = "2v24";
|
||||||
// could check http://www.espruino.com/json/BANGLEJS.json for this
|
// could check http://www.espruino.com/json/BANGLEJS.json for this
|
||||||
|
|
||||||
// We're only interested in Bangles
|
// We're only interested in Bangles
|
||||||
|
|
|
@ -90,19 +90,17 @@ exports.swipeOn = function(autohide) {
|
||||||
|
|
||||||
function queueDraw() {
|
function queueDraw() {
|
||||||
const o = exports.offset;
|
const o = exports.offset;
|
||||||
|
Bangle.appRect.y = o+24;
|
||||||
|
Bangle.appRect.h = 1 + Bangle.appRect.y2 - Bangle.appRect.y;
|
||||||
if (o>-24) {
|
if (o>-24) {
|
||||||
Bangle.appRect.y = o+24;
|
Bangle.setLCDOverlay(og, 0, o, {
|
||||||
Bangle.appRect.h = 1 + Bangle.appRect.y2 - Bangle.appRect.y;
|
id:"widget_utils",
|
||||||
if (o>-24) {
|
remove:()=>{
|
||||||
Bangle.setLCDOverlay(og, 0, o, {
|
require("widget_utils").cleanupOverlay();
|
||||||
id:"widget_utils",
|
}
|
||||||
remove:()=>{
|
});
|
||||||
require("widget_utils").cleanupOverlay();
|
} else {
|
||||||
}
|
Bangle.setLCDOverlay(undefined, {id: "widget_utils"});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Bangle.setLCDOverlay(undefined, {id: "widget_utils"});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|