Merge branch 'master' into master

master
Hilmar Strauch 2022-01-21 15:19:34 +01:00 committed by GitHub
commit 51c061fd83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 1468 additions and 99 deletions

2
.gitignore vendored
View File

@ -9,4 +9,4 @@ appdates.csv
_config.yml _config.yml
tests/Layout/bin/tmp.* tests/Layout/bin/tmp.*
tests/Layout/testresult.bmp tests/Layout/testresult.bmp
apps.json apps.local.json

View File

@ -1,25 +1,38 @@
--- ---
# =================================================================
# ALL THE INFORMATION INSIDE APPS.JSON HAS NOW BEEN MOVED
#
# You'll find it inside a file called apps/yourapp/metadata.json
#
# Otherwise nothing has changed. GitHub Pages will automatically
# create apps.json as your site is hosted, or if you're hosting
# yourself you can run bin/create_apps_json.sh
#
# If you serve the store from localhost for development/testing,
# the loader looks for apps.local.json instead, you can run
# `bin/create_apps_json.sh apps.local.json` to create that file.
# =================================================================
# Uncomment the following line if you only want explicitly listed
# apps to be available on your site
# restricted: ["boot", "launch", "antonclk", "health", "setting", "about", "widbat", "widbt", "widlock", "widid"]
--- ---
{% comment %} {%- if page.restricted == nil -%}
================================================================= {%- assign apps = site.static_files | where: "name", "metadata.json" | map: "path" -%}
ALL THE INFORMATION INSIDE APPS.JSON HAS NOW BEEN MOVED {%- else -%}
{%- capture temp -%}
You'll find it inside a file called apps/yourapp/metadata.json {%- for app in page.restricted %} /apps/{{app}}/metadata.json {%- endfor -%}
{%- endcapture -%}
Otherwise nothing has changed. GitHub Pages will automatically {%- assign apps = temp | strip | split: " " -%}
create apps.json as your site is hosted, or if you're hosting {%- endif -%}
yourself you can run bin/create_apps_json.sh
=================================================================
{% endcomment %}
{%- assign apps = site.static_files | where: "name", "metadata.json" -%}
[ [
{%- include_relative {{ apps.first.path }} -%} {%- include_relative {{ apps.first }} -%}
{%- for app in apps offset:1 -%} {%- for app in apps offset:1 -%}
,{%- include_relative {{ app.path }} -%} ,{%- include_relative {{ app }} -%}
{%- endfor -%} {%- endfor -%}
] ]

View File

