Merge branch 'master' of github.com:awkirk71/BangleApps into extended_sliding_clock

pull/2178/head
lu713691 2022-09-26 23:16:20 +01:00
commit f095bb352e
400 changed files with 8921 additions and 4390 deletions

3
.gitignore vendored
View File

@ -11,3 +11,6 @@ tests/Layout/testresult.bmp
apps.local.json
_site
.jekyll-cache
.owncloudsync.log
Desktop.ini
.sync_*.db*

View File

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

View File

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

View File

@ -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",

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Fullscreen settings.
0.02: Fullscreen settings.
0.03: Tell clock widgets to hide.

View File

@ -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");

View File

@ -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",

View File

@ -0,0 +1 @@
0.01: Initial version

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwJC/AAl/Agf/AAUAgIFDwEHAofgh/g/0Ag/wj+AnwVB/EegEfEIN4nkAh+AgE8vgVBAoV4Aoce/EAgfADQIFcjwpFHYIFCnxBFJopZBn5ZCMopxFPoqJFSowA/gA="))

View File

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

View File

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

View File

@ -69,4 +69,7 @@ function drawImage() {
// TODO: a nice little animation before
setTimeout(drawInfo, 1000);
setWatch(_=>load(), BTN1);
Bangle.setUI({
mode : "custom",
back : load
});

View File

@ -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",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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",

View File

@ -1,2 +1,6 @@
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.

View File

@ -1,3 +1,20 @@
# 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
### 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).
### Known Problems
Any all-day event lasts just one day: that is a GB limitation that we will likely fix in the future.

View File

@ -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: "agendaEntry-" + i,
get: () => ({ text: title + "\n" + dateStr, img: null}),
show: function() { agendaItems.items[i].emit("redraw"); },
hide: function () {}
});
});
return agendaItems;
})

View File

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

View File

@ -1,7 +1,7 @@
{
"id": "agenda",
"name": "Agenda",
"version": "0.02",
"version": "0.06",
"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"}]
}

View File

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

View File

@ -1,9 +1,9 @@
{ "id": "agpsdata",
"name": "A-GPS Data",
"name": "A-GPS Data Downloader App",
"shortName":"A-GPS Data",
"icon": "agpsdata.png",
"version":"0.02",
"description": "Download assisted GPS (A-GPS) data directly to your Bangle.js **using Gadgetbridge**",
"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"],

View File

@ -11,4 +11,5 @@
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)

View File

