pull/3528/head
Rob Pilling 2024-08-19 07:38:38 +01:00
commit f9663e3e26
62 changed files with 1385 additions and 222 deletions

BIN
apps/8ball/8ball.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

1
apps/8ball/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

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

@ -0,0 +1 @@
atob("MDCBAAAAAAAAAAAAAAAAAAAAH/AAAAAAf/4AAAAB4AeAAAAHgAHgAAAOAABwAAAcAAA4AAA4AMAcAABwAMAOAABgA/AGAADAAeADAAHAAMADgAGAAAABgAGAAAABgAMAAAAAwAMBAAAAwAMDAAAAwAMHwAAAwAMHwAAAwAMDAACAwAMBAADAwAMAAAPgwAMAAAPgwAMAAADAwAGAAACBgAGAAAABgAHAAAADgADAAAADAADgAAAHAAB////+AAB////+AABgAAAGAABgAAAGAABgAAAGAADAAAADAADAAAADAADAAAADAAGAAAABgAGAAAABgAH/////gAP/////wAYAAAAAYAYAAAAAYAf/////4AP/////wAAAAAAAAAAAAAAAAA==")

92
apps/8ball/app.js Normal file
View File

@ -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(); });

19
apps/8ball/metadata.json Normal file
View File

@ -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}
]
}

BIN
apps/8ball/screenshot-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
apps/8ball/screenshot-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
apps/8ball/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -3,3 +3,4 @@
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.05: Fixes step count not resetting after a new day starts
0.06 Added clockbackground app functionality

View File