@ -8,6 +8,7 @@
"tags": "Color,input,buttons,touch,UI", "tags": "Color,input,buttons,touch,UI",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS"],
"readme": "README.md", "readme": "README.md",
"screenshots": [{"url":"UI4swatch_icon.png"},{"url":"UI4swatch_s1.png"}],
"storage": [ "storage": [
{"name":"UI4swatch.app.js","url":"app.js"}, {"name":"UI4swatch.app.js","url":"app.js"},
{"name":"UI4swatch.img","url":"app-icon.js","evaluate":true} {"name":"UI4swatch.img","url":"app-icon.js","evaluate":true}

View File

@ -8,6 +8,7 @@
"tags": "outdoors,widget", "tags": "outdoors,widget",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS"],
"readme": "README.md", "readme": "README.md",
"screenshots": [{"url":"600.png"},{"url":"10600.png"},{"url":"1600.png"}],
"storage": [ "storage": [
{"name":"activepedom.wid.js","url":"widget.js"}, {"name":"activepedom.wid.js","url":"widget.js"},
{"name":"activepedom.settings.js","url":"settings.js"}, {"name":"activepedom.settings.js","url":"settings.js"},

View File

@ -5,7 +5,7 @@
"description": "Shows you the days left until a certain date. Date can be set with a settings app and is written to a file.", "description": "Shows you the days left until a certain date. Date can be set with a settings app and is written to a file.",
"icon": "app.png", "icon": "app.png",
"tags": "", "tags": "",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": false, "allow_emulator": false,
"storage": [ "storage": [
{"name":"daysl.app.js","url":"app.js"}, {"name":"daysl.app.js","url":"app.js"},

View File

@ -1,2 +1,2 @@
0.1: Added source code 0.01: Added source code
0.2: Added a README file 0.02: Added a README file

View File

@ -2,7 +2,7 @@
"id": "fd6fdetect", "id": "fd6fdetect",
"name": "fd6fdetect", "name": "fd6fdetect",
"shortName": "fd6fdetect", "shortName": "fd6fdetect",
"version": "0.2", "version": "0.02",
"description": "Allows you to see 0xFD6F beacons near you.", "description": "Allows you to see 0xFD6F beacons near you.",
"icon": "app.png", "icon": "app.png",
"tags": "tool", "tags": "tool",

View File

@ -1 +1 @@
1.00: First published version. 0.01: First published version.

View File

@ -1,7 +1,7 @@
{ {
"id": "mysticdock", "id": "mysticdock",
"name": "Mystic Dock", "name": "Mystic Dock",
"version": "1.00", "version": "0.01",
"description": "A retro-inspired dockface that displays the current time and battery charge while plugged in, and which features an interactive mode that shows the time, date, and a rotating data display line.", "description": "A retro-inspired dockface that displays the current time and battery charge while plugged in, and which features an interactive mode that shows the time, date, and a rotating data display line.",
"icon": "mystic-dock.png", "icon": "mystic-dock.png",
"type": "dock", "type": "dock",

View File

@ -9,3 +9,5 @@
0.09: Added dependancy on Pedometer Widget 0.09: Added dependancy on Pedometer Widget
0.10: Added Weather line, fixed issues on a Bangle 1, update every minute 0.10: Added Weather line, fixed issues on a Bangle 1, update every minute
0.11: Changed cycle on minute to prevInfo to avoid the 2nd one being the blank line 0.11: Changed cycle on minute to prevInfo to avoid the 2nd one being the blank line
0.12: Removed dependancy on widpedom, now uses Bangle.getHealthStatus("day").steps
which requires 2.11.27 firmware to reset at midnight

View File

@ -2,10 +2,10 @@
"id": "pastel", "id": "pastel",
"name": "Pastel Clock", "name": "Pastel Clock",
"shortName": "Pastel", "shortName": "Pastel",
"version": "0.11", "version": "0.12",
"description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times", "description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times. Requires firmware 2.11.27",
"icon": "pastel.png", "icon": "pastel.png",
"dependencies": {"mylocation":"app", "widpedom":"app","weather":"app"}, "dependencies": {"mylocation":"app","weather":"app"},
"screenshots": [{"url":"screenshot_pastel.png"}, {"url":"weather_icons.png"}], "screenshots": [{"url":"screenshot_pastel.png"}, {"url":"weather_icons.png"}],
"type": "clock", "type": "clock",
"tags": "clock, weather, tool", "tags": "clock, weather, tool",

View File

@ -64,13 +64,15 @@ function loadFonts() {
require("f_lato").add(Graphics); require("f_lato").add(Graphics);
} }
function stepsWidget() { function getSteps() {
if (WIDGETS.activepedom !== undefined) { try {
return WIDGETS.activepedom; return Bangle.getHealthStatus("day").steps;
} else if (WIDGETS.wpedom !== undefined) { } catch (e) {
return WIDGETS.wpedom; if (WIDGETS.wpedom !== undefined)
return WIDGETS.wpedom.getSteps();
else
return '???'
} }
return undefined;
} }
const infoData = { const infoData = {
@ -79,7 +81,7 @@ const infoData = {
ID_DAY: { calc: () => {var d = require("locale").dow(new Date).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} }, ID_DAY: { calc: () => {var d = require("locale").dow(new Date).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} },
ID_SR: { calc: () => 'Sunrise: ' + sunRise }, ID_SR: { calc: () => 'Sunrise: ' + sunRise },
ID_SS: { calc: () => 'Sunset: ' + sunSet }, ID_SS: { calc: () => 'Sunset: ' + sunSet },
ID_STEP: { calc: () => 'Steps: ' + stepsWidget().getSteps() }, ID_STEP: { calc: () => 'Steps: ' + getSteps() },
ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' }, ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' },
ID_MEM: { calc: () => {var val = process.memory(); return 'Ram: ' + Math.round(val.usage*100/val.total) + '%';} }, ID_MEM: { calc: () => {var val = process.memory(); return 'Ram: ' + Math.round(val.usage*100/val.total) + '%';} },
ID_ID: { calc: () => {var val = NRF.getAddress().split(':'); return 'Id: ' + val[4] + val[5];} }, ID_ID: { calc: () => {var val = NRF.getAddress().split(':'); return 'Id: ' + val[4] + val[5];} },

View File

@ -1 +1 @@
0.1: first release 0.01: first release

View File

@ -2,7 +2,7 @@
"id": "pebbled", "id": "pebbled",
"name": "Pebble Clock with distance", "name": "Pebble Clock with distance",
"shortName": "Pebble + distance", "shortName": "Pebble + distance",
"version": "0.1", "version": "0.01",
"description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).", "description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).",
"readme": "README.md", "readme": "README.md",
"icon": "pebbled.png", "icon": "pebbled.png",

View File

@ -1,8 +1,8 @@
0.1: Start of app. 0.01: Start of app.
0.5: BLE keyboard functionality. 0.02: BLE keyboard functionality.
1.0: BLE mouse functionality to scroll back/forward. 0.03: BLE mouse functionality to scroll back/forward.
1.5: Added accelerator style mouse. 0.04: Added accelerator style mouse.
2.0: Added touchpad style mouse. 0.05: Added touchpad style mouse.
2.1: Initial internal git(hub) release. Added icon and such. 0.06: Initial internal git(hub) release. Added icon and such.
2.2: Begin work on presentation parts. 0.07: Begin work on presentation parts.
3.0: Presentation parts! 0.08: Presentation parts!

View File

@ -1,7 +1,7 @@
{ {
"id": "presentor", "id": "presentor",
"name": "Presentor", "name": "Presentor",
"version": "3.0", "version": "0.08",
"description": "Use your Bangle to present!", "description": "Use your Bangle to present!",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -1,2 +1,2 @@
1.00: Hello Ruuvi Watch! 0.01: Hello Ruuvi Watch!
1.01: Clear gfx on startup. 0.02: Clear gfx on startup.

View File

@ -2,7 +2,7 @@
"name": "Ruuvi Watch", "name": "Ruuvi Watch",
"shortName":"Ruuvi Watch", "shortName":"Ruuvi Watch",
"icon": "ruuviwatch.png", "icon": "ruuviwatch.png",
"version":"1.01", "version":"0.02",
"description": "Keep an eye on RuuviTag devices (https://ruuvi.com). Only shows RuuviTags using the v5 format.", "description": "Keep an eye on RuuviTag devices (https://ruuvi.com). Only shows RuuviTags using the v5 format.",
"readme":"README.md", "readme":"README.md",
"tags": "bluetooth", "tags": "bluetooth",

View File

@ -1,2 +1,2 @@
0.1: Initial release 0.01: Initial release
0.2: Fixed launcher image 0.02: Fixed launcher image

View File

@ -2,7 +2,7 @@
"id": "showimg", "id": "showimg",
"name": "simple image viewer", "name": "simple image viewer",
"shortName":"showImage", "shortName":"showImage",
"version":"0.2", "version":"0.02",
"description": "Displays the image in \"showimg.user.img\". The file has to be uploaded via the espruino IDE. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.", "description": "Displays the image in \"showimg.user.img\". The file has to be uploaded via the espruino IDE. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.",
"icon": "app.png", "icon": "app.png",
"tags": "tool", "tags": "tool",

View File

@ -1,2 +1,2 @@
1.00 Added sonic clock app 0.01 Added sonic clock app
1.01 Fixed text alignment issue; Increased acceleration required to activate twist; 0.02 Fixed text alignment issue; Increased acceleration required to activate twist;

View File

@ -1,7 +1,7 @@
{ {
"id": "sonicclk", "id": "sonicclk",
"name": "Sonic Clock", "name": "Sonic Clock",
"version": "1.01", "version": "0.02",
"description": "A classic sonic clock featuring run, stop and wait animations.", "description": "A classic sonic clock featuring run, stop and wait animations.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],

View File

@ -5,6 +5,6 @@
0.05: Add setting to turn vibrate on/off. 0.05: Add setting to turn vibrate on/off.
0.06: Tweaks to vibration settings. 0.06: Tweaks to vibration settings.
0.07: Switch to BTN1 for Max toggle and reset function. 0.07: Switch to BTN1 for Max toggle and reset function.
1.00: New features. Added waypoints file and distance to selected waypoint display. Added integration with GPS Setup module to switch GPS to low power mode when screen off. Save display settings and restore when app restarted. 0.08: New features. Added waypoints file and distance to selected waypoint display. Added integration with GPS Setup module to switch GPS to low power mode when screen off. Save display settings and restore when app restarted.
1.01: Add third screen mode with large clock and waypoint selection display to ease visibility in bright daylight. 0.09: Add third screen mode with large clock and waypoint selection display to ease visibility in bright daylight.
1.02: Add Kalman filter to smooth the speed and altitude values. Can be disabled in settings. 0.10: Add Kalman filter to smooth the speed and altitude values. Can be disabled in settings.

View File

@ -2,7 +2,7 @@
"id": "speedalt", "id": "speedalt",
"name": "GPS Adventure Sports", "name": "GPS Adventure Sports",
"shortName": "GPS Adv Sport", "shortName": "GPS Adv Sport",
"version": "1.02", "version": "0.10",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -1,4 +1,3 @@
0.01: Initial import. 0.01: Initial import.
0.07: Add swipe to change screens. 0.02: Add swipe to change screens.
1.06: Misc memory and screen optimisations. 0.03: Misc memory and screen optimisations.
1.10: ...

View File

@ -2,7 +2,7 @@
"id": "speedalt2", "id": "speedalt2",
"name": "GPS Adventure Sports II", "name": "GPS Adventure Sports II",
"shortName":"GPS Adv Sport II", "shortName":"GPS Adv Sport II",
"version":"1.10", "version":"0.03",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -1,7 +1,7 @@
{ {
"id": "teatimer", "id": "teatimer",
"name": "Tea Timer", "name": "Tea Timer",
"version": "1.00", "version": "0.01",
"description": "A simple timer. You can easyly set up the time.", "description": "A simple timer. You can easyly set up the time.",
"icon": "teatimer.png", "icon": "teatimer.png",
"type": "app", "type": "app",

1
apps/timerclk/ChangeLog Normal file
View File

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

72
apps/timerclk/README.md Normal file
View File

@ -0,0 +1,72 @@
# Timer Clock
A clock based on the Anton Clock with stopwatches, timers and alarms based on the Stopwatch Touch style and an alarm widget based on the one from Default alarm & timer.
## Features
* two slots for stopwatches / timers on the clock screen
* configurable font and size (Anton font has fixed size)
* stopwatch with modifiable start value
* timer that can be paused
* alarms
* multiple stopwatches, timers and alarms
* stopwatches and timers keep running in the background
## Images
![](screenshot.png)
### Stopwatch
![](screenshot_stopwatch1.png)
![](screenshot_stopwatch2.png)
### Settings
![](screenshot_settings1.png)
![](screenshot_settings2.png)
![](screenshot_settings3.png)
## Controls
### Bangle.js 1
#### Clock
* Left: Stopwatch
* Right: Timer
* Button 1 / 2: Alarm
#### Stopwatch / Timer / Alarm
* Button 1:
* edit mode: increase
* control mode: play / pause
* Button 2: switch between edit / control mode
* Button 3:
* edit mode: decrease
* control mode: reset / remove
* Left:
* edit mode: previous index
* control mode: previous stopwatch / timer / alarm
* Right:
* edit mode: next index
* control mode: next stopwatch / timer / alarm
### Bangle.js 2
#### Clock
* Swipe left: Stopwatch
* Swipe right: Timer
* Swipe over date: Alarm
#### Stopwatch / Timer / Alarm
* Swipe left: previous stopwatch / timer / alarm
* Swipe right: next stopwatch / timer / alarm
* Swipe up: increase index swiped over
* Swipe down: decrease index swiped over

View File

@ -0,0 +1,57 @@
if (timerclkAlarmTimeout) clearInterval(timerclkAlarmTimeout);
var timerclk = require("timerclk.lib.js");
var settings = require('Storage').readJSON("timerclk.json", true) || {};
settings = Object.assign({
"vibrate":10
}, settings.alarm||{});
function showAlarm(alarm) {
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setLocked(false);
E.showPrompt("Alarm!",{
title:"ALARM!",
buttons : {/*LANG*/"Ok":true}
}).then(function(ok) {
buzzCount = 0;
if (ok) {
alarm.last = new Date().getDate();
}
require("Storage").write("timerclk.alarm.json",JSON.stringify(alarms));
load();
});
function vibrate(counter) {
VIBRATE.write(1);
setTimeout(() => VIBRATE.write(0), 100);
if (--counter) setTimeout(() => vibrate(counter), 250);
}
function buzz() {
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
vibrate(4);
if (buzzCount--)
setTimeout(buzz, 3000);
else { // auto-snooze
buzzCount = settings.vibrate;
setTimeout(buzz, 600000);
}
}
var buzzCount = settings.vibrate;
buzz();
}
// Check for alarms
console.log("checking for alarms...");
var alarms = require("Storage").readJSON("timerclk.alarm.json",1)||[];
var active = alarms.filter(e=>e.on);
if (active.length) {
// if there's an alarm, show it
active = active.sort((a,b)=>(a.time-b.time)+(a.last-b.last)*86400000);
if (active[0].last != new Date().getDate()) {
showAlarm(active[0]);
} else {
setTimeout(load, 100);
}
} else {
// otherwise just go back to default app
setTimeout(load, 100);
}

1
apps/timerclk/alarm.info Normal file
View File

@ -0,0 +1 @@
{"id":"timerclk","name":"tclk Alarm","src":"timerclk.alarm.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}

116
apps/timerclk/alarm.js Normal file
View File

@ -0,0 +1,116 @@
var timerclk = require("timerclk.lib.js");
const height = g.getHeight(), width = g.getWidth();
var all = require("Storage").readJSON("timerclk.alarm.json") || [];
var settings = require('Storage').readJSON("timerclk.json", true) || {};
settings = Object.assign({
"font":"Vector",
"fontSize":40,
"indexFont":"6x8",
"indexFontSize":3,
"buttonHeight":40,
"vibrate":4,
}, settings = settings.alarm||{});
var defaultElement = {time:43200000, on:true, last:null};
var current = 0;
var editIndex = 0;
var drawInterval;
var drawIntervalTimeout;
var buttons;
var dragBorderHrsMins=0, dragBorderMinsSecs=0;
function update() {
if (drawInterval) clearInterval(drawInterval);
if (drawIntervalTimeout) clearTimeout(drawIntervalTimeout);
if (all[current].start) {
drawIntervalTimeout = setTimeout(() => {drawInterval = setInterval(draw, 1000); draw();}, 1000 - (timerclk.getTime(all[current]) % 1000));
} else {
drawInterval = null;
drawIntervalTimeout = null;
}
draw();
drawButtons();
}
function activate() {
all[current].on = !all[current].on;
all[current].last = null;
update();
require("Storage").write("timerclk.alarm.json",JSON.stringify(all));
timerclkCheckAlarms();
}
function remove() {
all.splice(current, 1);
if (current == all.length) current--;
if (all.length == 0) {
all.push(defaultElement.clone());
current++;
}
update();
require("Storage").write("timerclk.alarm.json",JSON.stringify(all));
timerclkCheckAlarms();
}
function edit(position, change) {
if (position == 1) all[current].time += change*1000;
else if (position == 2) all[current].time += change*60000;
else if (position == 3) all[current].time += change*3600000;
require("Storage").write("timerclk.alarm.json",JSON.stringify(all));
timerclkCheckAlarms();
}
var buttons = {
reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: remove, img: timerclk.remove_img, col:"#f50"}, // remove
play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: activate, img: timerclk.play_img, col:"#0ff"}, // active
};
function drawButtons() {
if (all[current].on) {
buttons.play.img = timerclk.pause_img;
} else {
buttons.play.img = timerclk.play_img;
}
for (var button of buttons) {
g.setColor(button.col);
g.fillRect(button.pos[0], button.pos[1], button.pos[2], button.pos[3]);
g.setColor("#000");
// scale 24px images
let iw = settings.buttonHeight-10;
var scale = iw/24;
let ix = button.pos[0] + ((button.pos[2]-button.pos[0] - iw) /2);
let iy = button.pos[1] + ((button.pos[3]-button.pos[1] - iw) /2);
g.drawImage(button.img, ix, iy, {scale: scale});
}
}
function draw() {
var x = g.getWidth()/2;
var y = g.getHeight()/2;
g.reset();
g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2-settings.buttonHeight);
g.setFontAlign(0,0).setFont(settings.indexFont, settings.indexFontSize);
g.drawString(current+1, x, Bangle.appRect.y + (g.stringMetrics("0").height/2));
g.setFontAlign(0,0).setFont(settings.font, settings.fontSize);
var timeStr = timerclk.formatTime(all[current].time, false, false, true);
g.drawString(timeStr,x,y);
var start = (width-g.stringMetrics(timeStr).width)/2;
timeStr = timeStr.split(":");
var markerPosChange = g.stringMetrics("__").width/2;
if (editIndex == 3) x = start + g.stringMetrics(timeStr[0]).width - markerPosChange;
else if (editIndex == 2) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]).width - markerPosChange;
else if (editIndex == 1) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]+":"+timeStr[2]).width - markerPosChange;
else x = 0;
if (x) g.drawString("__", x, y);
dragBorderHrsMins = start+g.stringMetrics(timeStr[0]).width+g.stringMetrics(":").width/2;
dragBorderMinsSecs = start+g.stringMetrics(timeStr[0]+":"+timeStr[1]).width+g.stringMetrics(":").width/2;
}
if (all.length == 0) {
all.push(defaultElement.clone());
}
timerclk.registerControls(this);
Bangle.loadWidgets();
Bangle.drawWidgets();
update();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgP/AFHzvmf+f8z/8tnv/vs9/1t/v+kv94jR/H4n/wn4CBAYPwnEP8AFDg/AAoUwAoPgmABBwfQAonwAo0/4gFC4AFE4gFLmGEAoQDBxgFCwEQAIIFIj4FD/k//hNBAoZZBAoc8j6oS8/P+1NAoP63+7+wMCz/u/YEB/v/v4dI1+pAQIFBx/J/2/AoP5tFJr71eA=="))

BIN
apps/timerclk/app-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

151
apps/timerclk/app.js Normal file

File diff suppressed because one or more lines are too long

48
apps/timerclk/boot.js Normal file
View File

@ -0,0 +1,48 @@
var timerclkTimerTimeout;
var timerclkAlarmTimeout;
function timerclkCheckTimers() {
if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout);
var timers = require('Storage').readJSON('timerclk.timer.json',1)||[];
timers = timers.filter(e=>e.start);
if (timers.length) {
timers = timers.sort((a,b)=>{
var at = a.timeAdd;
if (a.start) at += Date.now()-a.start;
at = a.period-at;
var bt = b.timeAdd;
if (b.start) bt += Date.now()-b.start;
bt = b.period-bt;
return at-bt;
});
if (!require('Storage').read("timerclk.timer.alert.js")) {
console.log("No timer app!");
} else {
var time = timers[0].timeAdd;
if (timers[0].start) time += Date.now()-timers[0].start;
time = timers[0].time - time;
if (time<1000) t=1000;
if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout);
timerclkTimerTimeout = setTimeout(() => load("timerclk.timer.alert.js"),time);
}
}
}
function timerclkCheckAlarms() {
if (timerclkAlarmTimeout) clearTimeout(timerclkAlarmTimeout);
var alarms = require('Storage').readJSON('timerclk.alarm.json',1)||[];
var currentTime = require("timerclk.lib.js").getCurrentTime();
alarms = alarms.filter(e=>e.on);
if (alarms.length) {
alarms = alarms.sort((a,b)=>(a.time-b.time)+(a.last-b.last)*86400000);
if (!require('Storage').read("timerclk.alarm.alert.js")) {
console.log("No alarm app!");
} else {
var time = alarms[0].time-currentTime;
if (alarms[0].last == new Date().getDate() || time < 0) time += 86400000;
if (time<1000) t=1000;
if (timerclkAlarmTimeout) clearTimeout(timerclkAlarmTimeout);
timerclkAlarmTimeout = setTimeout(() => load("timerclk.alarm.alert.js"),time);
}
}
}
timerclkCheckTimers();
timerclkCheckAlarms();

127
apps/timerclk/lib.js Normal file
View File

@ -0,0 +1,127 @@
exports.pause_img = atob("GBiBAf///////////+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B////////////w==");
exports.play_img = atob("GBiBAf////////////P///D///A///Af//AH//AB//AAf/AAH/AAB/AAB/AAH/AAf/AB//AH//Af//A///D///P//////////////w==");
exports.reset_img = atob("GBiBAf////////////AAD+AAB+f/5+f/5+f/5+cA5+cA5+cA5+cA5+cA5+cA5+cA5+cA5+f/5+f/5+f/5+AAB/AAD////////////w==");
exports.remove_img = atob("GBiBAf///////////+P/x+H/h+D/B/B+D/g8H/wYP/4Af/8A//+B//+B//8A//4Af/wYP/g8H/B+D+D/B+H/h+P/x////////////w==");
exports.formatTime = function(t, short, tnthEnable, fullTime) {
var negative = "";
if (t < 0) {
t = t*(-1);
negative = "-";
}
let hrs = Math.floor(t/3600000);
let mins = Math.floor(t/60000)%60;
let secs = Math.floor(t/1000)%60;
var tnth = "";
if (tnthEnable) {
tnth = Math.floor(t/100)%10;
tnth = "."+tnth;
}
var hrsStr = hrs;
if (hrs < 10 && !negative) hrsStr = "0"+hrs;
var text;
if (short) {
if (hrs === 0) text = negative + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
else text = negative + hrsStr + "/" + ("0"+mins).substr(-2);
} else {
if (hrs === 0 && !fullTime) text = negative + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2) + tnth;
else text = negative + hrsStr + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
}
return text;
};
exports.getTime = function(e) {
var time = e.timeAdd;
if (e.start) {
time += Date.now() - e.start;
}
return time;
};
exports.getCurrentTime = function() {
var date = new Date();
return date.getHours()*3600000+date.getMinutes()*60000+date.getSeconds()*1000+date.getMilliseconds();
};
exports.registerControls = function(o) {
if (process.env.HWVERSION==1) {
setWatch(()=>{
if (o.editIndex == 0) o.buttons.play.callback();
else o.edit(o.editIndex, 1);
o.draw();
}, BTN1, {repeat:true});
setWatch(()=>{
o.editIndex = !o.editIndex;
o.draw();
}, BTN2, {repeat:true});
setWatch(()=>{
if (o.editIndex == 0) o.buttons.reset.callback();
else o.edit(o.editIndex, -1);
o.draw();
}, BTN3, {repeat:true});
setWatch(()=>{
if (o.editIndex) {
o.editIndex++;
if (o.editIndex > 3) o.editIndex = 1;
} else if (o.current > 0) o.current--;
o.update();
}, BTN4, {repeat:true});
setWatch(()=>{
if (o.editIndex) {
o.editIndex--;
if (o.editIndex < 1) o.editIndex = 3;
} else {
o.current++;
if (o.current == o.all.length) o.all.push(o.defaultElement.clone());
}
o.update();
}, BTN5, {repeat:true});
} else {
setWatch(()=>load(), BTN1);
Bangle.on('touch',(n,e)=>{
for (var button of o.buttons) {
if (e.x>=button.pos[0] && e.y>=button.pos[1] &&
e.x<button.pos[2] && e.y<button.pos[3]) {
button.callback();
break;
}
}
});
var absX, lastX, lastY;
Bangle.on('drag', e=>{
if (!e.b) {
if (lastX > 40) { // right
o.current++;
if (o.current == o.all.length) o.all.push(o.defaultElement.clone());
} else if (lastX < -40) { // left
if (o.current > 0) {
o.current--;
}
} else if (lastY > 30) { // down
if (absX < o.dragBorderHrsMins) {
o.edit(3, -1);
} else if (absX > o.dragBorderHrsMins && absX < o.dragBorderMinsSecs) {
o.edit(2, -1);
} else {
o.edit(1, -1);
}
} else if (lastY < -30) { // up
if (absX < o.dragBorderHrsMins) {
o.edit(3, 1);
} else if (absX > o.dragBorderHrsMins && absX < o.dragBorderMinsSecs) {
o.edit(2, 1);
} else {
o.edit(1, 1);
}
}
lastX = 0;
lastY = 0;
o.update();
} else {
absX = e.x;
lastX = lastX + e.dx;
lastY = lastY + e.dy;
}
});
}
};

View File

@ -0,0 +1,38 @@
{
"id": "timerclk",
"name": "Timer Clock",
"shortName":"Timer Clock",
"version":"0.01",
"description": "A clock with stopwatches, timers and alarms build in.",
"icon": "app-icon.png",
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS","BANGLEJS2"],
"screenshots": [
{"url":"screenshot.png"},
{"url":"screenshot_stopwatch1.png"},
{"url":"screenshot_stopwatch2.png"},
{"url":"screenshot_settings1.png"},
{"url":"screenshot_settings2.png"},
{"url":"screenshot_settings3.png"}
],
"readme": "README.md",
"storage": [
{"name":"timerclk.app.js","url":"app.js"},
{"name":"timerclk.img","url":"app-icon.js","evaluate":true},
{"name":"timerclk.boot.js","url":"boot.js"},
{"name":"timerclk.lib.js","url":"lib.js"},
{"name":"timerclk.wid.js","url":"wid.js"},
{"name":"timerclk.settings.js","url":"settings.js"},
{"name":"timerclk.stopwatch.js","url":"stopwatch.js"},
{"name":"timerclk.timer.js","url":"timer.js"},
{"name":"timerclk.timer.alert.js","url":"timer.alert.js"},
{"name":"timerclk.alarm.js","url":"alarm.js"},
{"name":"timerclk.alarm.alert.js","url":"alarm.alert.js"},
{"name":"timerclk.stopwatch.info","url":"stopwatch.info"},
{"name":"timerclk.timer.info","url":"timer.info"},
{"name":"timerclk.alarm.info","url":"alarm.info"}
],
"data": [{"name":"timerclk.json"},{"name":"timerclk.stopwatch.json"},{"name":"timerclk.timer.json"},{"name":"timerclk.alarm.json"}],
"sortorder": 0
}

BIN
apps/timerclk/pause-24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
apps/timerclk/play-24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
apps/timerclk/remove-24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
apps/timerclk/reset-24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

292
apps/timerclk/settings.js Normal file
View File

@ -0,0 +1,292 @@
(function(back) {
const FILE = "timerclk.json";
const BOOL_FORMAT = v=>v?/*LANG*/"On":/*LANG*/"Off";
// Load settings
var settings = require('Storage').readJSON(FILE, true) || {}
settings.clock = Object.assign({
"timeFont":"Anton",
"timeFontSize":0,
"dateFont":"6x8",
"dateFontSize":2,
"dowFont":"6x8",
"dowFontSize":2,
"specialFont":"6x8",
"specialFontSize":2,
"shortDate":true,
"showStopwatches":true,
"showTimers":true,
}, settings.clock||{});
settings.stopwatch = Object.assign({
"font":"Vector",
"fontSize":40,
"indexFont":"6x8",
"indexFontSize":3,
"buttonHeight":40,
}, settings.stopwatch||{});
settings.timer = Object.assign({
"font":"Vector",
"fontSize":40,
"indexFont":"6x8",
"indexFontSize":3,
"buttonHeight":40,
"vibrate":10,
}, settings.timer||{});
settings.alarm = Object.assign({
"font":"Vector",
"fontSize":40,
"indexFont":"6x8",
"indexFontSize":3,
"buttonHeight":40,
"vibrate":10,
}, settings.alarm||{});
var timeFonts = ["Anton"].concat(g.getFonts());
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
// Show the menu
var mainMenu = {
"" : { "title" : "Timer Clock" },
"< Back" : () => back(),
"Clock": ()=>{E.showMenu(clockMenu);},
"Stopwatch": ()=>{E.showMenu(stopwatchMenu);},
"Timer": ()=>{E.showMenu(timerMenu);},
"Alarm": ()=>{E.showMenu(alarmMenu);},
};
var clockMenu = {
"" : { "title" : "Clock" },
"< Back" : () => E.showMenu(mainMenu),
"time font":{
value: 0|timeFonts.indexOf(settings.clock.timeFont),
format: v => timeFonts[v],
min: 0, max: timeFonts.length-1,
onchange: v => {
settings.clock.timeFont = timeFonts[v];
writeSettings();
}
},
"time size":{
value: 0|settings.clock.timeFontSize,
min: 0,
onchange: v => {
settings.clock.timeFontSize = v;
writeSettings();
}
},
"date font":{
value: 0|g.getFonts().indexOf(settings.clock.dateFont),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.clock.dateFont = g.getFonts()[v];
writeSettings();
}
},
"date size":{
value: 0|settings.clock.dateFontSize,
min: 0,
onchange: v => {
settings.clock.dateFontSize = v;
writeSettings();
}
},
"dow font":{
value: 0|g.getFonts().indexOf(settings.clock.dowFont),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.clock.dowFont = g.getFonts()[v];
writeSettings();
}
},
"dow size":{
value: 0|settings.clock.dowFontSize,
min: 0,
onchange: v => {
settings.clock.dowFontSize = v;
writeSettings();
}
},
"short date": {
value: !!settings.clock.shortDate,
format: BOOL_FORMAT,
onchange: v => {
settings.clock.shortDate = v;
writeSettings();
}
},
"stopwatches": {
value: !!settings.clock.showStopwatches,
format: v=>v?/*LANG*/"Show":/*LANG*/"Hide",
onchange: v => {
settings.clock.showStopwatches = v;
writeSettings();
}
},
"timers": {
value: !!settings.clock.showTimers,
format: v=>v?/*LANG*/"Show":/*LANG*/"Hide",
onchange: v => {
settings.clock.showTimers = v;
writeSettings();
}
},
};
var stopwatchMenu = {
"" : { "title" : "Stopwatch" },
"< Back" : () => E.showMenu(mainMenu),
"font":{
value: 0|g.getFonts().indexOf(settings.stopwatch.font),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.settings.stopwatch.font = g.getFonts()[v];
writeSettings();
}
},
"fontsize":{
value: 0|settings.stopwatch.fontSize,
min: 0,
onchange: v => {
settings.stopwatch.fontSize = v;
writeSettings();
}
},
"index font":{
value: 0|g.getFonts().indexOf(settings.stopwatch.indexFont),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.settings.stopwatch.indexFont = g.getFonts()[v];
writeSettings();
}
},
"index size":{
value: 0|settings.stopwatch.indexFontSize,
min: 0,
onchange: v => {
settings.stopwatch.indexFontSize = v;
writeSettings();
}
},
"button height":{
value: 0|settings.stopwatch.buttonHeight,
min: 0,
onchange: v => {
settings.stopwatch.buttonHeight = v;
writeSettings();
}
},
};
var timerMenu = {
"" : { "title" : "Timer" },
"< Back" : () => E.showMenu(mainMenu),
"font":{
value: 0|g.getFonts().indexOf(settings.timer.font),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.settings.timer.font = g.getFonts()[v];
writeSettings();
}
},
"fontsize":{
value: 0|settings.timer.fontSize,
min: 0,
onchange: v => {
settings.timer.fontSize = v;
writeSettings();
}
},
"index font":{
value: 0|g.getFonts().indexOf(settings.timer.indexFont),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.settings.timer.indexFont = g.getFonts()[v];
writeSettings();
}
},
"index size":{
value: 0|settings.timer.indexFontSize,
min: 0,
onchange: v => {
settings.timer.indexFontSize = v;
writeSettings();
}
},
"button height":{
value: 0|settings.timer.buttonHeight,
min: 0,
onchange: v => {
settings.timer.buttonHeight = v;
writeSettings();
}
},
"vibrate":{
value: 0|settings.timer.vibrate,
min: 0,
onchange: v=>{
settings.timer.vibrate = v;
writeSettings();
}
}
};
var alarmMenu = {
"" : { "title" : "Alarm" },
"< Back" : () => E.showMenu(mainMenu),
"font":{
value: 0|g.getFonts().indexOf(settings.alarm.font),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.settings.alarm.font = g.getFonts()[v];
writeSettings();
}
},
"fontsize":{
value: 0|settings.alarm.fontSize,
min: 0,
onchange: v => {
settings.alarm.fontSize = v;
writeSettings();
}
},
"index font":{
value: 0|g.getFonts().indexOf(settings.alarm.indexFont),
format: v => g.getFonts()[v],
min: 0, max: g.getFonts().length-1,
onchange: v => {
settings.settings.alarm.indexFont = g.getFonts()[v];
writeSettings();
}
},
"index size":{
value: 0|settings.alarm.indexFontSize,
min: 0,
onchange: v => {
settings.alarm.indexFontSize = v;
writeSettings();
}
},
"button height":{
value: 0|settings.alarm.buttonHeight,
min: 0,
onchange: v => {
settings.alarm.buttonHeight = v;
writeSettings();
}
},
"vibrate":{
value: 0|settings.alarm.vibrate,
min: 0,
onchange: v=>{
settings.alarm.vibrate = v;
writeSettings();
}
}
};
E.showMenu(mainMenu);
});

