Merge branch 'espruino:master' into master

master
eleanor 2022-05-18 11:27:14 -05:00 committed by GitHub
commit e999660512
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 456 additions and 91 deletions

View File

@ -8,3 +8,4 @@
0.08: Use theme colors, Layout library
0.09: Fix time/date disappearing after fullscreen notification
0.10: Use ClockFace library
0.11: Use ClockFace.is12Hour

View File

@ -3,7 +3,6 @@
* A simple digital clock showing seconds as a bar
**/
// Check settings for what type our clock should be
const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
let locale = require("locale");
{ // add some more info to locale
let date = new Date();
@ -25,7 +24,7 @@ function renderBar(l) {
function timeText(date) {
if (!is12Hour) {
if (!clock.is12Hour) {
return locale.time(date, true);
}
const date12 = new Date(date.getTime());
@ -38,7 +37,7 @@ function timeText(date) {
return locale.time(date12, true);
}
function ampmText(date) {
return (is12Hour && locale.hasMeridian)? locale.meridian(date) : "";
return (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : "";
}
function dateText(date) {
const dayName = locale.dow(date, true),
@ -69,7 +68,7 @@ const ClockFace = require("ClockFace"),
}, {lazy: true});
// adjustments based on screen size and whether we display am/pm
let thickness; // bar thickness, same as time font "pixel block" size
if (is12Hour) {
if (this.is12Hour) {
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
thickness = Math.floor((Bangle.appRect.w-24)/(5*6));
} else {

View File

@ -1,7 +1,7 @@
{
"id": "barclock",
"name": "Bar Clock",
"version": "0.10",
"version": "0.11",
"description": "A simple digital clock showing seconds as a bar",
"icon": "clock-bar.png",
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],

View File

@ -51,3 +51,4 @@
0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk)
0.46: Fix no clock found error on Bangle.js 2
0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed)
0.48: Workaround for BTHRM issues on Bangle.js 1 (write .boot files in chunks)

View File

@ -197,8 +197,18 @@ bootFiles.forEach(bootFile=>{
require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset);
fileOffset+=2+bootFile.length+1;
var bf = require('Storage').read(bootFile);
require('Storage').write('.boot0',bf,fileOffset);
fileOffset+=bf.length;
// we can't just write 'bf' in one go because at least in 2v13 and earlier
// Espruino wants to read the whole file into RAM first, and on Bangle.js 1
// it can be too big (especially BTHRM).
var bflen = bf.length;
var bfoffset = 0;
while (bflen) {
var bfchunk = Math.min(bflen, 2048);
require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset);
fileOffset+=bfchunk;
bfoffset+=bfchunk;
bflen-=bfchunk;
}
require('Storage').write('.boot0',";\n",fileOffset);
fileOffset+=2;
});

View File

@ -1,7 +1,7 @@
{
"id": "boot",
"name": "Bootloader",
"version": "0.47",
"version": "0.48",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"icon": "bootloader.png",
"type": "bootloader",

1
apps/f9lander/ChangeLog Normal file
View File

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

33
apps/f9lander/README.md Normal file
View File

@ -0,0 +1,33 @@
# F9 Lander
Land a Falcon 9 booster on a drone ship.
## Game play
Attempt to land your Falcon 9 booster on a drone ship before running out of fuel.
A successful landing requires:
* setting down on the ship
* the booster has to be mostly vertical
* the landing speed cannot be too high
## Controls
The angle of the booster is controlled by tilting the watch side-to-side. The
throttle level is controlled by tilting the watch forward and back:
* screen horizontal (face up) means no throttle
* screen vertical corresponds to full throttle
The fuel burn rate is proportional to the throttle level.
## Creators
Liam Kl. B.
Marko Kl. B.
## Screenshots
![](f9lander_screenshot1.png)
![](f9lander_screenshot2.png)
![](f9lander_screenshot3.png)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwcA/4AD/P8yVJkgCCye27dt2wRE//kCIuSuwRIBwgCCpwRQpIRRnYRQkmdCIvPCJICBEZ4RG/IRP/15CJ/z5IRPz4RM/gQB/n+BxICCn/z/P/BxQCDz7mIAX4Cq31/CJ+ebpiYE/IR/CNP/5IROnn//4jP5DFQ5sJCKAjPk3oCMMk4QRQAX4Ckn7jBAA/5CK8nCJPJNHA"))

150
apps/f9lander/app.js Normal file
View File

@ -0,0 +1,150 @@
const falcon9 = Graphics.createImage(`
xxxxx
xxxxx xxxxx
x x
x x
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
xxxxxxxxx
xx xxxxx xx
xx xx`);
const droneShip = Graphics.createImage(`
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
`);
const droneX = Math.floor(Math.random()*(g.getWidth()-droneShip.width-40) + 20)
const cloudOffs = Math.floor(Math.random()*g.getWidth()/2);
const oceanHeight = g.getHeight()*0.1;
const targetY = g.getHeight()-oceanHeight-falcon9.height/2;
var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2,
y : 20,
vx : 0,
vy : 0,
mass : 100,
fuel : 100 };
var exploded = false;
var nExplosions = 0;
var landed = false;
const gravity = 4;
const dt = 0.1;
const fuelBurnRate = 20*(176/g.getHeight());
const maxV = 12;
function flameImageGen (throttle) {
var str = " xxx \n xxx \n";
str += "xxxxx\n".repeat(throttle);
str += " xxx \n x \n";
return Graphics.createImage(str);
}
function drawFalcon(x, y, throttle, angle) {
g.setColor(1, 1, 1).drawImage(falcon9, x, y, {rotate:angle});
if (throttle>0) {
var flameImg = flameImageGen(throttle);
var r = falcon9.height/2 + flameImg.height/2-1;
var xoffs = -Math.sin(angle)*r;
var yoffs = Math.cos(angle)*r;
if (Math.random()>0.7) g.setColor(1, 0.5, 0);
else g.setColor(1, 1, 0);
g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle});
}
}
function drawBG() {
g.setBgColor(0.2, 0.2, 1).clear();
g.setColor(0, 0, 1).fillRect(0, g.getHeight()-oceanHeight, g.getWidth()-1, g.getHeight()-1);
g.setColor(0.5, 0.5, 1).fillCircle(cloudOffs+34, 30, 15).fillCircle(cloudOffs+60, 35, 20).fillCircle(cloudOffs+75, 20, 10);
g.setColor(1, 1, 0).fillCircle(g.getWidth(), 0, 20);
g.setColor(1, 1, 1).drawImage(droneShip, droneX, g.getHeight()-oceanHeight-1);
}
function showFuel() {
g.setColor(0, 0, 0).setFont("4x6:2").setFontAlign(-1, -1, 0).drawString("Fuel: "+Math.abs(booster.fuel).toFixed(0), 4, 4);
}
function renderScreen(input) {
drawBG();
showFuel();
drawFalcon(booster.x, booster.y, Math.floor(input.throttle*12), input.angle);
}
function getInputs() {
var accel = Bangle.getAccel();
var a = Math.PI/2 + Math.atan2(accel.y, accel.x);
var t = (1+accel.z);
if (t > 1) t = 1;
if (t < 0) t = 0;
if (booster.fuel<=0) t = 0;
return {throttle: t, angle: a};
}
function epilogue(str) {
g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString(str, g.getWidth()/2, g.getHeight()/2).flip();
g.setFont("Vector", 16).drawString("<= again exit =>", g.getWidth()/2, g.getHeight()/2+20);
clearInterval(stepInterval);
Bangle.on("swipe", (d) => { if (d>0) load(); else load('f9lander.app.js'); });
}
function gameStep() {
if (exploded) {
if (nExplosions++ < 15) {
var r = Math.random()*25;
var x = Math.random()*30 - 15;
var y = Math.random()*30 - 15;
g.setColor(1, Math.random()*0.5+0.5, 0).fillCircle(booster.x+x, booster.y+y, r);
if (nExplosions==1) Bangle.buzz(600);
}
else epilogue("You crashed!");
}
else {
var input = getInputs();
if (booster.y >= targetY) {
// console.log(booster.x + " " + booster.y + " " + booster.vy + " " + droneX + " " + input.angle);
if (Math.abs(booster.x-droneX-droneShip.width/2)<droneShip.width/2 && Math.abs(input.angle)<Math.PI/8 && booster.vy<maxV) {
renderScreen({angle:0, throttle:0});
epilogue("You landed!");
}
else exploded = true;
}
else {
booster.x += booster.vx*dt;
booster.y += booster.vy*dt;
booster.vy += gravity*dt;
booster.fuel -= input.throttle*dt*fuelBurnRate;
booster.vy += -Math.cos(input.angle)*input.throttle*gravity*3*dt;
booster.vx += Math.sin(input.angle)*input.throttle*gravity*3*dt;
renderScreen(input);
}
}
}
var stepInterval;
Bangle.setLCDTimeout(0);
renderScreen({angle:0, throttle:0});
g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString("Swipe to start", g.getWidth()/2, g.getHeight()/2);
Bangle.on("swipe", () => {
stepInterval = setInterval(gameStep, Math.floor(1000*dt));
Bangle.removeListener("swipe");
});

