Merge branch 'espruino:master' into master
|
|
@ -22,9 +22,6 @@ jobs:
|
|||
- name: Install typescript dependencies
|
||||
working-directory: ./typescript
|
||||
run: npm ci
|
||||
- name: Build types
|
||||
working-directory: ./typescript
|
||||
run: npm run build:types
|
||||
- name: Build all TS apps and widgets
|
||||
working-directory: ./typescript
|
||||
run: npm run build
|
||||
|
|
|
|||
|
|
@ -11,3 +11,6 @@ tests/Layout/testresult.bmp
|
|||
apps.local.json
|
||||
_site
|
||||
.jekyll-cache
|
||||
.owncloudsync.log
|
||||
Desktop.ini
|
||||
.sync_*.db*
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ and which gives information about the app for the Launcher.
|
|||
```
|
||||
|
||||
* name, icon and description present the app in the app loader.
|
||||
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher` or empty.
|
||||
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher`, `bluetooth` or empty.
|
||||
* storage is used to identify the app files and how to handle them
|
||||
* data is used to clean up files when the app is uninstalled
|
||||
|
||||
|
|
|
|||
|
|
@ -149,11 +149,11 @@ function drawHSeg(x1,y1,x2,y2,Num,Color,Size) {
|
|||
if (Color == "fg") {
|
||||
g.setColor(g.theme.fg);
|
||||
} else {
|
||||
g.setColor(mColor[0],mColor[1],mColor[2]);
|
||||
g.setColor(mColor[0],mColor[1],mColor[2]);
|
||||
}
|
||||
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,Size);
|
||||
} else {
|
||||
g.setColor(bColor[0],bColor[1],bColor[2]);
|
||||
g.setColor(bColor[0],bColor[1],bColor[2]);
|
||||
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,1);
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ function drawSSeg(x1,y1,x2,y2,Num,Color,Size) {
|
|||
for (let j = 1; j < 8; j++) {
|
||||
if (Font[Num][j-1][i-1] == 1) {
|
||||
if (Color == "fg") {
|
||||
g.setColor(sColor[0],sColor[1],sColor[2]);
|
||||
g.setColor(sColor[0],sColor[1],sColor[2]);
|
||||
} else {
|
||||
g.setColor(g.theme.fg);
|
||||
//g.setColor(0.7,0.7,0.7);
|
||||
|
|
@ -253,8 +253,8 @@ function actions(v){
|
|||
if(BTN1.read() === true) {
|
||||
print("BTN pressed");
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(v==-1){
|
||||
print("up swipe event");
|
||||
if(settings.swupApp != "") load(settings.swupApp);
|
||||
|
|
@ -269,7 +269,7 @@ function actions(v){
|
|||
}
|
||||
|
||||
// Get Messages status
|
||||
var messages = require("Storage").readJSON("messages.json",1)||[];
|
||||
var messages_installed = require("Storage").read("messages") !== undefined;
|
||||
|
||||
//var BTconnected = NRF.getSecurityStatus().connected;
|
||||
//NRF.on('connect',BTconnected = NRF.getSecurityStatus().connected);
|
||||
|
|
@ -289,27 +289,27 @@ function drawWidgeds() {
|
|||
g.setColor((g.getBPP()>8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
|
||||
else
|
||||
g.setColor(g.theme.dark ? "#666" : "#999");
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),x1Bt,y1Bt);
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),x1Bt,y1Bt);
|
||||
|
||||
|
||||
|
||||
//Battery
|
||||
//print(E.getBattery());
|
||||
//print(Bangle.isCharging());
|
||||
|
||||
|
||||
var x1B = 130;
|
||||
var y1B = 2;
|
||||
var x2B = x1B + 20;
|
||||
var y2B = y1B + 15;
|
||||
|
||||
|
||||
g.setColor(g.theme.bg);
|
||||
g.clearRect(x1B,y1B,x2B,y2B);
|
||||
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawRect(x1B,y1B,x2B,y2B);
|
||||
g.fillRect(x1B,y1B,x1B+(E.getBattery()*(x2B-x1B)/100),y2B);
|
||||
g.fillRect(x2B,y1B+(y2B-y1B)/2-3,x2B+4,y1B+(y2B-y1B)/2+3);
|
||||
|
||||
|
||||
|
||||
|
||||
//Messages
|
||||
|
||||
|
|
@ -318,25 +318,25 @@ function drawWidgeds() {
|
|||
var x2M = x1M + 25;
|
||||
var y2M = y2B;
|
||||
|
||||
if (messages.some(m=>m.new)) {
|
||||
if (messages_installed && require("messages").status() == "new") {
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(x1M,y1M,x2M,y2M);
|
||||
g.setColor(g.theme.bg);
|
||||
g.drawLine(x1M,y1M,x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2);
|
||||
g.drawLine(x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2,x2M,y1M);
|
||||
}
|
||||
|
||||
|
||||
var strDow = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
|
||||
var d = new Date();
|
||||
var dow = d.getDay(),day = d.getDate(), month = d.getMonth() + 1, year = d.getFullYear();
|
||||
|
||||
print(strDow[dow] + ' ' + day + '.' + month + ' ' + year);
|
||||
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFontAlign(-1, -1,0);
|
||||
g.setFont("Vector", 20);
|
||||
g.drawString(strDow[dow] + ' ' + day, 0, 0, true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -354,7 +354,7 @@ function SetFull(on) {
|
|||
} else {
|
||||
Ys = 30;
|
||||
Bangle.setUI("updown",actions);
|
||||
Bangle.on('swipe', function(direction) {
|
||||
Bangle.on('swipe', function(direction) {
|
||||
switch (direction) {
|
||||
case 1:
|
||||
print("swipe left event");
|
||||
|
|
@ -362,7 +362,7 @@ function SetFull(on) {
|
|||
print(settings.swleftApp);
|
||||
break;
|
||||
case -1:
|
||||
print("swipe right event");
|
||||
print("swipe right event");
|
||||
if(settings.swrightApp != "") load(settings.swrightApp);
|
||||
print(settings.swrightApp);
|
||||
break;
|
||||
|
|
@ -374,7 +374,7 @@ function SetFull(on) {
|
|||
|
||||
SegH = (Ye-Ys)/2;
|
||||
Dy = SegH/16;
|
||||
|
||||
|
||||
draw();
|
||||
|
||||
if (on != true) {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Initial version for upload
|
||||
0.02: better theme support, configurable colors, small improvements
|
||||
0.02: Better theme support, configurable colors, small improvements
|
||||
0.03: Use `messages` library to check for new messages
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "7x7dotsclock",
|
||||
"name": "7x7 Dots Clock",
|
||||
"shortName":"7x7 Dots Clock",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "A clock with a big 7x7 dots Font",
|
||||
"icon": "dotsfontclock.png",
|
||||
"tags": "clock",
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Fullscreen settings.
|
||||
0.02: Fullscreen settings.
|
||||
0.03: Tell clock widgets to hide.
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ function draw() {
|
|||
}
|
||||
}
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
||||
// Clear the screen once, at startup
|
||||
|
|
@ -140,5 +143,3 @@ Bangle.on('lock', function(isLocked) {
|
|||
});
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "90sclk",
|
||||
"name": "90s Clock",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "A 90s style watch-face",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial version
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# a_dndtoggle - Toggle Quiet Mode of the watch
|
||||
|
||||
When Quiet mode is off, just start this app to set quiet mode. Start it again to turn off quiet mode.
|
||||
Work in progress.
|
||||
|
||||
#ToDo
|
||||
Settings page, current status indicator.
|
||||
|
||||
## Creator
|
||||
|
||||
Hank - contact at http://forum.espruino.com
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
const modeNames = [/*LANG*/"Noisy", /*LANG*/"Alarms", /*LANG*/"Silent"];
|
||||
let bSettings = require('Storage').readJSON('setting.json',true)||{};
|
||||
let current = 0|bSettings.quiet;
|
||||
//0 off
|
||||
//1 alarms
|
||||
//2 silent
|
||||
|
||||
console.log("old: " + current);
|
||||
|
||||
switch (current) {
|
||||
case 0:
|
||||
bSettings.quiet = 2;
|
||||
Bangle.buzz();
|
||||
setTimeout('Bangle.buzz();',500);
|
||||
break;
|
||||
case 1:
|
||||
bSettings.quiet = 0;
|
||||
Bangle.buzz();
|
||||
break;
|
||||
case 2:
|
||||
bSettings.quiet = 0;
|
||||
Bangle.buzz();
|
||||
break;
|
||||
default:
|
||||
bSettings.quiet = 0;
|
||||
Bangle.buzz();
|
||||
}
|
||||
|
||||
console.log("new: " + bSettings.quiet);
|
||||
|
||||
E.showMessage(modeNames[current] + " -> " + modeNames[bSettings.quiet]);
|
||||
setTimeout('exitApp();', 2000);
|
||||
|
||||
|
||||
function exitApp(){
|
||||
|
||||
require("Storage").writeJSON("setting.json", bSettings);
|
||||
// reload clocks with new theme, otherwise just wait for user to switch apps
|
||||
|
||||
load()
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 486 B |
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwJC/AAl/Agf/AAUAgIFDwEHAofgh/g/0Ag/wj+AnwVB/EegEfEIN4nkAh+AgE8vgVBAoV4Aoce/EAgfADQIFcjwpFHYIFCnxBFJopZBn5ZCMopxFPoqJFSowA/gA="))
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "a_dndtoggle",
|
||||
"name": "a_dndtoggle - Toggle Quiet Mode of the watch",
|
||||
"shortName": "A_DND Toggle",
|
||||
"version": "0.01",
|
||||
"description": "Toggle Quiet Mode of the watch just by starting this app.",
|
||||
"icon": "a_dndtoggle.png",
|
||||
"type": "app",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"a_dndtoggle.app.js","url":"a_dndtoggle.app.js"},
|
||||
{"name":"a_dndtoggle.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"readme": "README.md"
|
||||
}
|
||||
|
|
@ -10,3 +10,4 @@
|
|||
0.10: Added separate Bangle.js 2 file with Bangle.js 2 kickstarter pixels (as of 28 Oct 2021)
|
||||
0.11: Bangle.js2: New pixels, btn1 to exit
|
||||
0.12: Actual pixels as of 29th Nov 2021
|
||||
0.13: Bangle.js 2: Use setUI to add software back button
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ var img = atob("sIwDkm2S66DYwA2AAAAAHAHGSRxJEkAAgmGGBxDIADIdAFJIbAHF9HP00kBUC6Dt
|
|||
var imgHeight = g.imageMetrics(img).height;
|
||||
var imgScroll = Math.floor(Math.random()*imgHeight);
|
||||
|
||||
g.reset().setFont("6x15").setFontAlign(0,0);
|
||||
g.clear(1).setFont("6x15").setFontAlign(0,0);
|
||||
g.drawString(ENV.VERSION + " " + NRF.getAddress(), g.getWidth()/2, 171);
|
||||
g.drawImage(img,0,24);
|
||||
|
||||
|
|
@ -69,4 +69,7 @@ function drawImage() {
|
|||
|
||||
// TODO: a nice little animation before
|
||||
setTimeout(drawInfo, 1000);
|
||||
setWatch(_=>load(), BTN1);
|
||||
Bangle.setUI({
|
||||
mode : "custom",
|
||||
back : load
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "about",
|
||||
"name": "About",
|
||||
"version": "0.12",
|
||||
"version": "0.13",
|
||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system",
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Use the new multiplatform 'Layout' library
|
||||
0.03: Exit as first menu option, dont show decimal places for seconds
|
||||
0.04: Localisation, change Exit->Back to allow back-arrow to appear on 2v13 firmware
|
||||
0.05: Add max G values during recording, record actual G values and magnitude to CSV
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
var fileNumber = 0;
|
||||
var MAXLOGS = 9;
|
||||
var logRawData = false;
|
||||
|
||||
function getFileName(n) {
|
||||
return "accellog."+n+".csv";
|
||||
|
|
@ -24,6 +25,11 @@ function showMenu() {
|
|||
/*LANG*/"View Logs" : function() {
|
||||
viewLogs();
|
||||
},
|
||||
/*LANG*/"Log raw data" : {
|
||||
value : logRawData,
|
||||
format : v => v?/*LANG*/"Yes":/*LANG*/"No",
|
||||
onchange : v => { logRawData=v; }
|
||||
},
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
|
@ -78,6 +84,7 @@ function viewLogs() {
|
|||
}
|
||||
|
||||
function startRecord(force) {
|
||||
var stopped = false;
|
||||
if (!force) {
|
||||
// check for existing file
|
||||
var f = require("Storage").open(getFileName(fileNumber), "r");
|
||||
|
|
@ -92,39 +99,101 @@ function startRecord(force) {
|
|||
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout({ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Samples", pad:2},
|
||||
{type:"txt", id:"samples", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Time", pad:2},
|
||||
{type:"txt", id:"time", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
{type:"txt", font:"6x8:2", label:/*LANG*/"RECORDING", bgCol:"#f00", pad:5, fillx:1},
|
||||
]
|
||||
},{btns:[ // Buttons...
|
||||
{label:/*LANG*/"STOP", cb:()=>{
|
||||
Bangle.removeListener('accel', accelHandler);
|
||||
showMenu();
|
||||
{ type: "h", c: [
|
||||
{ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Samples", pad:2},
|
||||
{type:"txt", id:"samples", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
]},
|
||||
{ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Time", pad:2},
|
||||
{type:"txt", id:"time", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
]},
|
||||
]},
|
||||
{ type: "h", c: [
|
||||
{ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Max X", pad:2},
|
||||
{type:"txt", id:"maxX", font:"6x8", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
]},
|
||||
{ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Max Y", pad:2},
|
||||
{type:"txt", id:"maxY", font:"6x8", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
]},
|
||||
{ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Max Z", pad:2},
|
||||
{type:"txt", id:"maxZ", font:"6x8", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
]},
|
||||
]},
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Max G", pad:2},
|
||||
{type:"txt", id:"maxMag", font:"6x8:4", label:" - ", pad:5, bgCol:g.theme.bg},
|
||||
{type:"txt", id:"state", font:"6x8:2", label:/*LANG*/"RECORDING", bgCol:"#f00", pad:5, fillx:1},
|
||||
]},
|
||||
{
|
||||
btns:[ // Buttons...
|
||||
{id: "btnStop", label:/*LANG*/"STOP", cb:()=>{
|
||||
if (stopped) {
|
||||
showMenu();
|
||||
}
|
||||
else {
|
||||
Bangle.removeListener('accel', accelHandler);
|
||||
layout.state.label = /*LANG*/"STOPPED";
|
||||
layout.state.bgCol = /*LANG*/"#0f0";
|
||||
stopped = true;
|
||||
layout.render();
|
||||
}
|
||||
}}
|
||||
]});
|
||||
layout.render();
|
||||
|
||||
// now start writing
|
||||
var f = require("Storage").open(getFileName(fileNumber), "w");
|
||||
f.write("Time (ms),X,Y,Z\n");
|
||||
f.write("Time (ms),X,Y,Z,Total\n");
|
||||
var start = getTime();
|
||||
var sampleCount = 0;
|
||||
var maxMag = 0;
|
||||
var maxX = 0;
|
||||
var maxY = 0;
|
||||
var maxZ = 0;
|
||||
|
||||
function accelHandler(accel) {
|
||||
var t = getTime()-start;
|
||||
f.write([
|
||||
t*1000,
|
||||
accel.x*8192,
|
||||
accel.y*8192,
|
||||
accel.z*8192].map(n=>Math.round(n)).join(",")+"\n");
|
||||
if (logRawData) {
|
||||
f.write([
|
||||
t*1000,
|
||||
accel.x*8192,
|
||||
accel.y*8192,
|
||||
accel.z*8192,
|
||||
accel.mag*8192,
|
||||
].map(n=>Math.round(n)).join(",")+"\n");
|
||||
} else {
|
||||
f.write([
|
||||
Math.round(t*1000),
|
||||
accel.x,
|
||||
accel.y,
|
||||
accel.z,
|
||||
accel.mag,
|
||||
].join(",")+"\n");
|
||||
}
|
||||
if (accel.mag > maxMag) {
|
||||
maxMag = accel.mag.toFixed(2);
|
||||
}
|
||||
if (accel.x > maxX) {
|
||||
maxX = accel.x.toFixed(2);
|
||||
}
|
||||
if (accel.y > maxY) {
|
||||
maxY = accel.y.toFixed(2);
|
||||
}
|
||||
if (accel.z > maxZ) {
|
||||
maxZ = accel.z.toFixed(2);
|
||||
}
|
||||
|
||||
sampleCount++;
|
||||
layout.samples.label = sampleCount;
|
||||
layout.time.label = Math.round(t)+"s";
|
||||
layout.render(layout.samples);
|
||||
layout.render(layout.time);
|
||||
layout.maxX.label = maxX;
|
||||
layout.maxY.label = maxY;
|
||||
layout.maxZ.label = maxZ;
|
||||
layout.maxMag.label = maxMag;
|
||||
layout.render();
|
||||
}
|
||||
|
||||
Bangle.setPollInterval(80); // 12.5 Hz - the default
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "accellog",
|
||||
"name": "Acceleration Logger",
|
||||
"shortName": "Accel Log",
|
||||
"version": "0.04",
|
||||
"version": "0.05",
|
||||
"description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC",
|
||||
"icon": "app.png",
|
||||
"tags": "outdoor",
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@
|
|||
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
|
||||
0.07: Fix bug on the cutting edge firmware
|
||||
0.08: Use default Bangle formatter for booleans
|
||||
0.09: New app screen (instead of showing settings or the alert) and some optimisations
|
||||
0.10: Add software back button via setUI
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
(function () {
|
||||
// load variable before defining functions cause it can trigger a ReferenceError
|
||||
const activityreminder = require("activityreminder");
|
||||
const storage = require("Storage");
|
||||
let activityreminder_data = activityreminder.loadData();
|
||||
|
||||
function run() {
|
||||
E.showPrompt("Inactivity detected", {
|
||||
title: "Activity reminder",
|
||||
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 }
|
||||
}).then(function (v) {
|
||||
if (v == 1) {
|
||||
activityreminder_data.okDate = new Date();
|
||||
}
|
||||
if (v == 2) {
|
||||
activityreminder_data.dismissDate = new Date();
|
||||
}
|
||||
if (v == 3) {
|
||||
activityreminder_data.pauseDate = new Date();
|
||||
}
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
load();
|
||||
});
|
||||
|
||||
// Obey system quiet mode:
|
||||
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
||||
Bangle.buzz(400);
|
||||
}
|
||||
setTimeout(load, 20000);
|
||||
}
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
run();
|
||||
|
||||
})();
|
||||
|
|
@ -1,46 +1,58 @@
|
|||
(function () {
|
||||
// load variable before defining functions cause it can trigger a ReferenceError
|
||||
const activityreminder = require("activityreminder");
|
||||
const storage = require("Storage");
|
||||
const activityreminder_settings = activityreminder.loadSettings();
|
||||
let activityreminder_data = activityreminder.loadData();
|
||||
// load variable before defining functions cause it can trigger a ReferenceError
|
||||
const activityreminder = require("activityreminder");
|
||||
let activityreminder_data = activityreminder.loadData();
|
||||
let W = g.getWidth();
|
||||
// let H = g.getHeight();
|
||||
|
||||
function drawAlert() {
|
||||
E.showPrompt("Inactivity detected", {
|
||||
title: "Activity reminder",
|
||||
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 }
|
||||
}).then(function (v) {
|
||||
if (v == 1) {
|
||||
activityreminder_data.okDate = new Date();
|
||||
}
|
||||
if (v == 2) {
|
||||
activityreminder_data.dismissDate = new Date();
|
||||
}
|
||||
if (v == 3) {
|
||||
activityreminder_data.pauseDate = new Date();
|
||||
}
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
load();
|
||||
});
|
||||
|
||||
// Obey system quiet mode:
|
||||
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
||||
Bangle.buzz(400);
|
||||
}
|
||||
setTimeout(load, 20000);
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
|
||||
drawAlert();
|
||||
} else {
|
||||
eval(storage.read("activityreminder.settings.js"))(() => load());
|
||||
}
|
||||
}
|
||||
function getHoursMins(date){
|
||||
var h = date.getHours();
|
||||
var m = date.getMinutes();
|
||||
return (""+h).substr(-2) + ":" + ("0"+m).substr(-2);
|
||||
}
|
||||
|
||||
function drawData(name, value, y){
|
||||
g.drawString(name, 10, y);
|
||||
g.drawString(value, 100, y);
|
||||
}
|
||||
|
||||
function drawInfo() {
|
||||
var h=18, y = h;
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFont("Vector",h).setFontAlign(-1,-1);
|
||||
|
||||
// Header
|
||||
g.drawLine(0,25,W,25);
|
||||
g.drawLine(0,26,W,26);
|
||||
|
||||
g.drawString("Current Cycle", 10, y+=h);
|
||||
drawData("Start", getHoursMins(activityreminder_data.stepsDate), y+=h);
|
||||
drawData("Steps", getCurrentSteps(), y+=h);
|
||||
|
||||
/*
|
||||
g.drawString("Button Press", 10, y+=h*2);
|
||||
drawData("Ok", getHoursMins(activityreminder_data.okDate), y+=h);
|
||||
drawData("Dismiss", getHoursMins(activityreminder_data.dismissDate), y+=h);
|
||||
drawData("Pause", getHoursMins(activityreminder_data.pauseDate), y+=h);
|
||||
*/
|
||||
}
|
||||
|
||||
function getCurrentSteps(){
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
return health.steps - activityreminder_data.stepsOnDate;
|
||||
}
|
||||
|
||||
function run() {
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
run();
|
||||
|
||||
})();
|
||||
drawInfo();
|
||||
Bangle.setUI({
|
||||
mode : "custom",
|
||||
back : load
|
||||
})
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1,70 +1,81 @@
|
|||
(function () {
|
||||
// load variable before defining functions cause it can trigger a ReferenceError
|
||||
const activityreminder = require("activityreminder");
|
||||
const activityreminder_settings = activityreminder.loadSettings();
|
||||
let activityreminder_data = activityreminder.loadData();
|
||||
|
||||
if (activityreminder_data.firstLoad) {
|
||||
activityreminder_data.firstLoad = false;
|
||||
// load variable before defining functions cause it can trigger a ReferenceError
|
||||
const activityreminder = require("activityreminder");
|
||||
const activityreminder_settings = activityreminder.loadSettings();
|
||||
let activityreminder_data = activityreminder.loadData();
|
||||
|
||||
if (activityreminder_data.firstLoad) {
|
||||
activityreminder_data.firstLoad = false;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (isNotWorn()) return;
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
|
||||
if (isDuringAlertHours(h)) {
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|
||||
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
|
||||
activityreminder_data.stepsOnDate = health.steps;
|
||||
activityreminder_data.stepsDate = now;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (isNotWorn()) return;
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
|
||||
if (isDuringAlertHours(h)) {
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|
||||
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
|
||||
activityreminder_data.stepsOnDate = health.steps;
|
||||
activityreminder_data.stepsDate = now;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
/* todo in a futur release
|
||||
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
|
||||
(pass some argument to run() to handle long walks and not triggering so often)
|
||||
*/
|
||||
}
|
||||
|
||||
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
|
||||
load('activityreminder.app.js');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isNotWorn() {
|
||||
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
|
||||
}
|
||||
|
||||
function isDuringAlertHours(h) {
|
||||
if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight
|
||||
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour);
|
||||
} else { // passing through midnight
|
||||
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour);
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.on('midnight', function () {
|
||||
/*
|
||||
Usefull trick to have the app working smothly for people using it at night
|
||||
*/
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
if (activityreminder_settings.enabled && isDuringAlertHours(h)) {
|
||||
// updating only the steps and keeping the original stepsDate on purpose
|
||||
activityreminder_data.stepsOnDate = 0;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (activityreminder_settings.enabled) {
|
||||
setInterval(run, 60000);
|
||||
/* todo in a futur release
|
||||
increase setInterval time to something that is still sensible (5 mins ?)
|
||||
when we added a settimer
|
||||
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
|
||||
(pass some argument to run() to handle long walks and not triggering so often)
|
||||
*/
|
||||
}
|
||||
|
||||
if (mustAlert(now)) {
|
||||
load('activityreminder.alert.js');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isNotWorn() {
|
||||
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
|
||||
}
|
||||
|
||||
function isDuringAlertHours(h) {
|
||||
if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight
|
||||
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour);
|
||||
} else { // passing through midnight
|
||||
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour);
|
||||
}
|
||||
}
|
||||
|
||||
function mustAlert(now) {
|
||||
if ((now - activityreminder_data.stepsDate) / 60000 > activityreminder_settings.maxInnactivityMin) { // inactivity detected
|
||||
if ((now - activityreminder_data.okDate) / 60000 > 3 && // last alert anwsered with ok was more than 3 min ago
|
||||
(now - activityreminder_data.dismissDate) / 60000 > activityreminder_settings.dismissDelayMin && // last alert was more than dismissDelayMin ago
|
||||
(now - activityreminder_data.pauseDate) / 60000 > activityreminder_settings.pauseDelayMin) { // last alert was more than pauseDelayMin ago
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bangle.on('midnight', function () {
|
||||
/*
|
||||
Usefull trick to have the app working smothly for people using it at night
|
||||
*/
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
if (activityreminder_settings.enabled && isDuringAlertHours(h)) {
|
||||
// updating only the steps and keeping the original stepsDate on purpose
|
||||
activityreminder_data.stepsOnDate = 0;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (activityreminder_settings.enabled) {
|
||||
setInterval(run, 60000);
|
||||
/* todo in a futur release
|
||||
increase setInterval time to something that is still sensible (5 mins ?)
|
||||
when we added a settimer
|
||||
*/
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1,56 +1,44 @@
|
|||
exports.loadSettings = function () {
|
||||
return Object.assign({
|
||||
enabled: true,
|
||||
startHour: 9,
|
||||
endHour: 20,
|
||||
maxInnactivityMin: 30,
|
||||
dismissDelayMin: 15,
|
||||
pauseDelayMin: 120,
|
||||
minSteps: 50,
|
||||
tempThreshold: 27
|
||||
}, require("Storage").readJSON("activityreminder.s.json", true) || {});
|
||||
return Object.assign({
|
||||
enabled: true,
|
||||
startHour: 9,
|
||||
endHour: 20,
|
||||
maxInnactivityMin: 30,
|
||||
dismissDelayMin: 15,
|
||||
pauseDelayMin: 120,
|
||||
minSteps: 50,
|
||||
tempThreshold: 27
|
||||
}, require("Storage").readJSON("activityreminder.s.json", true) || {});
|
||||
};
|
||||
|
||||
exports.writeSettings = function (settings) {
|
||||
require("Storage").writeJSON("activityreminder.s.json", settings);
|
||||
require("Storage").writeJSON("activityreminder.s.json", settings);
|
||||
};
|
||||
|
||||
exports.saveData = function (data) {
|
||||
require("Storage").writeJSON("activityreminder.data.json", data);
|
||||
require("Storage").writeJSON("activityreminder.data.json", data);
|
||||
};
|
||||
|
||||
exports.loadData = function () {
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
let data = Object.assign({
|
||||
firstLoad: true,
|
||||
stepsDate: new Date(),
|
||||
stepsOnDate: health.steps,
|
||||
okDate: new Date(1970),
|
||||
dismissDate: new Date(1970),
|
||||
pauseDate: new Date(1970),
|
||||
},
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
let data = Object.assign({
|
||||
firstLoad: true,
|
||||
stepsDate: new Date(),
|
||||
stepsOnDate: health.steps,
|
||||
okDate: new Date(1970),
|
||||
dismissDate: new Date(1970),
|
||||
pauseDate: new Date(1970),
|
||||
},
|
||||
require("Storage").readJSON("activityreminder.data.json") || {});
|
||||
|
||||
if(typeof(data.stepsDate) == "string")
|
||||
data.stepsDate = new Date(data.stepsDate);
|
||||
if(typeof(data.okDate) == "string")
|
||||
data.okDate = new Date(data.okDate);
|
||||
if(typeof(data.dismissDate) == "string")
|
||||
data.dismissDate = new Date(data.dismissDate);
|
||||
if(typeof(data.pauseDate) == "string")
|
||||
data.pauseDate = new Date(data.pauseDate);
|
||||
if (typeof (data.stepsDate) == "string")
|
||||
data.stepsDate = new Date(data.stepsDate);
|
||||
if (typeof (data.okDate) == "string")
|
||||
data.okDate = new Date(data.okDate);
|
||||
if (typeof (data.dismissDate) == "string")
|
||||
data.dismissDate = new Date(data.dismissDate);
|
||||
if (typeof (data.pauseDate) == "string")
|
||||
data.pauseDate = new Date(data.pauseDate);
|
||||
|
||||
return data;
|
||||
return data;
|
||||
};
|
||||
|
||||
exports.mustAlert = function(activityreminder_data, activityreminder_settings) {
|
||||
let now = new Date();
|
||||
if ((now - activityreminder_data.stepsDate) / 60000 > activityreminder_settings.maxInnactivityMin) { // inactivity detected
|
||||
if ((now - activityreminder_data.okDate) / 60000 > 3 && // last alert anwsered with ok was more than 3 min ago
|
||||
(now - activityreminder_data.dismissDate) / 60000 > activityreminder_settings.dismissDelayMin && // last alert was more than dismissDelayMin ago
|
||||
(now - activityreminder_data.pauseDate) / 60000 > activityreminder_settings.pauseDelayMin) { // last alert was more than pauseDelayMin ago
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Activity Reminder",
|
||||
"shortName":"Activity Reminder",
|
||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||
"version":"0.08",
|
||||
"version":"0.10",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,activity",
|
||||
|
|
@ -13,11 +13,12 @@
|
|||
{"name": "activityreminder.app.js", "url":"app.js"},
|
||||
{"name": "activityreminder.boot.js", "url": "boot.js"},
|
||||
{"name": "activityreminder.settings.js", "url": "settings.js"},
|
||||
{"name": "activityreminder.alert.js", "url": "alert.js"},
|
||||
{"name": "activityreminder", "url": "lib.js"},
|
||||
{"name": "activityreminder.img", "url": "app-icon.js", "evaluate": true}
|
||||
],
|
||||
"data": [
|
||||
{"name": "activityreminder.s.json"},
|
||||
{"name": "activityreminder.data.json"}
|
||||
{"name": "activityreminder.data.json", "storageFile": true}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +1,86 @@
|
|||
(function (back) {
|
||||
// Load settings
|
||||
const activityreminder = require("activityreminder");
|
||||
let settings = activityreminder.loadSettings();
|
||||
// Load settings
|
||||
const activityreminder = require("activityreminder");
|
||||
let settings = activityreminder.loadSettings();
|
||||
|
||||
// Show the menu
|
||||
E.showMenu({
|
||||
"": { "title": "Activity Reminder" },
|
||||
"< Back": () => back(),
|
||||
'Enable': {
|
||||
value: settings.enabled,
|
||||
onchange: v => {
|
||||
settings.enabled = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Start hour': {
|
||||
value: settings.startHour,
|
||||
min: 0, max: 24,
|
||||
onchange: v => {
|
||||
settings.startHour = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'End hour': {
|
||||
value: settings.endHour,
|
||||
min: 0, max: 24,
|
||||
onchange: v => {
|
||||
settings.endHour = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Max inactivity': {
|
||||
value: settings.maxInnactivityMin,
|
||||
min: 15, max: 120,
|
||||
onchange: v => {
|
||||
settings.maxInnactivityMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
},
|
||||
format: x => x + "m"
|
||||
},
|
||||
'Dismiss delay': {
|
||||
value: settings.dismissDelayMin,
|
||||
min: 5, max: 60,
|
||||
onchange: v => {
|
||||
settings.dismissDelayMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
},
|
||||
format: x => x + "m"
|
||||
},
|
||||
'Pause delay': {
|
||||
value: settings.pauseDelayMin,
|
||||
min: 30, max: 240, step: 5,
|
||||
onchange: v => {
|
||||
settings.pauseDelayMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
},
|
||||
format: x => {
|
||||
return x + "m";
|
||||
}
|
||||
},
|
||||
'Min steps': {
|
||||
value: settings.minSteps,
|
||||
min: 10, max: 500, step: 10,
|
||||
onchange: v => {
|
||||
settings.minSteps = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Temp Threshold': {
|
||||
value: settings.tempThreshold,
|
||||
min: 20, max: 40, step: 0.5,
|
||||
format: v => v + "°C",
|
||||
onchange: v => {
|
||||
settings.tempThreshold = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
function getMainMenu(){
|
||||
var mainMenu = {
|
||||
"": { "title": "Activity Reminder" },
|
||||
"< Back": () => back(),
|
||||
'Enable': {
|
||||
value: settings.enabled,
|
||||
onchange: v => {
|
||||
settings.enabled = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
});
|
||||
},
|
||||
'Start hour': {
|
||||
value: settings.startHour,
|
||||
min: 0, max: 24,
|
||||
onchange: v => {
|
||||
settings.startHour = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'End hour': {
|
||||
value: settings.endHour,
|
||||
min: 0, max: 24,
|
||||
onchange: v => {
|
||||
settings.endHour = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Max inactivity': {
|
||||
value: settings.maxInnactivityMin,
|
||||
min: 15, max: 120,
|
||||
onchange: v => {
|
||||
settings.maxInnactivityMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
},
|
||||
format: x => x + "m"
|
||||
},
|
||||
'Dismiss delay': {
|
||||
value: settings.dismissDelayMin,
|
||||
min: 5, max: 60,
|
||||
onchange: v => {
|
||||
settings.dismissDelayMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
},
|
||||
format: x => x + "m"
|
||||
},
|
||||
'Pause delay': {
|
||||
value: settings.pauseDelayMin,
|
||||
min: 30, max: 240, step: 5,
|
||||
onchange: v => {
|
||||
settings.pauseDelayMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
},
|
||||
format: x => {
|
||||
return x + "m";
|
||||
}
|
||||
},
|
||||
'Min steps': {
|
||||
value: settings.minSteps,
|
||||
min: 10, max: 500, step: 10,
|
||||
onchange: v => {
|
||||
settings.minSteps = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Temp Threshold': {
|
||||
value: settings.tempThreshold,
|
||||
min: 20, max: 40, step: 0.5,
|
||||
format: v => v + "°C",
|
||||
onchange: v => {
|
||||
settings.tempThreshold = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return mainMenu;
|
||||
}
|
||||
|
||||
// Show the menu
|
||||
E.showMenu(getMainMenu());
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
0.01: AdvCasio first version
|
||||
0.02: Remove un-needed fonts to improve memory usage
|
||||
0.03: Tell clock widgets to hide.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
const storage = require('Storage');
|
||||
|
||||
require("Font6x12").add(Graphics);
|
||||
require("Font6x8").add(Graphics);
|
||||
require("Font8x12").add(Graphics);
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
|
||||
|
|
@ -257,7 +255,6 @@ function draw() {
|
|||
|
||||
|
||||
g.setColor(0, 0, 0);
|
||||
g.setFont("6x12");
|
||||
if(dataJson && dataJson.weather) drawWeather(dataJson.weather);
|
||||
if(dataJson && dataJson.tasks) drawTasks(dataJson.tasks);
|
||||
|
||||
|
|
@ -297,9 +294,10 @@ Bangle.on("lock", (locked) => {
|
|||
});
|
||||
|
||||
|
||||
Bangle.setUI("clock");
|
||||
|
||||
// Load widgets, but don't show them
|
||||
Bangle.loadWidgets();
|
||||
Bangle.setUI("clock");
|
||||
|
||||
g.reset();
|
||||
g.clear();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "advcasio",
|
||||
"name": "Advanced Casio Clock",
|
||||
"shortName":"advcasio",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"description": "An over-engineered clock inspired by Casio watches. It has a 4 days weather, a timer using swipe and a scratchpad. Can be updated using a dedicated webapp.",
|
||||
"icon": "app.png",
|
||||
"tags": "clock",
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
0.01: Basic agenda with events from GB
|
||||
0.02: Added settings page to force calendar sync
|
||||
0.03: Disable past events display from settings
|
||||
0.04: Added awareness of allDay field
|
||||
0.05: Displaying calendar colour and name
|
||||
0.06: Added clkinfo for clocks.
|
||||
0.07: Clkinfo improvements.
|
||||
|
|
@ -1,3 +1,30 @@
|
|||
# Agenda
|
||||
|
||||
Basic agenda reading the events synchronised from GadgetBridge
|
||||
Basic agenda reading the events synchronised from GadgetBridge.
|
||||
|
||||
### Functionalities
|
||||
|
||||
* List all events in the next week (or whatever is synchronized)
|
||||
* Optionally view past events (until GB removes them)
|
||||
* Show start time and location of the events in the list
|
||||
* Show the colour of the calendar in the list
|
||||
* Display description, location and calendar name after tapping on events
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
For the events sync to work, GadgetBridge needs to have the calendar permission and calendar sync should be enabled in the devices settings (gear sign in GB, also check the blacklisted calendars there, if events are missing).
|
||||
Keep in mind that GadgetBridge won't synchronize all events on your calendar, just the ones in a time window of 7 days (you don't want your watch to explode), ideally every day old events get deleted since they appear out of such window.
|
||||
|
||||
#### Force Sync
|
||||
|
||||
If for any reason events still cannot sync or some are missing, you can try any of the following (just one, you normally don't need to do this):
|
||||
1. from GB open the burger menu (side), tap debug and set time.
|
||||
2. from the bangle, open settings > apps > agenda > Force calendar sync, then select not to delete the local events (this is equivalent to option 1).
|
||||
3. do like option 2 but delete events, GB will synchronize a fresh database instead of patching the old one (good in case you somehow cannot get rid of older events)
|
||||
|
||||
After any of the options, you may need to disconnect/force close Gadgetbridge before reconnecting and let it sync (give it some time for that too), restart the agenda app on the bangle after a while to see the changes.
|
||||
|
||||
### Report a bug
|
||||
|
||||
You can easily open an issue in the espruino repo, but I won't be notified and it might take time.
|
||||
If you want a (hopefully) quicker response, just report [on my fork](https://github.com/glemco/BangleApps).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
(function() {
|
||||
var agendaItems = {
|
||||
name: "Agenda",
|
||||
img: atob("GBiBAf////////85z/AAAPAAAPgAAP////AAAPAAAPAAAPAAAOAAAeAAAeAAAcAAA8AAAoAABgAADP//+P//8PAAAPAAAPgAAf///w=="),
|
||||
items: []
|
||||
};
|
||||
|
||||
var now = new Date();
|
||||
var agenda = storage.readJSON("android.calendar.json")
|
||||
.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000)
|
||||
.sort((a,b)=>a.timestamp - b.timestamp);
|
||||
|
||||
agenda.forEach((entry, i) => {
|
||||
|
||||
var title = entry.title.slice(0,18);
|
||||
var date = new Date(entry.timestamp*1000);
|
||||
var dateStr = locale.date(date).replace(/\d\d\d\d/,"");
|
||||
dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : "";
|
||||
|
||||
agendaItems.items.push({
|
||||
name: null,
|
||||
get: () => ({ text: title + "\n" + dateStr, img: null}),
|
||||
show: function() { agendaItems.items[i].emit("redraw"); },
|
||||
hide: function () {}
|
||||
});
|
||||
});
|
||||
|
||||
return agendaItems;
|
||||
})
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
title,
|
||||
description,
|
||||
location,
|
||||
color:int,
|
||||
calName,
|
||||
allDay: bool,
|
||||
}
|
||||
*/
|
||||
|
|
@ -24,19 +26,23 @@ var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
|||
|
||||
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
|
||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||
var settings = require("Storage").readJSON("agenda.settings.json",true)||{};
|
||||
|
||||
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp)
|
||||
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp);
|
||||
|
||||
function getDate(timestamp) {
|
||||
return new Date(timestamp*1000);
|
||||
}
|
||||
function formatDateLong(date, includeDay) {
|
||||
if(includeDay)
|
||||
return Locale.date(date)+" "+Locale.time(date,1);
|
||||
return Locale.time(date,1);
|
||||
function formatDateLong(date, includeDay, allDay) {
|
||||
let shortTime = Locale.time(date,1)+Locale.meridian(date);
|
||||
if(allDay) shortTime = "";
|
||||
if(includeDay || allDay)
|
||||
return Locale.date(date)+" "+shortTime;
|
||||
return shortTime;
|
||||
}
|
||||
function formatDateShort(date) {
|
||||
return Locale.date(date).replace(/\d\d\d\d/,"")+Locale.time(date,1);
|
||||
function formatDateShort(date, allDay) {
|
||||
return Locale.date(date).replace(/\d\d\d\d/,"")+(allDay?
|
||||
"" : Locale.time(date,1)+Locale.meridian(date));
|
||||
}
|
||||
|
||||
var lines = [];
|
||||
|
|
@ -45,7 +51,7 @@ function showEvent(ev) {
|
|||
if(!ev) return;
|
||||
g.setFont(bodyFont);
|
||||
//var lines = [];
|
||||
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10)
|
||||
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10);
|
||||
var titleCnt = lines.length;
|
||||
var start = getDate(ev.timestamp);
|
||||
var end = getDate((+ev.timestamp) + (+ev.durationInSeconds));
|
||||
|
|
@ -53,22 +59,24 @@ function showEvent(ev) {
|
|||
if (titleCnt) lines.push(""); // add blank line after title
|
||||
if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth())
|
||||
includeDay = false;
|
||||
if(includeDay) {
|
||||
if(includeDay || ev.allDay) {
|
||||
lines = lines.concat(
|
||||
/*LANG*/"Start:",
|
||||
g.wrapString(formatDateLong(start, includeDay), g.getWidth()-10),
|
||||
g.wrapString(formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10),
|
||||
/*LANG*/"End:",
|
||||
g.wrapString(formatDateLong(end, includeDay), g.getWidth()-10));
|
||||
g.wrapString(formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10));
|
||||
} else {
|
||||
lines = lines.concat(
|
||||
g.wrapString(Locale.date(start), g.getWidth()-10),
|
||||
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay), g.getWidth()-10),
|
||||
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay), g.getWidth()-10));
|
||||
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10),
|
||||
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10));
|
||||
}
|
||||
if(ev.location)
|
||||
lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
|
||||
if(ev.description)
|
||||
lines = lines.concat("",g.wrapString(ev.description, g.getWidth()-10));
|
||||
if(ev.calName)
|
||||
lines = lines.concat(/*LANG*/"Calendar"+": ", g.wrapString(ev.calName, g.getWidth()-10));
|
||||
lines = lines.concat(["",/*LANG*/"< Back"]);
|
||||
E.showScroller({
|
||||
h : g.getFontHeight(), // height of each menu item in pixels
|
||||
|
|
@ -89,6 +97,12 @@ function showEvent(ev) {
|
|||
}
|
||||
|
||||
function showList() {
|
||||
//it might take time for GB to delete old events, decide whether to show them grayed out or hide entirely
|
||||
if(!settings.pastEvents) {
|
||||
let now = new Date();
|
||||
//TODO add threshold here?
|
||||
CALENDAR = CALENDAR.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000);
|
||||
}
|
||||
if(CALENDAR.length == 0) {
|
||||
E.showMessage("No events");
|
||||
return;
|
||||
|
|
@ -101,24 +115,21 @@ function showList() {
|
|||
g.setColor(g.theme.fg);
|
||||
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||
if (!ev) return;
|
||||
var isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
|
||||
var isPast = false;
|
||||
var x = r.x+2, title = ev.title;
|
||||
var body = formatDateShort(getDate(ev.timestamp))+"\n"+ev.location;
|
||||
var m = ev.title+"\n"+ev.location, longBody=false;
|
||||
var body = formatDateShort(getDate(ev.timestamp),ev.allDay)+"\n"+(ev.location?ev.location:/*LANG*/"No location");
|
||||
if(settings.pastEvents) isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
|
||||
if (title) g.setFontAlign(-1,-1).setFont(fontBig)
|
||||
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x,r.y+2);
|
||||
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x+4,r.y+2);
|
||||
if (body) {
|
||||
g.setFontAlign(-1,-1).setFont(fontMedium).setColor(isPast ? "#888" : g.theme.fg);
|
||||
var l = g.wrapString(body, r.w-(x+14));
|
||||
if (l.length>3) {
|
||||
l = l.slice(0,3);
|
||||
l[l.length-1]+="...";
|
||||
}
|
||||
longBody = l.length>2;
|
||||
g.drawString(l.join("\n"), x+10,r.y+20);
|
||||
g.drawString(body, x+10,r.y+20);
|
||||
}
|
||||
//if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
|
||||
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
|
||||
if(ev.color) {
|
||||
g.setColor("#"+(0x1000000+Number(ev.color)).toString(16).padStart(6,"0"));
|
||||
g.fillRect(r.x,r.y+4,r.x+3, r.y+r.h-4);
|
||||
}
|
||||
},
|
||||
select : idx => showEvent(CALENDAR[idx]),
|
||||
back : () => load()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "agenda",
|
||||
"name": "Agenda",
|
||||
"version": "0.02",
|
||||
"version": "0.07",
|
||||
"description": "Simple agenda",
|
||||
"icon": "agenda.png",
|
||||
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
"storage": [
|
||||
{"name":"agenda.app.js","url":"agenda.js"},
|
||||
{"name":"agenda.settings.js","url":"settings.js"},
|
||||
{"name":"agenda.clkinfo.js","url":"agenda.clkinfo.js"},
|
||||
{"name":"agenda.img","url":"agenda-icon.js","evaluate":true}
|
||||
]
|
||||
],
|
||||
"data": [{"name":"agenda.settings.json"}]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
Bluetooth.println("");
|
||||
Bluetooth.println(JSON.stringify(message));
|
||||
}
|
||||
var settings = require("Storage").readJSON("agenda.settings.json",1)||{};
|
||||
function updateSettings() {
|
||||
require("Storage").writeJSON("agenda.settings.json", settings);
|
||||
}
|
||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||
var mainmenu = {
|
||||
"" : { "title" : "Agenda" },
|
||||
|
|
@ -32,6 +36,13 @@
|
|||
E.showAlert(/*LANG*/"You are not connected").then(()=>E.showMenu(mainmenu));
|
||||
}
|
||||
},
|
||||
/*LANG*/"Show past events" : {
|
||||
value : !!settings.pastEvents,
|
||||
onchange: v => {
|
||||
settings.pastEvents = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1 +1,4 @@
|
|||
0.01: First, proof of concept
|
||||
0.02: Load AGPS data on app start and automatically in background
|
||||
0.03: Do not load AGPS data on boot
|
||||
Increase minimum interval to 6 hours
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
# A-GPS Data
|
||||
|
||||
Load assisted GPS data directly to the watch using the new http requests on Android GadgetBridge.
|
||||
Load assisted GPS (A-GPS) data directly to your Bangle.js using the new http requests on Android GadgetBridge.
|
||||
|
||||
Will download A-GPS data in background (if enabled in settings).
|
||||
|
||||
The GNSS type can be configured in the settings.
|
||||
|
||||
Make sure:
|
||||
* your GadgetBridge version supports http requests
|
||||
* turn on internet access in GadgetBridge settings
|
||||
|
||||
Currently proof of concept on Bangle2 only. Will eventually add a widget for automatic download.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
Currently proof of concept on Bangle.js 2 only.
|
||||
|
||||
## Creator
|
||||
[@pidajo](https://github.com/pidajo)
|
||||
|
||||
## Contributor
|
||||
[@myxor](https://github.com/myxor)
|
||||
|
|
|
|||
|
|
@ -1,125 +1,54 @@
|
|||
var _GB = global.GB;
|
||||
var counter = 0;
|
||||
|
||||
function GB(msg) {
|
||||
console.log(msg);
|
||||
if (msg.t == "http") {
|
||||
display("Received", "(" + msg.resp.length + ") Touch to apply", () => {
|
||||
display("Apply data..", "");
|
||||
setTimeout(() => {
|
||||
if (setAGPS(msg.resp)) {
|
||||
display("Success", "Touch for restart", httpTest);
|
||||
}
|
||||
else {
|
||||
display("Error", "Touch for restart", httpTest);
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
}
|
||||
if (_GB) {
|
||||
_GB(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function setAGPS(data) {
|
||||
var js = jsFromBase64(data);
|
||||
console.log(js);
|
||||
try {
|
||||
eval(js);
|
||||
return true;
|
||||
}
|
||||
catch(e) {
|
||||
console.log("Error:", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function jsFromBase64(b64) {
|
||||
var bin = atob(b64);
|
||||
var chunkSize = 128;
|
||||
var js = "Bangle.setGPSPower(1);\n"; // turn GPS on
|
||||
var gnss_select="1";
|
||||
js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnss_select)}")\n`; // set GNSS mode
|
||||
// What about:
|
||||
// NAV-TIMEUTC (0x01 0x10)
|
||||
// NAV-PV (0x01 0x03)
|
||||
// or AGPS.zip uses AID-INI (0x0B 0x01)
|
||||
|
||||
for (var i=0;i<bin.length;i+=chunkSize) {
|
||||
var chunk = bin.substr(i,chunkSize);
|
||||
js += `Serial1.write(atob("${btoa(chunk)}"))\n`;
|
||||
}
|
||||
return js;
|
||||
}
|
||||
|
||||
function CASIC_CHECKSUM(cmd) {
|
||||
var cs = 0;
|
||||
for (var i=1;i<cmd.length;i++)
|
||||
cs = cs ^ cmd.charCodeAt(i);
|
||||
return cmd+"*"+cs.toString(16).toUpperCase().padStart(2, '0');
|
||||
}
|
||||
|
||||
function display(text1, text2, next) {
|
||||
function display(text1, text2) {
|
||||
g.reset();
|
||||
g.clear();
|
||||
var img = require("Storage").read("agpsdata.img");
|
||||
if (img) {
|
||||
g.drawImage(img, g.getWidth() - 48, g.getHeight()-48-24);
|
||||
g.drawImage(img, g.getWidth() - 48, g.getHeight() - 48 - 24);
|
||||
}
|
||||
g.setFont("Vector", 30);
|
||||
g.setFont("Vector", 18);
|
||||
g.setFontAlign(0, 1);
|
||||
g.drawString(text1, g.getWidth() / 2, g.getHeight() / 3 + 24);
|
||||
g.setFont("Vector", 15);
|
||||
g.setFontAlign(-1, -1);
|
||||
g.drawString(text2, 0, g.getHeight() / 3 + 29);
|
||||
if (text2 != undefined) {
|
||||
g.setFont("Vector", 12);
|
||||
g.setFontAlign(-1, -1);
|
||||
g.drawString(text2, 5, g.getHeight() / 3 + 29);
|
||||
}
|
||||
Bangle.drawWidgets();
|
||||
nextStep = null;
|
||||
if (next) {
|
||||
nextStep = next;
|
||||
}
|
||||
}
|
||||
|
||||
function httpTest() {
|
||||
counter++;
|
||||
display("AGPS Data", "Touch for start", () => {
|
||||
display("Request...", "Touch for restart", httpTest);
|
||||
if (Bluetooth.println) {
|
||||
console.log("On device");
|
||||
Bluetooth.println(JSON.stringify({t:"info", msg:"HTTP Request"}));
|
||||
Bluetooth.println(JSON.stringify({t:"http", url:"https://www.espruino.com/agps/casic.base64"}));
|
||||
}
|
||||
else {
|
||||
console.log("Testing on Emulator");
|
||||
setTimeout(() => {
|
||||
GB({t:"http", resp:testData});
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var nextStep = null;
|
||||
|
||||
Bangle.on("touch", () => {
|
||||
if (nextStep) {
|
||||
nextStep();
|
||||
}
|
||||
});
|
||||
|
||||
httpTest();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
let waiting = false;
|
||||
|
||||
/*
|
||||
require("Storage").write("httptest.info",{
|
||||
"id":"httptest",
|
||||
"name":"Http Test",
|
||||
"src":"httptest.js",
|
||||
"icon":"wristlight.img"
|
||||
});
|
||||
*/
|
||||
function start() {
|
||||
g.reset();
|
||||
g.clear();
|
||||
waiting = false;
|
||||
display("Retry?", "touch to retry");
|
||||
Bangle.on("touch", () => { updateAgps(); });
|
||||
}
|
||||
|
||||
var testData = "QUdOU1MgZGF0YSBmcm9tIENBU0lDLgpEYXRhTGVuZ3RoOiAyNTk4LgpMaW1pdGF0aW9uOiAzLzEwMDAuCrrOSAAIB7YdxSr+Sg2h8NYlBux1jiUgQbrXgJk/KJvFZVv8pP//uy3i/PH6rv9EMQH6SwBfAOxepgDsXgAAlCULALv/AAtCAAAAAQMAALQ7kly6zkgACAdBzVam9HANoXGycgoqGmnG5X9h3mKrWicvBKhXAp7//+00U/9j/jP/SDHM/Vn/JADrXqYA614AAF6d6v8DAADaEAAAAAIDAADKmrVTus5IAAgHTUirJrjvDKHJDjACcmXJJ+8Lv6rw5LQnl4OChUCt//9XKG3/+u6ZE84ZQuwDAMf/7F6mAOxeAADa0Pb/mP8ABDUAAAADAwAA4pBeVLrOSAAIB5291DTIzAyhJGfzAJ7pCIcIUQMgcfEoJ2TIjrHkqv//YjDTCKj/wRPdGIz/8/9NAOxepgDsXgAAl9/6/yQAAPbhAAAABAMAAIJ7sXC6zkgACAeKKDOgmwEOocKrFwP8Jmgqq4lOQ1VtLSfEi8yDVqn//5MskP6E7WQRPxzv6vv/0//sXqYA7F4AAM4w/f/0/wDoJwAAAAUDAABcUW5Hus5IAAgHu+Va48nxDaFaw0UBkVc73Y3FB+HFmDgoUWIPW+Ck//+iLcf9X/t5/ikzgPrT/wgA7F6mAOxeAAADVgsAiQAACB0AAAAGAwAAvsu9zbrOSAAIB0OVp/7+JQ2hFANPCF+F6KOOMwe9/nC5JsX0CtthqP//WzhzADr/dgqbIRj/nwDj/+xepgDsXgAAP4oKAPv/AOg4AAAABwMAAM4qVwS6zkgACAey9J9b+zwOofLIzwObDg0HLdEmMOA3PydWaUQvr6b//0wxwf/7EJ8K/yLREhkANADsXqYA7F4AALug/f/y/wALLAAAAAgDAACs6Ue+us5IAAgHm7NJLLhaDKEumRQBAFtVTExfuke3AeEmNg9cr2Cq//92MTIJm/63FCkXPv4gABgA616mAOteAACQQfX/HgAAAzUAAAAJAwAAfmebX7rOSAAIB9fgVnnchw2hNlTrAyWnJ5rnBMWFV9GyJzPfZYV8rf//Oyh2AA3xmhLdGtXu/f/Z/+xepgDsXgAA8gXx/37/AAU/AAAACgMAAPbBtfm6zkgACAc+F7Ct97wMoVEVPQCJJG1yeakXMm80PSet+BBd+aH//5AzPf2J+uX97jG7+fj/DgDsXqYA7F4AAGqE//8WAADufQIAAAsDAADELmhius5IAAgHW3g6rwRJDqFpPmYEPf8lNRy9lKO/IX8nJPRjCLOt//90Lir7QQcEGFYUTAguAA4A7F6mAOxeAADrZfj/zP8A5SsAAAAMAwAA/vB8ZbrOSAAIB1mVSsfiXw2hUX8RA60JSyUgLkEgC910JykTq7Usqv//8S9rBw//HhIpGyT/+v8fAOxepgDsXgAAitgKAD4AAOcpAAAADQMAAPoqnZW6zkgACAcCLzz8Q1ANocBvBQFuUWqAxVSgoRjR0SaS5AkH3qr//7cx3vlcB2AYPROjCOr/vf/sXqYA7F4AAA5Y/P/7/wDvGwMAAA4DAABMXoD/us5IAAgH+RiyseCLDKGnOjkHATiXLOud6AnbGuclr2roqg2l///FNxcHi/0hFLoW4fyj/10A7F6mAOxeAAANRf7/GgAA6TQAAAAPAwAAOjJsarrOSAAIB1/+xFM3Xg2hVDKBBg6Tsx25u5hYROV9J/QyJQmLrf//zC1e+7QGxRfmFOAHhf+s/+xepgDsXgAAaE/v/+j/AOonAAAAEAMAAAb9ka66zkgACAc74z4AUZEMofLN6wbbUEjD/w54MWPC3SfOFa4yQ6f//4wtrwH5EOwKOCRTFLz/UP/sXqYA7F4AAOaAFAApAADoOQAAABEDAAC+xoUHus5IAAgH1yXSbYfZDaFOrzwBIM5keona3uYD8JYnC6ekWw+l//8JMC/9ivsaAGEwM/ssAN//7F6mAOxeAAAymwQAof8A7l8AAAASAwAA9kus4rrOSAAIB/9znedCsA2h5VDBBLdHG1Uw8P6P93bRJw5UgTR9qf//LS1BAj0TAwkqJioWBABHAOxepgDsXgAAD54FACwAAN6xAAAAEwMAAEboQta6zkgACAdVJvSzetsMoRclcgKU4/OAvUl5A73wdCYbpBB/P6X//08yQ/4N7voNgx5160gAFwDsXqYA7F4AADa+EADk/wDuLwAAABQDAADyTPBuus5IAAgHhpZXHJnuDaGfsmkMbSLS2QeTnDZBLR8ntwGPV8mj//+SM6n8rftj/EUyZfw7AaX/7F6mAOxeAAAvSQUAAAAA6j4AAAAVAwAAVC23P7rOSAAIB8FSSGuHmw2hXoTeBoU+tLS9TdsrYGopJ+h3h7O9p///mjEIB3X/GhN5GXP/c//V/+xepgDsXgAASREJADgAAO4rAAAAFgMAAMqlmN26zkgACAeGsPJwF8ENoU2cJwHhxiN62PG7uVyKfydPWVyEG6z//8spSgCQ8NkRnxsl7sr/EQDsXqYA7F4AAI0S///u/wDudQEAABcDAABUYe3ous5IAAgHtHJyvWVdDaFr1mwGwPVWIZcaxOQXwAwmeHqW1+Sh//+PPnAAQgAOCxwhof8sAGwA7F6mAOxeAAAhMQcAtf8ABjsAAAAYAwAAsOXsgbrOSAAIB4nxObhoSQ2hOjBcBf2n5ijflOaX/Iz8JpPiNAUTrP//ajEL++oEchfzE9AFSgDT/+tepgDrXgAAohMLACkAAAweAAAAGQMAAFrje3e6zkgACAexAdJ/MyYOoeXvjwPazb0PcXcXfIVaNCZ2mjMDkqn//z02W/qPBMcW7BM7BcX/6//sXqYA7F4AABv4BgAVAAAPIgAAABoDAACqA6wGus5IAAgHxYz/b8dNDaH6/WQF3AILHLKDgTJ+VponRocZMKSm//8bLycAMRANDG8i/RHR/2wA7F6mAOxeAAArEwcAHAAABEgAAQAbAwAA0hkH57rOSAAIB5HXkJcOjA2h8B4kAebzvl2gmkU1K1TzJ86wODP6qP//0CzCAM4OmQrkI1UR7f/n/+xepgDsXgAAcs/u/9//AOplAAAAHQMAAGqvKTa6zkgACAekogIGh+8NoYBTBQMDtQeTiKQ4u0KYHiYkF4HbzaT//7A80v9N/gQLmSC4/icA6v/sXqYA7F4AAB2V7v/2/wAIGQAAAB4DAACQRQ0Tus5IAAgHUsOCUJ71DaHkPFgFdJcpEDguP6ldR+UmgbrM2+6m//9vOPT/S/7vC1sh6/49AJH/7F6mAOxeAAAQCfr/8/8A4wwAAAAfAwAA7IYNqLrOSAAIByZDZIfa4AyhxlEVA9QtFKN+R+1NPIIIJ8xm1q8Qqv//djDzB9H+pBNQGGj+rv++/+xepgDsXgAA3f/6/67/AAFUAAAAIAMAAJSG0BW6zhQACAWVGZOmAAAAAPr///8SEpCmiQcDAD4zLlK6zhAACAZIDf33DwP+/jYK//gDAAAAoBoC9g==";
|
||||
function updateAgps() {
|
||||
g.reset();
|
||||
g.clear();
|
||||
if (!waiting) {
|
||||
waiting = true;
|
||||
display("Updating A-GPS...");
|
||||
require("agpsdata").pull(function() {
|
||||
waiting = false;
|
||||
display("A-GPS updated.", "touch to close");
|
||||
Bangle.on("touch", () => { load(); });
|
||||
},
|
||||
function(error) {
|
||||
waiting = false;
|
||||
E.showAlert(error, "Error")
|
||||
.then(() => { start(); });
|
||||
});
|
||||
} else {
|
||||
display("Waiting...");
|
||||
}
|
||||
}
|
||||
updateAgps();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
(function() {
|
||||
let waiting = false;
|
||||
let settings = require("Storage").readJSON("agpsdata.settings.json", 1) || {
|
||||
enabled: true,
|
||||
refresh: 1440
|
||||
};
|
||||
|
||||
if (settings.refresh == undefined) settings.refresh = 1440;
|
||||
|
||||
function successCallback(){
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
function errorCallback(){
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
if (settings.enabled) {
|
||||
setInterval(() => {
|
||||
if (!waiting && NRF.getSecurityStatus().connected){
|
||||
waiting = true;
|
||||
require("agpsdata").pull(successCallback, errorCallback);
|
||||
}
|
||||
}, settings.refresh * 1000 * 60);
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"enabled":true,"refresh":1440,"gnsstype":1}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
function readSettings() {
|
||||
settings = Object.assign(
|
||||
require('Storage').readJSON("agpsdata.default.json", true) || {},
|
||||
require('Storage').readJSON(FILE, true) || {});
|
||||
}
|
||||
|
||||
var FILE = "agpsdata.settings.json";
|
||||
var settings;
|
||||
readSettings();
|
||||
|
||||
function setAGPS(data) {
|
||||
var js = jsFromBase64(data);
|
||||
try {
|
||||
eval(js);
|
||||
return true;
|
||||
}
|
||||
catch(e) {
|
||||
console.log("error:", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function jsFromBase64(b64) {
|
||||
var bin = atob(b64);
|
||||
var chunkSize = 128;
|
||||
var js = "Bangle.setGPSPower(1);\n"; // turn GPS on
|
||||
var gnsstype = settings.gnsstype || 1; // default GPS
|
||||
js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnsstype)}")\n`; // set GNSS mode
|
||||
// What about:
|
||||
// NAV-TIMEUTC (0x01 0x10)
|
||||
// NAV-PV (0x01 0x03)
|
||||
// or AGPS.zip uses AID-INI (0x0B 0x01)
|
||||
|
||||
for (var i=0;i<bin.length;i+=chunkSize) {
|
||||
var chunk = bin.substr(i,chunkSize);
|
||||
js += `Serial1.write(atob("${btoa(chunk)}"))\n`;
|
||||
}
|
||||
return js;
|
||||
}
|
||||
|
||||
function CASIC_CHECKSUM(cmd) {
|
||||
var cs = 0;
|
||||
for (var i=1;i<cmd.length;i++)
|
||||
cs = cs ^ cmd.charCodeAt(i);
|
||||
return cmd+"*"+cs.toString(16).toUpperCase().padStart(2, '0');
|
||||
}
|
||||
|
||||
function updateLastUpdate() {
|
||||
const file = "agpsdata.json";
|
||||
let data = require("Storage").readJSON(file, 1) || {};
|
||||
data.lastUpdate = Math.round(Date.now());
|
||||
require("Storage").writeJSON(file, data);
|
||||
}
|
||||
|
||||
exports.pull = function(successCallback, failureCallback) {
|
||||
let uri = "https://www.espruino.com/agps/casic.base64";
|
||||
if (Bangle.http){
|
||||
Bangle.http(uri, {timeout:10000}).then(event => {
|
||||
let result = setAGPS(event.resp);
|
||||
if (result) {
|
||||
updateLastUpdate();
|
||||
if (successCallback) successCallback();
|
||||
} else {
|
||||
console.log("error applying AGPS data");
|
||||
if (failureCallback) failureCallback("Error applying AGPS data");
|
||||
}
|
||||
}).catch((e)=>{
|
||||
console.log("error", e);
|
||||
if (failureCallback) failureCallback(e);
|
||||
});
|
||||
} else {
|
||||
console.log("error: No http method found");
|
||||
if (failureCallback) failureCallback(/*LANG*/"No http method");
|
||||
}
|
||||
};
|
||||
|
|
@ -1,16 +1,24 @@
|
|||
{ "id": "agpsdata",
|
||||
"name": "A-GPS Data",
|
||||
"shortName":"AGPS Data",
|
||||
"name": "A-GPS Data Downloader App",
|
||||
"shortName":"A-GPS Data",
|
||||
"icon": "agpsdata.png",
|
||||
"version":"0.01",
|
||||
"description": "Download assisted GPS data directly to watch",
|
||||
"tags": "assisted,gps,agps,http",
|
||||
"version":"0.03",
|
||||
"description": "Once installed, this app allows you to download assisted GPS (A-GPS) data directly to your Bangle.js **via Gadgetbridge on an Android phone** when you run the app. If you just want to upload the latest AGPS data from this app loader, please use the `Assisted GPS Update (AGPS)` app.",
|
||||
"tags": "boot,tool,assisted,gps,agps,http",
|
||||
"allow_emulator":true,
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme":"README.md",
|
||||
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" }, { "url":"screenshot3.png" }, { "url":"screenshot4.png" }, { "url":"screenshot5.png" } ],
|
||||
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" } ],
|
||||
"storage": [
|
||||
{"name":"agpsdata.app.js","url":"app.js"},
|
||||
{"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true}
|
||||
{"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true},
|
||||
{"name":"agpsdata.default.json","url":"default.json"},
|
||||
{"name":"agpsdata.boot.js","url":"boot.js"},
|
||||
{"name":"agpsdata","url":"lib.js"},
|
||||
{"name":"agpsdata.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name": "agpsdata.json"},
|
||||
{"name": "agpsdata.settings.json"}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 613 B After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 695 B |
|
Before Width: | Height: | Size: 533 B |
|
Before Width: | Height: | Size: 578 B |
|
|
@ -0,0 +1,71 @@
|
|||
(function(back) {
|
||||
function writeSettings(key, value) {
|
||||
var s = Object.assign(
|
||||
require('Storage').readJSON(settingsDefaultFile, true) || {},
|
||||
require('Storage').readJSON(settingsFile, true) || {});
|
||||
s[key] = value;
|
||||
require('Storage').writeJSON(settingsFile, s);
|
||||
readSettings();
|
||||
}
|
||||
|
||||
function readSettings() {
|
||||
settings = Object.assign(
|
||||
require('Storage').readJSON(settingsDefaultFile, true) || {},
|
||||
require('Storage').readJSON(settingsFile, true) || {});
|
||||
}
|
||||
|
||||
var settingsFile = "agpsdata.settings.json";
|
||||
var settingsDefaultFile = "agpsdata.default.json";
|
||||
|
||||
var settings;
|
||||
readSettings();
|
||||
|
||||
const gnsstypes = [
|
||||
"", "GPS", "BDS", "GPS+BDS", "GLONASS", "GPS+GLONASS", "BDS+GLONASS",
|
||||
"GPS+BDS+GLON."
|
||||
];
|
||||
|
||||
function buildMainMenu() {
|
||||
var mainmenu = {
|
||||
'' : {'title' : 'AGPS download'},
|
||||
'< Back' : back,
|
||||
"Enabled" : {
|
||||
value : !!settings.enabled,
|
||||
onchange : v => { writeSettings("enabled", v); }
|
||||
},
|
||||
"Refresh every" : {
|
||||
value : settings.refresh / 60,
|
||||
min : 6,
|
||||
max : 168,
|
||||
step : 1,
|
||||
format : v => v + "h",
|
||||
onchange : v => { writeSettings("refresh", Math.round(v * 60)); }
|
||||
},
|
||||
"GNSS type" : {
|
||||
value : settings.gnsstype,
|
||||
min : 1,
|
||||
max : 7,
|
||||
step : 1,
|
||||
format : v => gnsstypes[v],
|
||||
onchange : x => writeSettings('gnsstype', x)
|
||||
},
|
||||
"Force refresh" : () => {
|
||||
E.showMessage("Loading A-GPS data");
|
||||
require("agpsdata")
|
||||
.pull(
|
||||
function() {
|
||||
E.showAlert("Success").then(
|
||||
() => { E.showMenu(buildMainMenu()); });
|
||||
},
|
||||
function(error) {
|
||||
E.showAlert(error, "Error")
|
||||
.then(() => { E.showMenu(buildMainMenu()); });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return mainmenu;
|
||||
}
|
||||
|
||||
E.showMenu(buildMainMenu());
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
0.01: New app!
|
||||
0.02: Design improvements and fixes.
|
||||
0.03: Indicate battery level through line occurrence.
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# AI Clock
|
||||
This clock was designed by stable diffusion ([paper](https://arxiv.org/abs/2112.10752)) using the following prompt:
|
||||
|
||||
`A rectangle banglejs watchface`
|
||||
|
||||
|
||||
The original output of stable diffusion is shown here:
|
||||
|
||||

|
||||
|
||||
My implementation is shown below. Note that horizontal lines occur randomly, but the
|
||||
probability is correlated with the battery level. So if your screen contains only
|
||||
a few lines its time to charge your bangle again ;)
|
||||
|
||||

|
||||
|
||||
|
||||
# Thanks to
|
||||
The great open-source community: I used an open-source diffusion model (https://github.com/CompVis/stable-diffusion)
|
||||
to generate a watch face for the open-source smartwatch BangleJs.
|
||||
|
||||
## Creator
|
||||
- [David Peer](https://github.com/peerdavid).
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/**
|
||||
* AI Clock
|
||||
*/
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
Graphics.prototype.setFontGochiHand = function(scale) {
|
||||
// Actual height 27 (29 - 3)
|
||||
this.setFontCustom(
|
||||
atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAA8AAAAADwAAAAAPAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAA/+AAAB//4AAH///gAH///gAAf//AAAB/+AAAAH8AAAAAAAAAAAAAAAAAAAAH8AAAAB/8AAAAP/4AAAB//wAAAPx/AAAB8B+AAAHgD4AAA+AHgAADwAeAAAPAB4AAA8AHgAAD4AeAAAPgB4AAAeAPgAAB8A8AAAH4HwAAAP/+AAAAf/wAAAA/+AAAAB/wAAAAB8AAAAAAAAAAADgAAAAAfAAAAAB4AAAAAPAAAAAB8AAAAAHgAAAAA8AAAAADwAAAAAf4AAAAB//8AAAD//4AAAH//gAAAD/+AAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AHgAAHgA+AAA/AD4AAD4AfgAAfAD+AAB4Af4AAHgD/gAAeAfeAAB4D54AAHw/HgAAf/4fAAA//B8AAD/4DwAAH+APAAAHgA8AAAAADwAAAAAOAAAAAAAAABgAAAAAPAAAAAB8AAAAAHwAYAAAeAD4AAD4APwAAPA4fgAA8Hw+AADwfB4AAPh4HwAA+HgPAAB/+A8AAH/4DwAAP/weAAAf/j4AAAc//gAAAB/8AAAAD/gAAAAD8AAAAAAAAAAAAAAAAAADAAAAAA+AAAAAP4AAAAB/wAAAAP/AAAAD+8AAAAfzwAAAf8HAAAB/gcAAAH/hwAAAf//gAAA//+AAAAf//gAAAP//gAAAD/+AAAAB/4AAAAH/AAAAAeAAAAAAgAAAAAAAAAAAAcAAAB8H8AAAP4f4AAA/x/wAAD/H/gAAf+A+AAB74B4AAHnwHgAAefAfAAB58A8AAHj4DwAAePgPAAB4fA8AAHh+HgAAeD8+AAB4P/4AAHgf/AAAeA/4AAAAA+AAAAAAAAAAAAAAAAAAHgAAAAD/wAAAA//gAAAH//AAAA//+AAAD4H8AAAfA/wAAB4D/AAAHgP+AAAeB54AAB4HngAAHweeAAAfB54AAA4HngAAAAeeAAAAB/4AAAAH/AAAAAP4AAAAAfAAADwAAAAAPAAAAAA8HgAAADweAAAAPB4AAAA8HgAAADweAAAAPh4AAAA+HgAAAB4eAAAAHx4AAAAf//8AAA///wAAD//+AAAH//4AAAAeAAAAAB4AAAAAHgAAAAAeAAAAAB4AAAAAHgAAAAAAAAAAAAAAAAAAD+AAAA+f+AAAH//8AAA///wAAH/4fgAAePgeAAB4+B4AAHj4HwAAePgPAAB4+A8AAHz4DwAAfngeAAA//B4AAD/+HgAAH//8AAAP//wAAAAf+AAAAA/wAAAAAYAAAAAAAAAAA/gAAAAH/AAAAA/8AAAAD34AAAAeHgAAAB4eAAAAHh4AAAA8HgAAADweAAAAPDwAAAA8PAAAADx4AAAAPvgAAAAf///AAB///8AAH///wAAP///AAA/wA4AABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOA4AAAA8DwAAADwPAAAAPA8AAAAYBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='),
|
||||
46,
|
||||
atob("DQoXEBQVExUUFRYUDQ=="),
|
||||
40+(scale<<8)+(1<<16)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set some important constants such as width, height and center
|
||||
*/
|
||||
var W = g.getWidth(),R=W/2;
|
||||
var H = g.getHeight();
|
||||
var cx = W/2;
|
||||
var cy = H/2;
|
||||
var drawTimeout;
|
||||
|
||||
/*
|
||||
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
|
||||
*/
|
||||
Graphics.prototype.drawRotRect = function(w, r1, r2, angle) {
|
||||
angle = angle % 360;
|
||||
var w2=w/2, h=r2-r1, theta=angle*Math.PI/180;
|
||||
return this.fillPoly(this.transformVertices([-w2,0,-w2,-h,w2,-h,w2,0],
|
||||
{x:cx+r1*Math.sin(theta),y:cy-r1*Math.cos(theta),rotate:theta}));
|
||||
};
|
||||
|
||||
|
||||
function drawBackground() {
|
||||
g.setFontAlign(0,0);
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
var bat = E.getBattery() / 100.0;
|
||||
var y = 0;
|
||||
while(y < H){
|
||||
// Show less lines in case of small battery level.
|
||||
if(Math.random() > bat){
|
||||
y += 5;
|
||||
continue;
|
||||
}
|
||||
|
||||
y += 3 + Math.floor(Math.random() * 10);
|
||||
g.drawLine(0, y, W, y);
|
||||
g.drawLine(0, y+1, W, y+1);
|
||||
g.drawLine(0, y+2, W, y+2);
|
||||
y += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function drawCircle(isLocked){
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(cx, cy, 12);
|
||||
|
||||
var c = isLocked ? "#f00" : g.theme.bg;
|
||||
g.setColor(c);
|
||||
g.fillCircle(cx, cy, 6);
|
||||
}
|
||||
|
||||
function toAngle(a){
|
||||
if (a < 0){
|
||||
return 360 + a;
|
||||
}
|
||||
|
||||
if(a > 360) {
|
||||
return 360 - a;
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
function drawTime(){
|
||||
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
|
||||
var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 );
|
||||
|
||||
g.setFontAlign(0,0);
|
||||
|
||||
// Compute angles
|
||||
var date = new Date();
|
||||
var m = parseInt(date.getMinutes() * 360 / 60);
|
||||
var h = date.getHours();
|
||||
h = h > 12 ? h-12 : h;
|
||||
h += date.getMinutes()/60.0;
|
||||
h = parseInt(h*360/12);
|
||||
|
||||
// Draw minute and hour bg
|
||||
g.setColor(g.theme.bg);
|
||||
drawHourHand(toAngle(h-3));
|
||||
drawHourHand(toAngle(h+3));
|
||||
drawMinuteHand(toAngle(m-2));
|
||||
drawMinuteHand(toAngle(m+3));
|
||||
|
||||
// Draw minute and hour fg
|
||||
g.setColor(g.theme.fg);
|
||||
drawHourHand(h);
|
||||
drawMinuteHand(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function drawDate(){
|
||||
var date = new Date();
|
||||
g.setFontAlign(0,0);
|
||||
g.setFontGochiHand();
|
||||
|
||||
var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2);
|
||||
var w = g.stringWidth(text);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12);
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
// Draw right line as designed by stable diffusion
|
||||
g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12);
|
||||
g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12);
|
||||
g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12);
|
||||
|
||||
// And finally the text
|
||||
g.drawString(text, cx, 40);
|
||||
}
|
||||
|
||||
|
||||
function drawDigits(){
|
||||
var date = new Date();
|
||||
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("7x11Numeric7Seg",3);
|
||||
|
||||
var text = ("0"+date.getHours()).substr(-2) + ":" + ("0"+date.getMinutes()).substr(-2); //Bangle.getHealthStatus("day").steps;
|
||||
var w = g.stringWidth(text);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(cx-w/2-4, 120, cx+w/2+4, 140+20);
|
||||
|
||||
// Draw right line as designed by stable diffusion
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawLine(cx+w/2+5, 120, cx+w/2+5, 140+20);
|
||||
g.drawLine(cx+w/2+6, 120, cx+w/2+6, 140+20);
|
||||
g.drawLine(cx+w/2+7, 120, cx+w/2+7, 140+20);
|
||||
|
||||
// And the 7set text
|
||||
g.setColor("#BBB");
|
||||
g.drawString("88:88", cx, 140);
|
||||
g.drawString("88:88", cx+1, 140);
|
||||
g.drawString("88:88", cx, 141);
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString(text, cx, 140);
|
||||
g.drawString(text, cx+1, 140);
|
||||
g.drawString(text, cx, 141);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function draw(){
|
||||
// Queue draw in one minute
|
||||
queueDraw();
|
||||
|
||||
|
||||
g.reset();
|
||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||
|
||||
g.setColor(1,1,1);
|
||||
drawBackground();
|
||||
drawDate();
|
||||
drawDigits();
|
||||
drawTime();
|
||||
drawCircle(Bangle.isLocked());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Listeners
|
||||
*/
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(true);
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
drawCircle(isLocked);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Some helpers
|
||||
*/
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lets start widgets, listen for btn etc.
|
||||
*/
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
/*
|
||||
* we are not drawing the widgets as we are taking over the whole screen
|
||||
* so we will blank out the draw() functions of each widget and change the
|
||||
* area to the top bar doesn't get cleared.
|
||||
*/
|
||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||
|
||||
// Clear the screen once, at startup and draw clock
|
||||
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
||||
draw();
|
||||
|
||||
// After drawing the watch face, we can draw the widgets
|
||||
// Bangle.drawWidgets();
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgP/ACfAEZU/ECZELIKhSR/+PAoWAv4FDhk/x/ggP+j0fx/AgP8n8PCIX8CwIFC/F/w4FBgP4gEHC4QFE//w//DC4QFB8YFC+P/8IdCAoYdBAoPxDoQAd+CiKh4dQwDhfAA4A="))
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"id": "aiclock",
|
||||
"name": "AI Clock",
|
||||
"shortName":"AI Clock",
|
||||
"icon": "aiclock.png",
|
||||
"version":"0.03",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"screenshots": [
|
||||
{"url":"orig.png"},
|
||||
{"url":"impl.png"}
|
||||
],
|
||||
"storage": [
|
||||
{"name":"aiclock.app.js","url":"aiclock.app.js"},
|
||||
{"name":"aiclock.img","url":"aiclock.icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 49 KiB |
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Added adjustment for Bangle.js magnetometer heading fix
|
||||
|
|
@ -224,7 +224,7 @@ Bangle.on('mag', function (m) {
|
|||
if (isNaN(m.heading))
|
||||
compass_heading = "---";
|
||||
else
|
||||
compass_heading = 360 - Math.round(m.heading);
|
||||
compass_heading = Math.round(m.heading);
|
||||
current_colour = g.getColor();
|
||||
g.reset();
|
||||
g.setColor(background_colour);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "alpinenav",
|
||||
"name": "Alpine Nav",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "App that performs GPS monitoring to track and display position relative to a given origin in realtime",
|
||||
"icon": "app-icon.png",
|
||||
"tags": "outdoors,gps",
|
||||
|
|
|
|||
|
|
@ -11,4 +11,6 @@
|
|||
0.10: Fix SMS bug
|
||||
0.12: Use default Bangle formatter for booleans
|
||||
0.13: Added Bangle.http function (see Readme file for more info)
|
||||
0.14: Fix timeout of http function not beeing cleaned up
|
||||
0.14: Fix timeout of http function not being cleaned up
|
||||
0.15: Allow method/body/headers to be specified for `http` (needs Gadgetbridge 0.68.0b or later)
|
||||
0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152)
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@
|
|||
// options = {id,timeout,xpath}
|
||||
Bangle.http = (url,options)=>{
|
||||
options = options||{};
|
||||
if (!NRF.getSecurityStatus().connected)
|
||||
return Promise.reject("Not connected to Bluetooth");
|
||||
if (Bangle.httpRequest === undefined)
|
||||
Bangle.httpRequest={};
|
||||
if (options.id === undefined) {
|
||||
|
|
@ -150,6 +152,9 @@
|
|||
//send the request
|
||||
var req = {t: "http", url:url, id:options.id};
|
||||
if (options.xpath) req.xpath = options.xpath;
|
||||
if (options.method) req.method = options.method;
|
||||
if (options.body) req.body = options.body;
|
||||
if (options.headers) req.headers = options.headers;
|
||||
gbSend(req);
|
||||
//create the promise
|
||||
var promise = new Promise(function(resolve,reject) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "android",
|
||||
"name": "Android Integration",
|
||||
"shortName": "Android",
|
||||
"version": "0.14",
|
||||
"version": "0.16",
|
||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
0.01: New App!
|
||||
0.02: Fix bug if image clock wasn't installed
|
||||
0.03: Update to use setUI
|
||||
0.04: Tell clock widgets to hide. Move loadWidgets() so it only runs on
|
||||
startup and not on every draw.
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ if (g.drawImages) {
|
|||
draw();
|
||||
var secondInterval = setInterval(draw,100);
|
||||
// load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
// Stop when LCD goes off
|
||||
Bangle.on('lcdPower',on=>{
|
||||
|
|
@ -104,3 +103,5 @@ if (g.drawImages) {
|
|||
}
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "animclk",
|
||||
"name": "Animated Clock",
|
||||
"shortName": "Anim Clock",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "An animated clock face using Mark Ferrari's amazing 8 bit game art and palette cycling: http://www.markferrari.com/art/8bit-game-art",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
|
|
|
|||
|
|
@ -10,4 +10,7 @@
|
|||
week is buffered until date or timezone changes
|
||||
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
||||
0.08: fixed calendar weeknumber not shortened to two digits
|
||||
0.09: Use default Bangle formatter for booleans
|
||||
0.09: Use default Bangle formatter for booleans
|
||||
0.10: Use Bangle.setUI({remove:...}) to allow loading the launcher without a full reset on 2v16
|
||||
0.11: Moved enhanced Anton clock to 'Anton Clock Plus' and stripped this clock back down to make it faster for new users (270ms -> 170ms)
|
||||
Modified to avoid leaving functions defined when using setUI({remove:...})
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"id": "antonclk",
|
||||
"name": "Anton Clock",
|
||||
"version": "0.09",
|
||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||
"readme":"README.md",
|
||||
"version": "0.11",
|
||||
"description": "A simple clock using the bold Anton font. See `Anton Clock Plus` for an enhanced version",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
|
|
@ -12,8 +11,6 @@
|
|||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"antonclk.app.js","url":"app.js"},
|
||||
{"name":"antonclk.settings.js","url":"settings.js"},
|
||||
{"name":"antonclk.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"antonclk.json"}]
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
0.01: New App!
|
||||
0.02: Load widgets after setUI so widclk knows when to hide
|
||||
0.03: Clock now shows day of week under date.
|
||||
0.04: Clock can optionally show seconds, date optionally in ISO-8601 format, weekdays and uppercase configurable, too.
|
||||
0.05: Clock can optionally show ISO-8601 calendar weeknumber (default: Off)
|
||||
when weekday name "Off": week #:<num>
|
||||
when weekday name "On": weekday name is cut at 6th position and .#<week num> is added
|
||||
0.06: fixes #1271 - wrong settings name
|
||||
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
|
||||
week is buffered until date or timezone changes
|
||||
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
||||
0.08: fixed calendar weeknumber not shortened to two digits
|
||||
0.09: Use default Bangle formatter for booleans
|
||||
0.10: Use Bangle.setUI({remove:...}) to allow loading the launcher without a full reset on 2v16
|
||||
Modified to avoid leaving functions defined when using setUI({remove:...})
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Anton Clock - Large font digital watch with seconds and date
|
||||
# Anton Clock Plus - Large font digital watch with seconds and date
|
||||
|
||||
Anton clock uses the "Anton" bold font to show the time in a clear, easily readable manner. On the Bangle.js 2, the time can be read easily even if the screen is locked and unlit.
|
||||
Anton Clock Plus uses the "Anton" bold font to show the time in a clear, easily readable manner. On the Bangle.js 2, the time can be read easily even if the screen is locked and unlit.
|
||||
|
||||
## Features
|
||||
|
||||
|
|
@ -16,16 +16,16 @@ The basic time representation only shows hours and minutes of the current time.
|
|||
|
||||
## Usage
|
||||
|
||||
Install Anton clock through the Bangle.js app loader.
|
||||
Configure it through the default Bangle.js configuration mechanism
|
||||
* Install Anton Clock Plus through the Bangle.js app loader.
|
||||
* Configure it through the default Bangle.js configuration mechanism
|
||||
(Settings app, "Apps" menu, "Anton clock" submenu).
|
||||
If you like it, make it your default watch face
|
||||
* If you like it, make it your default watch face
|
||||
(Settings app, "System" menu, "Clock" submenu, select "Anton clock").
|
||||
|
||||
## Configuration
|
||||
|
||||
Anton clock is configured by the standard settings mechanism of Bangle.js's operating system:
|
||||
Open the "Settings" app, then the "Apps" submenu and below it the "Anton clock" menu.
|
||||
Anton Clock is configured by the standard settings mechanism of Bangle.js's operating system:
|
||||
Open the `Settings` app, then the `Apps` submenu and below it the `Anton Clock+` menu.
|
||||
You configure Anton clock through several "on/off" switches in two menus.
|
||||
|
||||
### The main menu
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgf/AH4At/l/Aofgh4DB+EAj4REQoM/AgP4AoeACIoLCg4FB4AFDCIwLCgAROgYIB8EBAoUH/gVBCIxQBCKYHBCJp9DI4ICBLJYRCn4RQEYMOR5ARDIgIRMYQZZBgARGZwZBDCKQrCgEDR5AdBUIQRJDoLXFCJD7J/xrICIQFCn4RH/4LDAoTaCCI4Ar/LLDCBfypMkCgMkyV/CJOSCIOf5IRGFwOfCJNP//JnmT588z/+pM/BYIRCk4RC/88+f/n4RCngRCz1JCIf5/nzGoQRIHwXPCIPJI4f8CJHJGQJKCCI59LCI5ZCCJ/+v/kBoM/+V/HIJrHBYJWB/JKB5x9JEYP8AQKdBpwRL841Dp41KZoTxBHYTXBWY77PCKKhJ/4/CcgMkXoQAiA="))
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"id": "antonclkplus",
|
||||
"name": "Anton Clock Plus",
|
||||
"shortName": "Anton Clock+",
|
||||
"version": "0.10",
|
||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||
"readme":"README.md",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"antonclkplus.app.js","url":"app.js"},
|
||||
{"name":"antonclkplus.settings.js","url":"settings.js"},
|
||||
{"name":"antonclkplus.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"antonclkplus.json"}]
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"id": "assistedgps",
|
||||
"name": "Assisted GPS Update (AGPS)",
|
||||
"name": "Assisted GPS Updater (AGPS)",
|
||||
"version": "0.03",
|
||||
"description": "Downloads assisted GPS (AGPS) data to Bangle.js 1 or 2 for faster GPS startup and more accurate fixes. **No app will be installed**, this just uploads new data to the GPS chip.",
|
||||
"description": "Downloads assisted GPS (AGPS) data to Bangle.js for faster GPS startup and more accurate fixes. **No app will be installed**, this just uploads new data to the GPS chip.",
|
||||
"sortorder": -1,
|
||||
"icon": "app.png",
|
||||
"type": "RAM",
|
||||
"tags": "tool,outdoors,agps",
|
||||
"tags": "tool,outdoors,agps,gps,a-gps",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"custom": "custom.html",
|
||||
"customConnect": true,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
0.01: Create astral clock app
|
||||
0.02: Fixed Whirlpool galaxy RA/DA, larger compass display, fixed moonphase overlapping battery widget
|
||||
0.03: Update to use Bangle.setUI instead of setWatch
|
||||
0.04: Tell clock widgets to hide.
|
||||
0.05: Added adjustment for Bangle.js magnetometer heading fix
|
||||
|
|
|
|||
|
|
@ -767,6 +767,24 @@ function draw() {
|
|||
g.clear();
|
||||
current_moonphase = getMoonPhase();
|
||||
|
||||
Bangle.setUI("clockupdown", btn => {
|
||||
if (btn==0) {
|
||||
if (!processing) {
|
||||
if (!modeswitch) {
|
||||
modeswitch = true;
|
||||
if (mode == "planetary") mode = "extras";
|
||||
else mode = "planetary";
|
||||
}
|
||||
else
|
||||
modeswitch = false;
|
||||
}
|
||||
} else {
|
||||
if (!processing)
|
||||
ready_to_compute = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
|
@ -799,23 +817,6 @@ Bangle.setGPSPower(1);
|
|||
// Show launcher when button pressed
|
||||
Bangle.setClockMode();
|
||||
|
||||
Bangle.setUI("clockupdown", btn => {
|
||||
if (btn==0) {
|
||||
if (!processing) {
|
||||
if (!modeswitch) {
|
||||
modeswitch = true;
|
||||
if (mode == "planetary") mode = "extras";
|
||||
else mode = "planetary";
|
||||
}
|
||||
else
|
||||
modeswitch = false;
|
||||
}
|
||||
} else {
|
||||
if (!processing)
|
||||
ready_to_compute = true;
|
||||
}
|
||||
});
|
||||
|
||||
setWatch(function () {
|
||||
if (!astral_settings.astral_default) {
|
||||
colours_switched = true;
|
||||
|
|
@ -833,7 +834,7 @@ Bangle.on('mag', function (m) {
|
|||
if (isNaN(m.heading))
|
||||
compass_heading = "---";
|
||||
else
|
||||
compass_heading = 360 - Math.round(m.heading);
|
||||
compass_heading = Math.round(m.heading);
|
||||
// g.setColor("#000000");
|
||||
// g.fillRect(160, 10, 160, 20);
|
||||
g.setColor(display_colour);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "astral",
|
||||
"name": "Astral Clock",
|
||||
"version": "0.03",
|
||||
"version": "0.05",
|
||||
"description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Add sit ups
|
||||
Add more feedback to the user about the exercises
|
||||
Clean up code
|
||||
0.03: Add software back button on main menu
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ function showMainMenu() {
|
|||
let menu;
|
||||
menu = {
|
||||
"": {
|
||||
title: "BanglExercise"
|
||||
title: "BanglExercise",
|
||||
back: load
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -381,4 +382,5 @@ Bangle.on('HRM', function(hrm) {
|
|||
});
|
||||
|
||||
g.clear(1);
|
||||
Bangle.loadWidgets();
|
||||
showMainMenu();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "banglexercise",
|
||||
"name": "BanglExercise",
|
||||
"shortName":"BanglExercise",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "Can automatically track exercises while wearing the Bangle.js watch.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@
|
|||
0.12: Add settings to hide date,widgets
|
||||
0.13: Add font setting
|
||||
0.14: Use ClockFace_menu.addItems
|
||||
0.15: Add Power saving option
|
||||
|
|
@ -7,4 +7,5 @@ A simple digital clock showing seconds as a horizontal bar.
|
|||
|
||||
## Settings
|
||||
* `Show date`: display date at the bottom of screen
|
||||
* `Font`: choose between bitmap or vector fonts
|
||||
* `Font`: choose between bitmap or vector fonts
|
||||
* `Power saving`: (Bangle.js 2 only) don't draw the seconds bar while the watch is locked
|
||||
|
|
@ -13,16 +13,20 @@ let locale = require("locale");
|
|||
locale.hasMeridian = (locale.meridian(date)!=="");
|
||||
}
|
||||
|
||||
let barW = 0, prevX = 0;
|
||||
function renderBar(l) {
|
||||
if (!this.fraction) {
|
||||
// zero-size fillRect stills draws one line of pixels, we don't want that
|
||||
return;
|
||||
}
|
||||
const width = this.fraction*l.w;
|
||||
g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1);
|
||||
"ram";
|
||||
if (l) prevX = 0; // called from Layout: drawing area was cleared
|
||||
else l = clock.layout.bar;
|
||||
let x2 = l.x+barW;
|
||||
if (clock.powerSave && Bangle.isLocked()) x2 = 0; // hide bar
|
||||
if (x2===prevX) return; // nothing to do
|
||||
if (x2===0) x2--; // don't leave 1px line
|
||||
if (x2<Math.max(0, prevX)) g.setBgColor(l.bgCol || g.theme.bg).clearRect(x2+1, l.y, prevX, l.y2);
|
||||
else g.setColor(l.col || g.theme.fg).fillRect(prevX+1, l.y, x2, l.y2);
|
||||
prevX = x2;
|
||||
}
|
||||
|
||||
|
||||
function timeText(date) {
|
||||
if (!clock.is12Hour) {
|
||||
return locale.time(date, true);
|
||||
|
|
@ -47,22 +51,21 @@ function dateText(date) {
|
|||
return `${dayName} ${dayMonth}`;
|
||||
}
|
||||
|
||||
|
||||
const ClockFace = require("ClockFace"),
|
||||
clock = new ClockFace({
|
||||
precision:1,
|
||||
settingsFile:'barclock.settings.json',
|
||||
precision: 1,
|
||||
settingsFile: "barclock.settings.json",
|
||||
init: function() {
|
||||
const Layout = require("Layout");
|
||||
this.layout = new Layout({
|
||||
type: "v", c: [
|
||||
{
|
||||
type: "h", c: [
|
||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // updated below
|
||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col:g.theme.fg, bgCol: g.theme.bg},
|
||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col: g.theme.fg, bgCol: g.theme.bg}, // updated below
|
||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col: g.theme.fg, bgCol: g.theme.bg},
|
||||
],
|
||||
},
|
||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||
{id: "bar", type: "custom", fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||
this.showDate ? {height: 40} : {},
|
||||
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
|
||||
],
|
||||
|
|
@ -76,7 +79,8 @@ const ClockFace = require("ClockFace"),
|
|||
this.layout.ampm.label = "";
|
||||
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
||||
}
|
||||
this.layout.bar.height = thickness+1;
|
||||
let bar = this.layout.bar;
|
||||
bar.height = thickness+1;
|
||||
if (this.font===1) { // vector
|
||||
const B2 = process.env.HWVERSION>1;
|
||||
if (this.is12Hour && locale.hasMeridian) {
|
||||
|
|
@ -89,17 +93,32 @@ const ClockFace = require("ClockFace"),
|
|||
this.layout.time.font = "6x8:"+thickness;
|
||||
}
|
||||
this.layout.update();
|
||||
bar.y2 = bar.y+bar.height-1;
|
||||
},
|
||||
update: function(date, c) {
|
||||
"ram";
|
||||
if (c.m) this.layout.time.label = timeText(date);
|
||||
if (c.h) this.layout.ampm.label = ampmText(date);
|
||||
if (c.d && this.showDate) this.layout.date.label = dateText(date);
|
||||
const SECONDS_PER_MINUTE = 60;
|
||||
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
||||
this.layout.render();
|
||||
if (c.m) this.layout.render();
|
||||
if (c.s) {
|
||||
barW = Math.round(date.getSeconds()/60*this.layout.bar.w);
|
||||
renderBar();
|
||||
}
|
||||
},
|
||||
resume: function() {
|
||||
prevX = 0; // force redraw of bar
|
||||
this.layout.forgetLazyState();
|
||||
},
|
||||
});
|
||||
|
||||
// power saving: only update once a minute while locked, hide bar
|
||||
if (clock.powerSave) {
|
||||
Bangle.on("lock", lock => {
|
||||
clock.precision = lock ? 60 : 1;
|
||||
clock.tick();
|
||||
renderBar(); // hide/redraw bar right away
|
||||
});
|
||||
}
|
||||
|
||||
clock.start();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "barclock",
|
||||
"name": "Bar Clock",
|
||||
"version": "0.14",
|
||||
"version": "0.15",
|
||||
"description": "A simple digital clock showing seconds as a bar",
|
||||
"icon": "clock-bar.png",
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||
|
|
|
|||
|
|
@ -17,10 +17,14 @@
|
|||
onchange: v => save("font", v),
|
||||
},
|
||||
};
|
||||
require("ClockFace_menu").addItems(menu, save, {
|
||||
let items = {
|
||||
showDate: s.showDate,
|
||||
loadWidgets: s.loadWidgets,
|
||||
});
|
||||
|
||||
};
|
||||
// Power saving for Bangle.js 1 doesn't make sense (no updates while screen is off anyway)
|
||||
if (process.env.HWVERSION>1) {
|
||||
items.powerSave = s.powerSave;
|
||||
}
|
||||
require("ClockFace_menu").addItems(menu, save, items);
|
||||
E.showMenu(menu);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@
|
|||
0.07: Step count resets at midnight
|
||||
0.08: Step count stored in memory to survive reloads. Now shows step count daily and since last reboot.
|
||||
0.09: NOW it really should reset daily (instead of every other day...)
|
||||
0.10: Tell clock widgets to hide.
|
||||
|
|
|
|||
|
|
@ -416,13 +416,13 @@ var layout = new Layout( {
|
|||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
Bangle.setUI("clock");
|
||||
layout.render();
|
||||
|
||||
Bangle.on('lock', function(locked) {
|
||||
if(!locked) {
|
||||
layout.render();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Barcode clock",
|
||||
"shortName":"Barcode clock",
|
||||
"icon": "barcode.icon.png",
|
||||
"version":"0.09",
|
||||
"version":"0.10",
|
||||
"description": "EAN-8 compatible barcode clock.",
|
||||
"tags": "barcode,ean,ean-8,watchface,clock,clockface",
|
||||
"type": "clock",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Display pressure as number and hand
|
||||
0.02: Use theme color
|
||||
0.03: workaround for some firmwares that return 'undefined' for first call to barometer
|
||||
0.04: Update every second, go back with short button press
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ function drawTicks(){
|
|||
function drawScaleLabels(){
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFont("Vector",12);
|
||||
g.setFontAlign(-1,-1);
|
||||
|
||||
let label = MIN;
|
||||
for (let i=0;i <= NUMBER_OF_LABELS; i++){
|
||||
|
|
@ -103,22 +104,29 @@ function drawIcons() {
|
|||
}
|
||||
|
||||
g.setBgColor(g.theme.bg);
|
||||
g.clear();
|
||||
|
||||
drawTicks();
|
||||
drawScaleLabels();
|
||||
drawIcons();
|
||||
|
||||
try {
|
||||
function baroHandler(data) {
|
||||
if (data===undefined) // workaround for https://github.com/espruino/BangleApps/issues/1429
|
||||
setTimeout(() => Bangle.getPressure().then(baroHandler), 500);
|
||||
else
|
||||
g.clear();
|
||||
|
||||
drawTicks();
|
||||
drawScaleLabels();
|
||||
drawIcons();
|
||||
if (data!==undefined) {
|
||||
drawHand(Math.round(data.pressure));
|
||||
}
|
||||
}
|
||||
Bangle.getPressure().then(baroHandler);
|
||||
setInterval(() => Bangle.getPressure().then(baroHandler), 1000);
|
||||
} catch(e) {
|
||||
print(e.message);
|
||||
print("barometer not supporter, show a demo value");
|
||||
if (e !== undefined) {
|
||||
print(e.message);
|
||||
}
|
||||
print("barometer not supported, show a demo value");
|
||||
drawHand(MIN);
|
||||
}
|
||||
|
||||
Bangle.setUI({
|
||||
mode : "custom",
|
||||
back : function() {load();}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "barometer",
|
||||
"name": "Barometer",
|
||||
"shortName":"Barometer",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "A simple barometer that displays the current air pressure",
|
||||
"icon": "barometer.png",
|
||||
"tags": "tool,outdoors",
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: App Created!
|
||||
0.02: Update to use Bangle.setUI instead of setWatch
|
||||
0.03: Tell clock widgets to hide.
|
||||
|
|
|
|||
|
|
@ -249,6 +249,9 @@ g.clear();
|
|||
g.setColor(0, 0.5, 0).drawImage(bg_crack);
|
||||
g.setColor(1, 1, 1).drawImage(batman);
|
||||
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
|
|
@ -256,5 +259,3 @@ Bangle.drawWidgets();
|
|||
timeInterval = setInterval(showTime, 1000);
|
||||
showTime();
|
||||
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
|
|
|||