View File

@ -0,0 +1 @@
{"id":"timerclk","name":"tclk Stopwatch","src":"timerclk.stopwatch.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}

135
apps/timerclk/stopwatch.js Normal file
View File

@ -0,0 +1,135 @@
var timerclk = require("timerclk.lib.js");
const height = g.getHeight(), width = g.getWidth();
var all = require("Storage").readJSON("timerclk.stopwatch.json") || [];
var settings = require('Storage').readJSON("timerclk.json", true) || {};
settings = Object.assign({
"font":"Vector",
"fontSize":40,
"indexFont":"6x8",
"indexFontSize":3,
"buttonHeight":40,
}, settings.stopwatch||{});
var defaultElement = {start:null, timeAdd:0};
var current = 0;
var editIndex = 0;
var drawInterval;
var drawIntervalTimeout;
var buttons;
function update() {
if (drawInterval) clearInterval(drawInterval);
if (drawIntervalTimeout) clearTimeout(drawIntervalTimeout);
var interval = Math.floor(timerclk.getTime(all[current])/3600000)?1000:100;
if (all[current].start) {
drawIntervalTimeout = setTimeout(() => {drawInterval = setInterval(draw, interval); draw();}, interval - (timerclk.getTime(all[current]) % interval));
} else {
drawInterval = null;
drawIntervalTimeout = null;
}
draw();
drawButtons();
}
function play() {
if (all[current].start) { // running
all[current].timeAdd += Date.now() - all[current].start;
all[current].start = null;
update();
} else { // paused
all[current].start = Date.now();
update();
}
require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all));
}
function reset() {
all[current] = defaultElement.clone();
update();
require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all));
}
function remove() {
all.splice(current, 1);
if (current == all.length) current--;
if (all.length == 0) {
all.push(defaultElement.clone());
current++;
}
update();
require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all));
}
function edit(position, change) {
if (position == 1) all[current].timeAdd += change*1000;
else if (position == 2) all[current].timeAdd += change*60000;
else if (position == 3) all[current].timeAdd += change*3600000;
require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all));
}
var buttonsRunning = {
reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: reset, img: timerclk.reset_img, col:"#f50"},
play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:"#0ff"},
};
var buttonsNormal = {
reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: remove, img: timerclk.remove_img, col:buttonsRunning.reset.col},
play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:buttonsRunning.play.col},
};
buttons = buttonsNormal;
function drawButtons() {
if (all[current].start || all[current].time) {
buttons = buttonsRunning;
if (all[current].start) {
buttons.play.img = timerclk.pause_img;
} else {
buttons.play.img = timerclk.play_img;
}
} else {
buttons = buttonsNormal;
}
for (var button of buttons) {
g.setColor(button.col);
g.fillRect(button.pos[0], button.pos[1], button.pos[2], button.pos[3]);
g.setColor("#000");
// scale 24px images
let iw = settings.buttonHeight-10;
var scale = iw/24;
let ix = button.pos[0] + ((button.pos[2]-button.pos[0] - iw) /2);
let iy = button.pos[1] + ((button.pos[3]-button.pos[1] - iw) /2);
g.drawImage(button.img, ix, iy, {scale: scale});
}
}
function draw() {
var x = g.getWidth()/2;
var y = g.getHeight()/2;
g.reset();
var timeStr = timerclk.formatTime(timerclk.getTime(all[current]), false, true);
g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2-settings.buttonHeight);
g.setFontAlign(0,0).setFont(settings.indexFont, settings.indexFontSize);
g.drawString(current+1, x, Bangle.appRect.y + (g.stringMetrics("0").height/2));
g.setFontAlign(0,0).setFont(settings.font, settings.fontSize);
g.drawString(timeStr,x,y);
var start = (width-g.stringMetrics(timeStr).width)/2;
timeStr = timeStr.split(".")[0].split(":");
if (timeStr.length < 3) timeStr = [""].concat(timeStr);
var markerPosChange = g.stringMetrics("__").width/2;
if (editIndex == 3) x = start + g.stringMetrics(timeStr[0]).width - markerPosChange;
else if (editIndex == 2) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]).width - markerPosChange;
else if (editIndex == 1) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]+":"+timeStr[2]).width - markerPosChange;
else x = 0;
if (x) g.drawString("__", x, y);
dragBorderHrsMins = start+g.stringMetrics(timeStr[0]).width+g.stringMetrics(":").width/2;
dragBorderMinsSecs = start+g.stringMetrics(timeStr[0]+":"+timeStr[1]).width+g.stringMetrics(":").width/2;
}
if (all.length == 0) {
all.push(defaultElement.clone());
}
timerclk.registerControls(this);
Bangle.loadWidgets();
Bangle.drawWidgets();
update();