@ -150,6 +150,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) {

View File

@ -2,7 +2,7 @@
"id": "android",
"name": "Android Integration",
"shortName": "Android",
"version": "0.14",
"version": "0.15",
"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",

View File

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

View File

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

View File

@ -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",

View File

@ -10,4 +10,5 @@
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

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{
"id": "antonclk",
"name": "Anton Clock",
"version": "0.09",
"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",

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{
"id": "astral",
"name": "Astral Clock",
"version": "0.03",
"version": "0.04",
"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",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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",

View File

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

View File

@ -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");

View File

@ -2,7 +2,7 @@
"id": "batclock",
"name": "Bat Clock",
"shortName": "Bat Clock",
"version": "0.02",
"version": "0.03",
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
"icon": "bat-clock.png",
"screenshots": [{"url":"screenshot.png"}],

View File

@ -1,2 +1,3 @@
0.02: Modified for use with new bootloader and firmware
0.03: Update to use Bangle.setUI instead of setWatch
0.04: Tell clock widgets to hide.

View File

@ -100,10 +100,12 @@ Bangle.on('lcdPower', on => {
if (on) drawClock();
});
// Show launcher when button pressed
Bangle.setUI("clock");
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
setInterval(() => { drawClock(); }, 1000);
drawClock();
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,7 +1,7 @@
{
"id": "bclock",
"name": "Binary Clock",
"version": "0.03",
"version": "0.04",
"description": "A simple binary clock watch face",
"icon": "clock-binary.png",
"type": "clock",

View File

@ -3,3 +3,5 @@
0.03: Internationalisation; bug fix - battery icon responds promptly to charging state
0.04: bug fix
0.05: proper fix for the race condition in queueDraw()
0.06: Tell clock widgets to hide.
0.07: Better battery graphic - now has green, yellow and red sections; battery status reflected in the bar across the middle of the screen; current battery state checked only once every 15 minutes, leading to longer-lasting battery charge

View File

@ -11,6 +11,8 @@ Graphics.prototype.setFontOpenSans = function(scale) {
};
var drawTimeout;
var lastBattCheck = 0;
var width = 0;
function queueDraw(millis_now) {
if (drawTimeout) clearTimeout(drawTimeout);
@ -24,12 +26,15 @@ function draw() {
var date = new Date();
var h = date.getHours(),
m = date.getMinutes();
var d = date.getDate(),
w = date.getDay(); // d=1..31; w=0..6
const level = E.getBattery();
const width = level + (level/2);
var d = date.getDate();
var is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
var dows = require("date_utils").dows(0,1);
var dow = require("date_utils").dows(0,1)[date.getDay()];
if ((date.getTime() >= lastBattCheck + 15*60000) || Bangle.isCharging()) {
lastBattcheck = date.getTime();
width = E.getBattery();
width += width/2;
}
g.reset();
g.clear();
@ -47,24 +52,35 @@ function draw() {
g.drawString(d, g.getWidth() -6, 98);
g.setFont('Vector', 52);
g.setFontAlign(-1, -1);
g.drawString(dows[w].slice(0,2).toUpperCase(), 6, 103);
g.drawString(dow.slice(0,2).toUpperCase(), 6, 103);
g.fillRect(9,159,166,171);
g.fillRect(167,163,170,167);
if (Bangle.isCharging()) {
g.setColor(1,1,0);
} else if (level > 40) {
g.setColor(0,1,0);
g.fillRect(12,162,12+width,168);
} else {
g.setColor(1,0,0);
g.fillRect(12,162,57,168);
g.setColor(1,1,0);
g.fillRect(58,162,72,168);
g.setColor(0,1,0);
g.fillRect(73,162,162,168);
}
g.fillRect(12,162,12+width,168);
if (level < 100) {
if (width < 150) {
g.setColor(g.theme.bg);
g.fillRect(12+width+1,162,162,168);
}
g.setColor(0, 1, 0);
if (Bangle.isCharging()) {
g.setColor(1,1,0);
} else if (width <= 45) {
g.setColor(1,0,0);
} else if (width <= 60) {
g.setColor(1,1,0);
} else {
g.setColor(0, 1, 0);
}
g.fillRect(0, 90, g.getWidth(), 94);
// widget redraw
@ -85,7 +101,8 @@ Bangle.on('charging', (charging) => {
draw();
});
Bangle.setUI("clock");
Bangle.loadWidgets();
draw();
Bangle.setUI("clock");

View File

@ -1,7 +1,7 @@
{ "id": "bigdclock",
"name": "Big digit clock containing just the essentials",
"shortName":"Big digit clk",
"version":"0.05",
"version":"0.07",
"description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.",
"icon": "bigdclock.png",
"type": "clock",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,3 +1,4 @@
0.01: New App!
0.02: Fixed bug where screen didn't clear so incorrect time displayed.
0.03: Update to use Bangle.setUI instead of setWatch
0.04: Tell clock widgets to hide.

View File

@ -164,9 +164,6 @@ Bangle.on('lcdPower',on=>{
draw(); // draw immediately
}
});
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=>{
if (btn!=1) return;
@ -176,3 +173,6 @@ Bangle.setUI("clockupdown", btn=>{
displayTime = 0;
}
});
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -2,7 +2,7 @@
"id": "binclock",
"name": "Binary Clock",
"shortName": "Binary Clock",
"version": "0.03",
"version": "0.04",
"description": "A binary clock with hours and minutes. BTN1 toggles a digital clock.",
"icon": "app.png",
"type": "clock",

View File

@ -2,3 +2,5 @@
0.02: first running version for BangleJs2
0.03: corrected icon, added screen shot, extended description
0.04: corrected format of background image (raw binary)
0.05: move setUI() up before draw() as to not have a false positive 'sanity
check' when building on github.

View File

@ -334,6 +334,7 @@ function setRuntimeValues(resolution) {
var hour = 0, minute = 1, second = 50;
var batVLevel = 20;
Bangle.setUI("clock");
function draw() {
var d = new Date();
@ -371,7 +372,6 @@ function draw() {
}
// Show launcher when button pressed
Bangle.setUI("clock");
setRuntimeValues(g.getWidth());
g.reset().clear();
Bangle.loadWidgets();

View File

@ -3,7 +3,7 @@
"shortName":"BinWatch",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"version":"0.04",
"version":"0.05",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"allow_emulator":true,

View File

@ -5,3 +5,4 @@
0.04: Modified to account for changes in the behavior of Graphics.fillPoly
0.05: Slight increase to draw speed after LCD on
0.06: Update to use Bangle.setUI instead of setWatch, allow themes and different size screens
0.07: Tell clock widgets to hide.

View File

@ -99,6 +99,10 @@ function startTimers() {
Bangle.drawWidgets();
intervalRef = setInterval(redraw,1000);
}
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
startTimers();
Bangle.on('lcdPower',function(on) {
@ -108,5 +112,3 @@ Bangle.on('lcdPower',function(on) {
clearTimers();
}
});
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -2,7 +2,7 @@
"id": "blobclk",
"name": "Large Digit Blob Clock",
"shortName": "Blob Clock",
"version": "0.06",
"version": "0.07",
"description": "A clock with big digits",
"icon": "clock-blob.png",
"type": "clock",

View File

@ -3,3 +3,4 @@
0.04: Work with themes, smaller screens
0.05: Adjust hand lengths to be within 'tick' points
0.06: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
0.07: Tell clock widgets to hide.

View File

@ -130,9 +130,10 @@ Bangle.on('lcdPower', (on) => {
}
});
// Show launcher when button pressed
Bangle.setUI("clock");
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
startTimers();
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,7 +1,7 @@
{
"id": "boldclk",
"name": "Bold Clock",
"version": "0.06",
"version": "0.07",
"description": "Simple, readable and practical clock",
"icon": "bold_clock.png",
"screenshots": [{"url":"screenshot_bold.png"}],

View File

@ -10,4 +10,14 @@
0.10: HomeAssistant integration if HomeAssistant is installed.
0.11: Performance improvements.
0.12: Implements a 2D menu.
0.13: Clicks < 24px are for widgets, if fullscreen mode is disabled.
0.13: Clicks < 24px are for widgets, if fullscreen mode is disabled.
0.14: Adds humidity to weather data.
0.15: Added option for a dynamic mode to show widgets only if unlocked.
0.16: You can now show your agenda if your calendar is synced with Gadgetbridge.
0.17: Fix - Step count was no more shown in the menu.
0.18: Set timer for an agenda entry by simply clicking in the middle of the screen. Only one timer can be set.
0.19: Fix - Compatibility with "Digital clock widget"
0.20: Better handling of async data such as getPressure.
0.21: On the default menu the week of year can be shown.
0.22: Use the new clkinfo module for the menu.
0.23: Feedback of apps after run is now optional and decided by the corresponding clkinfo.

View File

@ -1,46 +1,49 @@
# BW Clock
A very minimalistic clock with date and time in focus.
A very minimalistic clock.
![](screenshot.png)
## Features
The BW clock provides many features as well as 3rd party integrations:
The BW clock implements features that are exposed by other apps through the `clkinfo` module.
For example, if you install the HomeAssistant app, this menu item will be shown if you click right
and additionally allows you to send triggers directly from the clock (select triggers via up/down and
send via click center). Here are examples of other apps that are integrated:
- Bangle data such as steps, heart rate, battery or charging state.
- A timer can be set directly. *Requirement: Scheduler library*
- Show agenda entries. A timer for an agenda entry can also be set by simply clicking in the middle of the screen. This can be used to not forget a meeting etc. Note that only one agenda-timer can be set at a time. *Requirement: Gadgetbridge calendar sync enabled*
- Weather temperature as well as the wind speed can be shown. *Requirement: Weather app*
- HomeAssistant triggers can be executed directly. *Requirement: HomeAssistant app*
Note: If some apps are not installed (e.gt. weather app), then this menu item is hidden.
## Menu
2D menu allows you to display lots of different data including data from 3rd party apps and it's also possible to control things e.g. to set a timer or send a HomeAssistant trigger.
Simply click left / right to go through the menu entries such as Bangle, Timer etc.
and click up/down to move into this sub-menu. You can then click in the middle of the screen
to e.g. send a trigger via HomeAssistant once you selected it.
```
+5min
|
Bangle -- Timer[Optional] -- Weather[Optional] -- HomeAssistant [Optional]
| | | |
Bpm -5min Temperature Trigger1
| | |
Steps ... ...
|
Battery
```
## Settings
- Fullscreen on/off (widgets are still loaded).
- Enable/disable lock icon in the settings. Useful if fullscreen is on.
- Screen: Normal (widgets shown), Dynamic (widgets shown if unlocked) or Full (widgets are hidden).
- Enable/disable lock icon in the settings. Useful if fullscreen mode is on.
- The colon (e.g. 7:35 = 735) can be hidden in the settings for an even larger time font to improve readability further.
- There are no design settings, as your bangle sys settings are used.
- Your bangle uses the sys color settings so you can change the color too.
## Menu structure
2D menu allows you to display lots of different data including data from 3rd party apps and it's also possible to control things e.g. to trigger HomeAssistant.
Simply click left / right to go through the menu entries such as Bangle, Weather etc.
and click up/down to move into this sub-menu. You can then click in the middle of the screen
to e.g. send a trigger via HomeAssistant once you selected it. The actions really depend
on the app that provide this sub-menu through the `clkinfo` module.
```
Bangle -- Agenda -- Weather -- HomeAssistant
| | | |
Battery Entry 1 Temperature Trigger1
| | | |
Steps ... ... ...
|
...
```
## Thanks to
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
- Thanks to Gordon Williams not only for the great BangleJs, but specifically also for the implementation of `clkinfo` which simplified the BWClock a lot and moved complexety to the apps where it should be located.
- <a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
## Creator
[David Peer](https://github.com/peerdavid)

View File

@ -1,22 +1,25 @@
/************
/************************************************
* Includes
*/
const locale = require('locale');
const storage = require('Storage');
const clock_info = require("clock_info");
/************
* Statics
/************************************************
* Globals
*/
const SETTINGS_FILE = "bwclk.setting.json";
const TIMER_IDX = "bwclk";
const W = g.getWidth();
const H = g.getHeight();
var lock_input = false;
/************
/************************************************
* Settings
*/
let settings = {
fullscreen: false,
screen: "Normal",
showLock: true,
hideColon: false,
menuPosX: 0,
@ -28,22 +31,10 @@ for (const key in saved_settings) {
settings[key] = saved_settings[key]
}
/************
/************************************************
* Assets
*/
// Manrope font
Graphics.prototype.setXLargeFont = function(scale) {
// Actual height 53 (55 - 3)
this.setFontCustom(
E.toString(require('heatshrink').decompress(atob('AHM/8AIG/+AA4sD/wQGh/4EWQA/AC8YA40HNA0BRY8/RY0P/6LFgf//4iFA4IiFj4HBEQkHCAQiDHIIZGv4HCFQY5BDAo5CAAIpDDAfACA3wLYv//hsFKYxcCMgoiBOooiBQwwiBS40AHIgA/ACS/DLYjYCBAjQEBAYQDBAgHDUAbyDZQi3CegoHEVQQZFagUfW4Y0DaAgECaIJSEFYMPbIYNDv5ACGAIrBCgJ1EFYILCAAQWCj4zDGgILCegcDEQRNDHIIiCHgZ2BEQShFIqUDFYidCh5ODg4NCn40DAgd/AYR5BDILZEAAIMDAAYVCh7aHdYhKDbQg4Dv7rGBAihFCAwIDCAgA/AB3/eoa7GAAk/dgbVGDJrvCDK67DDIjaGdYpbCdYonCcQjjDEVUBEQ4A/AEMcAYV/NAUHcYUDawd/cYUPRYSmBBgaLBToP8BgYiBSgIiCj4iCg//EQSuDW4IMDVwYiCBgIiBBgrRDCATeBaIYqCv70DCgT4CEQMfIgQZBBoRnDv/3EQIvBDIffEQMHFwReBRYUfOgX/+IiDKIeHEQRRECwUHKwIuB8AiDIoJEBCwZFCv/4HIZaBIgPAEQS2CUYQiCD4SABEQcfOwIZBEQaHBO4RcEAAI/BEQQgBSIQiDTIRZBEQZuBVYQiDHoKWCEQQICFQIiDBAQeCEQQA/AANwA40BLIJ5BO4JWCBAUPAYR5En7RBUIQECN4SYCQQIiEh6CCEQk/BoQiBgYeCBoTrCAgT0CCgIfCFYQiBg4IBGgIiDj6rBg4rCBYLRDFYIiBbYIfBLgQiBIQYiD4JCCLgf/bQIWDBYV/EQV/BYXz/5FBgIiD5//IowZBD4M/NAX/BIPgDIJoC//5GgKUDn//4f/8KLE/wTBAAI8BEQPwj4HBVwYmBDgIZDN4QZCGYKJCHQP/JoSgCBATrCh5dBKITVDG4gICAAbvDAH5SCL4QADK4J5CCAiTCCAp1BCAqCDCAgiGCAIiFCAQiFeoIiFg6/FCAgiECAXnEQgQB/kfEQYQC4F/EQYQCgIiDfoIQBg4iDCAUAEQZUCcgIiDDIIQBEQhuBBoIiENoYiFDwQiECAQiFwEBPQQNCAQKDDEYMDDoMfRh4iGUwqvEESBiBaQ5oEbgr0FNAo+EEIwA+oAHGgJoFRAMHe4L0CAALNBBAT0BfwScDCAXweAL0DWgUPQYQiDwF/QYQiC/zTB+C0FBAL0CEQYIBGgMPCgIxBg4rCJIKsCh5IBBwTPCj4WBgYLBZ4V/MAIiBBQQrBEQYtCBYQiCO4QLFCwgiDIQIiGIoMHEQpFBn5FFD4JoENwRoGDgSUCAoKfBw//DgIiCT4auCFwN/T4RRET4TaCEQKoCDIQiCGgK/DAAQICdYQACHoIqCBAoQFEwIhFAH4AFQIROEj4IGXwIIGNwIACbgIhEBAiRCVwoqDTogHEW4QZFXgIZB/z9Cv49CF4MPBwI0Ca4LlB8ATCJoP4AoINDfQPAg7PBg4cBBwUfD4MfFYILCCwgOCf4QLEwEPCwILCgJaBn4WBBYQxCIQQiD+EDCYI5CBYRQBIo4fBMQIuBC4N/NAv8AoIcBSgU/FYIIBZIYrCW4hOCXIQZCgYUBv7jEh4uBZAscewZ8CgEgUYT0EEoQIBA4gICFQQIEHYQA+KQzdDAArdCAArpCEScHaIQiEvwiGe4QiFUwQiEbgIiFYIL0DEQTkBEQrJEEQc/cYYiCg4HBDIQiCfoRoEHQLaDEQQHBbQYiBCAT8Dn/BCAoXBJYP/OgZKC/6OEEARLCEQZLEEQZLEEQjKFEQI6EEQZLDEQbsGEQLjGYYYA/JIxzEg/AfgJSDAoPgfgiDC8COFAoPnaQj6CAAR+CW4TCFA4i6CDIqhCDIfwHoYHCYIN/GgKuBJ4JDBFYUf/C5CBYIZBv/Ag4ZBg4rBBYQTBAQIcBg4FBn5UBAQUfFwIfCEQeAgYfBAQUBFAKbCAQQiCGwIiE+A2BwBFNwE/AoM/EQJoIWwKCCh4cBFYKUERYV/W46uHFYIZGaJA0B/glBGYT0JIITiEMIJvCFQQAEHYQA/ABBlEOIhdGQAIRFSgQIBgQICn4IB8EAjiBCUYglCbQYeBEoQZCTwM/CYIZD/gEBUwIzBJ4UHYAU/EwIrBh4rCAoIXCn4rBCgUDAQN/FYMfBYIXBCYJnCBYXggf8HgQLCwEPEQQuBgJOECwILDCwgiLHIUHBYJFGD4IxBgYWCn4rBBwJoFDIYNBCgPADgKHBRYfDBQN/GAIrBToTLDVwYACDILiCWAb8DAAYzBYAjTCAAI9BAARNCBAoqCBAgQDFgbYCAH4AufgQACf4T8CAAT/CfgQACBwITCAAYOBCYQioh4iEAHQA=='))),
46,
atob("FR4uHyopKyksJSssGA=="),
70+(scale<<8)+(1<<16)
);
};
Graphics.prototype.setLargeFont = function(scale) {
// Actual height 47 (48 - 2)
this.setFontCustom(
@ -55,14 +46,12 @@ Graphics.prototype.setLargeFont = function(scale) {
return this;
};
Graphics.prototype.setMediumFont = function(scale) {
// Actual height 41 (42 - 2)
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAB/AAAAAAAP/AAAAAAD//AAAAAA///AAAAAP///AAAAB///8AAAAf///AAAAH///wAAAB///+AAAAH///gAAAAH//4AAAAAH/+AAAAAAH/wAAAAAAH8AAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAH////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gAAH+AAD+AAAD/AAH8AAAB/AAH4AAAA/gAH4AAAAfgAH4AAAAfgAPwAAAAfgAPwAAAAfgAPwAAAAfgAHwAAAAfgAH4AAAAfgAH4AAAA/gAH8AAAA/AAD+AAAD/AAD/gAAH/AAB/////+AAB/////8AAA/////4AAAf////wAAAH////gAAAB///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAfwAAAAAAA/gAAAAAAA/AAAAAAAB/AAAAAAAD+AAAAAAAD8AAAAAAAH8AAAAAAAH//////AAH//////AAH//////AAH//////AAH//////AAH//////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAA/AAAP4AAB/AAAf4AAD/AAA/4AAD/AAB/4AAH/AAD/4AAP/AAH/AAAf/AAH8AAA//AAH4AAB//AAP4AAD//AAPwAAH+/AAPwAAP8/AAPwAAf4/AAPwAA/4/AAPwAA/w/AAPwAB/g/AAPwAD/A/AAP4AH+A/AAH8AP8A/AAH/A/4A/AAD///wA/AAD///gA/AAB///AA/AAA//+AA/AAAP/8AA/AAAD/wAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAH4AAAHwAAH4AAAH4AAH4AAAH8AAH4AAAP+AAH4AAAH+AAH4A4AB/AAH4A+AA/AAH4B/AA/gAH4D/AAfgAH4H+AAfgAH4P+AAfgAH4f+AAfgAH4/+AAfgAH5/+AAfgAH5//AAfgAH7+/AA/gAH/8/gB/AAH/4f4H/AAH/wf//+AAH/gP//8AAH/AH//8AAH+AD//wAAH8AB//gAAD4AAf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAD/AAAAAAAP/AAAAAAB//AAAAAAH//AAAAAAf//AAAAAB///AAAAAH///AAAAAf/8/AAAAB//w/AAAAH/+A/AAAA//4A/AAAD//gA/AAAH/+AA/AAAH/4AA/AAAH/gAA/AAAH+AAA/AAAHwAAA/AAAHAAf///AAEAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAP/AHgAAH///AP4AAH///gP8AAH///gP8AAH///gP+AAH///gD/AAH/A/AB/AAH4A/AA/gAH4A+AAfgAH4B+AAfgAH4B+AAfgAH4B8AAfgAH4B8AAfgAH4B+AAfgAH4B+AAfgAH4B+AA/gAH4B/AA/AAH4A/gD/AAH4A/4H+AAH4Af//+AAH4AP//8AAH4AP//4AAHwAD//wAAAAAB//AAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAD////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gP4H+AAD/AfgD/AAH8A/AB/AAH8A/AA/gAH4B+AAfgAH4B+AAfgAPwB8AAfgAPwB8AAfgAPwB+AAfgAPwB+AAfgAH4B+AAfgAH4B/AA/gAH8B/AB/AAH+A/wD/AAD+A/8P+AAB8Af//+AAB4AP//8AAAwAH//4AAAAAD//gAAAAAA//AAAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAHAAPwAAAA/AAPwAAAD/AAPwAAAf/AAPwAAB//AAPwAAP//AAPwAA//8AAPwAH//wAAPwAf/+AAAPwB//4AAAPwP//AAAAPw//8AAAAP3//gAAAAP//+AAAAAP//wAAAAAP//AAAAAAP/4AAAAAAP/gAAAAAAP+AAAAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAAH+A//gAAAf/h//4AAA//z//8AAB/////+AAD/////+AAD///+H/AAH+H/4B/AAH8B/wA/gAH4A/gAfgAH4A/gAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAH4A/gAfgAH4A/gAfgAH8B/wA/gAH/H/4B/AAD///+H/AAD/////+AAB/////+AAA//z//8AAAf/h//4AAAH+A//gAAAAAAH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAAD/8AAAAAAP/+AAAAAAf//AAcAAA///gA8AAB///wB+AAD/x/4B/AAD+AP4B/AAH8AH8A/gAH4AH8A/gAH4AD8AfgAP4AD8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAH4AD8AfgAH4AD4A/gAH8AH4B/AAD+APwD/AAD/g/wP+AAB/////+AAA/////8AAAf////4AAAP////wAAAH////AAAAA///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DxcjFyAfISAiHCAiEg=="), 54+(scale<<8)+(1<<16));
return this;
};
Graphics.prototype.setSmallFont = function(scale) {
// Actual height 28 (27 - 0)
this.setFontCustom(
@ -74,6 +63,16 @@ Graphics.prototype.setSmallFont = function(scale) {
return this;
};
Graphics.prototype.setMiniFont = function(scale) {
// Actual height 16 (15 - 0)
this.setFontCustom(
atob('AAAAAAAAAAAAAP+w/5AAAAAA4ADgAOAA4AAAAAAAAAABgBmAGbAb8D+A+YDZ8B/wf4D5gJmAGQAQAAAAAAAeOD8cMwzxj/GPMYwc/Az4AAAAAHAA+DDIYMjA+YBzAAYADeA7MHMw4zDD4ADAAAAz4H/wzjDHMMMwwbBj4APgADAAAAAA4ADgAAAAAAAAAAfwH/54B+ABAAAAAOABeAcf/gfwAAAAACAAaAD4APgAOABgAAAAAAACAAIAAgA/wAMAAgACAAAAAAAAPAA4AAAAAAIAAgACAAIAAgAAAAAAADAAMAAAAAAAcAfwf4D4AIAAAAA/wH/gwDDAMMAwwDB/4D/AAAAAAGAAwAD/8P/wAAAAAHAw8HDA8MHww7DnMH4wGBAAAMBgyHDcMPww/DDv4MfAAAAAAAHgD+A+YPhgwGAH8AfwAEAAAAAA/GD8cMwwzDDMMM5wx+ABgAAAP8B/4MwwzDDMMMwwx+ADwAAAgADAAMBwwfDPgP4A8ADAAAAAe+D/8M4wxjDGMP5wf+ABwAAAfAB+cMYwwjDCMMYwf+A/wAAAAAAAAAxgBCAAAAAAAAAYPBA4AAAAAAAAAgAHAA+AHMAYYAAAAAAAAAAAAAAJAAkACQAJAAkACQAJAAkAAAAAAAAAAAAAABhgHMAPgAcAAgAAAAAAAABgAOAAwbDDsMYA/AA4AAAAAAAD4A/wGBgxzDPsMyQjJDPkM+wYIBxgD+AAAAAAABAA8A/gf8DwwODA/sAfwAHwADAAAP/w//DGMMYwxjDOMP9we+ABwA8AP8Bw4MAwwDDAMMAwwDDgcHDgMMAAAAAA//D/8MAwwDDAMMAw4HB/4D/AAAAAAP/w//DGMMYwxjDGMMQwgBAAAP/w//DDAMMAwwDDAMAADwA/wHDgwDDAMMAwwDDCMOJwc+ADwAAA//D/8AMAAwADAAMAAwD/8P/wAAAAAP/w//AAAABgAHAAMAAwAHD/4P+AAAAAAP/w//AOAB+AOcBw4MBwgDAAEAAA//D/8AAwADAAMAAwADAAAP/w//A8AA8AA+AA8AHwB8AeAHgA//D/8AAAAAD/8P/wcAAcAA8AA4AB4P/w//AAAA8AP8Bw4MAwwDDAMMAwwDDgcH/gP8AAAAAA//D/8MMAwwDDAMYA7gB8ABgADwA/wHDgwDDAMMAwwDDA8ODwf/A/8AAAAAD/8P/wwwDDAMMAx4Dv4HxwEBAAAHjg/HDMMMYwxjDGMONwc+ABwMAAwADAAMAA//D/8MAAwADAAIAAAAD/wP/gAHAAMAAwADAAMAHg/8AAAMAA+AA/AAfgAPAA8AfgPwD4AMAAwAD4AD+AA/AA8A/g/gDwAP4AH8AB8APwH8D8AMAAgBDAMPDgO8APAB8AOcDw8MAwgBCAAOAAeAAeAAfwH/B4AOAAwAAAAMAwwPDB8Mew3jD4MPAwwDAAAAAAAAB//3//QABAAAAAAADgAP4AH+AB8AAQAABAAEAAf/9//wAAAAAAAAAAGAAwAGAAwABgADAAGAAAAAAAAAQABAAEAAQABAAEAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAQA3wHbAZMBswGzAf4A/wAAAAAP/w//AYMBgwGDAYMA/gB8AAAAEAD+Ae8BgwGDAYMBgwDGAAAAMAD+Ae8BgwGDAYMBhw//D/8AAAAYAP4B/wGTAZMBkwGTAP4AcAEAAYAP/w//CQAJAAAwAP4hz3GDMQMxAzGHcf/h/8AAAAAP/w//AYABgAGAAYAA/wB/AAAAAA3/Df8AAAAAOf/9//AAAAAP/w//ADgAfADGAYMBAQAAD/8P/wAAAAAB/wH/AYABgAGAAf8A/wGAAYABgAH/AP8AAAAAAf8B/wGAAYABgAGAAP8AfwAAADAA/gHvAYMBgwGDAYMA/gB8AAAAAAH/8f/xgwGDAYMBgwD+AHwAAAAwAP4B7wGDAYMBgwGHAf/x//AAAAAB/wH/AYABgAEAAAAA5gHzAbMBkwGbAd8AzgEAAYAP/wf/AQMBAwAAAAAB/gH/AAMAAwADAAcB/wH/AAABAAHgAPwAHwAPAH4B8AGAAQAB8AB+AA8APwHwAeAA/AAPAD8B+AHAAQEBgwHOAHwAOAD+AccBAwAAAQAB4AD4EB/wB8A/APgBwAAAAAEBgwGPAZ8B8wHjAcMBAQAAAAAABgf/9/n2AAAAAAAP/w//AAAEAAYAB/nz//AGAAAAAAAAAAAAcABgAGAAcAAwAHAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'),
32,
atob("AwUHDwoOCwQHBwcJBAcEBgoGCQkKCQoICQoFBQoMCgkPCgoMCwkICwsECAoIDgsMCgwKCgoLCg8KCQoHBgcLCwgJCgkKCQYKCgQECAQOCgoKCgYIBwoIDAkJCAcEBwsQ"),
16+(scale<<8)+(1<<16)
);
return this;
};
function imgLock(){
return {
@ -83,279 +82,100 @@ function imgLock(){
}
}
function imgSteps(){
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/H///wv4CBn4CD8ACCj4IBj8f+Eeh/wjgCBngCCg/4nEH//4h/+jEP/gRBAQX+jkf/wgB//8GwP4FoICDHgICCBwIA=="))
}
}
function imgBattery(){
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/4AN4EAg4TBgd///9oEAAQv8ARQRDDQQgCEwQ4OA"))
}
}
function imgCharging() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("//+v///k///4AQPwBANgBoMxBoMb/P+h/w/kH8H4gfB+EBwfggHH4EAt4CBn4CBj4CBh4FCCIO/8EB//Agf/wEH/8Gh//x////fAQIA="))
}
}
function imgBpm() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/4AOn4CD/wCCjgCCv/8jF/wGYgOA5MB//BC4PDAQnjAQPnAQgANA"))
}
}
function imgTemperature() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("//D///wICBjACBngCNkgCP/0kv/+s1//nDn/8wICEBAIOC/08v//IYJECA=="))
}
}
function imgWeather(){
return {
width : 24, height : 24, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AAcYAQ0MgEwAQUAngLB/8AgP/wACCgf/4Fz//OAQQICCIoaCEAQpGHA4ACA="))
}
}
function imgWind () {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/0f//8h///Pn//zAQXzwf/88B//mvGAh18gEevn/DIICB/PwgEBAQMHBAIADFwM/wEAGAP/54CD84CE+eP//wIQU/A=="))
}
}
function imgTimer() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/+B/4CD84CEBAPygFP+F+h/x/+P+fz5/n+HnAQNn5/wuYCBmYCC5kAAQfOgFz80As/ngHn+fD54mC/F+j/+gF/HAQA=="))
}
}
function imgWatch() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/8B//+ARANB/l4//5/1/+f/n/n5+fAQnf9/P44CC8/n7/n+YOB/+fDQQgCEwQsCHBBEC"))
}
}
function imgHomeAssistant() {
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AD8BwAFDg/gAocP+AFDj4FEn/8Aod//wFD/1+FAf4j+8AoMD+EPDAUH+OPAoUP+fPAoUfBYk/C4l/EYIwC//8n//FwIFEgYFD4EH+E8nkP8BdBAonjjk44/wj/nzk58/4gAFDF4PgCIMHAoPwhkwh4FB/EEkEfIIWAHwIFC4A+BAoXgg4FDL4IFDL4IFDLIYFkAEQA=="))
}
}
/************
* 2D MENU with entries of:
* [name, icon, opt[customDownFun], opt[customUpFun], opt[customCenterFun]]
*
/************************************************
* Menu
*/
var menu = [
[
function(){ return [ null, null ] },
],
[
function(){ return [ "Bangle", imgWatch() ] },
function(){ return [ E.getBattery() + "%", Bangle.isCharging() ? imgCharging() : imgBattery() ] },
function(){ return [ getSteps(), imgSteps() ] },
function(){ return [ Math.round(Bangle.getHealthStatus("last").bpm) + " bpm", imgBpm()] },
// Custom bwItems menu - therefore, its added here and not in a clkinfo.js file.
var bwItems = {
name: null,
img: null,
items: [
{ name: "WeekOfYear",
get: () => ({ text: "Week " + weekOfYear(), img: null}),
show: function() { bwItems.items[0].emit("redraw"); },
hide: function () {}
},
]
]
};
/*
* Timer Menu
*/
try{
require('sched');
menu.push([
function(){
var text = isAlarmEnabled() ? getAlarmMinutes() + " min." : "Timer";
return [text, imgTimer(), () => decreaseAlarm(), () => increaseAlarm(), null ]
},
]);
} catch(ex) {
// If sched is not installed, we hide this menu item
}
/*
* WEATHER MENU
*/
if(storage.readJSON('weather.json') !== undefined){
menu.push([
function(){ return [ "Weather", imgWeather() ] },
function(){ return [ getWeather().temp, imgTemperature() ] },
function(){ return [ getWeather().wind, imgWind() ] },
]);
function weekOfYear() {
var date = new Date();
date.setHours(0, 0, 0, 0);
// Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
// January 4 is always in week 1.
var week1 = new Date(date.getFullYear(), 0, 4);
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
- 3 + (week1.getDay() + 6) % 7) / 7);
}
/*
* HOME ASSISTANT MENU
*/
try{
var triggers = require("ha.lib.js").getTriggers();
var haMenu = [
function(){ return [ "Home", imgHomeAssistant() ] },
];
// Load menu
var menu = clock_info.load();
menu = menu.concat(bwItems);
triggers.forEach(trigger => {
haMenu.push(function(){
return [trigger.display, trigger.getIcon(), () => {}, () => {}, function(){
var ha = require("ha.lib.js");
ha.sendTrigger("TRIGGER_BW");
ha.sendTrigger(trigger.trigger);
}]
});
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
settings.menuPosX = 0;
settings.menuPosY = 0;
}
// Set draw functions for each item
menu.forEach((menuItm, x) => {
menuItm.items.forEach((item, y) => {
function drawItem() {
// For the clock, we have a special case, as we don't wanna redraw
// immediately when something changes. Instead, we update data each minute
// to save some battery etc. Therefore, we hide (and disable the listener)
// immedeately after redraw...
item.hide();
// After drawing the item, we enable inputs again...
lock_input = false;
var info = item.get();
drawMenuItem(info.text, info.img);
}
item.on('redraw', drawItem);
})
menu.push(haMenu);
} catch(ex){
// If HomeAssistant is not installed, we hide this item
}
});
function getMenuEntry(){
// In case the user removes HomeAssistant entries, showInfo
// could be larger than infoArray.length...
settings.menuPosX = settings.menuPosX % menu.length;
settings.menuPosY = settings.menuPosY % menu[settings.menuPosX].length;
return menu[settings.menuPosX][settings.menuPosY]();
}
/************
* Helper
*/
function getSteps() {
var steps = 0;
try{
if (WIDGETS.wpedom !== undefined) {
steps = WIDGETS.wpedom.getSteps();
} else if (WIDGETS.activepedom !== undefined) {
steps = WIDGETS.activepedom.getSteps();
} else {
steps = Bangle.getHealthStatus("day").steps;
}
} catch(ex) {
// In case we failed, we can only show 0 steps.
function canRunMenuItem(){
if(settings.menuPosY == 0){
return false;
}
steps = Math.round(steps/100) / 10; // This ensures that we do not show e.g. 15.0k and 15k instead
return steps + "k";
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
return item.run !== undefined;
}
function getWeather(){
var weatherJson;
try {
weatherJson = storage.readJSON('weather.json');
var weather = weatherJson.weather;
// Temperature
weather.temp = locale.temp(weather.temp-273.15);
// Humidity
weather.hum = weather.hum + "%";
// Wind
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
weather.wind = Math.round(wind[1]) + "kph";
return weather
} catch(ex) {
// Return default
function runMenuItem(){
if(settings.menuPosY == 0){
return;
}
return {
temp: " ? ",
hum: " ? ",
txt: " ? ",
wind: " ? ",
wdir: " ? ",
wrose: " ? "
};
}
function isAlarmEnabled(){
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
try{
var alarm = require('sched');
var alarmObj = alarm.getAlarm(TIMER_IDX);
if(alarmObj===undefined || !alarmObj.on){
return false;
var ret = item.run();
if(ret){
Bangle.buzz(300, 0.6);
}
return true;
} catch(ex){ }
return false;
}
function getAlarmMinutes(){
if(!isAlarmEnabled()){
return -1;
} catch (ex) {
// Simply ignore it...
}
var alarm = require('sched');
var alarmObj = alarm.getAlarm(TIMER_IDX);
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
}
function increaseAlarm(){
try{
var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
var alarm = require('sched')
alarm.setAlarm(TIMER_IDX, {
timer : (minutes+5)*60*1000,
});
alarm.reload();
} catch(ex){ }
}
function decreaseAlarm(){
try{
var minutes = getAlarmMinutes();
minutes -= 5;
var alarm = require('sched')
alarm.setAlarm(TIMER_IDX, undefined);
if(minutes > 0){
alarm.setAlarm(TIMER_IDX, {
timer : minutes*60*1000,
});
}
alarm.reload();
} catch(ex){ }
}
/************
* DRAW
/************************************************
* Draw
*/
function draw() {
// Queue draw again
@ -363,7 +183,7 @@ function draw() {
// Draw clock
drawDate();
drawTime();
drawMenuAndTime();
drawLock();
drawWidgets();
}
@ -371,12 +191,12 @@ function draw() {
function drawDate(){
// Draw background
var y = H/5*2;
g.reset().clearRect(0,0,W,W);
var y = H/5*2 + (isFullscreen() ? 0 : 8);
g.reset().clearRect(0,0,W,y);
// Draw date
y = parseInt(y/2)+4;
y += settings.fullscreen ? 0 : 13;
y += isFullscreen() ? 0 : 8;
var date = new Date();
var dateStr = date.getDate();
dateStr = ("0" + dateStr).substr(-2);
@ -395,15 +215,12 @@ function drawDate(){
g.setMediumFont();
g.setColor(g.theme.fg);
g.drawString(dateStr, W/2 - fullDateW / 2, y+1);
g.drawString(dateStr, W/2 - fullDateW / 2, y+2);
}
function drawTime(){
function drawTime(y, smallText){
// Draw background
var y = H/5*2 + (settings.fullscreen ? 0 : 8);
g.setColor(g.theme.fg);
g.fillRect(0,y,W,H);
var date = new Date();
// Draw time
@ -419,45 +236,65 @@ function drawTime(){
// Set y coordinates correctly
y += parseInt((H - y)/2) + 5;
var menuEntry = getMenuEntry();
var menuName = menuEntry[0];
var menuImg = menuEntry[1];
var printImgLeft = settings.menuPosY != 0;
// Show large or small time depending on info entry
if(menuName == null){
if(settings.hideColon){
g.setXLargeFont();
} else {
g.setLargeFont();
}
} else {
if(smallText){
y -= 15;
g.setMediumFont();
} else {
g.setLargeFont();
}
g.drawString(timeStr, W/2, y);
}
// Draw menu if set
if(menuName == null){
function drawMenuItem(text, image){
// First clear the time region
var y = H/5*2 + (isFullscreen() ? 0 : 8);
g.setColor(g.theme.fg);
g.fillRect(0,y,W,H);
// Draw menu text
var hasText = (text != null && text != "");
if(hasText){
g.setFontAlign(0,0);
// For multiline text we show an even smaller font...
text = String(text);
if(text.split('\n').length > 1){
g.setMiniFont();
} else {
g.setSmallFont();
}
var imgWidth = image == null ? 0 : 24;
var strWidth = g.stringWidth(text);
g.setColor(g.theme.fg).fillRect(0, 149-14, W, H);
g.setColor(g.theme.bg).drawString(text, W/2 + imgWidth/2 + 2, 149+3);
if(image != null){
var scale = imgWidth / image.width;
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 149 - parseInt(imgWidth/2), {scale: scale});
}
}
// Draw time
drawTime(y, hasText);
}
function drawMenuAndTime(){
var menuEntry = menu[settings.menuPosX];
// The first entry is the overview...
if(settings.menuPosY == 0){
drawMenuItem(menuEntry.name, menuEntry.img);
return;
}
y += 35;
g.setFontAlign(0,0);
g.setSmallFont();
var imgWidth = 0;
if(menuImg !== undefined){
imgWidth = 24.0;
var strWidth = g.stringWidth(menuName);
var scale = imgWidth / menuImg.width;
g.drawImage(
menuImg,
W/2 + (printImgLeft ? -strWidth/2-2 : strWidth/2+2) - parseInt(imgWidth/2),
y - parseInt(imgWidth/2),
{ scale: scale }
);
}
g.drawString(menuName, printImgLeft ? W/2 + imgWidth/2 + 2 : W/2 - imgWidth/2 - 2, y+3);
// Draw item if needed
lock_input = true;
var item = menuEntry.items[settings.menuPosY-1];
item.show();
}
@ -470,7 +307,7 @@ function drawLock(){
function drawWidgets(){
if(settings.fullscreen){
if(isFullscreen()){
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
} else {
Bangle.drawWidgets();
@ -478,9 +315,19 @@ function drawWidgets(){
}
function isFullscreen(){
var s = settings.screen.toLowerCase();
if(s == "dynamic"){
return Bangle.isLocked()
} else {
return s == "full"
}
}
/*
* Draw timeout
/************************************************
* Listener
*/
// timeout used to update every minute
var drawTimeout;
@ -508,6 +355,13 @@ Bangle.on('lcdPower',on=>{
Bangle.on('lock', function(isLocked) {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
if(!isLocked && settings.screen.toLowerCase() == "dynamic"){
// If we have to show the widgets again, we load it from our
// cache and not through Bangle.loadWidgets as its much faster!
for (let wd of WIDGETS) {wd.draw=wd._draw;wd.area=wd._area;}
}
draw();
});
@ -516,13 +370,13 @@ Bangle.on('charging',function(charging) {
drawTimeout = undefined;
// Jump to battery
settings.menuPosX = 1;
settings.menuPosX = 0;
settings.menuPosY = 1;
draw();
});
Bangle.on('touch', function(btn, e){
var widget_size = settings.fullscreen ? 0 : 20; // Its not exactly 24px -- empirically it seems that 20 worked better...
var widget_size = isFullscreen() ? 0 : 20; // Its not exactly 24px -- empirically it seems that 20 worked better...
var left = parseInt(g.getWidth() * 0.22);
var right = g.getWidth() - left;
var upper = parseInt(g.getHeight() * 0.22) + widget_size;
@ -534,17 +388,15 @@ Bangle.on('touch', function(btn, e){
var is_right = e.x > right && !is_upper && !is_lower;
var is_center = !is_upper && !is_lower && !is_left && !is_right;
if(lock_input){
return;
}
if(is_lower){
Bangle.buzz(40, 0.6);
settings.menuPosY = (settings.menuPosY+1) % menu[settings.menuPosX].length;
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
// Handle custom menu entry function
var menuEntry = getMenuEntry();
if(menuEntry.length > 2){
menuEntry[2]();
}
drawTime();
drawMenuAndTime();
}
if(is_upper){
@ -554,22 +406,16 @@ Bangle.on('touch', function(btn, e){
Bangle.buzz(40, 0.6);
settings.menuPosY = settings.menuPosY-1;
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].length-1 : settings.menuPosY;
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
// Handle custom menu entry function
var menuEntry = getMenuEntry();
if(menuEntry.length > 3){
menuEntry[3]();
}
drawTime();
drawMenuAndTime();
}
if(is_right){
Bangle.buzz(40, 0.6);
settings.menuPosX = (settings.menuPosX+1) % menu.length;
settings.menuPosY = 0;
drawTime();
drawMenuAndTime();
}
if(is_left){
@ -577,23 +423,12 @@ Bangle.on('touch', function(btn, e){
settings.menuPosY = 0;
settings.menuPosX = settings.menuPosX-1;
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
drawTime();
drawMenuAndTime();
}
if(is_center){
var menuEntry = getMenuEntry();
if(menuEntry.length > 4){
Bangle.buzz(80, 0.6).then(()=>{
try{
menuEntry[4]();
setTimeout(()=>{
Bangle.buzz(80, 0.6);
}, 250);
} catch(ex){
// In case it fails, we simply ignore it.
}
}
);
if(canRunMenuItem()){
runMenuItem();
}
}
});
@ -608,17 +443,24 @@ E.on("kill", function(){
});
/*
* Draw clock the first time
/************************************************
* Startup Clock
*/
// The upper part is inverse i.e. light if dark and dark if light theme
// is enabled. In order to draw the widgets correctly, we invert the
// dark/light theme as well as the colors.
g.setTheme({bg:g.theme.fg,fg:g.theme.bg, dark:!g.theme.dark}).clear();
// Load widgets and draw clock the first time
Bangle.loadWidgets();
draw();
// Show launcher when middle button pressed
Bangle.setUI("clock");
// Load widgets and draw clock the first time
Bangle.loadWidgets();
// Cache draw function for dynamic screen to hide / show widgets
// Bangle.loadWidgets() could also be called later on but its much slower!
for (let wd of WIDGETS) {wd._draw=wd.draw; wd._area=wd.area;}
// Draw first time
draw();

View File

@ -1,8 +1,8 @@
{
"id": "bwclk",
"name": "BW Clock",
"version": "0.13",
"description": "A very minimalistic clock with date and time in focus.",
"version": "0.23",
"description": "A very minimalistic clock to mainly show date and time.",
"readme": "README.md",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}, {"url":"screenshot_4.png"}],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -4,7 +4,7 @@
// initialize with default settings...
const storage = require('Storage')
let settings = {
fullscreen: false,
screen: "Normal",
showLock: true,
hideColon: false,
};
@ -17,15 +17,16 @@
storage.write(SETTINGS_FILE, settings)
}
var screenOptions = ["Normal", "Dynamic", "Full"];
E.showMenu({
'': { 'title': 'BW Clock' },
'< Back': back,
'Fullscreen': {
value: settings.fullscreen,
format: () => (settings.fullscreen ? 'Yes' : 'No'),
onchange: () => {
settings.fullscreen = !settings.fullscreen;
'Screen': {
value: 0 | screenOptions.indexOf(settings.screen),
min: 0, max: 2,
format: v => screenOptions[v],
onchange: v => {
settings.screen = screenOptions[v];
save();
},
},

3
apps/calclock/ChangeLog Normal file
View File

@ -0,0 +1,3 @@
0.01: Initial version
0.02: More compact rendering & app icon
0.03: Tell clock widgets to hide.

9
apps/calclock/README.md Normal file
View File

@ -0,0 +1,9 @@
# Calendar Clock - Your day at a glance
This clock shows a chronological view of your current and future events.
It uses events synced from Gadgetbridge to achieve this.
The current time and date is highlighted in cyan.
## Screenshot
![](screenshot.png)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwgpm5gAB4AVRhgWCAAQWWDCARC/4ACJR4uB54WDAAP8DBotFGIgXLFwv4GAouQC4gwMLooXF/gXJOowXGJBIXBCIgXQxgXLMAIXXMAmIC5OIx4XJhH/wAXIxnIC78IxGIHoIABI44MBC4wQBEQIDB5gXGPAJgEC6IxBC5oABC4wwDa4YTCxAWD5nPDAzvGFYgAB5AXWJBK+GcAq5CGBIuBC5X4GBIJBdoQXB/GIx4CDPJAuEC5JoCDAgWBFwYXJxCBIFwYXKYwoACCwZ3IPQoWIC5YABGYIABCwpHKAQYMBCwwX/C5QAMC8R3/R/4XNhAXNwAXHgGIABgWIAFwA=="))

119
apps/calclock/calclock.js Normal file
View File

@ -0,0 +1,119 @@
var calendar = [];
var current = [];
var next = [];
function updateCalendar() {
calendar = require("Storage").readJSON("android.calendar.json",true)||[];
calendar = calendar.filter(e => isActive(e) || getTime() <= e.timestamp);
calendar.sort((a,b) => a.timestamp - b.timestamp);
current = calendar.filter(isActive);
next = calendar.filter(e=>!isActive(e));
}
function isActive(event) {
var timeActive = getTime() - event.timestamp;
return timeActive >= 0 && timeActive <= event.durationInSeconds;
}
function zp(str) {
return ("0"+str).substr(-2);
}
function drawEventHeader(event, y) {
g.setFont("Vector", 24);
var time = isActive(event) ? new Date() : new Date(event.timestamp * 1000);
var timeStr = zp(time.getHours()) + ":" + zp(time.getMinutes());
g.drawString(timeStr, 5, y);
y += 24;
g.setFont("12x20", 1);
if (isActive(event)) {
g.drawString(zp(time.getDate())+". " + require("locale").month(time,1),15*timeStr.length,y-21);
} else {
var offset = 0-time.getTimezoneOffset()/1440;
var days = Math.floor((time.getTime()/1000)/86400+offset)-Math.floor(getTime()/86400+offset);
if(days > 0) {
var daysStr = days===1?/*LANG*/"tomorrow":/*LANG*/"in "+days+/*LANG*/" days";
g.drawString(daysStr,15*timeStr.length,y-21);
}
}
return y;
}
function drawEventBody(event, y) {
g.setFont("12x20", 1);
var lines = g.wrapString(event.title, g.getWidth()-10);
if (lines.length > 2) {
lines = lines.slice(0,2);
lines[1] = lines[1].slice(0,-3)+"...";
}
g.drawString(lines.join('\n'), 5, y);
y+=20 * lines.length;
if(event.location) {
g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),5,y);
g.drawString(event.location, 20, y);
y+=20;
}
y+=5;
return y;
}
function drawEvent(event, y) {
y = drawEventHeader(event, y);
y = drawEventBody(event, y);
return y;
}
var curEventHeight = 0;
function drawCurrentEvents(y) {
g.setColor("#0ff");
g.clearRect(5, y, g.getWidth() - 5, y + curEventHeight);
curEventHeight = y;
if(current.length === 0) {
y = drawEvent({timestamp: getTime(), durationInSeconds: 100}, y);
} else {
y = drawEventHeader(current[0], y);
for (var e of current) {
y = drawEventBody(e, y);
}
}
curEventHeight = y - curEventHeight;
return y;
}
function drawFutureEvents(y) {
g.setColor(g.theme.fg);
for (var e of next) {
y = drawEvent(e, y);
if(y>g.getHeight())break;
}
return y;
}
function fullRedraw() {
g.clearRect(5,24,g.getWidth()-5,g.getHeight());
updateCalendar();
var y = 30;
y = drawCurrentEvents(y);
drawFutureEvents(y);
}
function redraw() {
g.reset();
if (current.find(e=>!isActive(e)) || next.find(isActive)) {
fullRedraw();
} else {
drawCurrentEvents(30);
}
}
g.clear();
fullRedraw();
var minuteInterval = setInterval(redraw, 60 * 1000);
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();

BIN
apps/calclock/calclock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
apps/calclock/location.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

View File

@ -0,0 +1,17 @@
{
"id": "calclock",
"name": "Calendar Clock",
"shortName": "CalClock",
"version": "0.03",
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
"icon": "calclock.png",
"type": "clock",
"tags": "clock agenda",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"calclock.app.js","url":"calclock.js"},
{"name":"calclock.img","url":"calclock-icon.js","evaluate":true}
],
"screenshots": [{"url":"screenshot.png"}]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -8,4 +8,5 @@
0.7: Update Rocket Sequences Scope to not use memory all time
0.8: Update Some Variable Scopes to not use memory until need
0.9: Remove ESLint spaces
0.10: Show daily steps, heartrate and the temperature if weather information is available.
0.10: Show daily steps, heartrate and the temperature if weather information is available.
0.11: Tell clock widgets to hide.

View File

@ -165,11 +165,11 @@ Bangle.on("lock", (locked) => {
}
});
Bangle.setUI("clock");
// Load widgets, but don't show them
Bangle.loadWidgets();
Bangle.setUI("clock");
g.reset();
g.clear();
draw();
draw();

View File

@ -4,7 +4,7 @@
"description": "Animated Clock with Space Cassio Watch Style",
"screenshots": [{ "url": "screens/screen_night.png" },{ "url": "screens/screen_day.png" }],
"icon": "app.png",
"version": "0.10",
"version": "0.11",
"type": "clock",
"tags": "clock, weather, cassio, retro",
"supports": ["BANGLEJS2"],

2
apps/chimer/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: Initial Creation
0.02: Fixed some sleep bugs. Added a sleep mode toggle

11
apps/chimer/README.MD Normal file
View File

@ -0,0 +1,11 @@
# Chimer - For the BangleJS
A fork of [Hour Chime](https://github.com/espruino/BangleApps/tree/master/apps/widchime) that adds extra features such as:
- Buzz or beep on every 60, 30 or 15 minutes.
- Repeat Chime up to 3 times
- Set hours to disable chime
Setting the hours you don't want your watch to chime for is done by setting the hour you want it to stop, and the hour you want it to start.
Hours range from 0 - 23.

2
apps/chimer/icon.txt Normal file
View File

@ -0,0 +1,2 @@
widget.png: "https://icons8.com/icon/114436/alarm"

16
apps/chimer/metadata.json Normal file
View File

@ -0,0 +1,16 @@
{
"id": "chimer",
"name": "Chimer",
"version": "0.02",
"description": "A fork of Hour Chime that adds extra features such as: \n - Buzz or beep on every 60, 30 or 15 minutes. \n - Reapeat Chime up to 3 times \n - Set hours to disable chime",
"icon": "widget.png",
"type": "widget",
"tags": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.MD",
"storage": [
{ "name": "chimer.wid.js", "url": "widget.js" },
{ "name": "chimer.settings.js", "url": "settings.js" }
],
"data": [{ "name": "chimer.json" }]
}

94
apps/chimer/settings.js Normal file
View File

@ -0,0 +1,94 @@
/**
* @param {function} back Use back() to return to settings menu
*/
(function (back) {
// default to buzzing
var FILE = "chimer.json";
var settings = {};
const chimes = ["Off", "Buzz", "Beep", "Both"];
const frequency = ["60 min", "30 min", "15 min", "1 min"];
var showMainMenu = () => {
E.showMenu({
"": { title: "Chimer" },
"< Back": () => back(),
"Chime Type": {
value: settings.type,
min: 0,
max: 2, // both is just silly
format: (v) => chimes[v],
onchange: (v) => {
settings.type = v;
writeSettings(settings);
},
},
Frequency: {
value: settings.freq,
min: 0,
max: 2,
format: (v) => frequency[v],
onchange: (v) => {
settings.freq = v;
writeSettings(settings);
},
},
Repetition: {
value: settings.repeat,
min: 1,
max: 5,
format: (v) => v,
onchange: (v) => {
settings.repeat = v;
writeSettings(settings);
},
},
"Sleep Mode": {
value: !!settings.sleep,
onchange: (v) => {
settings.sleep = v;
writeSettings(settings);
},
},
"Sleep Start": {
value: settings.start,
min: 0,
max: 23,
format: (v) => v,
onchange: (v) => {
settings.start = v;
writeSettings(settings);
},
},
"Sleep End": {
value: settings.end,
min: 0,
max: 23,
format: (v) => v,
onchange: (v) => {
settings.end = v;
writeSettings(settings);
},
},
});
};
var readSettings = () => {
var settings = require("Storage").readJSON(FILE, 1) || {
type: 1,
freq: 0,
repeat: 1,
sleep: true,
start: 6,
end: 22,
};
return settings;
};
var writeSettings = (settings) => {
require("Storage").writeJSON(FILE, settings);
};
settings = readSettings();
showMainMenu();
});

134
apps/chimer/widget.js Normal file
View File

@ -0,0 +1,134 @@
(function () {
// 0: off, 1: buzz, 2: beep, 3: both
var FILE = "chimer.json";
var readSettings = () => {
var settings = require("Storage").readJSON(FILE, 1) || {
type: 1,
freq: 0,
repeat: 1,
sleep: true,
start: 6,
end: 22,
};
return settings;
};
var settings = readSettings();
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function chime() {
for (var i = 0; i < settings.repeat; i++) {
if (settings.type === 1) {
Bangle.buzz(100);
} else if (settings.type === 2) {
Bangle.beep();
} else {
return;
}
sleep(150);
}
}
let lastHour = new Date().getHours();
let lastMinute = new Date().getMinutes(); // don't chime when (re)loaded at a whole hour
function check() {
const now = new Date(),
h = now.getHours(),
m = now.getMinutes(),
s = now.getSeconds(),
ms = now.getMilliseconds();
if (
(settings.sleep && h > settings.end) ||
(settings.sleep && h >= settings.end && m !== 0) ||
(settings.sleep && h < settings.start)
) {
var mLeft = 60 - m,
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
setTimeout(check, msLeft);
return;
}
if (settings.freq === 1) {
if ((m !== lastMinute && m === 0) || (m !== lastMinute && m === 30))
chime();
lastHour = h;
lastMinute = m;
// check again in 30 minutes
switch (true) {
case m / 30 >= 1:
var mLeft = 30 - (m - 30),
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
break;
case m / 30 < 1:
var mLeft = 30 - m,
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
break;
}
setTimeout(check, msLeft);
} else if (settings.freq === 2) {
if (
(m !== lastMinute && m === 0) ||
(m !== lastMinute && m === 15) ||
(m !== lastMinute && m === 30) ||
(m !== lastMinute && m === 45)
)
chime();
lastHour = h;
lastMinute = m;
// check again in 15 minutes
switch (true) {
case m / 15 >= 3:
var mLeft = 15 - (m - 45),
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
break;
case m / 15 >= 2:
var mLeft = 15 - (m - 30),
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
break;
case m / 15 >= 1:
var mLeft = 15 - (m - 15),
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
break;
case m / 15 < 1:
var mLeft = 15 - m,
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
break;
}
setTimeout(check, msLeft);
} else if (settings.freq === 3) {
if (m !== lastMinute) chime();
lastHour = h;
lastMinute = m;
// check again in 1 minute
var mLeft = 1,
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
setTimeout(check, msLeft);
} else {
if (h !== lastHour && m === 0) chime();
lastHour = h;
// check again in 60 minutes
var mLeft = 60 - m,
sLeft = mLeft * 60 - s,
msLeft = sLeft * 1000 - ms;
setTimeout(check, msLeft);
}
}
check();
})();

BIN
apps/chimer/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -2,3 +2,5 @@
0.02: Added scrollable calendar and swipe gestures
0.03: Configurable drag gestures
0.04: Use default Bangle formatter for booleans
0.05: Improved colors (connected vs disconnected)
0.06: Tell clock widgets to hide.

View File

@ -1,3 +1,4 @@
Bangle.setUI("clock");
Bangle.loadWidgets();
var s = Object.assign({
@ -123,7 +124,7 @@ function drawMinutes() {
var d = new Date();
var hours = s.MODE24 ? d.getHours().toString().padStart(2, ' ') : ((d.getHours() + 24) % 12 || 12).toString().padStart(2, ' ');
var minutes = d.getMinutes().toString().padStart(2, '0');
var textColor = NRF.getSecurityStatus().connected ? '#fff' : '#f00';
var textColor = NRF.getSecurityStatus().connected ? '#99f' : '#fff';
var size = 50;
var clock_x = (w - 20) / 2;
if (dimSeconds) {
@ -307,4 +308,4 @@ NRF.on('disconnect', BTevent);
dimSeconds = Bangle.isLocked();
drawWatch();
Bangle.setUI("clock");

Some files were not shown because too many files have changed in this diff Show More