@ -5,11 +5,11 @@
* ---------------------------------------------------------------
*/
let background = require("clockbg");
let storage = require("Storage");
let locale = require("locale");
let widgets = require("widget_utils");
let date = new Date();
let bgImage;
let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0;
let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.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);
boxKeys.forEach((key) => {
@ -224,9 +216,7 @@
return function(boxes) {
date = new Date();
g.clear();
if (bgImage) {
g.drawImage(bgImage, 0, 0);
}
background.fillRect(Bangle.appRect);
if (boxes.time) {
boxes.time.string = modString(boxes.time, locale.time(date, isBool(boxes.time.short, true) ? 1 : 0));
updatePerMinute = isBool(boxes.time.short, true);

Binary file not shown.

View File

@ -4,6 +4,7 @@
"version": "0.05",
"description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background",
"icon": "app.png",
"dependencies" : { "clockbg":"module" },
"screenshots": [
{"url":"screenshot.png"},
{"url":"screenshot-1.png"},

1
apps/dedreckon/ChangeLog Normal file
View File

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

20
apps/dedreckon/README.md Normal file
View File

@ -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.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwhHXAH4A/AH4A/AFsAFtoADF1wwqF4wwhEI5goGGIjFYN4wFF1KbHGUolIMc4lGSdIwJd9DstAH7FrBywwgad4veDwojJBIIvcFwIACGBYICGDYvEGBYvdFwqyLL8i+LF7oxFRxgveGAQ0EF5IwfMY4vpL5AFLAEYv/F8owoE44vrAY4vmAQIEEF85dGGE0AE4gvoFwpmHd0oINAH4A/AH4AvA"))

BIN
apps/dedreckon/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -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();
}

View File

@ -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}
]
}

View File

@ -1,3 +1,4 @@
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

View File

@ -1,5 +1,5 @@
const w = require("weather");
//const locale = require("locale");
const locale = require("locale");
// Weather icons from https://icons8.com/icon/set/weather/color
function getSun() {
@ -67,6 +67,33 @@ function chooseIcon(condition) {
return getPartSun;
} 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) {
condition = condition.toLowerCase();
if (condition.includes("thunderstorm") ||
@ -143,8 +170,17 @@ const clock = new ClockFace({
//let cWea =(curr === "no data" ? "no data" : curr.txt);
let cTemp= (curr === "no data" ? 273 : curr.temp);
// 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.drawString(format(hour), this.centerTimeScaleX, this.center.y - 31 * this.scale);

View File

@ -1,7 +1,7 @@
{
"id": "ffcniftyapp",
"name": "Nifty-A Clock ++",
"version": "0.01",
"version": "0.02",
"description": "A nifty clock with time and date and more",
"dependencies": {"weather":"app"},
"icon": "app.png",

View File

@ -106,6 +106,7 @@ function onInit(device) {
else if (crcs[0] == 3816337552) version = "2v21";
else if (crcs[0] == 3329616485) version = "2v22";
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
var crc = crcs[1];
if (crc==1339551013) { version = "2v10.219"; ok = false; }

View File

@ -660,11 +660,11 @@ class Status {
towards = next_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;
c = this.projected_point.coordinates(
const c = this.projected_point.coordinates(
this.displayed_position,
this.adjusted_cos_direction,
this.adjusted_sin_direction,
@ -1394,7 +1394,7 @@ function ask_options(fn) {
g.flip();
function options_select(b, xy) {
end = false;
let end = false;
if (xy.y < 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);

View File

@ -1 +1,2 @@
0.01: attempt to import
0.02: implement colors and lines

View File

@ -3,5 +3,18 @@
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
watch to view the results. Draw using the provided tools, then press
the button, and you'll get result on the console.
watch to view the results.
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -10,14 +10,16 @@
let kule = [0, 0, 0]; // R, G, B
var font_height = 22, font_width = 8;
var zoom_x = 64, zoom_y = 24, zoom_f = 6;
var color = true;
let oldLock = false;
let sg = null;
const top_bar = 20;
function clear(m) {
sg.setColor(1,1,1).fillRect(0,0, font_width, font_height);
}
function setup(m) {
function __setup(m) {
mode = m;
switch (m) {
case 'font':
@ -37,19 +39,32 @@
zoom_f = 2;
break;
}
}
function setup(m) {
__setup(m);
sg = Graphics.createArrayBuffer(font_width, font_height, 8, {});
clear();
}
function icon_big() {
zoom_x = 16;
zoom_y = 25;
zoom_f = 3;
}
function icon_small() {
__setup("icon");
}
function updateLock() {
if (oldLock) {
return;
}
g.setColor('#fff');
g.fillRect(0, 0, g.getWidth(), 20);
g.setFont('6x8', 2);
g.setFont('Vector', 22);
g.setColor('#000');
g.drawString('PLEASE UNLOCK', 10, 2);
g.drawString('PLEASE\nUNLOCK', 10, 2);
oldLock = true;
}
Bangle.on("lock", function() {
@ -60,17 +75,20 @@ Bangle.on("lock", function() {
drawUtil();
}
});
function nextColor () {
function nextColor() {
kule[0] = Math.random();
kule[1] = Math.random();
kule[2] = Math.random();
}
function selectColor (x) {
let c;
function selectColor(x) {
if (color) {
let i = Math.floor((x - 32) / 4);
kule = toColor(i);
return;
}
let c = 255;
if (x < g.getWidth()/2) {
c = 0;
} else {
c = 255;
}
kule[0] = c;
kule[1] = c;
@ -79,8 +97,8 @@ Bangle.on("lock", function() {
function nextPen () {
switch (pen) {
case 'circle': pen = 'pixel'; break;
case 'pixel': pen = 'crayon'; break;
case 'crayon': pen = 'square'; break;
case 'pixel': pen = 'line'; break;
case 'line': pen = 'square'; break;
case 'square': pen = 'circle'; 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);
}
var oldX = -1;
var oldY = -1;
var oldX = -1, oldY = -1;
var line_from = null;
function drawBrushIcon () {
const w = g.getWidth();
@ -110,13 +128,17 @@ Bangle.on("lock", function() {
g.drawLine(w - 14, 6, w - 10, 12);
g.drawLine(w - 6, 6, w - 10, 12);
break;
case 'line':
g.drawLine(w - 5, 5, w - 15, 15);
break;
}
}
function drawArea () {
function drawArea() {
g.clear();
if (mode == "draw")
return;
const w = g.getWidth;
g.setColor(0, 0, 0.5);
g.fillRect(0, 0, g.getWidth(), g.getHeight());
g.setColor(1, 1, 1);
@ -129,13 +151,28 @@ Bangle.on("lock", function() {
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()) {
updateLock();
}
// titlebar
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
g.setColor('#000'); // black
g.fillCircle(10, 10, 8, 8);
@ -149,7 +186,7 @@ Bangle.on("lock", function() {
drawBrushIcon();
}
function transform (p) {
function transform(p) {
if (p.x < zoom_x || p.y < zoom_y)
return p;
p.x = ((p.x - zoom_x) / zoom_f);
@ -159,8 +196,12 @@ Bangle.on("lock", function() {
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) {
case 'line':
case 'pixel':
g.drawLine(from.x, from.y, to.x, to.y);
break;
@ -170,27 +211,25 @@ Bangle.on("lock", function() {
g.drawLine(from.x + 2, from.y + 2, to.x, to.y + 2);
break;
case 'circle':
var XS = (to.x - from.x) / 32;
var YS = (to.y - from.y) / 32;
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;
case 'square':
var XS = (to.x - from.x) / 32;
var YS = (to.y - from.y) / 32;
for (let i = 0; i < 32; i++) {
const posX = from.x + (i * XS);
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;
default:
print("Unkown pen ", pen);
}
}
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 });
}
@ -227,7 +266,7 @@ Bangle.on("lock", function() {
}, 100);
// 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 (tapTimer === null) {
tapTimer = setTimeout(function () {
@ -244,7 +283,7 @@ Bangle.on("lock", function() {
}
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 (tapTimer === null) {
tapTimer = setTimeout(function () {
@ -264,7 +303,7 @@ Bangle.on("lock", function() {
}
drawUtil();
return;
} else if (tap.y < 32) {
} else if (tap.y < top_bar) {
if (mode == "draw")
nextColor();
else
@ -272,20 +311,31 @@ Bangle.on("lock", function() {
drawUtil();
return;
}
oldX = to.x;
oldY = to.y;
sg.setColor(kule[0], kule[1], kule[2]);
g.setColor(kule[0], kule[1], kule[2]);
oldX = to.x;
oldY = to.y;
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();
}
function on_btn(n) {
function dump(n) {
function f(i) {
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);
// 0..black, 65535..white
for (let y = 0; y < font_height; y++) {
@ -296,41 +346,55 @@ Bangle.on("lock", function() {
}
s += f(v);
}
print("Manual bitmap\n");
print('ft("' + s + '");');
if (1) {
s = "";
if (mode == "font")
print('show_font("' + s + '");');
var im = sg.asImage("string");
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)+'");');
//print('show_unc_icon("'+btoa(im)+'");');
print('show_icon("'+btoa(require('heatshrink').compress(im))+'");');
}
}
setup("icon");
drawArea();
Bangle.setUI({
setup("icon");
drawArea();
Bangle.setUI({
"mode": "custom",
"drag": on_drag,
"btn": on_btn,
});
drawUtil();
"btn": dump,
});
drawUtil();
function ft(icon) {
function show_font(icon) {
g.reset().clear();
g.setFont("Vector", 26).drawString("Hellord" + icon, 0, 0);
}
function fi(icon) {
function show_bin_icon(icon) {
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);

View File

@ -1,6 +1,6 @@
{ "id": "iconbits",
"name": "Icon bits",
"version": "0.01",
"version": "0.02",
"description": "Bitmap editor suitable for creating icons",
"icon": "app.png",
"readme": "README.md",

View File

@ -1,3 +1,4 @@
0.01: first release
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.04: add optional date display, and a settings page to configure it

View File

@ -5,6 +5,7 @@ A simple clock with the Lato font, with fast load and clock_info
![](screenshot1.png)
![](screenshot2.png)
![](screenshot3.png)
![](screenshot4.png)
This clock is a Lato version of Simplest++. Simplest++ provided the
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.
* Date display can be enabled and disabled, along with format choice in the app settings
## About Clock Info's
* 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
and discussion please post in the [Bangle JS
Forum](http://forum.espruino.com/microcosms/1424/)
Date functionality added by [Septolum](https://github.com/Septolum)

View File

@ -38,6 +38,11 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
// 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 settings = Object.assign({
dateDisplay: false,
dateFormat: 0,
}, require("Storage").readJSON("lato.json", true) || {});
let draw = function() {
var date = new Date();
var timeStr = require("locale").time(date,1);
@ -53,6 +58,25 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
g.setFontAlign(0, 0);
g.setColor(g.theme.fg);
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
// schedule a draw for the next minute

View File

@ -1,7 +1,7 @@
{
"id": "lato",
"name": "Lato",
"version": "0.03",
"version": "0.04",
"description": "A Lato Font clock with fast load and clock_info",
"readme": "README.md",
"icon": "app.png",
@ -12,6 +12,10 @@
"dependencies" : { "clock_info":"module" },
"storage": [
{"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"}
]
}

BIN
apps/lato/screenshot4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

24
apps/lato/settings.js Normal file
View File

@ -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)
})

View File

@ -196,12 +196,6 @@ module.exports = {
"no-undef"
]
},
"apps/sixths/sixths.app.js": {
"hash": "2a4676828bdf78df052df402de34e6f1abd1c847ebe0d193fc789cd6e9dd0e5c",
"rules": [
"no-undef"
]
},
"apps/scribble/app.js": {
"hash": "6d13abd27bab8009a6bdabe1df2df394bc14aac20c68f67e8f8b085fa6b427cd",
"rules": [
@ -1021,12 +1015,6 @@ module.exports = {
"no-undef"
]
},
"apps/gipy/app.js": {
"hash": "41f342e8ef6f2a87b3aea19b75ee45cfdfeff723b94281049e3ae0ec89cddba5",
"rules": [
"no-undef"
]
},
"apps/geissclk/precompute.js": {
"hash": "2317812a9e348e7883e93a4be9e294ad7accd4dc3f0e31ee00343e2412030f98",
"rules": [
@ -1249,12 +1237,6 @@ module.exports = {
"no-undef"
]
},
"apps/accelrec/app.js": {
"hash": "b5369a60afc8f360f0b33f71080eb3f5d09a1bf3703acfcf07cd80dd19f1997d",
"rules": [
"no-undef"
]
},
"apps/BLEcontroller/app-joy.js": {
"hash": "e4f34bb1bc11b52c3d7a1c537a140b0e23ccef82694dcd602cb517a8ba342898",
"rules": [

View File

@ -1,2 +1,3 @@
0.1: Initial release
0.2: Draw line for 3d effect, fix number alignment
0.3: Fix day-end overflowing hour calculation

View File

@ -78,6 +78,19 @@
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 () {
g.clear();
var d = new Date();
@ -101,12 +114,12 @@
switch (yTopLines - 88 + mins) {
case -60:
lineEnd = lineEndFull;
g.drawString(d.getHours()-1, hourStringXOffset(d.getHours()-1), yTopLines, true);
drawHourString(d.getHours() - 1, yTopLines);
break;
case 0:
case 60:
lineEnd = lineEndFull;
g.drawString(d.getHours(), hourStringXOffset(d.getHours()), yTopLines, true);
drawHourString(d.getHours(), yTopLines);
break;
case 45:
case -45:
@ -136,11 +149,11 @@
case 0:
case 60:
lineEnd = lineEndFull;
g.drawString(d.getHours() + 1, hourStringXOffset(d.getHours()+1), yBottomLines, true);
drawHourString(d.getHours() + 1, yBottomLines);
break;
case 120:
lineEnd = lineEndFull;
g.drawString(d.getHours() + 2, hourStringXOffset(d.getHours()+2), yBottomLines, true);
drawHourString(d.getHours() + 2, yBottomLines);
break;
case 15:
case 75:

View File

@ -1,7 +1,7 @@
{
"id": "measuretime",
"name": "Measure Time",
"version": "0.2",
"version": "0.3",
"description": "Measure Time in a fancy way.",
"icon": "measuretime_icon.png",
"screenshots": [

View File

@ -28,6 +28,9 @@ E.showMenu = function (items) {
y += 22;
var lastIdx = 0;
var selectEdit = undefined;
var scroller = {
scroll: selected,
};
var l = {
draw: function (rowmin, rowmax) {
var rows = 0 | Math.min((y2 - y) / fontHeight, menuItems.length);
@ -76,10 +79,11 @@ E.showMenu = function (items) {
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);
}
else if (name.length >= 15) {
else if (vplain && name.length >= 15) {
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
}
else {
@ -138,9 +142,11 @@ E.showMenu = function (items) {
else {
var lastSelected = selected;
selected = (selected + dir + menuItems.length) % menuItems.length;
scroller.scroll = selected;
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
}
},
scroller: scroller,
};
l.draw();
var back = options.back;

View File

@ -35,6 +35,10 @@ E.showMenu = (items?: Menu): MenuInstance => {
let lastIdx = 0;
let selectEdit: undefined | ActualMenuItem = undefined;
const scroller = {
scroll: selected,
};
const l = {
draw: (rowmin?: number, rowmax?: number) => {
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);
}else if(name.length >= 15){
}else if(vplain && name.length >= 15){
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
}else{
g.drawString(name, x + 3.7, iy + 2.7);
@ -156,9 +161,11 @@ E.showMenu = (items?: Menu): MenuInstance => {
} else {
const lastSelected = selected;
selected = (selected + dir + /*keep +ve*/menuItems.length) % menuItems.length;
scroller.scroll = selected;
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
}
},
scroller,
};
l.draw();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4X/AwX48AFCqoAEC4oL/Bf4L/Bf4LTAH4A/ADGqAAIL/Bf4LD"))

142
apps/quarterclock/app.js Normal file
View File

@ -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();
}

BIN
apps/quarterclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

View File

@ -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"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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']),
});
})

View File

@ -651,11 +651,11 @@ function showUtilMenu() {
E.showMessage(/*LANG*/'Flattening battery - this can take hours.\nLong-press button to cancel.');
Bangle.setLCDTimeout(0);
Bangle.setLCDPower(1);
Bangle.setLCDBrightness(1);
if (Bangle.setGPSPower) Bangle.setGPSPower(1,"flat");
if (Bangle.setHRMPower) Bangle.setHRMPower(1,"flat");
if (Bangle.setCompassPower) Bangle.setCompassPower(1,"flat");
if (Bangle.setBarometerPower) Bangle.setBarometerPower(1,"flat");
if (Bangle.setHRMPower) Bangle.setGPSPower(1,"flat");
setInterval(function() {
var i=1000;while (i--);
}, 1);

View File

@ -1,2 +1,3 @@
0.01: attempt to import
0.02: Minor code improvements
0.03: big rewrite, adding time-adjust and altitude-adjust functionality

View File

@ -1,6 +1,6 @@
{ "id": "skyspy",
"name": "Sky Spy",
"version": "0.02",
"version": "0.03",
"description": "Application for debugging GPS problems",
"icon": "app.png",
"readme": "README.md",

View File

@ -1,20 +1,121 @@
/* Sky spy */
/* 0 .. DD.ddddd
/* fmt library v0.1 */
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"
*/
var mode = 1;
*/
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 debug = 0;
var cancel_gps, gps_start;
var gps_start;
var cur_altitude;
var wi = 24;
var h = 176-wi, w = 176;
var fix;
var adj_time = 0, adj_alt = 0;
function radA(p) { return p*(Math.PI*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;
}
function format(x) {
switch (mode) {
case 0:
return "" + x;
case 1:
d = Math.floor(x);
m = x - d;
m = m*60;
return "" + d + " " + m.toFixed(3) + "'";
case 2:
d = Math.floor(x);
m = x - d;
m = m*60;
mf = Math.floor(m);
s = m - mf;
s = s*60;
return "" + d + " " + mf + "'" + s.toFixed(0) + '"';
}
}
var qalt = -1;
var qalt = -1, min_dalt, max_dalt, step;
function resetAlt() {
min_dalt = 9999; max_dalt = -9999; step = 0;
}
@ -64,65 +146,96 @@ function calcAlt(alt, cur_altitude) {
return ddalt;
}
function updateGps() {
let /*have = false,*/ lat = "lat", lon = "lon", alt = "alt",
speed = "speed", hdop = "hdop"; // balt = "balt";
let lat = "lat ", alt = "?",
speed = "speed ", hdop = "?", adelta = "adelta ",
tdelta = "tdelta ";
if (cancel_gps)
return;
fix = Bangle.getGPSFix();
fix = gps.getGPSFix();
if (adj_time) {
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 {
Bangle.getPressure().then((x) => {
cur_altitude = x.altitude;
}, print);
} 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) {
lat = "" + format(fix.lat);
lon = "" + format(fix.lon);
alt = "" + fix.alt.toFixed(1);
lat = "" + fmt.fmtPos(fix);
alt = "" + fix.alt.toFixed(0);
adelta = "" + (cur_altitude - fix.alt).toFixed(0);
speed = "" + fix.speed.toFixed(1);
hdop = "" + fix.hdop.toFixed(1);
//have = true;
hdop = "" + fix.hdop.toFixed(0);
} 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);
if (display == 1)
g.reset().setFont("Vector", 20)
.setColor(1,1,1)
.fillRect(0, wi, 176, 176)
.setColor(0,0,0)
.drawString("Acquiring GPS", 0, 30)
.drawString(lat, 0, 50)
.drawString(lon, 0, 70)
.drawString("alt "+alt, 0, 90)
.drawString("speed "+speed, 0, 110)
.drawString("hdop "+hdop, 0, 130)
.drawString("balt" + cur_altitude, 0, 150);
let msg = "";
if (display == 1) {
msg = lat +
"\ne" + hdop + "m "+tdelta+"s\n" +
speed + "km/h\n"+ alt + "m+" + adelta + "\nmsghere";
}
if (display == 2) {
g.reset().setFont("Vector", 20)
.setColor(1,1,1)
.fillRect(0, wi, 176, 176)
.setColor(0,0,0)
.drawString("GPS status", 0, 30)
.drawString("speed "+speed, 0, 50)
.drawString("hdop "+hdop, 0, 70)
.drawString("dd "+qalt.toFixed(0) + " (" + ddalt.toFixed(0) + ")", 0, 90)
.drawString("alt "+alt, 0, 110)
.drawString("balt " + cur_altitude, 0, 130)
.drawString(step, 0, 150);
/* qalt is altitude quality estimate -- over ten seconds,
computes differences between GPS and barometric altitude.
The lower the better.
ddalt is just a debugging -- same estimate, but without
waiting 10 seconds, so will be always optimistic at start
of the cycle */
msg = speed + "km/h\n" +
"e"+hdop + "m"
+"\ndd "+qalt.toFixed(0) + "\n(" + step + "/" + ddalt.toFixed(0) + ")" +
"\n"+alt + "m+" + adelta;
}
step++;
if (step == 10) {
qalt = max_dalt - min_dalt;
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)
print(fix);
setTimeout(updateGps, 1000);
@ -184,7 +297,7 @@ function drawSats(sats) {
var sats = [];
var snum = 0;
//var sats_receiving = 0;
var sats_used = 0;
function parseRaw(msg, lost) {
if (lost)
@ -199,6 +312,7 @@ function parseRaw(msg, lost) {
if (s[2] == "1") {
snum = 0;
sats = [];
sats_used = 0;
}
let view = 1 * s[3];
@ -217,6 +331,8 @@ function parseRaw(msg, lost) {
sat.ele = 1*s[i++];
sat.azi = 1*s[i++];
sat.snr = s[i++];
if (sat.snr != "")
sats_used++;
if (debug > 0)
print(" ", sat);
sats[snum++] = sat;
@ -231,30 +347,80 @@ function parseRaw(msg, lost) {
}
}
function stopGps() {
cancel_gps=true;
Bangle.setGPSPower(0, "skyspy");
}
function markGps() {
cancel_gps = false;
Bangle.setGPSPower(1, "skyspy");
Bangle.on('GPS-raw', parseRaw);
gps_start = getTime();
updateGps();
}
function onSwipe(dir) {
display = display + 1;
if (display == 3)
display = 0;
function drawMsg(msg) {
g.reset().setFont("Vector", 35)
.setColor(1,1,1)
.fillRect(0, wi, 176, 176)
.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({
mode : "custom",
swipe : onSwipe,
clock : 0
});
Bangle.loadWidgets();
Bangle.drawWidgets();
drawBusy();
markGps();

View File

@ -11,3 +11,4 @@
Stop ClockInfo text drawing outside the allocated area
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.11: Avoid rendering clkinfo in the same colour as the background

View File

@ -86,6 +86,7 @@ let draw = function() {
let isAnimIn = true;
let animInterval;
let minuteX;
// Draw *just* the minute image
let drawMinute = function() {
var yo = slopeBorder + offsy + y - 2*slope*minuteX/R.w;
@ -128,9 +129,9 @@ let clockInfoDraw = (itm, info, options) => {
let texty = options.y+41;
// 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.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
let x = options.x+2;
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
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

View File

@ -1,6 +1,6 @@
{ "id": "slopeclockpp",
"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.",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],

View File

@ -7,3 +7,4 @@
0.07: Update clock_info to avoid a redraw
0.08: Timer ClockInfo now updates once a minute
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

View File

@ -28,7 +28,7 @@
var min = getAlarmMinutes();
if(min < 0)
return "OFF";
return "T-" + String(min)+ " min";
return min + " min";
}
function increaseAlarm(t){
@ -80,7 +80,7 @@
offsets.forEach((o, i) => {
smpltmrItems.items = smpltmrItems.items.concat({
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() { },
hide: function() { },
blur: restoreMainItem,

View File

@ -2,7 +2,7 @@
"id": "smpltmr",
"name": "Simple Timer",
"shortName": "Simple Timer",
"version": "0.09",
"version": "0.10",
"description": "A very simple app to start a timer.",
"icon": "app.png",
"tags": "tool,alarm,timer,clkinfo",

View File

@ -1 +1,2 @@
0.01: New Clock!
0.02: Clockinfos now save under correct name, and wrap correctly to >1 line

View File

@ -134,8 +134,8 @@ for (var i=0;i<10;i++)
if (g.stringWidth(txt) > options.w) // if too big, smaller font
g.setFont("LECO1976Regular14");
if (g.stringWidth(txt) > options.w) {// if still too big, split to 2 lines
var l = g.wrapString(txt, options.w);
txt = l.slice(0,2).join("\n") + (l.length>2)?"...":"";
var l = g.wrapString(txt, options.w-4);
txt = l.slice(0,2).join("\n") + ((l.length>2)?"...":"");
}
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
@ -147,12 +147,12 @@ for (var i=0;i<10;i++)
};
clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, {
app:"pebblepp",
app:"twotwoclock",
x : g.getWidth()-clockInfoW, y: 0, w: clockInfoW, h:clockInfoH,
draw : clockInfoDraw
});
clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
app:"pebblepp",
app:"twotwoclock",
x : g.getWidth()-clockInfoW, y: clockInfoH, w: clockInfoW, h:clockInfoH,
draw : clockInfoDraw
});

View File

@ -1,7 +1,7 @@
{ "id": "twotwoclock",
"name": "TwoTwo 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",
"icon": "icon.png",
"type": "clock",

2
core

@ -1 +1 @@
Subproject commit 1cdcb3405f78ef35f231b9c3df501721bda75525
Subproject commit 294690a4f0257cfb2221770b2e48eb20404e6a68

View File

@ -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.';
}
var RECOMMENDED_VERSION = "2v23";
var RECOMMENDED_VERSION = "2v24";
// could check http://www.espruino.com/json/BANGLEJS.json for this
// We're only interested in Bangles

View File

@ -90,7 +90,6 @@ exports.swipeOn = function(autohide) {
function queueDraw() {
const o = exports.offset;
if (o>-24) {
Bangle.appRect.y = o+24;
Bangle.appRect.h = 1 + Bangle.appRect.y2 - Bangle.appRect.y;
if (o>-24) {
@ -104,7 +103,6 @@ exports.swipeOn = function(autohide) {
Bangle.setLCDOverlay(undefined, {id: "widget_utils"});
}
}
}
for (var w of global.WIDGETS) if (!w._draw) { // already hidden
w._draw = w.draw;