View File

@ -0,0 +1,62 @@
if (timerclkTimerTimeout) clearInterval(timerclkTimerTimeout);
var timerclk = require("timerclk.lib.js");
var settings = require('Storage').readJSON("timerclk.json", true) || {};
settings = Object.assign({
"vibrate":10
}, settings.timer||{});
function showTimer(timer) {
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setLocked(false);
E.showPrompt("Timer finished!",{
title:"TIMER!",
buttons : {/*LANG*/"Ok":true}
}).then(function(ok) {
buzzCount = 0;
if (ok) {
timer.time += Date.now() - timer.start;
timer.start = null;
}
require("Storage").write("timerclk.timer.json",JSON.stringify(timers));
load();
});
function vibrate(counter) {
VIBRATE.write(1);
setTimeout(() => VIBRATE.write(0), 100);
if (--counter) setTimeout(() => vibrate(counter), 250);
}
function buzz() {
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
vibrate(4);
if (buzzCount--)
setTimeout(buzz, 3000);
else { // auto-snooze
buzzCount = settings.vibrate;
setTimeout(buzz, 600000);
}
}
var buzzCount = settings.vibrate;
buzz();
}
// Check for timers
console.log("checking for timers...");
var timers = require("Storage").readJSON("timerclk.timer.json",1)||[];
var active = timers.filter(e=>e.start);
if (active.length) {
// if there's an timer, show it
active = active.sort((a,b)=>{
var at = a.time;
if (a.start) at += Date.now()-a.start;
at = a.period-at;
var bt = b.time;
if (b.start) bt += Date.now()-b.start;
bt = b.period-bt;
return at-bt;
});
showTimer(active[0]);
} else {
// otherwise just go back to default app
setTimeout(load, 100);
}