BIN
apps/f9lander/f9lander.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,15 @@
{ "id": "f9lander",
"name": "Falcon9 Lander",
"shortName":"F9lander",
"version":"0.01",
"description": "Land a rocket booster",
"icon": "f9lander.png",
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
"readme": "README.md",
"tags": "game",
"supports" : ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"f9lander.app.js","url":"app.js"},
{"name":"f9lander.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -13,3 +13,4 @@
0.12: Add setting for Daily Step Goal
0.13: Add support for internationalization
0.14: Move settings
0.15: Fix charts (fix #1366)

View File

@ -46,7 +46,7 @@ function menuHRM() {
function stepsPerHour() {
E.showMessage(/*LANG*/"Loading...");
let data = new Uint16Array(24);
var data = new Uint16Array(24);
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
g.clear(1);
Bangle.drawWidgets();
@ -57,7 +57,7 @@ function stepsPerHour() {
function stepsPerDay() {
E.showMessage(/*LANG*/"Loading...");
let data = new Uint16Array(31);
var data = new Uint16Array(31);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
g.clear(1);
Bangle.drawWidgets();
@ -68,8 +68,8 @@ function stepsPerDay() {
function hrmPerHour() {
E.showMessage(/*LANG*/"Loading...");
let data = new Uint16Array(24);
let cnt = new Uint8Array(23);
var data = new Uint16Array(24);
var cnt = new Uint8Array(23);
require("health").readDay(new Date(), h=>{
data[h.hr]+=h.bpm;
if (h.bpm) cnt[h.hr]++;
@ -84,8 +84,8 @@ function hrmPerHour() {
function hrmPerDay() {
E.showMessage(/*LANG*/"Loading...");
let data = new Uint16Array(31);
let cnt = new Uint8Array(31);
var data = new Uint16Array(31);
var cnt = new Uint8Array(31);
require("health").readDailySummaries(new Date(), h=>{
data[h.day]+=h.bpm;
if (h.bpm) cnt[h.day]++;
@ -100,7 +100,7 @@ function hrmPerDay() {
function movementPerHour() {
E.showMessage(/*LANG*/"Loading...");
let data = new Uint16Array(24);
var data = new Uint16Array(24);
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
g.clear(1);
Bangle.drawWidgets();
@ -111,7 +111,7 @@ function movementPerHour() {
function movementPerDay() {
E.showMessage(/*LANG*/"Loading...");
let data = new Uint16Array(31);
var data = new Uint16Array(31);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
g.clear(1);
Bangle.drawWidgets();
@ -183,7 +183,7 @@ function drawBarChart() {
}
// draw a fake 0 height bar if chart_index is outside the bounds of the array
if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len)
if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len && chart_max_datum > 0)
bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum;
else
bar_top = bar_bot;

View File

@ -1,7 +1,7 @@
{
"id": "health",
"name": "Health Tracking",
"version": "0.14",
"version": "0.15",
"description": "Logs health data and provides an app to view it",
"icon": "app.png",
"tags": "tool,system,health",

View File

@ -3,3 +3,4 @@
0.03: Added dependancy on Pedometer Widget
0.04: Fixed icon and png to 48x48 pixels
0.05: added charging icon
0.06: Add 12h support and autocycle control

View File

@ -2,7 +2,7 @@
"id": "rebble",
"name": "Rebble Clock",
"shortName": "Rebble",
"version": "0.05",
"version": "0.06",
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
"readme": "README.md",
"icon": "rebble.png",

View File

@ -1,8 +1,10 @@
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
const SETTINGS_FILE = "rebble.json";
const LOCATION_FILE = "mylocation.json";
const GLOBAL_SETTINGS = "setting.json";
let settings;
let location;
let is12Hour;
Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
// Actual height 22 (21 - 0)
@ -33,12 +35,26 @@ function loadLocation() {
}
function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green'};
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'autoCycle': true};
is12Hour = (require("Storage").readJSON(GLOBAL_SETTINGS, 1) || {})["12hour"] || false;
}
function formatHours(hh) {
if (is12Hour) {
let hours = parseInt(hh,10);
if (hours == 0) {
hours = 12;
} else if (hours >= 12) {
if (hours>12) hours -= 12;
}
hh = (" "+hours).substr(-2);
}
return hh;
}
function extractTime(d){
var h = d.getHours(), m = d.getMinutes();
return(("0"+h).substr(-2) + ":" + ("0"+m).substr(-2));
return(formatHours(("0"+h).substr(-2)) + ":" + ("0"+m).substr(-2));
}
function updateSunRiseSunSet(lat, lon){
@ -81,6 +97,9 @@ function draw() {
let da = date.toString().split(" ");
let hh = da[4].substr(0,2);
let mm = da[4].substr(3,2);
hh = formatHours(hh);
//const t = 6;
if (drawCount % 60 == 0)
@ -260,7 +279,9 @@ function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
nextSidebar();
if (!settings.autoCycle) {
nextSidebar();
}
draw();
}, 60000 - (Date.now() % 60000));
}

View File

@ -2,19 +2,19 @@
const SETTINGS_FILE = "rebble.json";
// initialize with default settings...
let s = {'bg': '#0f0', 'color': 'Green'}
let localSettings = {'bg': '#0f0', 'color': 'Green', 'autoCycle': true}
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const storage = require('Storage')
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
let settings = storage.readJSON(SETTINGS_FILE, 1) || localSettings;
const saved = settings || {}
for (const key in saved) {
s[key] = saved[key]
localSettings[key] = saved[key]
}
function save() {
settings = s
settings = localSettings
storage.write(SETTINGS_FILE, settings)
}
@ -25,14 +25,22 @@
'': { 'title': 'Rebble Clock' },
'< Back': back,
'Colour': {
value: 0 | color_options.indexOf(s.color),
value: 0 | color_options.indexOf(localSettings.color),
min: 0, max: 5,
format: v => color_options[v],
onchange: v => {
s.color = color_options[v];
s.bg = bg_code[v];
localSettings.color = color_options[v];
localSettings.bg = bg_code[v];
save();
},
},
'Auto Cycle': {
value: "autoCycle" in localSettings ? localSettings.autoCycle : true,
format: () => (localSettings.autoCycle ? 'Yes' : 'No'),
onchange: () => {
localSettings.autoCycle = !localSettings.autoCycle;
save();
}
}
});
})

View File

@ -2,3 +2,5 @@
0.02: Fix crash on start #1423
0.03: Added power saving mode, move all read/write log actions into lib/module
0.04: Fix #1445, display loading info, add icons to display service states
0.05: Fix LOW_MEMORY,MEMORY error on to big log size
0.06: Reduced log size further to 750 entries

View File

@ -22,6 +22,7 @@ also provides a power saving mode using the built in movement calculation. The i
To minimize the log size only a changed state is logged. The logged timestamp is matching the beginning of its measurement period.
When not on power saving mode a movement is detected nearly instantaneous and the detection of a no movement period is delayed by the minimal no movement duration. To match the beginning of the measurement period a cached timestamp (_sleeplog.firstnomodate_) is logged.
On power saving mode the measurement period is fixed to 10 minutes and all logged timestamps are also set back 10 minutes.
To prevent a LOW_MEMORY,MEMORY error the log size is limited to 750 entries, older entries will be overwritten.
---
### Control

View File

@ -98,6 +98,9 @@ exports = {
input = log;
}
// check and if neccessary reduce logsize to prevent low mem
if (input.length > 750) input = input.slice(-750);
// simple check for log plausibility
if (input[0].length > 1 && input[0][0] * 1 > 9E11) {
// write log to storage

View File

@ -2,7 +2,7 @@
"id":"sleeplog",
"name":"Sleep Log",
"shortName": "SleepLog",
"version": "0.04",
"version": "0.06",
"description": "Log and view your sleeping habits. This app derived from SleepPhaseAlarm and uses also the principe of Estimation of Stationary Sleep-segments (ESS). It also provides a power saving mode using the built in movement calculation.",
"icon": "app.png",
"type": "app",

View File

@ -1 +1,2 @@
0.0.1: Initial implementation
0.0.2: Fix app icon

View File

@ -1 +1 @@
atob("MDDBAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gAAAAD//gAAAAD//gAAAAf//8AAAAf//8AAAAf//8AAAD/////gAD/////gAD/////gAf/////8Af/////8Af/////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8f//////gf//////gf//////gD/////gAD/////gAD/////gAAf///8AAAf///8AAAf///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
atob("MDCI/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v////////////////////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v////////////////////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v////////////////////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v///wAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v///wAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v///wAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v///wAAAP///////////////////wAAAP////////////7+/v7+/v7+/v7+/v7+/v///wAAAP///////////////////wAAAP////////////7+/v7+/v7+/v7+/v7+/v///wAAAP///////////////////wAAAP////////////7+/v7+/v7+/v7+/v///wAAAP///////wAAAP///wAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v///wAAAP///////wAAAP///wAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v///wAAAP///////wAAAP///wAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v///wAAAP///////////////////wAAAP///////////////wAAAP////7+/v7+/v///wAAAP///////////////////wAAAP///////////////wAAAP////7+/v7+/v///wAAAP///////////////////wAAAP///////////////wAAAP////7+/v7+/v///wAAAP///wAAAP///////////wAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v///wAAAP///wAAAP///////////wAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v///wAAAP///wAAAP///////////wAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v///wAAAP///////////////////////wAAAP///////////wAAAP////7+/v7+/v///wAAAP///////////////////////wAAAP///////////wAAAP////7+/v7+/v///wAAAP///////////////////////wAAAP///////////wAAAP////7+/v7+/v///wAAAP///////////////////////wAAAAAAAAAAAAAAAAAAAP////7+/v7+/v///wAAAP///////////////////////wAAAAAAAAAAAAAAAAAAAP////7+/v7+/v///wAAAP///////////////////////wAAAAAAAAAAAAAAAAAAAP////7+/v///wAAAP///////////////////////////////////wAAAP////////7+/v7+/v///wAAAP///////////////////////////////////wAAAP////////7+/v7+/v///wAAAP///////////////////////////////////wAAAP////////7+/v7+/v7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v7+/v7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v7+/v7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////7+/v7+/v7+/v7+/v7+/v7+/v////////////////////////////////////7+/v7+/v7+/v7+/v7+/v7+/v7+/v////////////////////////////////////7+/v7+/v7+/v7+/v7+/v7+/v7+/v////////////////////////////////////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/g==")

View File

@ -2,12 +2,12 @@
"id": "tabanchi",
"name": "Tabanchi",
"shortName": "Tabanchi",
"version": "0.0.1",
"version": "0.0.2",
"type": "app",
"description": "Tamagotchi WatchApp",
"icon": "app.png",
"allow_emulator": true,
"tags": "watch, pet",
"tags": "clock, watch, virtual pet",
"supports": [
"BANGLEJS2"
],

View File

@ -1,2 +1,3 @@
0.01: Initial release
0.02: Don't start drawing with white colour on white canvas
0.03: Fix segmented line glitch when drawing, optimize screen lock detection

View File

@ -1,14 +1,13 @@
TinyDraw
========
This is a simple drawing application to make sketches
using different brushes and colors for your BangleJS2 watch!
This is a simple drawing application to make sketches using different
brushes and colors for your BangleJS2 watch!
* Brush types: dot, brush, circle, square
It is my first BangleJS application, I plan
to continue improving this app over time, but
if you want to contribute or provide feedback
It is my first BangleJS application, I plan to continue improving
this app over time, but if you want to contribute or provide feedback
don't hesitate to contact me!
--pancake

View File

@ -1,10 +1,10 @@
(function () {
var pen = 'circle';
var discard = null;
var kule = [0, 255, 255]; // R, G, B
var oldLock = false;
let pen = 'circle';
let discard = null;
const kule = [0, 255, 255]; // R, G, B
let oldLock = false;
setInterval(() => {
Bangle.on("lock", function() {
if (Bangle.isLocked()) {
if (oldLock) {
return;
@ -19,8 +19,7 @@
oldLock = false;
drawUtil();
}
}, 1000);
});
function nextColor () {
kule[0] = Math.random();
kule[1] = Math.random();
@ -35,10 +34,33 @@
case 'square': pen = 'circle'; break;
default: pen = 'pixel'; break;
}
console.log('set time');
drawUtil();
discard = setTimeout(function () { console.log('timeout'); discard = null; }, 500);
discard = setTimeout(function () { oldX = -1; oldY = -1; console.log('timeout'); discard = null; }, 500);
}
var oldX = -1;
var oldY = -1;
function drawBrushIcon () {
const w = g.getWidth();
switch (pen) {
case 'circle':
g.fillCircle(w - 10, 10, 5);
break;
case 'square':
g.fillRect(w - 5, 5, w - 15, 15);
break;
case 'pixel':
g.setPixel(10, 10);
g.fillCircle(w - 10, 10, 2);
break;
case 'crayon':
g.drawLine(w - 10, 5, w - 10, 15);
g.drawLine(w - 14, 6, w - 10, 12);
g.drawLine(w - 6, 6, w - 10, 12);
break;
}
}
function drawUtil () {
@ -58,35 +80,32 @@
g.setColor('#fff');
g.fillCircle(g.getWidth() - 10, 10, 8);
g.setColor('#000');
var w = g.getWidth();
switch (pen) {
case 'circle':
g.fillCircle(w - 10, 10, 5);
break;
case 'square':
g.fillRect(w - 5, 5, w - 15, 15);
break;
case 'pixel':
g.setPixel(10, 10);
g.fillCircle(w - 10, 10, 2);
break;
case 'crayon':
var tap = { x: 10, y: 15, dy: -5, dx: 5 };
g.drawLine(w - tap.x, tap.y, w - tap.x + tap.dx, tap.y + tap.dy);
g.drawLine(w - tap.x + 1, tap.y + 2, w - tap.x + tap.dx, tap.y + tap.dy - 2);
g.drawLine(w - tap.x + 2, tap.y + 2, w - tap.x + tap.dx, tap.y + tap.dy + 2);
break;
}
drawBrushIcon();
}
var tapTimer = null;
let tapTimer = null;
let dragTimer = null;
Bangle.on('drag', function (tap) {
let from = { x: tap.x, y: tap.y };
const to = { x: tap.x + tap.dx, y: tap.y + tap.dy };
if (oldX != -1) {
from = { x: oldX, y: oldY };
}
if (tap.b === 0) {
if (tapTimer !== null) {
clearTimeout(tapTimer);
tapTimer = null;
}
}
if (dragTimer != null) {
clearTimeout(dragTimer);
dragTimer = null;
}
dragTimer = setTimeout(function () {
oldX = -1;
oldY = -1;
}, 100);
// tap and hold the clear button
if (tap.x < 32 && tap.y < 32) {
if (tap.b === 1) {
@ -110,6 +129,8 @@
tapTimer = setTimeout(function () {
g.clear();
drawUtil();
oldX = -1; oldY = -1;
tapTimer = null;
}, 800);
}
@ -127,28 +148,34 @@
drawUtil();
return;
}
oldX = to.x;
oldY = to.y;
g.setColor(kule[0], kule[1], kule[2]);
switch (pen) {
case 'pixel':
g.setPixel(tap.x, tap.y);
g.drawLine(tap.x, tap.y, tap.x + tap.dx, tap.y + tap.dy);
g.drawLine(from.x, from.y, to.x, to.y);
break;
case 'crayon':
g.drawLine(tap.x, tap.y, tap.x + tap.dx, tap.y + tap.dy);
g.drawLine(tap.x + 1, tap.y + 2, tap.x + tap.dx, tap.y + tap.dy - 2);
g.drawLine(tap.x + 2, tap.y + 2, tap.x + tap.dx, tap.y + tap.dy + 2);
g.drawLine(from.x, from.y, to.x, to.y);
g.drawLine(from.x + 1, from.y + 2, to.x, to.y - 2);
g.drawLine(from.x + 2, from.y + 2, to.x, to.y + 2);
break;
case 'circle':
var XS = tap.dx / 10;
var YS = tap.dy / 10;
for (i = 0; i < 10; i++) {
g.fillCircle(tap.x + (i * XS), tap.y + (i * YS), 4, 4);
var XS = (to.x - from.x) / 32;
var YS = (to.y - from.y) / 32;
for (i = 0; i < 32; i++) {
g.fillCircle(from.x + (i * XS), from.y + (i * YS), 4, 4);
}
break;
case 'square':
g.fillRect(tap.x - 10, tap.y - 10, tap.x + 10, tap.y + 10);
var XS = (to.x - from.x) / 32;
var YS = (to.y - from.y) / 32;
for (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);
}
break;
}
drawUtil();
@ -157,3 +184,4 @@
g.clear();
drawUtil();
})();

View File

@ -1,7 +1,7 @@
{ "id": "tinydraw",
"name": "TinyDraw",
"shortName":"TinyDraw",
"version":"0.02",
"version":"0.03",
"type": "app",
"description": "Draw stuff in your wrist",
"icon": "app.png",

View File

@ -8,3 +8,4 @@
0.09: Vibrate on connection loss
0.10: Bug fix
0.11: Avoid too many notifications. Change disconnected colour to red.
0.12: Prevent repeated execution of `draw()` from the current app.

View File

@ -1,7 +1,7 @@
{
"id": "widbt_notify",
"name": "Bluetooth Widget with Notification",
"version": "0.11",
"version": "0.12",
"description": "Show the current Bluetooth connection status in the top right of the clock and vibrate when disconnected.",
"icon": "widget.png",
"type": "widget",

View File

@ -28,7 +28,7 @@ WIDGETS.bluetooth_notify = {
disconnect: function() {
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
E.showMessage(/*LANG*/'Connection\nlost.', 'Bluetooth');
setInterval(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
WIDGETS.bluetooth_notify.warningEnabled = 0;
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.

View File

@ -45,6 +45,7 @@
"Messages": "Messaggi",
"No Messages": "Nessun messaggio",
"Keep Msgs": "Tieni i messaggi",
"Mark all read": "Segna tutto come letto",
"Mark Unread": "Segna come non letto",
"Vibrate": "Vibrazione",
"Are you sure": "Sei sicuro/a",
@ -83,8 +84,8 @@
"Background": "Sfondo",
"Foreground 2": "Primo piano 2",
"Background 2": "Sfondo 2",
"Highlight FG": "Selezione PP",
"Highlight BG": "Selezione Sf",
"Highlight FG": "Primo piano selezione",
"Highlight BG": "Sfondo selezione",
"Utilities": "Utilità",
"Storage": "Memoria",
"Compact Storage": "Compatta memoria",
@ -110,7 +111,7 @@
"Loading": "Caricamento",
"Launcher Settings": "Impostazioni Launcher",
"Font": "Font",
"Show clocks": "Mostra orologi",
"Show Clocks": "Mostra orologi",
"Log": "Log",
"Steps": "Passi",
"steps": "passi",
@ -151,19 +152,76 @@
"start&lap/reset, BTN1: EXIT": "start&lap/reset, BTN1: EXIT",
"back": "indietro",
"color": "colore",
"BACK": "INDIETRO"
"BACK": "INDIETRO",
"Select App": "Seleziona app",
"No Apps Found": "Nessuna app trovata",
"Edit Alarm": "Modifica sveglia",
"Auto Snooze": "Ripeti automaticamente",
"Delete Alarm": "Cancella sveglia",
"Repeat Alarm": "Ripeti sveglia",
"Once": "Una volta",
"Workdays": "Giorni lavorativi",
"Weekends": "Weekend",
"Every Day": "Ogni giorno",
"Custom": "Personalizza",
"Edit Timer": "Modifica timer",
"Delete Timer": "Cancella timer",
"Scheduler Settings": "Impostazioni schedulatore",
"Nothing to Enable": "Niente da attivare",
"Nothing to Disable": "Niente da disattivare",
"Enable All": "Attiva tutto",
"Disable All": "Disattiva tutto",
"Delete All": "Cancella tutto",
"Updating boot0": "Aggiornamento boot0 in corso",
"Reloading": "Ricaricamento in corso",
"Date & Time": "Data & ora",
"Small": "Piccolo",
"Medium": "Medio",
"Big": "Grande",
"Text size": "Dimensione testo",
"Find Phone": "Trova telefono",
"Movement": "Movimento",
"Heart Rate": "Frequenza cardiaca",
"Step Counting": "Conteggio passi",
"Daily Step Goal": "Obiettivo passi giornalieri",
"Fullscreen": "Schermo intero",
"Unlock Watch": "Sblocca orologio",
"Flash Icon": "Icona lampeggiante",
"Auto-Open Music": "Apri modalità musica automaticamente",
"Colour": "Colore",
"Notifications": "Notifiche",
"Scheduler": "Schedulatore",
"Stop": "Stop",
"Min Font": "Dimensione minima del font"
},
"//2": "App-specific overrides",
"alarm": {
"//": "'Crea' instead of 'Nuovo' because 'Nuovo sveglia' would be weird (and wrong)",
"New": "Crea",
"Custom Days": "Personalizza i giorni",
"Advanced": "Altre funzionalità"
},
"health": {
"Health Tracking": "Monitoraggio salute",
"HRM Interval": "Intervallo monitoraggio cardiaco",
"3 min": "3 min",
"10 min": "10 min",
"Always": "Sempre"
},
"launch": {
"Vector font size": "Dim. font vett.",
"Vector Font Size": "Dim. font vett.",
"App Source\nNot found": "Codice app\nnon trovato"
},
"messages": {
"Unread timer": "Timer msg non letti"
"Unread timer": "Timer messaggi non letti"
},
"run": {
"Record Run": "Registra corsa"
},
"sched": {
"Unlock at Buzz": "Sblocca quando vibra",
"s": "s"
},
"setting": {
"Clock Style": "Formato ora",
"Compacting...\nTakes approx\n1 minute": "Compattamento in corso...\nCi vorrà circa un minuto",

View File

@ -32,6 +32,8 @@ function ClockFace(options) {
if (dir>0 && options.down) options.down.apply(this);
};
if (options.upDown) this._upDown = options.upDown;
this.is12Hour = !!(require("Storage").readJSON("setting.json", 1) || {})["12hour"];
}
ClockFace.prototype.tick = function() {
@ -69,6 +71,7 @@ ClockFace.prototype.start = function() {
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d]));
else Bangle.setUI("clock");
delete this._last;
this.paused = false;
this.tick();
Bangle.on("lcdPower", on => {
@ -87,7 +90,7 @@ ClockFace.prototype.pause = function() {
ClockFace.prototype.resume = function() {
if (this._timeout) return; // not paused
delete this._last;
delete this.paused;
this.paused = false;
if (this._resume) this._resume.apply(this);
this.tick(true);
};

View File

@ -59,6 +59,7 @@ var clock = new ClockFace({
draw: function(time, changed) { // at least draw or update is required
// (re)draw entire clockface, time is a Date object
// `changed` is the same format as for update() below, but always all true
// You can use `this.is12Hour` to test if the 'Time Format' setting is set to "12h" or "24h"
},
// The difference between draw() and update() is that the screen is cleared
// before draw() is called, so it needs to always redraw the entire clock
@ -108,3 +109,28 @@ var clock = new ClockFace(function(time) {
clock.start();
```
Properties
----------
The following properties are automatically set on the clock:
* `is12Hour`: `true` if the "Time Format" setting is set to "12h", `false` for "24h".
* `paused`: `true` while the clock is paused. (You don't need to check this inside your `draw()` code)
Inside the `draw()`/`update()` function you can access these using `this`:
```js
var ClockFace = require("ClockFace");
var clock = new ClockFace({
draw: function (time) {
if (this.is12Hour) // draw 12h time
else // use 24h format
}
});
clock.start();
Bangle.on('step', function(steps) {
if (clock.paused === false) // draw step count
});
```