1
apps/timerclk/timer.info Normal file
View File

@ -0,0 +1 @@
{"id":"timerclk","name":"tclk Timer","src":"timerclk.timer.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}

139
apps/timerclk/timer.js Normal file
View File

@ -0,0 +1,139 @@
var timerclk = require("timerclk.lib.js");
const height = g.getHeight(), width = g.getWidth();
var all = require("Storage").readJSON("timerclk.timer.json") || [];
var settings = require('Storage').readJSON("timerclk.json", true) || {};
settings = Object.assign({
"font":"Vector",
"fontSize":40,
"indexFont":"6x8",
"indexFontSize":3,
"buttonHeight":40,
"vibrate":4,
}, settings = settings.timer||{});
var defaultElement = {time:300000, start:null, timeAdd:0};
var current = 0;
var editIndex = 0;
var drawInterval;
var drawIntervalTimeout;
var buttons;
var dragBorderHrsMins=0, dragBorderMinsSecs=0;
function update() {
if (drawInterval) clearInterval(drawInterval);
if (drawIntervalTimeout) clearTimeout(drawIntervalTimeout);
if (all[current].start) {
drawIntervalTimeout = setTimeout(() => {drawInterval = setInterval(draw, 1000); draw();}, 1000 - (timerclk.getTime(all[current]) % 1000));
} else {
drawInterval = null;
drawIntervalTimeout = null;
}
draw();
drawButtons();
}
function play() {
if (all[current].start) { // running
all[current].timeAdd += Date.now() - all[current].start;
all[current].start = null;
update();
} else { // paused
all[current].start = Date.now();
update();
}
require("Storage").write("timerclk.timer.json",JSON.stringify(all));
timerclkCheckTimers();
}
function reset() {
all[current] = defaultElement.clone();
update();
require("Storage").write("timerclk.timer.json",JSON.stringify(all));
timerclkCheckTimers();
}
function remove() {
all.splice(current, 1);
if (current == all.length) current--;
if (all.length == 0) {
all.push(defaultElement.clone());
current++;
}
update();
require("Storage").write("timerclk.timer.json",JSON.stringify(all));
timerclkCheckTimers();
}
function edit(position, change) {
if (position == 1) all[current].time += change*1000;
else if (position == 2) all[current].time += change*60000;
else if (position == 3) all[current].time += change*3600000;
require("Storage").write("timerclk.timer.json",JSON.stringify(all));
timerclkCheckTimers();
}
var buttonsRunning = {
reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: reset, img: timerclk.reset_img, col:"#f50"},
play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:"#0ff"},
};
var buttonsNormal = {
reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: remove, img: timerclk.remove_img, col:buttonsRunning.reset.col},
play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:buttonsRunning.play.col},
};
buttons = buttonsNormal;
function drawButtons() {
if (all[current].start || all[current].timeAdd) {
buttons = buttonsRunning;
if (all[current].start) {
buttons.play.img = timerclk.pause_img;
} else {
buttons.play.img = timerclk.play_img;
}
} else {
buttons = buttonsNormal;
}
for (var button of buttons) {
g.setColor(button.col);
g.fillRect(button.pos[0], button.pos[1], button.pos[2], button.pos[3]);
g.setColor("#000");
// scale 24px images
let iw = settings.buttonHeight-10;
var scale = iw/24;
let ix = button.pos[0] + ((button.pos[2]-button.pos[0] - iw) /2);
let iy = button.pos[1] + ((button.pos[3]-button.pos[1] - iw) /2);
g.drawImage(button.img, ix, iy, {scale: scale});
}
}
function draw() {
var x = g.getWidth()/2;
var y = g.getHeight()/2;
g.reset();
var time = all[current].time - timerclk.getTime(all[current]);
g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2-settings.buttonHeight);
g.setFontAlign(0,0).setFont(settings.indexFont, settings.indexFontSize);
g.drawString(current+1, x, Bangle.appRect.y + (g.stringMetrics("0").height/2));
g.setFontAlign(0,0).setFont(settings.font, settings.fontSize);
var timeStr = timerclk.formatTime(time, false, false, true);
g.drawString(timeStr,x,y);
var start = (width-g.stringMetrics(timeStr).width)/2;
timeStr = timeStr.split(":");
var markerPosChange = g.stringMetrics("__").width/2;
if (editIndex == 3) x = start + g.stringMetrics(timeStr[0]).width - markerPosChange;
else if (editIndex == 2) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]).width - markerPosChange;
else if (editIndex == 1) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]+":"+timeStr[2]).width - markerPosChange;
else x = 0;
if (x) g.drawString("__", x, y);
dragBorderHrsMins = start+g.stringMetrics(timeStr[0]).width+g.stringMetrics(":").width/2;
dragBorderMinsSecs = start+g.stringMetrics(timeStr[0]+":"+timeStr[1]).width+g.stringMetrics(":").width/2;
}
if (all.length == 0) {
all.push(defaultElement.clone());
}
timerclk.registerControls(this);
Bangle.loadWidgets();
Bangle.drawWidgets();
update();

7
apps/timerclk/wid.js Normal file
View File

@ -0,0 +1,7 @@
WIDGETS["timerclk.alarm"]={area:"tl",width:0,draw:function() {
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
},reload:function() {
WIDGETS["timerclk.alarm"].width = (require('Storage').readJSON('timerclk.alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0;
}
};
WIDGETS["timerclk.alarm"].reload();

View File

@ -7,6 +7,7 @@
"icon": "app.png", "icon": "app.png",
"type": "launch", "type": "launch",
"tags": "tool,system,launcher", "tags": "tool,system,launcher",
"screenshots": [{"url":"screenshot1.jpg"}],
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -8,6 +8,7 @@
"tags": "health,bluetooth", "tags": "health,bluetooth",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"screenshots": [{"url":"Vernier-go-direct-respiration-belt-screenshot.png"}],
"storage": [ "storage": [
{"name":"vernierrespirate.app.js","url":"app.js"}, {"name":"vernierrespirate.app.js","url":"app.js"},
{"name":"vernierrespirate.img","url":"app-icon.js","evaluate":true} {"name":"vernierrespirate.img","url":"app-icon.js","evaluate":true}

View File

@ -1 +1 @@
0.1: First release. 0.01: First release.

View File

@ -2,7 +2,7 @@
"name": "Charging Status", "name": "Charging Status",
"shortName":"ChargingStatus", "shortName":"ChargingStatus",
"icon": "widget.png", "icon": "widget.png",
"version":"0.1", "version":"0.01",
"type": "widget", "type": "widget",
"description": "A simple widget that shows a yellow lightning icon to indicate whenever the watch is charging. This way one can see the charging status at a glance, no matter which battery widget is being used.", "description": "A simple widget that shows a yellow lightning icon to indicate whenever the watch is charging. This way one can see the charging status at a glance, no matter which battery widget is being used.",
"tags": "widget", "tags": "widget",

View File

@ -1,4 +1,4 @@
1.00: Release for Bangle 2 (2021/11/18) 0.01: Release for Bangle 2 (2021/11/18)
1.01: Internal id update to wid_* as per Gordon's request (2021/11/21) 0.02: Internal id update to wid_* as per Gordon's request (2021/11/21)
1.02: Support dark themes 0.03: Support dark themes
1.03: Increase screen update rate when charging 0.04: Increase screen update rate when charging

View File

@ -3,7 +3,7 @@
"name": "A Battery Widget (with percentage)", "name": "A Battery Widget (with percentage)",
"shortName":"A Battery Widget", "shortName":"A Battery Widget",
"icon": "widget.png", "icon": "widget.png",
"version":"1.03", "version":"0.04",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",

View File

@ -1,2 +1,3 @@
0.01: Created 0.01: Created
0.02: Set sort order to -10 so always display in right hand corner 0.02: Set sort order to -10 so always display in right hand corner
0.03: Set sort order from the code

View File

@ -4,13 +4,12 @@
"shortName":"Battery Theme", "shortName":"Battery Theme",
"icon": "widbata.png", "icon": "widbata.png",
"screenshots": [{"url":"screenshot_widbata_1.png"}], "screenshots": [{"url":"screenshot_widbata_1.png"}],
"version":"0.02", "version":"0.03",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"description": "Shows the current battery level status in the top right using the clocks colour theme", "description": "Shows the current battery level status in the top right using the clocks colour theme",
"tags": "widget,battery", "tags": "widget,battery",
"sortorder": -10,
"storage": [ "storage": [
{"name":"widbata.wid.js","url":"widbata.wid.js"} {"name":"widbata.wid.js","url":"widbata.wid.js"}
] ]

View File

@ -2,7 +2,7 @@ setInterval(()=>WIDGETS["bata"].draw(), 60000);
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["bata"].draw(); if (on) WIDGETS["bata"].draw();
}); });
WIDGETS["bata"]={area:"tr",width:27,draw:function() { WIDGETS["bata"]={area:"tr",sortorder:-10,width:27,draw:function() {
var s = 26; var s = 26;
var t = 13; // thickness var t = 13; // thickness
var x = this.x, y = this.y; var x = this.x, y = this.y;

View File

@ -9,6 +9,7 @@
"tags": "widget,battery", "tags": "widget,battery",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"screenshots": [{"url":"widbatpc.full.jpg"},{"url":"widbatpc.part.jpg"}],
"storage": [ "storage": [
{"name":"widbatpc.wid.js","url":"widget.js"}, {"name":"widbatpc.wid.js","url":"widget.js"},
{"name":"widbatpc.settings.js","url":"settings.js"} {"name":"widbatpc.settings.js","url":"settings.js"}

View File

@ -7,6 +7,7 @@
"type": "widget", "type": "widget",
"tags": "widget,tool", "tags": "widget,tool",
"allow_emulator": true, "allow_emulator": true,
"screenshots": [{"url":"wash-hand-timer-screenshot.png"}],
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [ "storage": [
{"name":"widhwt.app.js","url":"app.js"}, {"name":"widhwt.app.js","url":"app.js"},

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -3,3 +3,4 @@
0.03: Don't try to be fancy - just bail out on firmwares without a lock event 0.03: Don't try to be fancy - just bail out on firmwares without a lock event
0.04: Set sortorder to -1 so that widget always takes up the furthest left position 0.04: Set sortorder to -1 so that widget always takes up the furthest left position
0.05: Set sortorder to -10 so that others can take -1 etc 0.05: Set sortorder to -10 so that others can take -1 etc
0.06: Set sortorder to -10 in widget code

View File

@ -1,13 +1,12 @@
{ {
"id": "widlock", "id": "widlock",
"name": "Lock Widget", "name": "Lock Widget",
"version": "0.05", "version": "0.06",
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked", "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,lock", "tags": "widget,lock",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"sortorder": -10,
"storage": [ "storage": [
{"name":"widlock.wid.js","url":"widget.js"} {"name":"widlock.wid.js","url":"widget.js"}
] ]

View File

@ -4,7 +4,7 @@
WIDGETS["lock"].width = Bangle.isLocked()?16:0; WIDGETS["lock"].width = Bangle.isLocked()?16:0;
Bangle.drawWidgets(); Bangle.drawWidgets();
}); });
WIDGETS["lock"]={area:"tl",width:Bangle.isLocked()?16:0,draw:function(w) { WIDGETS["lock"]={area:"tl",sortorder:10,width:Bangle.isLocked()?16:0,draw:function(w) {
if (Bangle.isLocked()) if (Bangle.isLocked())
g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x+1, w.y+4); g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x+1, w.y+4);
}}; }};

View File

@ -1,3 +1,4 @@
0.01: First release 0.01: First release
0.02: Size widget after step count is reset 0.02: Size widget after step count is reset
0.03: set sortorder to -1 0.03: set sortorder to -1
0.04: set sortorder through code

View File

@ -4,13 +4,12 @@
"shortName":"Simple Pedometer", "shortName":"Simple Pedometer",
"icon": "screenshot_widpa.png", "icon": "screenshot_widpa.png",
"screenshots": [{"url":"screenshot_widpa.png"}], "screenshots": [{"url":"screenshot_widpa.png"}],
"version":"0.03", "version":"0.04",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"description": "Displays the current step count from `Bangle.getHealthStatus(\"day\").steps` in 12x16 font, requires firmware v2.11.21 or later", "description": "Displays the current step count from `Bangle.getHealthStatus(\"day\").steps` in 12x16 font, requires firmware v2.11.21 or later",
"tags": "widget,battery", "tags": "widget,battery",
"sortorder": -1,
"storage": [ "storage": [
{"name":"widpa.wid.js","url":"widpa.wid.js"} {"name":"widpa.wid.js","url":"widpa.wid.js"}
] ]

View File

@ -2,7 +2,7 @@ Bangle.on('step', function(s) { WIDGETS["widpa"].draw(); });
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["widpa"].draw(); if (on) WIDGETS["widpa"].draw();
}); });
WIDGETS["widpa"]={area:"tl",width:13,draw:function() { WIDGETS["widpa"]={area:"tl",sortorder:-1,width:13,draw:function() {
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
var steps = Bangle.getHealthStatus("day").steps; var steps = Bangle.getHealthStatus("day").steps;
var w = 1 + (steps.toString().length)*12; var w = 1 + (steps.toString().length)*12;

View File

@ -2,3 +2,4 @@
0.02: Fixed widget id to wibpb, Size widget after step count is reset 0.02: Fixed widget id to wibpb, Size widget after step count is reset
0.03: Fixed widget id in onStep() come on get it right! 0.03: Fixed widget id in onStep() come on get it right!
0.04: set sortorder to -1 0.04: set sortorder to -1
0.05: set sortorder through code

View File

@ -4,13 +4,12 @@
"shortName":"Lato Pedometer", "shortName":"Lato Pedometer",
"icon": "screenshot_widpb.png", "icon": "screenshot_widpb.png",
"screenshots": [{"url":"screenshot_widpb.png"}], "screenshots": [{"url":"screenshot_widpb.png"}],
"version":"0.04", "version":"0.05",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"description": "Displays the current step count from `Bangle.getHealthStatus(\"day\").steps` in the Lato font, requires firmware v2.11.21 or later", "description": "Displays the current step count from `Bangle.getHealthStatus(\"day\").steps` in the Lato font, requires firmware v2.11.21 or later",
"tags": "widget,battery", "tags": "widget,battery",
"sortorder": -1,
"storage": [ "storage": [
{"name":"widpb.wid.js","url":"widpb.wid.js"} {"name":"widpb.wid.js","url":"widpb.wid.js"}
] ]

View File

@ -3,7 +3,7 @@ Bangle.on('step', function(s) { WIDGETS["widpb"].draw(); });
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["widpb"].draw(); if (on) WIDGETS["widpb"].draw();
}); });
WIDGETS["widpb"]={area:"tl",width:13,draw:function() { WIDGETS["widpb"]={area:"tl",sortorder:-1,width:13,draw:function() {
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
var steps = Bangle.getHealthStatus("day").steps; var steps = Bangle.getHealthStatus("day").steps;
var w = 1 + (steps.toString().length)*12; var w = 1 + (steps.toString().length)*12;

View File

@ -13,6 +13,7 @@ for Noble.
var SETTINGS = { var SETTINGS = {
pretokenise : true pretokenise : true
}; };
var APPSDIR = __dirname+"/../apps/";
var Utils = require("../core/js/utils.js"); var Utils = require("../core/js/utils.js");
var AppInfo = require("../core/js/appinfo.js"); var AppInfo = require("../core/js/appinfo.js");
var noble; var noble;
@ -29,18 +30,27 @@ if (!noble) {
console.log(" npm install noble") console.log(" npm install noble")
} }
var apps; var apps = [];
function ERROR(msg) { function ERROR(msg) {
console.error(msg); console.error(msg);
process.exit(1); process.exit(1);
} }
try { var apps = [];
apps = JSON.parse(require("fs").readFileSync(__dirname+"/../apps.json")); var dirs = require("fs").readdirSync(APPSDIR, {withFileTypes: true});
} catch(e) { dirs.forEach(dir => {
ERROR("'apps.json' could not be loaded"); var appsFile;
} if (dir.name.startsWith("_example") || !dir.isDirectory())
return;
try {
appsFile = require("fs").readFileSync(APPSDIR+dir.name+"/metadata.json").toString();
} catch (e) {
ERROR(dir.name+"/metadata.json does not exist");
return;
}
apps.push(JSON.parse(appsFile));
});
var args = process.argv; var args = process.argv;

View File

@ -13,17 +13,36 @@
# #
# If you do this, please do not attempt to commit your modified # If you do this, please do not attempt to commit your modified
# apps.json back into the main BangleApps repository! # apps.json back into the main BangleApps repository!
#
# You can pass an optional filename to this script, and it will write
# to that instead, apps.local.json is used when opening the loader on localhost
outfile="${1:-apps.json}"
cd `dirname $0`/.. cd `dirname $0`/..
echo "[" > apps.json echo "[" > "$outfile"
first=1
for app in apps/*/; do for app in apps/*/; do
echo "Processing $app..."; echo "Processing $app...";
if [[ "$app" =~ ^apps/_example.* ]]; then if [[ "$app" =~ ^apps/_example.* ]]; then
echo "Ignoring $app" echo "Ignoring $app"
else else
cat ${app}metadata.json >> apps.json if [ $first -eq 1 ]; then
first=0;
else
echo "," >> "$outfile"
fi;
cat ${app}metadata.json >> "$outfile"
# echo ",\"$app\"," >> apps.json # DEBUG ONLY # echo ",\"$app\"," >> apps.json # DEBUG ONLY
echo "," >> apps.json
fi fi
done done
echo "null]" >> apps.json echo "]" >> "$outfile"
if [ -z "$1"]; then
# Running with no arguments: prevent accidental commit of modified apps.json.
# You can use `create_apps.json.sh apps.json` if you really want to both
# overwrite and still commit apps.json
git update-index --skip-worktree apps.json
echo "Told git to ignore modified apps.json."
# If you want to unignore it, use
# 'git update-index --no-skip-worktree apps.json'
fi

View File

@ -10,7 +10,6 @@ var SETTINGS = {
var path = require('path'); var path = require('path');
var ROOTDIR = path.join(__dirname, '..'); var ROOTDIR = path.join(__dirname, '..');
var APPDIR = ROOTDIR+'/apps'; var APPDIR = ROOTDIR+'/apps';
var APPJSON = ROOTDIR+'/apps.json';
var OUTFILE = ROOTDIR+'/firmware.js'; var OUTFILE = ROOTDIR+'/firmware.js';
var DEVICE = "BANGLEJS"; var DEVICE = "BANGLEJS";
var APPS = [ // IDs of apps to install var APPS = [ // IDs of apps to install
@ -28,7 +27,6 @@ global.Const = {
}; };
var AppInfo = require(ROOTDIR+"/core/js/appinfo.js"); var AppInfo = require(ROOTDIR+"/core/js/appinfo.js");
var appjson = JSON.parse(fs.readFileSync(APPJSON).toString());
var appfiles = []; var appfiles = [];
function fileGetter(url) { function fileGetter(url) {
@ -58,8 +56,11 @@ function fileGetter(url) {
} }
Promise.all(APPS.map(appid => { Promise.all(APPS.map(appid => {
var app = appjson.find(app=>app.id==appid); try {
if (app===undefined) throw new Error(`App ${appid} not found`); var app = JSON.parse(fs.readFileSync(APPDIR + "/" + appid + "metadata.json").toString());
} catch (e) {
throw new Error(`App ${appid} not found`);
}
return AppInfo.getFiles(app, { return AppInfo.getFiles(app, {
fileGetter : fileGetter, fileGetter : fileGetter,
settings : SETTINGS, settings : SETTINGS,

View File

@ -16,7 +16,6 @@ var DEVICE = process.argv[2];
var path = require('path'); var path = require('path');
var ROOTDIR = path.join(__dirname, '..'); var ROOTDIR = path.join(__dirname, '..');
var APPDIR = ROOTDIR+'/apps'; var APPDIR = ROOTDIR+'/apps';
var APPJSON = ROOTDIR+'/apps.json';
var MINIFY = true; var MINIFY = true;
var OUTFILE, APPS; var OUTFILE, APPS;
@ -86,7 +85,6 @@ function atob(input) {
} }
var AppInfo = require(ROOTDIR+"/core/js/appinfo.js"); var AppInfo = require(ROOTDIR+"/core/js/appinfo.js");
var appjson = JSON.parse(fs.readFileSync(APPJSON).toString());
var appfiles = []; var appfiles = [];
function fileGetter(url) { function fileGetter(url) {
@ -134,8 +132,11 @@ function evaluateFile(file) {
} }
Promise.all(APPS.map(appid => { Promise.all(APPS.map(appid => {
var app = appjson.find(app=>app.id==appid); try {
if (app===undefined) throw new Error(`App ${appid} not found`); var app = JSON.parse(fs.readFileSync(APPDIR + "/" + appid + "metadata.json").toString());
} catch (e) {
throw new Error(`App ${appid} not found`);
}
return AppInfo.getFiles(app, { return AppInfo.getFiles(app, {
fileGetter : fileGetter, fileGetter : fileGetter,
settings : SETTINGS, settings : SETTINGS,

2
core

@ -1 +1 @@
Subproject commit 5023ee1228030130ba9f026d5dbe920f7527ee7d Subproject commit 3093d78a5d752cbf03ea8f9a1a7c0b50b9c8123b

53
lang/cs_CZ.json Normal file
View File

@ -0,0 +1,53 @@
{
"//":"Czech language translations",
"GLOBAL": {
"//":"Translations that apply for all apps",
"Alarm" : "Budík",
"Hour" : "Hodina",
"Hours" : "Hodiny",
"Minute" : "Minuta",
"Minutes" : "Minuty",
"Second" : "Sekunda",
"Seconds" : "Sekundy",
"Month" : "Měsíc",
"Enabled" : "Povoleno",
"Background" : "Pozadí",
"Connected" : "Připojeno",
"Settings" : "Nastavení",
"Save" : "Uložit",
"Back" : "Zpět",
"Repeat" : "Opakovat",
"Delete" : "Smazat",
"Sleep" : "Uspat",
"Alarms" : "Budíky",
"ALARM!" : "BUDÍK!",
" (repeat)" : " (opakovat)",
"< Back" : "< Zpět",
"> Delete" : "> Smazat",
"> Save" : " > Uložit",
"ALARM " : "BUDÍK ",
"Add Device" : "Přidat zařízení",
"App Settings" : "Nast. Aplikací",
"Apps" : "Aplikace"
},
"alarm": {
"//":"App-specific overrides",
"Alarm/Timer" : "Budik/Časovač",
"rpt" : "Opk.",
"New Alarm" : "Nový budík",
"New Timer" : "Nový časovač",
"Auto snooze" : "Auto odložit"
},
"setting" : {
"Quiet Mode" : "Tichý režim"
},
"messages": {
"Are you sure?" : "Opravdu?"
}
}

View File

@ -1,5 +1,6 @@
[ [
{"code":"en_GB","name":"British English","url":"en_GB.json"}, {"code":"en_GB","name":"British English","url":"en_GB.json"},
{"code":"cs_CZ","name":"Czech","url":"cs_CZ.json"},
{"code":"de_DE","name":"German","url":"de_DE.json"}, {"code":"de_DE","name":"German","url":"de_DE.json"},
{"code":"es_ES","name":"Spanish","url":"es_ES.json"}, {"code":"es_ES","name":"Spanish","url":"es_ES.json"},
{"code":"fi_FI","name":"Finnish","url":"fi_FI.json"}, {"code":"fi_FI","name":"Finnish","url":"fi_FI.json"},

View File

@ -5,6 +5,11 @@ if (window.location.host=="banglejs.com") {
document.title += " [Development]"; document.title += " [Development]";
document.getElementById("apploaderlinks").innerHTML = document.getElementById("apploaderlinks").innerHTML =
'This is the development Bangle.js App Loader - you can also try the <a href="https://banglejs.com/apps/">Official Version</a> for stable apps.'; 'This is the development Bangle.js App Loader - you can also try the <a href="https://banglejs.com/apps/">Official Version</a> for stable apps.';
} else if (window.location.hostname==='localhost') {
document.title += " [Local]";
Const.APPS_JSON_FILE = "apps.local.json";
document.getElementById("apploaderlinks").innerHTML =
'This is your local Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.';
} else { } else {
document.title += " [Unofficial]"; document.title += " [Unofficial]";
document.getElementById("apploaderlinks").innerHTML = document.getElementById("apploaderlinks").innerHTML =

View File

@ -9,9 +9,17 @@
"scripts": { "scripts": {
"lint-apps": "eslint ./apps --ext .js", "lint-apps": "eslint ./apps --ext .js",
"test": "node bin/sanitycheck.js && eslint ./apps --ext .js", "test": "node bin/sanitycheck.js && eslint ./apps --ext .js",
"update-local-apps": "./bin/create_apps_json.sh apps.local.json",
"local": "npm-watch & npx http-server -a localhost -c-1",
"start": "npx http-server -c-1" "start": "npx http-server -c-1"
}, },
"watch": {
"update-local-apps": "apps/*/metadata.json"
},
"dependencies": { "dependencies": {
"acorn": "^7.2.0" "acorn": "^7.2.0"
},
"devDpendencies": {
"npm-watch": "^0.11.0"
} }
} }