Merge branch 'master' into agenda_today
|
|
@ -4,4 +4,5 @@ apps/schoolCalendar/fullcalendar/main.js
|
||||||
apps/authentiwatch/qr_packed.js
|
apps/authentiwatch/qr_packed.js
|
||||||
apps/qrcode/qr-scanner.umd.min.js
|
apps/qrcode/qr-scanner.umd.min.js
|
||||||
apps/gipy/pkg/gpconv.js
|
apps/gipy/pkg/gpconv.js
|
||||||
|
apps/health/chart.min.js
|
||||||
*.test.js
|
*.test.js
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,7 @@ body:
|
||||||
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: apps
|
||||||
|
attributes:
|
||||||
|
label: Installed apps
|
||||||
|
|
@ -98,7 +98,7 @@ This is the best way to test...
|
||||||
|
|
||||||
**Note:** It's a great idea to get a local copy of the repository on your PC,
|
**Note:** It's a great idea to get a local copy of the repository on your PC,
|
||||||
then run `bin/sanitycheck.js` - it'll run through a bunch of common issues
|
then run `bin/sanitycheck.js` - it'll run through a bunch of common issues
|
||||||
that there might be.
|
that there might be. To get the project running locally, you have to initialize and update the git submodules first: `git submodule --init && git submodule update`.
|
||||||
|
|
||||||
Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect.
|
Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: Beta version for Bangle 2 (2021/11/28)
|
0.01: Beta version for Bangle 2 (2021/11/28)
|
||||||
|
0.02: Shows night time on the map (2022/12/28)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* Other time zones
|
* Other time zones
|
||||||
* Currently hardcoded to Paris and Tokyo (this will be customizable in a future version)
|
* Currently hardcoded to Paris and Tokyo (this will be customizable in a future version)
|
||||||
* World Map
|
* World Map
|
||||||
* The yellow line shows the position of the sun
|
* The map shows day and night on Earth and the position of the Sun (yellow line)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,17 @@ function draw() {
|
||||||
g.setColor('#ff0').drawLine(x_sun, g.getHeight()-IMAGEHEIGHT, x_sun, g.getHeight());
|
g.setColor('#ff0').drawLine(x_sun, g.getHeight()-IMAGEHEIGHT, x_sun, g.getHeight());
|
||||||
g.reset();
|
g.reset();
|
||||||
|
|
||||||
|
var x_night_start = 176 - (((getGmt().getHours()-6)%24) / 24 * 176 + 4);
|
||||||
|
var x_night_end = 176 - (((getGmt().getHours()+6)%24) / 24 * 176 + 4);
|
||||||
|
for (let x = x_night_start; x < 176; x+=2) {
|
||||||
|
g.setColor('#000').drawLine(x, g.getHeight()-IMAGEHEIGHT, x, g.getHeight());
|
||||||
|
}
|
||||||
|
if (x_night_end < x_night_start) {
|
||||||
|
for (let x = 0; x < x_night_end; x+=2) {
|
||||||
|
g.setColor('#000').drawLine(x, g.getHeight()-IMAGEHEIGHT, x, g.getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var locale = require("locale");
|
var locale = require("locale");
|
||||||
|
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "a_clock_timer",
|
"id": "a_clock_timer",
|
||||||
"name": "A Clock with Timer",
|
"name": "A Clock with Timer",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "A Clock with Timer, Map and Time Zones",
|
"description": "A Clock with Timer, Map and Time Zones",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 3.8 KiB |
|
|
@ -1,2 +1,3 @@
|
||||||
1.00: Release (2021/12/01)
|
1.00: Release (2021/12/01)
|
||||||
1.01: Grey font when timer is frozen (2021/12/04)
|
1.01: Grey font when timer is frozen (2021/12/04)
|
||||||
|
1.02: Force light theme, since the app is not designed for dark theme (2022/12/28)
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ function draw() {
|
||||||
g.drawRect(88+8,138-24, 176-10, 138+22);
|
g.drawRect(88+8,138-24, 176-10, 138+22);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
||||||
require("FontHaxorNarrow7x17").add(Graphics);
|
require("FontHaxorNarrow7x17").add(Graphics);
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id":"a_speech_timer",
|
"id":"a_speech_timer",
|
||||||
"name":"Speech Timer",
|
"name":"Speech Timer",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"1.01",
|
"version":"1.02",
|
||||||
"description": "A timer designed to help keeping your speeches and presentations to time.",
|
"description": "A timer designed to help keeping your speeches and presentations to time.",
|
||||||
"tags": "tool,timer",
|
"tags": "tool,timer",
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@
|
||||||
0.09: Ensure Agenda supplies an image for clkinfo items
|
0.09: Ensure Agenda supplies an image for clkinfo items
|
||||||
0.10: Update clock_info to avoid a redraw
|
0.10: Update clock_info to avoid a redraw
|
||||||
0.11: Setting to use "Today" and "Yesterday" instead of dates
|
0.11: Setting to use "Today" and "Yesterday" instead of dates
|
||||||
|
Added dynamic, short and range fields to clkinfo
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
(function() {
|
(function() {
|
||||||
|
function getPassedSec(date) {
|
||||||
|
var now = new Date();
|
||||||
|
var passed = (now-date)/1000;
|
||||||
|
if(passed<0) return 0;
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
var agendaItems = {
|
var agendaItems = {
|
||||||
name: "Agenda",
|
name: "Agenda",
|
||||||
img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="),
|
img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="),
|
||||||
|
dynamic: true,
|
||||||
items: []
|
items: []
|
||||||
};
|
};
|
||||||
var locale = require("locale");
|
var locale = require("locale");
|
||||||
|
|
@ -15,11 +22,15 @@
|
||||||
var title = entry.title.slice(0,12);
|
var title = entry.title.slice(0,12);
|
||||||
var date = new Date(entry.timestamp*1000);
|
var date = new Date(entry.timestamp*1000);
|
||||||
var dateStr = locale.date(date).replace(/\d\d\d\d/,"");
|
var dateStr = locale.date(date).replace(/\d\d\d\d/,"");
|
||||||
|
var shortStr = ((date-now) > 86400000 || entry.allDay) ? dateStr : locale.time(date,1);
|
||||||
dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : "";
|
dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : "";
|
||||||
|
|
||||||
agendaItems.items.push({
|
agendaItems.items.push({
|
||||||
name: "Agenda "+i,
|
name: "Agenda "+i,
|
||||||
get: () => ({ text: title + "\n" + dateStr, img: agendaItems.img }),
|
hasRange: true,
|
||||||
|
get: () => ({ text: title + "\n" + dateStr,
|
||||||
|
img: agendaItems.img, short: shortStr.trim(),
|
||||||
|
v: getPassedSec(date), min: 0, max: entry.durationInSeconds}),
|
||||||
show: function() {},
|
show: function() {},
|
||||||
hide: function () {}
|
hide: function () {}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,5 @@
|
||||||
0.03: Do not load AGPS data on boot
|
0.03: Do not load AGPS data on boot
|
||||||
Increase minimum interval to 6 hours
|
Increase minimum interval to 6 hours
|
||||||
0.04: Write AGPS data chunks with delay to improve reliability
|
0.04: Write AGPS data chunks with delay to improve reliability
|
||||||
|
0.05: Show last success date
|
||||||
|
Do not start A-GPS update automatically
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,26 @@ Bangle.drawWidgets();
|
||||||
|
|
||||||
let waiting = false;
|
let waiting = false;
|
||||||
|
|
||||||
function start() {
|
function start(restart) {
|
||||||
g.reset();
|
g.reset();
|
||||||
g.clear();
|
g.clear();
|
||||||
waiting = false;
|
waiting = false;
|
||||||
|
if (!restart) {
|
||||||
|
display("Start?", "touch to start");
|
||||||
|
}
|
||||||
|
else {
|
||||||
display("Retry?", "touch to retry");
|
display("Retry?", "touch to retry");
|
||||||
|
}
|
||||||
Bangle.on("touch", () => { updateAgps(); });
|
Bangle.on("touch", () => { updateAgps(); });
|
||||||
|
|
||||||
|
const file = "agpsdata.json";
|
||||||
|
let data = require("Storage").readJSON(file, 1) || {};
|
||||||
|
if (data.lastUpdate) {
|
||||||
|
g.setFont("Vector", 11);
|
||||||
|
g.drawString("last success:", 5, g.getHeight() - 22);
|
||||||
|
g.drawString(new Date(data.lastUpdate).toISOString(), 5, g.getHeight() - 11);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAgps() {
|
function updateAgps() {
|
||||||
|
|
@ -36,7 +50,7 @@ function updateAgps() {
|
||||||
g.clear();
|
g.clear();
|
||||||
if (!waiting) {
|
if (!waiting) {
|
||||||
waiting = true;
|
waiting = true;
|
||||||
display("Updating A-GPS...", "takes ~ 10 seconds");
|
display("Updating A-GPS...", "takes ~10 seconds");
|
||||||
require("agpsdata").pull(function() {
|
require("agpsdata").pull(function() {
|
||||||
waiting = false;
|
waiting = false;
|
||||||
display("A-GPS updated.", "touch to close");
|
display("A-GPS updated.", "touch to close");
|
||||||
|
|
@ -45,10 +59,10 @@ function updateAgps() {
|
||||||
function(error) {
|
function(error) {
|
||||||
waiting = false;
|
waiting = false;
|
||||||
E.showAlert(error, "Error")
|
E.showAlert(error, "Error")
|
||||||
.then(() => { start(); });
|
.then(() => { start(true); });
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
display("Waiting...");
|
display("Waiting...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateAgps();
|
start(false);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "A-GPS Data Downloader App",
|
"name": "A-GPS Data Downloader App",
|
||||||
"shortName":"A-GPS Data",
|
"shortName":"A-GPS Data",
|
||||||
"icon": "agpsdata.png",
|
"icon": "agpsdata.png",
|
||||||
"version":"0.04",
|
"version":"0.05",
|
||||||
"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.",
|
"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",
|
"tags": "boot,tool,assisted,gps,agps,http",
|
||||||
"allow_emulator":true,
|
"allow_emulator":true,
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: Indicate battery level through line occurrence.
|
0.03: Indicate battery level through line occurrence.
|
||||||
0.04: Use widget_utils module.
|
0.04: Use widget_utils module.
|
||||||
0.05: Support for clkinfo.
|
0.05: Support for clkinfo.
|
||||||
|
0.06: ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc.
|
||||||
|
|
@ -29,7 +29,6 @@ var H = g.getHeight();
|
||||||
var cx = W/2;
|
var cx = W/2;
|
||||||
var cy = H/2;
|
var cy = H/2;
|
||||||
var drawTimeout;
|
var drawTimeout;
|
||||||
var lock_input = false;
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
|
|
@ -93,28 +92,6 @@ menu = menu.concat(clockItems);
|
||||||
settings.menuPosY = 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);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function canRunMenuItem(){
|
function canRunMenuItem(){
|
||||||
if(settings.menuPosY == 0){
|
if(settings.menuPosY == 0){
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -204,8 +181,6 @@ function drawMenuItem(text, image){
|
||||||
drawTime();
|
drawTime();
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// image = atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==");
|
|
||||||
|
|
||||||
text = String(text);
|
text = String(text);
|
||||||
|
|
||||||
g.reset().setBgColor("#fff").setColor("#000");
|
g.reset().setBgColor("#fff").setColor("#000");
|
||||||
|
|
@ -292,7 +267,7 @@ function drawDigits(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function drawDate(){
|
function drawMenu(){
|
||||||
var menuEntry = menu[settings.menuPosX];
|
var menuEntry = menu[settings.menuPosX];
|
||||||
|
|
||||||
// The first entry is the overview...
|
// The first entry is the overview...
|
||||||
|
|
@ -302,9 +277,8 @@ function drawDate(){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw item if needed
|
// Draw item if needed
|
||||||
lock_input = true;
|
var item = menuEntry.items[settings.menuPosY-1].get();
|
||||||
var item = menuEntry.items[settings.menuPosY-1];
|
drawMenuItem(item.text, item.img);
|
||||||
item.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -320,7 +294,7 @@ function draw(){
|
||||||
g.setColor(1,1,1);
|
g.setColor(1,1,1);
|
||||||
|
|
||||||
drawBackground();
|
drawBackground();
|
||||||
drawDate();
|
drawMenu();
|
||||||
drawCircle(Bangle.isLocked());
|
drawCircle(Bangle.isLocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,10 +327,6 @@ Bangle.on('touch', function(btn, e){
|
||||||
var is_right = e.x > right && !is_upper && !is_lower;
|
var is_right = e.x > right && !is_upper && !is_lower;
|
||||||
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
||||||
|
|
||||||
if(lock_input){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_lower){
|
if(is_lower){
|
||||||
Bangle.buzz(40, 0.6);
|
Bangle.buzz(40, 0.6);
|
||||||
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
|
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "AI Clock",
|
"name": "AI Clock",
|
||||||
"shortName":"AI Clock",
|
"shortName":"AI Clock",
|
||||||
"icon": "aiclock.png",
|
"icon": "aiclock.png",
|
||||||
"version":"0.05",
|
"version":"0.06",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,4 @@
|
||||||
0.34: Add "Confirm" option to alarm/timer edit menus
|
0.34: Add "Confirm" option to alarm/timer edit menus
|
||||||
0.35: Add automatic translation of more strings
|
0.35: Add automatic translation of more strings
|
||||||
0.36: alarm widget moved out of app
|
0.36: alarm widget moved out of app
|
||||||
|
0.37: add message input and dated Events
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
# Alarms & Timers
|
# Alarms & Timers
|
||||||
|
|
||||||
This app allows you to add/modify any alarms and timers.
|
This app allows you to add/modify any alarms, timers and events.
|
||||||
|
|
||||||
|
Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered.
|
||||||
|
|
||||||
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
||||||
|
|
||||||
## Menu overview
|
## Menu overview
|
||||||
|
|
||||||
- `New...`
|
- `New...`
|
||||||
- `New Alarm` → Configure a new alarm
|
- `New Alarm` → Configure a new alarm (triggered based on time and day of week)
|
||||||
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
||||||
- `New Timer` → Configure a new timer
|
- `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds)
|
||||||
|
- `New Event` → Configure a new event (triggered based on time and date)
|
||||||
- `Advanced`
|
- `Advanced`
|
||||||
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
||||||
- `Enable All` → Enable _all_ disabled alarms & timers
|
- `Enable All` → Enable _all_ disabled alarms & timers
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,10 @@ function showMainMenu() {
|
||||||
};
|
};
|
||||||
|
|
||||||
alarms.forEach((e, index) => {
|
alarms.forEach((e, index) => {
|
||||||
var label = e.timer
|
var label = (e.timer
|
||||||
? require("time_utils").formatDuration(e.timer)
|
? require("time_utils").formatDuration(e.timer)
|
||||||
: require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeDOW(e)}` : "");
|
: (e.date ? `${e.date.substring(5,10)} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeDOW(e)}` : ""))
|
||||||
|
) + (e.msg ? " " + e.msg : "");
|
||||||
menu[label] = {
|
menu[label] = {
|
||||||
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
||||||
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
||||||
|
|
@ -67,11 +68,12 @@ function showNewMenu() {
|
||||||
"": { "title": /*LANG*/"New..." },
|
"": { "title": /*LANG*/"New..." },
|
||||||
"< Back": () => showMainMenu(),
|
"< Back": () => showMainMenu(),
|
||||||
/*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined),
|
/*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined),
|
||||||
/*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined)
|
/*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined),
|
||||||
|
/*LANG*/"Event": () => showEditAlarmMenu(undefined, undefined, true)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
||||||
var isNew = alarmIndex === undefined;
|
var isNew = alarmIndex === undefined;
|
||||||
|
|
||||||
var alarm = require("sched").newDefaultAlarm();
|
var alarm = require("sched").newDefaultAlarm();
|
||||||
|
|
@ -82,11 +84,16 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var time = require("time_utils").decodeTime(alarm.t);
|
var time = require("time_utils").decodeTime(alarm.t);
|
||||||
|
if (withDate && !alarm.date) alarm.date = new Date().toLocalISOString().slice(0,10);
|
||||||
|
var date = alarm.date ? new Date(alarm.date) : undefined;
|
||||||
|
var title = date ? (isNew ? /*LANG*/"New Event" : /*LANG*/"Edit Event") : (isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm");
|
||||||
|
var keyboard = "textinput";
|
||||||
|
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
"": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" },
|
"": { "title": title },
|
||||||
"< Back": () => {
|
"< Back": () => {
|
||||||
prepareAlarmForSave(alarm, alarmIndex, time);
|
prepareAlarmForSave(alarm, alarmIndex, time, date);
|
||||||
saveAndReload();
|
saveAndReload();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
},
|
},
|
||||||
|
|
@ -106,6 +113,36 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
wrap: true,
|
wrap: true,
|
||||||
onchange: v => time.m = v
|
onchange: v => time.m = v
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Day": {
|
||||||
|
value: date ? date.getDate() : null,
|
||||||
|
min: 1,
|
||||||
|
max: 31,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => date.setDate(v)
|
||||||
|
},
|
||||||
|
/*LANG*/"Month": {
|
||||||
|
value: date ? date.getMonth() + 1 : null,
|
||||||
|
format: v => require("date_utils").month(v),
|
||||||
|
onchange: v => date.setMonth((v+11)%12)
|
||||||
|
},
|
||||||
|
/*LANG*/"Year": {
|
||||||
|
value: date ? date.getFullYear() : null,
|
||||||
|
min: new Date().getFullYear(),
|
||||||
|
max: 2100,
|
||||||
|
onchange: v => date.setFullYear(v)
|
||||||
|
},
|
||||||
|
/*LANG*/"Message": {
|
||||||
|
value: alarm.msg,
|
||||||
|
onchange: () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
keyboard.input({text:alarm.msg}).then(result => {
|
||||||
|
alarm.msg = result;
|
||||||
|
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
||||||
|
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
/*LANG*/"Enabled": {
|
/*LANG*/"Enabled": {
|
||||||
value: alarm.on,
|
value: alarm.on,
|
||||||
onchange: v => alarm.on = v
|
onchange: v => alarm.on = v
|
||||||
|
|
@ -115,8 +152,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, alarm.dow, (repeat, dow) => {
|
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, alarm.dow, (repeat, dow) => {
|
||||||
alarm.rp = repeat;
|
alarm.rp = repeat;
|
||||||
alarm.dow = dow;
|
alarm.dow = dow;
|
||||||
alarm.t = require("time_utils").encodeTime(time);
|
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
||||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex);
|
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v),
|
/*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v),
|
||||||
|
|
@ -136,6 +173,15 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!keyboard) delete menu[/*LANG*/"Message"];
|
||||||
|
if (alarm.date || withDate) {
|
||||||
|
delete menu[/*LANG*/"Repeat"];
|
||||||
|
} else {
|
||||||
|
delete menu[/*LANG*/"Day"];
|
||||||
|
delete menu[/*LANG*/"Month"];
|
||||||
|
delete menu[/*LANG*/"Year"];
|
||||||
|
}
|
||||||
|
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
menu[/*LANG*/"Delete"] = () => {
|
menu[/*LANG*/"Delete"] = () => {
|
||||||
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Alarm" }).then((confirm) => {
|
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Alarm" }).then((confirm) => {
|
||||||
|
|
@ -145,7 +191,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
} else {
|
} else {
|
||||||
alarm.t = require("time_utils").encodeTime(time);
|
alarm.t = require("time_utils").encodeTime(time);
|
||||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex);
|
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -154,15 +200,18 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareAlarmForSave(alarm, alarmIndex, time) {
|
function prepareAlarmForSave(alarm, alarmIndex, time, date, temp) {
|
||||||
alarm.t = require("time_utils").encodeTime(time);
|
alarm.t = require("time_utils").encodeTime(time);
|
||||||
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
|
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
|
||||||
|
if(date) alarm.date = date.toLocalISOString().slice(0,10);
|
||||||
|
|
||||||
|
if(!temp) {
|
||||||
if (alarmIndex === undefined) {
|
if (alarmIndex === undefined) {
|
||||||
alarms.push(alarm);
|
alarms.push(alarm);
|
||||||
} else {
|
} else {
|
||||||
alarms[alarmIndex] = alarm;
|
alarms[alarmIndex] = alarm;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAndReload() {
|
function saveAndReload() {
|
||||||
|
|
@ -255,6 +304,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var time = require("time_utils").decodeTime(timer.timer);
|
var time = require("time_utils").decodeTime(timer.timer);
|
||||||
|
var keyboard = "textinput";
|
||||||
|
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
|
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
|
||||||
|
|
@ -285,6 +336,18 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
wrap: true,
|
wrap: true,
|
||||||
onchange: v => time.s = v
|
onchange: v => time.s = v
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Message": {
|
||||||
|
value: timer.msg,
|
||||||
|
onchange: () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
keyboard.input({text:timer.msg}).then(result => {
|
||||||
|
timer.msg = result;
|
||||||
|
prepareTimerForSave(timer, timerIndex, time, true);
|
||||||
|
setTimeout(showEditTimerMenu, 10, timer, timerIndex);
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
/*LANG*/"Enabled": {
|
/*LANG*/"Enabled": {
|
||||||
value: timer.on,
|
value: timer.on,
|
||||||
onchange: v => timer.on = v
|
onchange: v => timer.on = v
|
||||||
|
|
@ -306,6 +369,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!keyboard) delete menu[/*LANG*/"Message"];
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
menu[/*LANG*/"Delete"] = () => {
|
menu[/*LANG*/"Delete"] = () => {
|
||||||
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => {
|
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => {
|
||||||
|
|
@ -324,16 +388,18 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareTimerForSave(timer, timerIndex, time) {
|
function prepareTimerForSave(timer, timerIndex, time, temp) {
|
||||||
timer.timer = require("time_utils").encodeTime(time);
|
timer.timer = require("time_utils").encodeTime(time);
|
||||||
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
|
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
|
||||||
timer.last = 0;
|
timer.last = 0;
|
||||||
|
|
||||||
|
if (!temp) {
|
||||||
if (timerIndex === undefined) {
|
if (timerIndex === undefined) {
|
||||||
alarms.push(timer);
|
alarms.push(timer);
|
||||||
} else {
|
} else {
|
||||||
alarms[timerIndex] = timer;
|
alarms[timerIndex] = timer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAdvancedMenu() {
|
function showAdvancedMenu() {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.36",
|
"version": "0.37",
|
||||||
"description": "Set alarms and timers on your Bangle",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm",
|
"tags": "tool,alarm",
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,4 @@
|
||||||
0.18: Use new message library
|
0.18: Use new message library
|
||||||
If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged)
|
If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged)
|
||||||
0.19: Add automatic translation for a couple of strings.
|
0.19: Add automatic translation for a couple of strings.
|
||||||
|
0.20: Fix wrong event used for forwarded GPS data from Gadgetbridge and add mapper to map longitude value correctly.
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,10 @@
|
||||||
event.satellites = NaN;
|
event.satellites = NaN;
|
||||||
event.course = NaN;
|
event.course = NaN;
|
||||||
event.fix = 1;
|
event.fix = 1;
|
||||||
Bangle.emit('gps', event);
|
event.lon = event.long;
|
||||||
|
delete event.long;
|
||||||
|
|
||||||
|
Bangle.emit('GPS', event);
|
||||||
},
|
},
|
||||||
"is_gps_active": function() {
|
"is_gps_active": function() {
|
||||||
gbSend({ t: "gps_power", status: Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0 });
|
gbSend({ t: "gps_power", status: Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0 });
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "android",
|
"id": "android",
|
||||||
"name": "Android Integration",
|
"name": "Android Integration",
|
||||||
"shortName": "Android",
|
"shortName": "Android",
|
||||||
"version": "0.19",
|
"version": "0.20",
|
||||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,6 @@
|
||||||
0.22: Use the new clkinfo module for the menu.
|
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.
|
0.23: Feedback of apps after run is now optional and decided by the corresponding clkinfo.
|
||||||
0.24: Update clock_info to avoid a redraw
|
0.24: Update clock_info to avoid a redraw
|
||||||
|
0.25: Use Bangle.setUI({remove:...}) to allow loading the launcher without a full reset on fw2v16.
|
||||||
|
ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
{ // must be inside our own scope here so that when we are unloaded everything disappears
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Includes
|
* Includes
|
||||||
*/
|
*/
|
||||||
|
|
@ -12,8 +14,6 @@ const clock_info = require("clock_info");
|
||||||
const SETTINGS_FILE = "bwclk.setting.json";
|
const SETTINGS_FILE = "bwclk.setting.json";
|
||||||
const W = g.getWidth();
|
const W = g.getWidth();
|
||||||
const H = g.getHeight();
|
const H = g.getHeight();
|
||||||
var lock_input = false;
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Settings
|
* Settings
|
||||||
|
|
@ -28,7 +28,7 @@ let settings = {
|
||||||
|
|
||||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
for (const key in saved_settings) {
|
for (const key in saved_settings) {
|
||||||
settings[key] = saved_settings[key]
|
settings[key] = saved_settings[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
|
|
@ -74,20 +74,20 @@ Graphics.prototype.setMiniFont = function(scale) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
function imgLock(){
|
let imgLock = function() {
|
||||||
return {
|
return {
|
||||||
width : 16, height : 16, bpp : 1,
|
width : 16, height : 16, bpp : 1,
|
||||||
transparent : 0,
|
transparent : 0,
|
||||||
buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w="))
|
buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w="))
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Menu
|
* Menu
|
||||||
*/
|
*/
|
||||||
// Custom bwItems menu - therefore, its added here and not in a clkinfo.js file.
|
// Custom bwItems menu - therefore, its added here and not in a clkinfo.js file.
|
||||||
var bwItems = {
|
let bwItems = {
|
||||||
name: null,
|
name: null,
|
||||||
img: null,
|
img: null,
|
||||||
items: [
|
items: [
|
||||||
|
|
@ -99,7 +99,7 @@ var bwItems = {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
function weekOfYear() {
|
let weekOfYear = function() {
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
date.setHours(0, 0, 0, 0);
|
date.setHours(0, 0, 0, 0);
|
||||||
// Thursday in current week decides the year.
|
// Thursday in current week decides the year.
|
||||||
|
|
@ -109,11 +109,11 @@ function weekOfYear() {
|
||||||
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
||||||
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
|
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
|
||||||
- 3 + (week1.getDay() + 6) % 7) / 7);
|
- 3 + (week1.getDay() + 6) % 7) / 7);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
// Load menu
|
// Load menu
|
||||||
var menu = clock_info.load();
|
let menu = clock_info.load();
|
||||||
menu = menu.concat(bwItems);
|
menu = menu.concat(bwItems);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -123,29 +123,7 @@ if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPos
|
||||||
settings.menuPosY = 0;
|
settings.menuPosY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set draw functions for each item
|
let canRunMenuItem = function() {
|
||||||
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);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function canRunMenuItem(){
|
|
||||||
if(settings.menuPosY == 0){
|
if(settings.menuPosY == 0){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -153,10 +131,10 @@ function canRunMenuItem(){
|
||||||
var menuEntry = menu[settings.menuPosX];
|
var menuEntry = menu[settings.menuPosX];
|
||||||
var item = menuEntry.items[settings.menuPosY-1];
|
var item = menuEntry.items[settings.menuPosY-1];
|
||||||
return item.run !== undefined;
|
return item.run !== undefined;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
function runMenuItem(){
|
let runMenuItem = function() {
|
||||||
if(settings.menuPosY == 0){
|
if(settings.menuPosY == 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -171,13 +149,13 @@ function runMenuItem(){
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// Simply ignore it...
|
// Simply ignore it...
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Draw
|
* Draw
|
||||||
*/
|
*/
|
||||||
function draw() {
|
let draw = function() {
|
||||||
// Queue draw again
|
// Queue draw again
|
||||||
queueDraw();
|
queueDraw();
|
||||||
|
|
||||||
|
|
@ -186,10 +164,10 @@ function draw() {
|
||||||
drawMenuAndTime();
|
drawMenuAndTime();
|
||||||
drawLock();
|
drawLock();
|
||||||
drawWidgets();
|
drawWidgets();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
function drawDate(){
|
let drawDate = function() {
|
||||||
// Draw background
|
// Draw background
|
||||||
var y = H/5*2 + (isFullscreen() ? 0 : 8);
|
var y = H/5*2 + (isFullscreen() ? 0 : 8);
|
||||||
g.reset().clearRect(0,0,W,y);
|
g.reset().clearRect(0,0,W,y);
|
||||||
|
|
@ -216,10 +194,10 @@ function drawDate(){
|
||||||
g.setMediumFont();
|
g.setMediumFont();
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.drawString(dateStr, W/2 - fullDateW / 2, y+2);
|
g.drawString(dateStr, W/2 - fullDateW / 2, y+2);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
function drawTime(y, smallText){
|
let drawTime = function(y, smallText) {
|
||||||
// Draw background
|
// Draw background
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
|
|
||||||
|
|
@ -244,9 +222,9 @@ function drawTime(y, smallText){
|
||||||
g.setLargeFont();
|
g.setLargeFont();
|
||||||
}
|
}
|
||||||
g.drawString(timeStr, W/2, y);
|
g.drawString(timeStr, W/2, y);
|
||||||
}
|
};
|
||||||
|
|
||||||
function drawMenuItem(text, image){
|
let drawMenuItem = function(text, image) {
|
||||||
// First clear the time region
|
// First clear the time region
|
||||||
var y = H/5*2 + (isFullscreen() ? 0 : 8);
|
var y = H/5*2 + (isFullscreen() ? 0 : 8);
|
||||||
|
|
||||||
|
|
@ -279,10 +257,10 @@ function drawMenuItem(text, image){
|
||||||
|
|
||||||
// Draw time
|
// Draw time
|
||||||
drawTime(y, hasText);
|
drawTime(y, hasText);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
function drawMenuAndTime(){
|
let drawMenuAndTime = function() {
|
||||||
var menuEntry = menu[settings.menuPosX];
|
var menuEntry = menu[settings.menuPosX];
|
||||||
|
|
||||||
// The first entry is the overview...
|
// The first entry is the overview...
|
||||||
|
|
@ -292,37 +270,35 @@ function drawMenuAndTime(){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw item if needed
|
// Draw item if needed
|
||||||
lock_input = true;
|
var item = menuEntry.items[settings.menuPosY-1].get();
|
||||||
var item = menuEntry.items[settings.menuPosY-1];
|
drawMenuItem(item.text, item.img);
|
||||||
item.show();
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
let drawLock = function() {
|
||||||
function drawLock(){
|
|
||||||
if(settings.showLock && Bangle.isLocked()){
|
if(settings.showLock && Bangle.isLocked()){
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.drawImage(imgLock(), W-16, 2);
|
g.drawImage(imgLock(), W-16, 2);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
function drawWidgets(){
|
let drawWidgets = function() {
|
||||||
if(isFullscreen()){
|
if(isFullscreen()){
|
||||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||||
} else {
|
} else {
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
function isFullscreen(){
|
let isFullscreen = function() {
|
||||||
var s = settings.screen.toLowerCase();
|
var s = settings.screen.toLowerCase();
|
||||||
if(s == "dynamic"){
|
if(s == "dynamic"){
|
||||||
return Bangle.isLocked()
|
return Bangle.isLocked();
|
||||||
} else {
|
} else {
|
||||||
return s == "full"
|
return s == "full";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -330,29 +306,30 @@ function isFullscreen(){
|
||||||
* Listener
|
* Listener
|
||||||
*/
|
*/
|
||||||
// timeout used to update every minute
|
// timeout used to update every minute
|
||||||
var drawTimeout;
|
let drawTimeout;
|
||||||
|
|
||||||
// schedule a draw for the next minute
|
// schedule a draw for the next minute
|
||||||
function queueDraw() {
|
let queueDraw = function() {
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = setTimeout(function() {
|
drawTimeout = setTimeout(function() {
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
draw();
|
draw();
|
||||||
}, 60000 - (Date.now() % 60000));
|
}, 60000 - (Date.now() % 60000));
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
// Stop updates when LCD is off, restart when on
|
// Stop updates when LCD is off, restart when on
|
||||||
Bangle.on('lcdPower',on=>{
|
let lcdListenerBw = function(on) {
|
||||||
if (on) {
|
if (on) {
|
||||||
draw(); // draw immediately, queue redraw
|
draw(); // draw immediately, queue redraw
|
||||||
} else { // stop draw timer
|
} else { // stop draw timer
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
Bangle.on('lcdPower', lcdListenerBw);
|
||||||
|
|
||||||
Bangle.on('lock', function(isLocked) {
|
let lockListenerBw = function(isLocked) {
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
|
|
||||||
|
|
@ -363,9 +340,10 @@ Bangle.on('lock', function(isLocked) {
|
||||||
}
|
}
|
||||||
|
|
||||||
draw();
|
draw();
|
||||||
});
|
};
|
||||||
|
Bangle.on('lock', lockListenerBw);
|
||||||
|
|
||||||
Bangle.on('charging',function(charging) {
|
let chargingListenerBw = function(charging) {
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
|
|
||||||
|
|
@ -373,9 +351,10 @@ Bangle.on('charging',function(charging) {
|
||||||
settings.menuPosX = 0;
|
settings.menuPosX = 0;
|
||||||
settings.menuPosY = 1;
|
settings.menuPosY = 1;
|
||||||
draw();
|
draw();
|
||||||
});
|
};
|
||||||
|
Bangle.on('charging', chargingListenerBw);
|
||||||
|
|
||||||
Bangle.on('touch', function(btn, e){
|
let touchListenerBw = function(btn, e) {
|
||||||
var widget_size = isFullscreen() ? 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 left = parseInt(g.getWidth() * 0.22);
|
||||||
var right = g.getWidth() - left;
|
var right = g.getWidth() - left;
|
||||||
|
|
@ -388,10 +367,6 @@ Bangle.on('touch', function(btn, e){
|
||||||
var is_right = e.x > right && !is_upper && !is_lower;
|
var is_right = e.x > right && !is_upper && !is_lower;
|
||||||
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
||||||
|
|
||||||
if(lock_input){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_lower){
|
if(is_lower){
|
||||||
Bangle.buzz(40, 0.6);
|
Bangle.buzz(40, 0.6);
|
||||||
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
|
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
|
||||||
|
|
@ -431,17 +406,11 @@ Bangle.on('touch', function(btn, e){
|
||||||
runMenuItem();
|
runMenuItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
Bangle.on('touch', touchListenerBw);
|
||||||
|
|
||||||
E.on("kill", function(){
|
|
||||||
try{
|
|
||||||
storage.write(SETTINGS_FILE, settings);
|
|
||||||
} catch(ex){
|
|
||||||
// If this fails, we still kill the app...
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
let save = () => storage.write(SETTINGS_FILE, settings);
|
||||||
|
E.on("kill", save);
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Startup Clock
|
* Startup Clock
|
||||||
|
|
@ -450,10 +419,26 @@ E.on("kill", function(){
|
||||||
// The upper part is inverse i.e. light if dark and dark if light theme
|
// 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
|
// is enabled. In order to draw the widgets correctly, we invert the
|
||||||
// dark/light theme as well as the colors.
|
// dark/light theme as well as the colors.
|
||||||
|
let themeBackup = g.theme;
|
||||||
g.setTheme({bg:g.theme.fg,fg:g.theme.bg, dark:!g.theme.dark}).clear();
|
g.setTheme({bg:g.theme.fg,fg:g.theme.bg, dark:!g.theme.dark}).clear();
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
// Show launcher when middle button pressed
|
||||||
Bangle.setUI("clock");
|
Bangle.setUI({
|
||||||
|
mode : "clock",
|
||||||
|
remove : function() {
|
||||||
|
// Called to unload all of the clock app
|
||||||
|
Bangle.removeListener('lcdPower', lcdListenerBw);
|
||||||
|
Bangle.removeListener('lock', lockListenerBw);
|
||||||
|
Bangle.removeListener('charging', chargingListenerBw);
|
||||||
|
Bangle.removeListener('touch', touchListenerBw);
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
// save settings
|
||||||
|
save();
|
||||||
|
E.removeListener("kill", save);
|
||||||
|
g.setTheme(themeBackup);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Load widgets and draw clock the first time
|
// Load widgets and draw clock the first time
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
@ -464,3 +449,5 @@ for (let wd of WIDGETS) {wd._draw=wd.draw; wd._area=wd.area;}
|
||||||
|
|
||||||
// Draw first time
|
// Draw first time
|
||||||
draw();
|
draw();
|
||||||
|
|
||||||
|
} // End of app scope
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "bwclk",
|
"id": "bwclk",
|
||||||
"name": "BW Clock",
|
"name": "BW Clock",
|
||||||
"version": "0.24",
|
"version": "0.25",
|
||||||
"description": "A very minimalistic clock to mainly show date and time.",
|
"description": "A very minimalistic clock to mainly show date and time.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
||||||
|
|
@ -39,3 +39,4 @@
|
||||||
0.21: Remade all icons without a palette for dark theme
|
0.21: Remade all icons without a palette for dark theme
|
||||||
Now re-adds widgets if they were hidden when fast-loading
|
Now re-adds widgets if they were hidden when fast-loading
|
||||||
0.22: Fixed crash if item has no image and cutting long overflowing text
|
0.22: Fixed crash if item has no image and cutting long overflowing text
|
||||||
|
0.23: Setting circles colours per clkinfo and not position
|
||||||
|
|
|
||||||
|
|
@ -20,24 +20,12 @@ let settings = Object.assign(
|
||||||
storage.readJSON("circlesclock.default.json", true) || {},
|
storage.readJSON("circlesclock.default.json", true) || {},
|
||||||
storage.readJSON(SETTINGS_FILE, true) || {}
|
storage.readJSON(SETTINGS_FILE, true) || {}
|
||||||
);
|
);
|
||||||
//TODO deprecate this (and perhaps use in the clkinfo module)
|
|
||||||
// Load step goal from health app and pedometer widget as fallback
|
|
||||||
if (settings.stepGoal == undefined) {
|
|
||||||
let d = storage.readJSON("health.json", true) || {};
|
|
||||||
settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.stepGoal : undefined;
|
|
||||||
|
|
||||||
if (settings.stepGoal == undefined) {
|
|
||||||
d = storage.readJSON("wpedom.json", true) || {};
|
|
||||||
settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let drawTimeout;
|
let drawTimeout;
|
||||||
const showWidgets = settings.showWidgets || false;
|
const showWidgets = settings.showWidgets || false;
|
||||||
const circleCount = settings.circleCount || 3;
|
const circleCount = settings.circleCount || 3;
|
||||||
const showBigWeather = settings.showBigWeather || false;
|
const showBigWeather = settings.showBigWeather || false;
|
||||||
|
|
||||||
let hrtValue; //TODO deprecate this
|
|
||||||
let now = Math.round(new Date().getTime() / 1000);
|
let now = Math.round(new Date().getTime() / 1000);
|
||||||
|
|
||||||
// layout values:
|
// layout values:
|
||||||
|
|
@ -128,8 +116,11 @@ let draw = function() {
|
||||||
queueDraw();
|
queueDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
let getCircleColor = function(index) {
|
let getCircleColor = function(item, clkmenu) {
|
||||||
let color = settings["circle" + index + "color"];
|
let colorKey = clkmenu.name;
|
||||||
|
if(!clkmenu.dynamic) colorKey += "/"+item.name;
|
||||||
|
colorKey += "_color";
|
||||||
|
let color = settings[colorKey];
|
||||||
if (color && color != "") return color;
|
if (color && color != "") return color;
|
||||||
return g.theme.fg;
|
return g.theme.fg;
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +129,7 @@ let getGradientColor = function(color, percent) {
|
||||||
if (isNaN(percent)) percent = 0;
|
if (isNaN(percent)) percent = 0;
|
||||||
if (percent > 1) percent = 1;
|
if (percent > 1) percent = 1;
|
||||||
let colorList = [
|
let colorList = [
|
||||||
'#00FF00', '#80FF00', '#FFFF00', '#FF8000', '#FF0000'
|
'#00ff00', '#80ff00', '#ffff00', '#ff8000', '#ff0000'
|
||||||
];
|
];
|
||||||
if (color == "fg") {
|
if (color == "fg") {
|
||||||
color = colorFg;
|
color = colorFg;
|
||||||
|
|
@ -151,6 +142,17 @@ let getGradientColor = function(color, percent) {
|
||||||
let colorIndex = colorList.length - Math.round(colorList.length * percent);
|
let colorIndex = colorList.length - Math.round(colorList.length * percent);
|
||||||
return colorList[Math.min(colorIndex, colorList.length)] || "#ff0000";
|
return colorList[Math.min(colorIndex, colorList.length)] || "#ff0000";
|
||||||
}
|
}
|
||||||
|
colorList = [
|
||||||
|
'#0000ff', '#8800ff', '#ff00ff', '#ff0088', '#ff0000'
|
||||||
|
];
|
||||||
|
if (color == "blue-red") {
|
||||||
|
let colorIndex = Math.round(colorList.length * percent);
|
||||||
|
return colorList[Math.min(colorIndex, colorList.length) - 1] || "#0000ff";
|
||||||
|
}
|
||||||
|
if (color == "red-blue") {
|
||||||
|
let colorIndex = colorList.length - Math.round(colorList.length * percent);
|
||||||
|
return colorList[Math.min(colorIndex, colorList.length)] || "#ff0000";
|
||||||
|
}
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,10 +174,10 @@ let drawEmpty = function(img, w, color) {
|
||||||
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
||||||
}
|
}
|
||||||
|
|
||||||
let drawCircle = function(index, item, data) {
|
let drawCircle = function(index, item, data, clkmenu) {
|
||||||
var w = circlePosX[index-1];
|
var w = circlePosX[index-1];
|
||||||
drawCircleBackground(w);
|
drawCircleBackground(w);
|
||||||
const color = getCircleColor(index);
|
const color = getCircleColor(item, clkmenu);
|
||||||
//drawEmpty(info? info.img : null, w, color);
|
//drawEmpty(info? info.img : null, w, color);
|
||||||
var img = data.img;
|
var img = data.img;
|
||||||
var percent = 1; //fill up if no range
|
var percent = 1; //fill up if no range
|
||||||
|
|
@ -338,7 +340,8 @@ Bangle.setUI({
|
||||||
|
|
||||||
let clockInfoDraw = (itm, info, options) => {
|
let clockInfoDraw = (itm, info, options) => {
|
||||||
//print("Draw",itm.name,options);
|
//print("Draw",itm.name,options);
|
||||||
drawCircle(options.circlePosition, itm, info);
|
let clkmenu = clockInfoItems[options.menuA];
|
||||||
|
drawCircle(options.circlePosition, itm, info, clkmenu);
|
||||||
if (options.focus) g.reset().drawRect(options.x, options.y, options.x+options.w-2, options.y+options.h-1)
|
if (options.focus) g.reset().drawRect(options.x, options.y, options.x+options.w-2, options.y+options.h-1)
|
||||||
};
|
};
|
||||||
let clockInfoItems = require("clock_info").load();
|
let clockInfoItems = require("clock_info").load();
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,21 @@
|
||||||
"showWidgets": false,
|
"showWidgets": false,
|
||||||
"weatherCircleData": "humidity",
|
"weatherCircleData": "humidity",
|
||||||
"circleCount": 3,
|
"circleCount": 3,
|
||||||
"circle1color": "green-red",
|
"Bangle/Battery_color":"red-green",
|
||||||
"circle2color": "#0000ff",
|
"Bangle/Steps_color":"#0000ff",
|
||||||
"circle3color": "red-green",
|
"Bangle/HRM_color":"green-red",
|
||||||
"circle4color": "#ffff00",
|
"Bangle/Altitude_color":"#00ff00",
|
||||||
|
"Weather/conditionWithTemperature_color":"#ffff00",
|
||||||
|
"Weather/condition_color":"#00ffff",
|
||||||
|
"Weather/humidity_color":"#00ffff",
|
||||||
|
"Weather/wind_color":"fg",
|
||||||
|
"Weather/temperature_color":"blue-red",
|
||||||
|
"Alarms_color":"#00ff00",
|
||||||
|
"Agenda_color":"#ff0000",
|
||||||
"circle1colorizeIcon": true,
|
"circle1colorizeIcon": true,
|
||||||
"circle2colorizeIcon": true,
|
"circle2colorizeIcon": true,
|
||||||
"circle3colorizeIcon": true,
|
"circle3colorizeIcon": true,
|
||||||
"circle4colorizeIcon": false,
|
"circle4colorizeIcon": false,
|
||||||
"updateInterval": 60,
|
"updateInterval": 60,
|
||||||
"showBigWeather": false,
|
"showBigWeather": false
|
||||||
|
|
||||||
"minHR": 40,
|
|
||||||
"maxHR": 200,
|
|
||||||
"confidence": 0,
|
|
||||||
"stepGoal": 10000,
|
|
||||||
"stepDistanceGoal": 8000,
|
|
||||||
"stepLength": 0.8,
|
|
||||||
"hrmValidity": 60
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "circlesclock",
|
{ "id": "circlesclock",
|
||||||
"name": "Circles clock",
|
"name": "Circles clock",
|
||||||
"shortName":"Circles clock",
|
"shortName":"Circles clock",
|
||||||
"version":"0.22",
|
"version":"0.23",
|
||||||
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff",
|
const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff",
|
||||||
"#00ffff", "#fff", "#000", "green-red", "red-green", "fg"];
|
"#00ffff", "#fff", "#000", "green-red", "red-green", "blue-red", "red-blue", "fg"];
|
||||||
const namesColors = ["default", "red", "green", "blue", "yellow", "magenta",
|
const namesColors = ["default", "red", "green", "blue", "yellow", "magenta",
|
||||||
"cyan", "white", "black", "green->red", "red->green", "foreground"];
|
"cyan", "white", "black", "green->red", "red->green", "blue->red", "red->blue", "foreground"];
|
||||||
|
|
||||||
const weatherData = ["empty", "humidity", "wind"];
|
|
||||||
|
|
||||||
function showMainMenu() {
|
function showMainMenu() {
|
||||||
let menu ={
|
let menu ={
|
||||||
|
|
@ -30,31 +28,11 @@
|
||||||
step: 1,
|
step: 1,
|
||||||
onchange: x => save('circleCount', x),
|
onchange: x => save('circleCount', x),
|
||||||
},
|
},
|
||||||
/*LANG*/'circle 1': ()=>showCircleMenu(1),
|
|
||||||
/*LANG*/'circle 2': ()=>showCircleMenu(2),
|
|
||||||
/*LANG*/'circle 3': ()=>showCircleMenu(3),
|
|
||||||
/*LANG*/'circle 4': ()=>showCircleMenu(4),
|
|
||||||
/*LANG*/'battery warn': {
|
|
||||||
value: settings.batteryWarn,
|
|
||||||
min: 10,
|
|
||||||
max : 100,
|
|
||||||
step: 10,
|
|
||||||
format: x => {
|
|
||||||
return x + '%';
|
|
||||||
},
|
|
||||||
onchange: x => save('batteryWarn', x),
|
|
||||||
},
|
|
||||||
/*LANG*/'show widgets': {
|
/*LANG*/'show widgets': {
|
||||||
value: !!settings.showWidgets,
|
value: !!settings.showWidgets,
|
||||||
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
||||||
onchange: x => save('showWidgets', x),
|
onchange: x => save('showWidgets', x),
|
||||||
},
|
},
|
||||||
/*LANG*/'weather data': {
|
|
||||||
value: weatherData.indexOf(settings.weatherCircleData),
|
|
||||||
min: 0, max: 2,
|
|
||||||
format: v => weatherData[v],
|
|
||||||
onchange: x => save('weatherCircleData', weatherData[x]),
|
|
||||||
},
|
|
||||||
/*LANG*/'update interval': {
|
/*LANG*/'update interval': {
|
||||||
value: settings.updateInterval,
|
value: settings.updateInterval,
|
||||||
min: 0,
|
min: 0,
|
||||||
|
|
@ -65,41 +43,54 @@
|
||||||
},
|
},
|
||||||
onchange: x => save('updateInterval', x),
|
onchange: x => save('updateInterval', x),
|
||||||
},
|
},
|
||||||
//TODO deprecated local icons, may disappear in future
|
|
||||||
/*LANG*/'legacy weather icons': {
|
|
||||||
value: !!settings.legacyWeatherIcons,
|
|
||||||
format: () => (settings.legacyWeatherIcons ? 'Yes' : 'No'),
|
|
||||||
onchange: x => save('legacyWeatherIcons', x),
|
|
||||||
},
|
|
||||||
/*LANG*/'show big weather': {
|
/*LANG*/'show big weather': {
|
||||||
value: !!settings.showBigWeather,
|
value: !!settings.showBigWeather,
|
||||||
format: () => (settings.showBigWeather ? 'Yes' : 'No'),
|
format: () => (settings.showBigWeather ? 'Yes' : 'No'),
|
||||||
onchange: x => save('showBigWeather', x),
|
onchange: x => save('showBigWeather', x),
|
||||||
}
|
},
|
||||||
|
/*LANG*/'colorize icons': ()=>showCircleMenus()
|
||||||
};
|
};
|
||||||
E.showMenu(menu);
|
clock_info.load().forEach(e=>{
|
||||||
}
|
if(e.dynamic) {
|
||||||
|
const colorKey = e.name + "_color";
|
||||||
function showCircleMenu(circleId) {
|
menu[e.name+/*LANG*/' color'] = {
|
||||||
const circleName = "circle" + circleId;
|
|
||||||
const colorKey = circleName + "color";
|
|
||||||
const colorizeIconKey = circleName + "colorizeIcon";
|
|
||||||
|
|
||||||
const menu = {
|
|
||||||
'': { 'title': /*LANG*/'Circle ' + circleId },
|
|
||||||
/*LANG*/'< Back': ()=>showMainMenu(),
|
|
||||||
/*LANG*/'color': {
|
|
||||||
value: valuesColors.indexOf(settings[colorKey]) || 0,
|
value: valuesColors.indexOf(settings[colorKey]) || 0,
|
||||||
min: 0, max: valuesColors.length - 1,
|
min: 0, max: valuesColors.length - 1,
|
||||||
format: v => namesColors[v],
|
format: v => namesColors[v],
|
||||||
onchange: x => save(colorKey, valuesColors[x]),
|
onchange: x => save(colorKey, valuesColors[x]),
|
||||||
},
|
|
||||||
/*LANG*/'colorize icon': {
|
|
||||||
value: settings[colorizeIconKey] || false,
|
|
||||||
format: () => (settings[colorizeIconKey] ? 'Yes' : 'No'),
|
|
||||||
onchange: x => save(colorizeIconKey, x),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
let values = e.items.map(i=>e.name+"/"+i.name);
|
||||||
|
let names = e.name=="Bangle" ? e.items.map(i=>i.name) : values;
|
||||||
|
values.forEach((v,i)=>{
|
||||||
|
const colorKey = v + "_color";
|
||||||
|
menu[names[i]+/*LANG*/' color'] = {
|
||||||
|
value: valuesColors.indexOf(settings[colorKey]) || 0,
|
||||||
|
min: 0, max: valuesColors.length - 1,
|
||||||
|
format: v => namesColors[v],
|
||||||
|
onchange: x => save(colorKey, valuesColors[x]),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCircleMenus() {
|
||||||
|
const menu = {
|
||||||
|
'': { 'title': /*LANG*/'Colorize icons'},
|
||||||
|
/*LANG*/'< Back': ()=>showMainMenu(),
|
||||||
|
};
|
||||||
|
for(var circleId=1; circleId<=4; ++circleId) {
|
||||||
|
const circleName = "circle" + circleId;
|
||||||
|
const colorKey = circleName + "color";
|
||||||
|
const colorizeIconKey = circleName + "colorizeIcon";
|
||||||
|
menu[/*LANG*/'circle ' + circleId] = {
|
||||||
|
value: settings[colorizeIconKey] || false,
|
||||||
|
format: () => (settings[colorizeIconKey]? /*LANG*/'Yes': /*LANG*/'No'),
|
||||||
|
onchange: x => save(colorizeIconKey, x),
|
||||||
|
};
|
||||||
|
}
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: Now displays the opened text string at launch.
|
0.04: Now displays the opened text string at launch.
|
||||||
0.05: Now scrolls text when string gets longer than screen width.
|
0.05: Now scrolls text when string gets longer than screen width.
|
||||||
0.06: The code is now more reliable and the input snappier. Widgets will be drawn if present.
|
0.06: The code is now more reliable and the input snappier. Widgets will be drawn if present.
|
||||||
|
0.07: Settings for display colors
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,8 @@ Known bugs:
|
||||||
- Initially developed for use with dark theme set on Bangle.js 2 - that is still the preferred way to view it although it now works with other themes.
|
- Initially developed for use with dark theme set on Bangle.js 2 - that is still the preferred way to view it although it now works with other themes.
|
||||||
- When repeatedly doing 'del' on an empty text-string, the letter case is changed back and forth between upper and lower case.
|
- When repeatedly doing 'del' on an empty text-string, the letter case is changed back and forth between upper and lower case.
|
||||||
|
|
||||||
To do:
|
Settings:
|
||||||
- Possibly provide a dragboard.settings.js file
|
- CAPS LOCK: all characters are displayed and typed in uppercase
|
||||||
|
- ABC Color: color of the characters row
|
||||||
|
- Num Color: color of the digits and symbols row
|
||||||
|
- Highlight Color: color of the currently highlighted character
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@ exports.input = function(options) {
|
||||||
options = options||{};
|
options = options||{};
|
||||||
var text = options.text;
|
var text = options.text;
|
||||||
if ("string"!=typeof text) text="";
|
if ("string"!=typeof text) text="";
|
||||||
|
let settings = require('Storage').readJSON('dragboard.json',1)||{}
|
||||||
|
|
||||||
var R = Bangle.appRect;
|
var R = Bangle.appRect;
|
||||||
|
const paramToColor = (param) => g.toColor(`#${settings[param].toString(16).padStart(3,0)}`);
|
||||||
var BGCOLOR = g.theme.bg;
|
var BGCOLOR = g.theme.bg;
|
||||||
var HLCOLOR = g.theme.fg;
|
var HLCOLOR = settings.Highlight ? paramToColor("Highlight") : g.theme.fg;
|
||||||
var ABCCOLOR = g.toColor(1,0,0);//'#FF0000';
|
var ABCCOLOR = settings.ABC ? paramToColor("ABC") : g.toColor(1,0,0);//'#FF0000';
|
||||||
var NUMCOLOR = g.toColor(0,1,0);//'#00FF00';
|
var NUMCOLOR = settings.Num ? paramToColor("Num") : g.toColor(0,1,0);//'#00FF00';
|
||||||
var BIGFONT = '6x8:3';
|
var BIGFONT = '6x8:3';
|
||||||
var BIGFONTWIDTH = parseInt(BIGFONT.charAt(0)*parseInt(BIGFONT.charAt(-1)));
|
var BIGFONTWIDTH = parseInt(BIGFONT.charAt(0)*parseInt(BIGFONT.charAt(-1)));
|
||||||
var SMALLFONT = '6x8:1';
|
var SMALLFONT = '6x8:1';
|
||||||
|
|
@ -102,6 +104,7 @@ exports.input = function(options) {
|
||||||
//setTimeout(initDraw, 0); // So Bangle.appRect reads the correct environment. It would draw off to the side sometimes otherwise.
|
//setTimeout(initDraw, 0); // So Bangle.appRect reads the correct environment. It would draw off to the side sometimes otherwise.
|
||||||
|
|
||||||
function changeCase(abcHL) {
|
function changeCase(abcHL) {
|
||||||
|
if (settings.uppercase) return;
|
||||||
g.setColor(BGCOLOR);
|
g.setColor(BGCOLOR);
|
||||||
g.setFontAlign(-1, -1, 0);
|
g.setFontAlign(-1, -1, 0);
|
||||||
g.drawString(ABC, ABCPADDING, (R.y+R.h)/2);
|
g.drawString(ABC, ABCPADDING, (R.y+R.h)/2);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "dragboard",
|
{ "id": "dragboard",
|
||||||
"name": "Dragboard",
|
"name": "Dragboard",
|
||||||
"version":"0.06",
|
"version":"0.07",
|
||||||
"description": "A library for text input via swiping keyboard",
|
"description": "A library for text input via swiping keyboard",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type":"textinput",
|
"type":"textinput",
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"textinput","url":"lib.js"}
|
{"name":"textinput","url":"lib.js"},
|
||||||
|
{"name":"dragboard.settings.js","url":"settings.js"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
(function(back) {
|
||||||
|
let settings = require('Storage').readJSON('dragboard.json',1)||{};
|
||||||
|
const colors = {
|
||||||
|
4095: /*LANG*/"White",
|
||||||
|
4080: /*LANG*/"Yellow",
|
||||||
|
3840: /*LANG*/"Red",
|
||||||
|
3855: /*LANG*/"Magenta",
|
||||||
|
255: /*LANG*/"Cyan",
|
||||||
|
240: /*LANG*/"Green",
|
||||||
|
15: /*LANG*/"Blue",
|
||||||
|
0: /*LANG*/"Black",
|
||||||
|
'-1': /*LANG*/"Default"
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () => require('Storage').write('dragboard.json', settings);
|
||||||
|
function colorMenu(key) {
|
||||||
|
let menu = {'': {title: key}, '< Back': () => E.showMenu(appMenu)};
|
||||||
|
Object.keys(colors).forEach(color => {
|
||||||
|
var label = colors[color];
|
||||||
|
menu[label] = {
|
||||||
|
value: settings[key] == color,
|
||||||
|
onchange: () => {
|
||||||
|
if (color >= 0) {
|
||||||
|
settings[key] = color;
|
||||||
|
} else {
|
||||||
|
delete settings[key];
|
||||||
|
}
|
||||||
|
save();
|
||||||
|
setTimeout(E.showMenu, 10, appMenu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appMenu = {
|
||||||
|
'': {title: 'Dragboard'}, '< Back': back,
|
||||||
|
/*LANG*/'CAPS LOCK': {
|
||||||
|
value: !!settings.uppercase,
|
||||||
|
onchange: v => {settings.uppercase = v; save();}
|
||||||
|
},
|
||||||
|
/*LANG*/'ABC Color': () => E.showMenu(colorMenu("ABC")),
|
||||||
|
/*LANG*/'Num Color': () => E.showMenu(colorMenu("Num")),
|
||||||
|
/*LANG*/'Highlight Color': () => E.showMenu(colorMenu("Highlight"))
|
||||||
|
};
|
||||||
|
|
||||||
|
E.showMenu(appMenu);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: App created!
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Gemini clock
|
||||||
|
|
||||||
|
A simple clock face using the Buro Destruct Geminis font, inspired by their Pebble Watch designs: https://burodestruct.net/work/pebble-watchfaces
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It is designed for maximum legibility and utility whilst still showing widgets.
|
||||||
|
|
||||||
|
If editing or remixing this code, please retain leading zeroes on the hours, they are an integral part of the design.
|
||||||
|
|
||||||
|
The minutes are not right-aligned deliberately so that the numbers don't jump around too much when they change.
|
||||||
|
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
Created by Giles Booth:
|
||||||
|
- http://www.suppertime.co.uk/blogmywiki/
|
||||||
|
- https://mastodon.social/@blogmywiki
|
||||||
|
- https://github.com/blogmywiki
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEw4f/AoP//+iiE00u++/nnMooWSyhT/AA8C9u27dtAQNkgEKAwdQCIUD5MkyVJAQNOwG9DIf+oARBgIPDAQOZoHSA4cj0ARIyeA6AIDpnAI4X2FgXcwgRBp0SDQYRCgErKAVt+ARBi4HC3AREAAnMCIIFCgN4CJOuiYRDgFmCKFXhIR/CMSPFCI0reYazCCJEDuzXGUIvoCIXCfY0Brdt284pOfqjLBBwQCCzNArf///9DoMx3gaBEAYTBnmA5wZCiQDB4+QCIeY+3bFgPQFg3QCIeXKwdMCJfOCIcbI4gRLhIhCvARMR4oRPWYIR/CNNnCI+Z9u20AREtCPHzMbtv8wEXv7GB3ARHAQXJoGuA4VCCIjdCCIWTwHQfY8DnIHDAQNACI+AgNvHwIACI4Nd23btoCC3x3BEQsggEKCAm26iIGAH4ATA=="))
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1,17 @@
|
||||||
|
{ "id": "geminiclock",
|
||||||
|
"name": "Gemini clock",
|
||||||
|
"shortName":"Gemini Clock",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Watch face using retro Gemini font",
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
"readme": "README.md",
|
||||||
|
"allow_emulator": true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"geminiclock.app.js","url":"gemini-watch-app.js"},
|
||||||
|
{"name":"geminiclock.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 205 KiB |
|
|
@ -7,3 +7,4 @@
|
||||||
0.08: Leave GPS power switched on on exit (will switch off after 0.5 seconds anyway)
|
0.08: Leave GPS power switched on on exit (will switch off after 0.5 seconds anyway)
|
||||||
0.09: Fix FIFO_FULL error
|
0.09: Fix FIFO_FULL error
|
||||||
0.10: Show satellites "in view" separated by GNS-system
|
0.10: Show satellites "in view" separated by GNS-system
|
||||||
|
0.11: Show number of packets received
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ function satelliteImage() {
|
||||||
var Layout = require("Layout");
|
var Layout = require("Layout");
|
||||||
var layout;
|
var layout;
|
||||||
//Bangle.setGPSPower(1, "app");
|
//Bangle.setGPSPower(1, "app");
|
||||||
E.showMessage(/*LANG*/"Loading..."); // avoid showing rubbish on screen
|
E.showMessage(/*LANG*/"Waiting for GNS data..."); // avoid showing rubbish on screen
|
||||||
|
|
||||||
var lastFix = {
|
var lastFix = {
|
||||||
fix: -1,
|
fix: -1,
|
||||||
|
|
@ -19,6 +19,7 @@ var lastFix = {
|
||||||
var SATinView = 0, lastSATinView = -1, nofGP = 0, nofBD = 0, nofGL = 0;
|
var SATinView = 0, lastSATinView = -1, nofGP = 0, nofBD = 0, nofGL = 0;
|
||||||
const leaveNofixLayout = 1; // 0 = stay on initial screen for debugging (default = 1)
|
const leaveNofixLayout = 1; // 0 = stay on initial screen for debugging (default = 1)
|
||||||
var listenerGPSraw = 0;
|
var listenerGPSraw = 0;
|
||||||
|
var dataCounter = 0;
|
||||||
|
|
||||||
function formatTime(now) {
|
function formatTime(now) {
|
||||||
if (now == undefined) {
|
if (now == undefined) {
|
||||||
|
|
@ -80,11 +81,15 @@ function onGPS(fix) {
|
||||||
type:"v", c: [
|
type:"v", c: [
|
||||||
{type:"txt", font:"6x8:2", label:"GPS Info" },
|
{type:"txt", font:"6x8:2", label:"GPS Info" },
|
||||||
{type:"img", src:satelliteImage, pad:4 },
|
{type:"img", src:satelliteImage, pad:4 },
|
||||||
{type:"txt", font:"6x8", label:"Waiting for GPS" },
|
{type:"txt", font:"6x8", label:"Waiting for GPS fix" },
|
||||||
{type:"h", c: [
|
{type:"h", c: [
|
||||||
{type:"txt", font:"10%", label:fix.satellites, pad:2, id:"sat" },
|
{type:"txt", font:"10%", label:fix.satellites, pad:2, id:"sat" },
|
||||||
{type:"txt", font:"6x8", pad:3, label:"Satellites used" }
|
{type:"txt", font:"6x8", pad:3, label:"Satellites used" }
|
||||||
]},
|
]},
|
||||||
|
{type:"h", c: [
|
||||||
|
{type:"txt", font:"10%", label:dataCounter, pad:2, id:"dataCounter" },
|
||||||
|
{type:"txt", font:"6x8", pad:3, label:"packets received" }
|
||||||
|
]},
|
||||||
{type:"txt", font:"6x8", label:"", fillx:true, id:"progress" }
|
{type:"txt", font:"6x8", label:"", fillx:true, id:"progress" }
|
||||||
]},{lazy:false});
|
]},{lazy:false});
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +127,9 @@ function onGPS(fix) {
|
||||||
layout.progress.label = "in view GP/BD/GL: " + nofGP + " " + nofBD + " " + nofGL;
|
layout.progress.label = "in view GP/BD/GL: " + nofGP + " " + nofBD + " " + nofGL;
|
||||||
// console.log("in view GP/BD/GL: " + nofGP + " " + nofBD + " " + nofGL);
|
// console.log("in view GP/BD/GL: " + nofGP + " " + nofBD + " " + nofGL);
|
||||||
layout.render(layout.progress);
|
layout.render(layout.progress);
|
||||||
|
layout.clear(layout.dataCounter);
|
||||||
|
layout.dataCounter.label = ++dataCounter;
|
||||||
|
layout.render(layout.dataCounter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"id": "gpsinfo",
|
"id": "gpsinfo",
|
||||||
"name": "GPS Info",
|
"name": "GPS Info",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "An application that displays information about altitude, lat/lon, satellites and time",
|
"description": "An application that displays information about latitude, longitude, altitude, speed, satellites and time",
|
||||||
"icon": "gps-info.png",
|
"icon": "gps-info.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "gps,outdoors",
|
"tags": "gps,outdoors,tools",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"gpsinfo.app.js","url":"gps-info.js"},
|
{"name":"gpsinfo.app.js","url":"gps-info.js"},
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,4 @@
|
||||||
0.15: Fix charts (fix #1366)
|
0.15: Fix charts (fix #1366)
|
||||||
0.16: Code tidyup, add back button in top left of health app graphs
|
0.16: Code tidyup, add back button in top left of health app graphs
|
||||||
0.17: Add automatic translation of bar chart labels
|
0.17: Add automatic translation of bar chart labels
|
||||||
|
0.18: Show step goal in daily step chart
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Health Tracking
|
# Health Tracking
|
||||||
|
|
||||||
Logs health data to a file every 10 minutes, and provides an app to view it
|
Logs health data to a file in a defined interval, and provides an app to view it
|
||||||
|
|
||||||
**BETA - requires firmware 2v11 or later**
|
**BETA - requires firmware 2v11 or later**
|
||||||
|
|
||||||
|
|
@ -22,6 +22,7 @@ Stores:
|
||||||
|
|
||||||
* **Heart Rt** - Whether to monitor heart rate or not
|
* **Heart Rt** - Whether to monitor heart rate or not
|
||||||
* **Off** - Don't turn HRM on, but record heart rate if the HRM was turned on by another app/widget
|
* **Off** - Don't turn HRM on, but record heart rate if the HRM was turned on by another app/widget
|
||||||
|
* **3 Min** - Turn HRM on every 3 minutes (for each heath entry) and turn it off after 1 minute, or when a good reading is found
|
||||||
* **10 Min** - Turn HRM on every 10 minutes (for each heath entry) and turn it off after 2 minutes, or when a good reading is found
|
* **10 Min** - Turn HRM on every 10 minutes (for each heath entry) and turn it off after 2 minutes, or when a good reading is found
|
||||||
* **Always** - Keep HRM on all the time (more accurate recording, but reduces battery life to ~36 hours)
|
* **Always** - Keep HRM on all the time (more accurate recording, but reduces battery life to ~36 hours)
|
||||||
* **Daily Step Goal** - Default 10000, daily step goal for pedometer apps to use
|
* **Daily Step Goal** - Default 10000, daily step goal for pedometer apps to use
|
||||||
|
|
@ -49,3 +50,7 @@ and run `EspruinoDocs/bin/minify.js lib.js lib.min.js`
|
||||||
* Yearly view
|
* Yearly view
|
||||||
* Heart rate 'zone' graph
|
* Heart rate 'zone' graph
|
||||||
* .. other
|
* .. other
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The graphs on the web interface use Chart.js, licensed under MIT License.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ function menuHRM() {
|
||||||
|
|
||||||
function stepsPerHour() {
|
function stepsPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
current_selection = "stepsPerHour";
|
||||||
var data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
||||||
setButton(menuStepCount);
|
setButton(menuStepCount);
|
||||||
|
|
@ -46,14 +47,17 @@ function stepsPerHour() {
|
||||||
|
|
||||||
function stepsPerDay() {
|
function stepsPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
current_selection = "stepsPerDay";
|
||||||
var data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
||||||
setButton(menuStepCount);
|
setButton(menuStepCount);
|
||||||
barChart(/*LANG*/"DAY", data);
|
barChart(/*LANG*/"DAY", data);
|
||||||
|
drawHorizontalLine(settings.stepGoal);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hrmPerHour() {
|
function hrmPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
current_selection = "hrmPerHour";
|
||||||
var data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
var cnt = new Uint8Array(23);
|
var cnt = new Uint8Array(23);
|
||||||
require("health").readDay(new Date(), h=>{
|
require("health").readDay(new Date(), h=>{
|
||||||
|
|
@ -67,6 +71,7 @@ function hrmPerHour() {
|
||||||
|
|
||||||
function hrmPerDay() {
|
function hrmPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
current_selection = "hrmPerDay";
|
||||||
var data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
var cnt = new Uint8Array(31);
|
var cnt = new Uint8Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>{
|
require("health").readDailySummaries(new Date(), h=>{
|
||||||
|
|
@ -80,6 +85,7 @@ function hrmPerDay() {
|
||||||
|
|
||||||
function movementPerHour() {
|
function movementPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
current_selection = "movementPerHour";
|
||||||
var data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
||||||
setButton(menuMovement);
|
setButton(menuMovement);
|
||||||
|
|
@ -88,6 +94,7 @@ function movementPerHour() {
|
||||||
|
|
||||||
function movementPerDay() {
|
function movementPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
|
current_selection = "movementPerDay";
|
||||||
var data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
||||||
setButton(menuMovement);
|
setButton(menuMovement);
|
||||||
|
|
@ -97,12 +104,14 @@ function movementPerDay() {
|
||||||
// Bar Chart Code
|
// Bar Chart Code
|
||||||
const w = g.getWidth();
|
const w = g.getWidth();
|
||||||
const h = g.getHeight();
|
const h = g.getHeight();
|
||||||
|
const bar_bot = 140;
|
||||||
|
|
||||||
var data_len;
|
var data_len;
|
||||||
var chart_index;
|
var chart_index;
|
||||||
var chart_max_datum;
|
var chart_max_datum;
|
||||||
var chart_label;
|
var chart_label;
|
||||||
var chart_data;
|
var chart_data;
|
||||||
|
var current_selection;
|
||||||
|
|
||||||
// find the max value in the array, using a loop due to array size
|
// find the max value in the array, using a loop due to array size
|
||||||
function max(arr) {
|
function max(arr) {
|
||||||
|
|
@ -131,7 +140,6 @@ function barChart(label, dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawBarChart() {
|
function drawBarChart() {
|
||||||
const bar_bot = 140;
|
|
||||||
const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre
|
const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre
|
||||||
var bar_top;
|
var bar_top;
|
||||||
var bar;
|
var bar;
|
||||||
|
|
@ -157,6 +165,11 @@ function drawBarChart() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawHorizontalLine(value) {
|
||||||
|
const top = bar_bot - 100 * value / chart_max_datum;
|
||||||
|
g.setColor(g.theme.fg).drawLine(0, top ,g.getWidth(), top);
|
||||||
|
}
|
||||||
|
|
||||||
function setButton(fn) {
|
function setButton(fn) {
|
||||||
Bangle.setUI({mode:"custom",
|
Bangle.setUI({mode:"custom",
|
||||||
back:fn,
|
back:fn,
|
||||||
|
|
@ -170,9 +183,13 @@ function setButton(fn) {
|
||||||
return fn();
|
return fn();
|
||||||
}
|
}
|
||||||
drawBarChart();
|
drawBarChart();
|
||||||
|
if (current_selection == "stepsPerDay") {
|
||||||
|
drawHorizontalLine(settings.stepGoal);
|
||||||
|
}
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
var settings = require("Storage").readJSON("health.json",1)||{};
|
||||||
menuMain();
|
menuMain();
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@
|
||||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="table"></div>
|
<div id="content"></div>
|
||||||
|
|
||||||
<script src="../../core/lib/interface.js"></script>
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
<script type="module" src="chart.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const DB_RECORD_LEN = 4;
|
const DB_RECORD_LEN = 4;
|
||||||
const DB_RECORDS_PER_HR = 6;
|
const DB_RECORDS_PER_HR = 6;
|
||||||
|
|
@ -14,7 +15,7 @@ const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31;
|
||||||
const DB_HEADER_LEN = 8;
|
const DB_HEADER_LEN = 8;
|
||||||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||||
|
|
||||||
var domTable = document.getElementById("table");
|
var domContent = document.getElementById("content");
|
||||||
|
|
||||||
function saveCSV(data, date, title) {
|
function saveCSV(data, date, title) {
|
||||||
// date = "2021-9"/ etc
|
// date = "2021-9"/ etc
|
||||||
|
|
@ -59,7 +60,7 @@ function downloadHealth(filename, callback) {
|
||||||
}
|
}
|
||||||
function getMonthList() {
|
function getMonthList() {
|
||||||
Util.showModal("Loading...");
|
Util.showModal("Loading...");
|
||||||
domTable.innerHTML = "";
|
domContent.innerHTML = "";
|
||||||
Puck.eval(`require("Storage").list(/^health-.*\\.raw$/)`,files=>{
|
Puck.eval(`require("Storage").list(/^health-.*\\.raw$/)`,files=>{
|
||||||
files = files.map(f => {
|
files = files.map(f => {
|
||||||
var m = f.match(/^health-([^\.]+)\.raw$/);
|
var m = f.match(/^health-([^\.]+)\.raw$/);
|
||||||
|
|
@ -69,7 +70,7 @@ function getMonthList() {
|
||||||
str : new Date(m[1]).toLocaleString(undefined, {month:'long',year:'numeric'})
|
str : new Date(m[1]).toLocaleString(undefined, {month:'long',year:'numeric'})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
var html = `<table class="table table-striped table-hover">
|
var htmlOverview = `<table class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Month</th>
|
<th>Month</th>
|
||||||
|
|
@ -78,36 +79,39 @@ function getMonthList() {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>\n`;
|
<tbody>\n`;
|
||||||
files.forEach(f => {
|
files.forEach(f => {
|
||||||
html += `
|
htmlOverview += `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${f.str}</td>
|
<td>${f.str}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" task="downloadcsv">Download CSV</button>
|
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" task="downloadcsv">Download CSV</button>
|
||||||
<button class="btn btn-default" filename="${f.filename}" date="${f.date}" task="delete">Delete</button>
|
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" monthstr="${f.str}" task="monthtable">Table</button>
|
||||||
|
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" monthstr="${f.str}" task="monthgraph">Graph</button>
|
||||||
|
<button class="btn btn-error" filename="${f.filename}" date="${f.date}" task="delete" style="float: right;margin-right: 5px;">Delete</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
if (files.length==0) {
|
if (files.length==0) {
|
||||||
html += `
|
htmlOverview += `
|
||||||
<tr>
|
<tr>
|
||||||
<td>No data recorded</td>
|
<td>No data recorded</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
html += `
|
htmlOverview += `
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>`;
|
</table>`;
|
||||||
domTable.innerHTML = html;
|
domContent.innerHTML = htmlOverview;
|
||||||
Util.hideModal();
|
Util.hideModal();
|
||||||
var buttons = domTable.querySelectorAll("button");
|
var buttons = domContent.querySelectorAll("button");
|
||||||
for (var i=0;i<buttons.length;i++) {
|
for (var i=0;i<buttons.length;i++) {
|
||||||
buttons[i].addEventListener("click",event => {
|
buttons[i].addEventListener("click",event => {
|
||||||
var button = event.currentTarget;
|
var button = event.currentTarget;
|
||||||
var filename = button.getAttribute("filename");
|
var filename = button.getAttribute("filename");
|
||||||
var date = button.getAttribute("date");
|
var date = button.getAttribute("date");
|
||||||
if (!filename || !date) return;
|
if (!filename || !date) return;
|
||||||
|
var monthstr = button.getAttribute("monthstr");
|
||||||
var task = button.getAttribute("task");
|
var task = button.getAttribute("task");
|
||||||
if (task=="delete") {
|
if (task=="delete") {
|
||||||
Util.showModal("Deleting...");
|
Util.showModal("Deleting...");
|
||||||
|
|
@ -119,11 +123,179 @@ function getMonthList() {
|
||||||
if (task=="downloadcsv") {
|
if (task=="downloadcsv") {
|
||||||
downloadHealth(filename, data => saveCSV(data, date, `Bangle.js Health ${date}`));
|
downloadHealth(filename, data => saveCSV(data, date, `Bangle.js Health ${date}`));
|
||||||
}
|
}
|
||||||
|
if (task=="monthtable") {
|
||||||
|
viewMonthDataAsTable(filename, date, monthstr);
|
||||||
|
}
|
||||||
|
if (task=="monthgraph") {
|
||||||
|
viewMonthDataAsGraph(filename, date, monthstr);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDailyData(data) {
|
||||||
|
var dailyData = [];
|
||||||
|
var idx = DB_HEADER_LEN;
|
||||||
|
for (var day = 0; day < 31; day++) {
|
||||||
|
var dayData = {steps: 0, bpm: 0, movement: 0};
|
||||||
|
for (var hr = 0; hr < 24; hr++) { // actually 25, see below
|
||||||
|
for (var m = 0; m < DB_RECORDS_PER_HR; m++) {
|
||||||
|
var h = data.substr(idx, DB_RECORD_LEN);
|
||||||
|
if (h != "\xFF\xFF\xFF\xFF") {
|
||||||
|
var h = {
|
||||||
|
day : day + 1,
|
||||||
|
hr : hr,
|
||||||
|
min : m * 10,
|
||||||
|
steps : (h.charCodeAt(0) << 8) | h.charCodeAt(1),
|
||||||
|
bpm : h.charCodeAt(2),
|
||||||
|
movement : h.charCodeAt(3)
|
||||||
|
};
|
||||||
|
dayData.steps += h.steps; // sum
|
||||||
|
dayData.bpm = (dayData.bpm + h.bpm) / 2; // average
|
||||||
|
dayData.movement += h.movement; // sum
|
||||||
|
}
|
||||||
|
idx += DB_RECORD_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx += DB_RECORD_LEN; // +1 because we have an extra record with totals
|
||||||
|
// for the end of the day
|
||||||
|
|
||||||
|
dailyData[day + 1] = dayData;
|
||||||
|
}
|
||||||
|
return dailyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewMonthDataAsTable(filename, date, monthstr) {
|
||||||
|
Util.showModal("Reading Health info...");
|
||||||
|
Util.readStorage(
|
||||||
|
filename, data => {
|
||||||
|
Util.hideModal();
|
||||||
|
|
||||||
|
var htmlOverview = `<h1>` + monthstr + `</ h1>
|
||||||
|
<button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button>
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Day</th>
|
||||||
|
<th>Steps</th>
|
||||||
|
<th>BPM</th>
|
||||||
|
<th>Movement</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>\n`;
|
||||||
|
|
||||||
|
var dailyData = getDailyData(data);
|
||||||
|
for (var i = 1; i < dailyData.length + 1; i++) {
|
||||||
|
var dayData = dailyData[i];
|
||||||
|
if (dayData) {
|
||||||
|
htmlOverview += `<tr>
|
||||||
|
<td>${i}</td>
|
||||||
|
<td>${dayData.steps}</td>
|
||||||
|
<td>${Math.round(dayData.bpm)}</td>
|
||||||
|
<td>${dayData.movement}</td></tr>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
htmlOverview += `</tbody></table>`;
|
||||||
|
domContent.innerHTML = htmlOverview;
|
||||||
|
domContent.querySelector("#backtomonth").addEventListener("click",event => {
|
||||||
|
getMonthList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewMonthDataAsGraph(filename, date, monthstr) {
|
||||||
|
Util.showModal("Reading Health info...");
|
||||||
|
Util.readStorage(
|
||||||
|
filename, data => {
|
||||||
|
Util.hideModal();
|
||||||
|
|
||||||
|
var html = `<h1>` + monthstr + `</ h1>
|
||||||
|
<button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button>
|
||||||
|
<h2>Steps</h2>
|
||||||
|
<canvas id="chartSteps"></canvas>
|
||||||
|
<h2>BPM</h2>
|
||||||
|
<canvas id="chartBPM"></canvas>
|
||||||
|
<h2>Movement</h2>
|
||||||
|
<canvas id="chartMovement"></canvas>`
|
||||||
|
domContent.innerHTML = html;
|
||||||
|
domContent.querySelector("#backtomonth").addEventListener("click",event => {
|
||||||
|
getMonthList();
|
||||||
|
});
|
||||||
|
|
||||||
|
var labels = [];
|
||||||
|
var dataSteps = [], dataBPM = [], dataMovement = [];
|
||||||
|
|
||||||
|
var dailyData = getDailyData(data);
|
||||||
|
for (var i = 1; i < dailyData.length + 1; i++) {
|
||||||
|
var dayData = dailyData[i];
|
||||||
|
if (dayData) {
|
||||||
|
labels.push(i);
|
||||||
|
dataSteps.push(dayData.steps);
|
||||||
|
dataBPM.push(dayData.bpm);
|
||||||
|
dataMovement.push(dayData.movement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Chart(document.getElementById("chartSteps"), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: '# of steps',
|
||||||
|
data: dataSteps,
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new Chart(document.getElementById("chartBPM"), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Beats per minute',
|
||||||
|
data: dataBPM,
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new Chart(document.getElementById("chartMovement"), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Movement',
|
||||||
|
data: dataMovement,
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function onInit() {
|
function onInit() {
|
||||||
getMonthList();
|
getMonthList();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ exports.readDailySummaries = function(d, cb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read all records from the given month
|
// Read all records from the given day
|
||||||
exports.readDay = function(d, cb) {
|
exports.readDay = function(d, cb) {
|
||||||
var rec = getRecordIdx(d);
|
var rec = getRecordIdx(d);
|
||||||
var fn = getRecordFN(d);
|
var fn = getRecordFN(d);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "health",
|
"id": "health",
|
||||||
"name": "Health Tracking",
|
"name": "Health Tracking",
|
||||||
"shortName": "Health",
|
"shortName": "Health",
|
||||||
"version": "0.17",
|
"version": "0.18",
|
||||||
"description": "Logs health data and provides an app to view it",
|
"description": "Logs health data and provides an app to view it",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,health",
|
"tags": "tool,system,health",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"id": "infoclk",
|
"id": "infoclk",
|
||||||
"name": "Informational clock",
|
"name": "Informational clock",
|
||||||
"version": "0.08",
|
"version": "0.08",
|
||||||
|
"dependencies": {"weather":"app"},
|
||||||
"description": "A configurable clock with extra info and shortcuts when unlocked, but large time when locked",
|
"description": "A configurable clock with extra info and shortcuts when unlocked, but large time when locked",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
|
|
@ -10,7 +11,6 @@
|
||||||
"supports": [
|
"supports": [
|
||||||
"BANGLEJS2"
|
"BANGLEJS2"
|
||||||
],
|
],
|
||||||
"allow_emulator": true,
|
|
||||||
"storage": [
|
"storage": [
|
||||||
{
|
{
|
||||||
"name": "infoclk.app.js",
|
"name": "infoclk.app.js",
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,6 @@
|
||||||
0.20: Use alarm for alarm functionality instead of own implementation.
|
0.20: Use alarm for alarm functionality instead of own implementation.
|
||||||
0.21: Add custom theming.
|
0.21: Add custom theming.
|
||||||
0.22: Fix alarm and add build in function for step counting.
|
0.22: Fix alarm and add build in function for step counting.
|
||||||
0.23: Add warning for low flash memory
|
0.23: Add warning for low flash memory.
|
||||||
0.24: Add ability to disable alarm functionality
|
0.24: Add ability to disable alarm functionality.
|
||||||
|
0.25: Add more colors to the settings and add the ability to disable the data charts+Markup.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ the "sched" app must be installed on your device.
|
||||||
* The lower orange line indicates the battery level.
|
* The lower orange line indicates the battery level.
|
||||||
* Display graphs (day or month) for steps + hrm on the second screen.
|
* Display graphs (day or month) for steps + hrm on the second screen.
|
||||||
* Customizable theming colors in the settings menu of the app.
|
* Customizable theming colors in the settings menu of the app.
|
||||||
|
* Enable or disable the alarm feature.
|
||||||
|
* Enable or disbale the graphs for steps + hrm.
|
||||||
|
|
||||||
## Data that can be configured
|
## Data that can be configured
|
||||||
* Steps - Steps loaded via the wpedom app.
|
* Steps - Steps loaded via the wpedom app.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ let settings = {
|
||||||
themeColor2BG: "#FF00DC",
|
themeColor2BG: "#FF00DC",
|
||||||
themeColor3BG: "#0094FF",
|
themeColor3BG: "#0094FF",
|
||||||
disableAlarms: false,
|
disableAlarms: false,
|
||||||
|
disableData: false,
|
||||||
};
|
};
|
||||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
for (const key in saved_settings) {
|
for (const key in saved_settings) {
|
||||||
|
|
@ -55,7 +56,7 @@ function convert24to16(input)
|
||||||
return "0x"+RGB565.toString(16);
|
return "0x"+RGB565.toString(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
var color1C = convert24to16(color1);
|
var color1C = convert24to16(color1);//Converting colors to the correct format.
|
||||||
var color2C = convert24to16(color2);
|
var color2C = convert24to16(color2);
|
||||||
var color3C = convert24to16(color3);
|
var color3C = convert24to16(color3);
|
||||||
|
|
||||||
|
|
@ -63,7 +64,7 @@ var color3C = convert24to16(color3);
|
||||||
* Requirements and globals
|
* Requirements and globals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var colorPalette = new Uint16Array([
|
var colorPalette = new Uint16Array([//Used to change the color of the image if the user selects a color that is diffrent than the default.
|
||||||
0x0000, // not used
|
0x0000, // not used
|
||||||
color2C, // second
|
color2C, // second
|
||||||
color3C, // third
|
color3C, // third
|
||||||
|
|
@ -709,6 +710,7 @@ Bangle.on('touch', function(btn, e){
|
||||||
var is_upper = e.y < upper;
|
var is_upper = e.y < upper;
|
||||||
var is_lower = e.y > lower;
|
var is_lower = e.y > lower;
|
||||||
|
|
||||||
|
if(!settings.disableData){
|
||||||
if(is_left && lcarsViewPos == 1){
|
if(is_left && lcarsViewPos == 1){
|
||||||
feedback();
|
feedback();
|
||||||
lcarsViewPos = 0;
|
lcarsViewPos = 0;
|
||||||
|
|
@ -721,6 +723,7 @@ Bangle.on('touch', function(btn, e){
|
||||||
draw();
|
draw();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(lcarsViewPos == 0){
|
if(lcarsViewPos == 0){
|
||||||
if(is_upper && !settings.disableAlarms){
|
if(is_upper && !settings.disableAlarms){
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
themeColor2BG: "#FF00DC",
|
themeColor2BG: "#FF00DC",
|
||||||
themeColor3BG: "#0094FF",
|
themeColor3BG: "#0094FF",
|
||||||
disableAlarms: false,
|
disableAlarms: false,
|
||||||
|
disableData: false,
|
||||||
};
|
};
|
||||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
for (const key in saved_settings) {
|
for (const key in saved_settings) {
|
||||||
|
|
@ -27,8 +28,8 @@
|
||||||
|
|
||||||
var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"];
|
var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"];
|
||||||
var speedOptions = ["kph", "mph"];
|
var speedOptions = ["kph", "mph"];
|
||||||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue','Yellow','White'];
|
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue','Yellow','White','Purple','Pink','Light Green','Dark Green'];
|
||||||
var bg_code = ['#00ff00','#FF9900','#0094FF','#FF00DC','#ff0000','#0000ff','#ffef00','#FFFFFF'];
|
var bg_code = ['#00ff00','#FF9900','#0094FF','#FF00DC','#ff0000','#0000ff','#ffef00','#FFFFFF','#FF00FF','#6C00FF','#99FF00','#556B2F'];
|
||||||
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
'': { 'title': 'LCARS Clock' },
|
'': { 'title': 'LCARS Clock' },
|
||||||
|
|
@ -79,7 +80,7 @@
|
||||||
},
|
},
|
||||||
'Theme Color 1': {
|
'Theme Color 1': {
|
||||||
value: 0 | bg_code.indexOf(settings.themeColor1BG),
|
value: 0 | bg_code.indexOf(settings.themeColor1BG),
|
||||||
min: 0, max: 7,
|
min: 0, max: 11,
|
||||||
format: v => color_options[v],
|
format: v => color_options[v],
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.themeColor1BG = bg_code[v];
|
settings.themeColor1BG = bg_code[v];
|
||||||
|
|
@ -88,7 +89,7 @@
|
||||||
},
|
},
|
||||||
'Theme Color 2': {
|
'Theme Color 2': {
|
||||||
value: 0 | bg_code.indexOf(settings.themeColor2BG),
|
value: 0 | bg_code.indexOf(settings.themeColor2BG),
|
||||||
min: 0, max: 7,
|
min: 0, max: 11,
|
||||||
format: v => color_options[v],
|
format: v => color_options[v],
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.themeColor2BG = bg_code[v];
|
settings.themeColor2BG = bg_code[v];
|
||||||
|
|
@ -97,7 +98,7 @@
|
||||||
},
|
},
|
||||||
'Theme Color 3': {
|
'Theme Color 3': {
|
||||||
value: 0 | bg_code.indexOf(settings.themeColor3BG),
|
value: 0 | bg_code.indexOf(settings.themeColor3BG),
|
||||||
min: 0, max: 7,
|
min: 0, max: 11,
|
||||||
format: v => color_options[v],
|
format: v => color_options[v],
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.themeColor3BG = bg_code[v];
|
settings.themeColor3BG = bg_code[v];
|
||||||
|
|
@ -112,5 +113,13 @@
|
||||||
save();
|
save();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'Disable data pages functionality': {
|
||||||
|
value: settings.disableData,
|
||||||
|
format: () => (settings.disableData ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
settings.disableData = !settings.disableData;
|
||||||
|
save();
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "LCARS Clock",
|
"name": "LCARS Clock",
|
||||||
"shortName":"LCARS",
|
"shortName":"LCARS",
|
||||||
"icon": "lcars.png",
|
"icon": "lcars.png",
|
||||||
"version":"0.24",
|
"version":"0.25",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"description": "Library Computer Access Retrieval System (LCARS) clock.",
|
"description": "Library Computer Access Retrieval System (LCARS) clock.",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: New App.
|
0.01: New App.
|
||||||
0.02: Performance improvements.
|
0.02: Performance improvements.
|
||||||
0.03: Update clock_info to avoid a redraw
|
0.03: Update clock_info to avoid a redraw
|
||||||
|
0.04: Fix clkinfo -- use .get instead of .show
|
||||||
|
|
@ -76,21 +76,6 @@ var H = g.getHeight();
|
||||||
var menu = clock_info.load();
|
var menu = clock_info.load();
|
||||||
menu = menu.concat(dateMenu);
|
menu = menu.concat(dateMenu);
|
||||||
|
|
||||||
// Set draw functions for each item
|
|
||||||
menu.forEach((menuItm, x) => {
|
|
||||||
menuItm.items.forEach((item, y) => {
|
|
||||||
function drawItem() {
|
|
||||||
item.hide();
|
|
||||||
|
|
||||||
var info = item.get();
|
|
||||||
drawText(item.name, info.text, (y%4)+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.on('redraw', drawItem);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
|
// 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 ){
|
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
|
||||||
settings.menuPosX = 0;
|
settings.menuPosX = 0;
|
||||||
|
|
@ -184,8 +169,9 @@ function drawMenuItems(menuItem) {
|
||||||
if (i >= menuItem.items.length) {
|
if (i >= menuItem.items.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lock_input++;
|
|
||||||
menuItem.items[i].show();
|
var item = menuItem.items[i];
|
||||||
|
drawText(item.name, item.get().text, (i%4)+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,7 +203,6 @@ function drawText(key, value, line){
|
||||||
value = String(value).replace("\n", " ");
|
value = String(value).replace("\n", " ");
|
||||||
g.drawString(key + value, x, y);
|
g.drawString(key + value, x, y);
|
||||||
|
|
||||||
lock_input -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -289,14 +274,8 @@ Bangle.on('charging',function(charging) {
|
||||||
draw();
|
draw();
|
||||||
});
|
});
|
||||||
|
|
||||||
var lock_input = 0;
|
|
||||||
|
|
||||||
Bangle.on('touch', function(btn, e){
|
Bangle.on('touch', function(btn, e){
|
||||||
if(lock_input > 0){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lock_input = 0;
|
|
||||||
|
|
||||||
var left = parseInt(g.getWidth() * 0.22);
|
var left = parseInt(g.getWidth() * 0.22);
|
||||||
var right = g.getWidth() - left;
|
var right = g.getWidth() - left;
|
||||||
var upper = parseInt(g.getHeight() * 0.22) + 20;
|
var upper = parseInt(g.getHeight() * 0.22) + 20;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "linuxclock",
|
"id": "linuxclock",
|
||||||
"name": "Linux Clock",
|
"name": "Linux Clock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "A Linux inspired clock.",
|
"description": "A Linux inspired clock.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New app!
|
||||||
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -0,0 +1,3 @@
|
||||||
|
Bangle.on("message", (t, m) => {
|
||||||
|
require("Storage").open("messagesdebug.log", "a").write(`${t}: ${JSON.stringify(m)}\n`);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
<title>Messages Debug</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="data"></div>
|
||||||
|
<button class="btn btn-default" id="btnSave">Save</button>
|
||||||
|
<button class="btn btn-default" id="btnDelete">Clear</button>
|
||||||
|
<button class="btn btn-default" id="btnReload" style="float:right">Reload</button>
|
||||||
|
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
<script>
|
||||||
|
const dataElement = document.getElementById("data");
|
||||||
|
let messages = "";
|
||||||
|
|
||||||
|
function getData() {
|
||||||
|
// show loading window
|
||||||
|
Util.showModal("Loading...");
|
||||||
|
// get the data
|
||||||
|
dataElement.innerHTML = "";
|
||||||
|
Util.readStorageFile(`messagesdebug.log`, data => {
|
||||||
|
messages = data.trim();
|
||||||
|
// remove window
|
||||||
|
Util.hideModal();
|
||||||
|
let disable = false;
|
||||||
|
if (data.length) {
|
||||||
|
dataElement.innerHTML = `<pre style="overflow:auto;border:1px solid black;">${data}</pre>`;
|
||||||
|
} else {
|
||||||
|
dataElement.innerHTML = "<b>No messages found</b>";
|
||||||
|
disable = true;
|
||||||
|
}
|
||||||
|
['btnSave','btnDelete','btnCopy'].forEach(id=>{
|
||||||
|
document.getElementById(id).disabled = disable;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const button = (id, fn) => document.getElementById(id).addEventListener("click", fn);
|
||||||
|
// Save messages to file
|
||||||
|
button("btnSave", function() {
|
||||||
|
let a = document.createElement("a");
|
||||||
|
let url = URL.createObjectURL(new Blob([messages], {type: "text/plain"}));
|
||||||
|
a.href = url;
|
||||||
|
a.download = "messagesdebug.log";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(function() {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
// Delete the file from the watch
|
||||||
|
button("btnDelete", function() {
|
||||||
|
Util.showModal("Deleting...");
|
||||||
|
Util.eraseStorageFile("messagesdebug.log", function() {
|
||||||
|
Util.hideModal();
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Reload, in case we're e.g. using the IDE to send test messages
|
||||||
|
button("btnReload", getData);
|
||||||
|
|
||||||
|
// Called when app starts
|
||||||
|
function onInit() {
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"id": "messagesdebug",
|
||||||
|
"name": "Messages Debug",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "Write all messages to a file, for debugging purposes",
|
||||||
|
"icon": "app.png",
|
||||||
|
"type": "bootloader",
|
||||||
|
"tags": "tool,system",
|
||||||
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"interface": "interface.html",
|
||||||
|
"storage": [
|
||||||
|
{"name":"messagesdebug.boot.js","url":"boot.js"}
|
||||||
|
],
|
||||||
|
"data": [{"name":"messagesdebug.log"}]
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: First release
|
0.01: First release
|
||||||
|
0.02: Use locale time
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id":"mosaic",
|
"id":"mosaic",
|
||||||
"name":"Mosaic Clock",
|
"name":"Mosaic Clock",
|
||||||
"shortName": "Mosaic Clock",
|
"shortName": "Mosaic Clock",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "A fabulously colourful clock",
|
"description": "A fabulously colourful clock",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon":"mosaic.png",
|
"icon":"mosaic.png",
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,15 @@ function draw() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let t = new Date();
|
let t = require("locale").time(new Date(), 1);
|
||||||
|
let hour = parseInt(t.split(":")[0]);
|
||||||
|
let minute = parseInt(t.split(":")[1]);
|
||||||
g.setBgColor(theme.fg);
|
g.setBgColor(theme.fg);
|
||||||
g.setColor(theme.bg);
|
g.setColor(theme.bg);
|
||||||
g.drawImage(digits[Math.floor(t.getHours()/10)], (mid_x-5)*s+o_w, (mid_y-7)*s+o_h, {scale:s});
|
g.drawImage(digits[Math.floor(hour/10)], (mid_x-5)*s+o_w, (mid_y-7)*s+o_h, {scale:s});
|
||||||
g.drawImage(digits[t.getHours() % 10], (mid_x+1)*s+o_w, (mid_y-7)*s+o_h, {scale:s});
|
g.drawImage(digits[hour % 10], (mid_x+1)*s+o_w, (mid_y-7)*s+o_h, {scale:s});
|
||||||
g.drawImage(digits[Math.floor(t.getMinutes()/10)], (mid_x-5)*s+o_w, (mid_y+1)*s+o_h, {scale:s});
|
g.drawImage(digits[Math.floor(minute/10)], (mid_x-5)*s+o_w, (mid_y+1)*s+o_h, {scale:s});
|
||||||
g.drawImage(digits[t.getMinutes() % 10], (mid_x+1)*s+o_w, (mid_y+1)*s+o_h, {scale:s});
|
g.drawImage(digits[minute % 10], (mid_x+1)*s+o_w, (mid_y+1)*s+o_h, {scale:s});
|
||||||
|
|
||||||
queueDraw(timeout);
|
queueDraw(timeout);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
"BANGLEJS",
|
"BANGLEJS",
|
||||||
"BANGLEJS2"
|
"BANGLEJS2"
|
||||||
],
|
],
|
||||||
"allow_emulator": true,
|
|
||||||
"storage": [
|
"storage": [
|
||||||
{
|
{
|
||||||
"name": "pomoplus.app.js",
|
"name": "pomoplus.app.js",
|
||||||
|
|
|
||||||
|
|
@ -60,3 +60,5 @@
|
||||||
0.53: Ensure that when clock is set, clockHasWidgets is set correctly too
|
0.53: Ensure that when clock is set, clockHasWidgets is set correctly too
|
||||||
0.54: If setting.json is corrupt, ensure it gets re-written
|
0.54: If setting.json is corrupt, ensure it gets re-written
|
||||||
0.55: More strings tagged for automatic translation.
|
0.55: More strings tagged for automatic translation.
|
||||||
|
0.56: make System menu items shorter and more consistant, Eg 'Clock', intead
|
||||||
|
of 'Select Clock'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "setting",
|
"id": "setting",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"version": "0.55",
|
"version": "0.56",
|
||||||
"description": "A menu for setting up Bangle.js",
|
"description": "A menu for setting up Bangle.js",
|
||||||
"icon": "settings.png",
|
"icon": "settings.png",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,8 @@ function showSystemMenu() {
|
||||||
/*LANG*/'Theme': ()=>showThemeMenu(),
|
/*LANG*/'Theme': ()=>showThemeMenu(),
|
||||||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||||
/*LANG*/'Select Clock': ()=>showClockMenu(),
|
/*LANG*/'Clock': ()=>showClockMenu(),
|
||||||
/*LANG*/'Select Launcher': ()=>showLauncherMenu(),
|
/*LANG*/'Launcher': ()=>showLauncherMenu(),
|
||||||
/*LANG*/'Date & Time': ()=>showSetTimeMenu()
|
/*LANG*/'Date & Time': ()=>showSetTimeMenu()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: first release
|
||||||
|
0.02: removed fast load, minimalism is useful for narrowing down on issues
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Simplest++ Clock
|
# Simplest++ Clock
|
||||||
|
|
||||||
The simplest working clock, with fast load and clock_info
|
The simplest working clock, with clock_info
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
@ -36,8 +36,6 @@ This provides a working demo of how to use the clock_info modules.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
* [What is Fast Load and how does it work](http://www.espruino.com/Bangle.js+Fast+Load)
|
|
||||||
|
|
||||||
* [Clock Info Tutorial](http://www.espruino.com/Bangle.js+Clock+Info)
|
* [Clock Info Tutorial](http://www.espruino.com/Bangle.js+Clock+Info)
|
||||||
|
|
||||||
* [How to load modules through the IDE](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
* [How to load modules through the IDE](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,6 @@
|
||||||
/**
|
// Simplestpp Clock, see comments 'clock_info_support'
|
||||||
*
|
|
||||||
* Simplestpp Clock
|
|
||||||
*
|
|
||||||
* The entire clock code is contained within the block below this
|
|
||||||
* supports 'fast load'
|
|
||||||
*
|
|
||||||
* To add support for clock_info_supprt we add the code marked at [1] and [2]
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
{
|
function draw() {
|
||||||
// must be inside our own scope here so that when we are unloaded everything disappears
|
|
||||||
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
|
||||||
|
|
||||||
let draw = function() {
|
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
var timeStr = require("locale").time(date,1);
|
var timeStr = require("locale").time(date,1);
|
||||||
var h = g.getHeight();
|
var h = g.getHeight();
|
||||||
|
|
@ -27,26 +14,32 @@
|
||||||
g.setFontAlign(0, 0);
|
g.setFontAlign(0, 0);
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.drawString(timeStr, w/2, h/2);
|
g.drawString(timeStr, w/2, h/2);
|
||||||
clockInfoMenu.redraw(); // clock_info_support
|
|
||||||
|
|
||||||
// schedule a draw for the next minute
|
clockInfoMenu.redraw(); // clock_info_support
|
||||||
|
queueDraw(); // queue draw in one minute
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout used to update every minute
|
||||||
|
var drawTimeout;
|
||||||
|
|
||||||
|
// schedule a draw for the next minute
|
||||||
|
function queueDraw() {
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = setTimeout(function() {
|
drawTimeout = setTimeout(function() {
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
draw();
|
draw();
|
||||||
}, 60000 - (Date.now() % 60000));
|
}, 60000 - (Date.now() % 60000));
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clock_info_support
|
* clock_info_support
|
||||||
* this is the callback function that get invoked by clockInfoMenu.redraw();
|
* this is the callback function that get invoked by clockInfoMenu.redraw();
|
||||||
*
|
*
|
||||||
* We will display the image and text on the same line and centre the combined
|
* We will display the image and text on the same line and centre the combined
|
||||||
* length of the image+text
|
* length of the image+text
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
let clockInfoDraw = (itm, info, options) => {
|
function clockInfoDraw(itm, info, options) {
|
||||||
|
|
||||||
g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg);
|
g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg);
|
||||||
|
|
||||||
|
|
@ -74,35 +67,29 @@
|
||||||
g.setFontAlign(0,0).drawString(info.text, g.getWidth() / 2, options.y+12);
|
g.setFontAlign(0,0).drawString(info.text, g.getWidth() / 2, options.y+12);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
// clock_info_support
|
/**
|
||||||
// retrieve all the clock_info modules that are installed
|
* clock_info_support: retrieve all the clock_info modules that are
|
||||||
let clockInfoItems = require("clock_info").load();
|
* installed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let clockInfoItems = require("clock_info").load();
|
||||||
|
|
||||||
// clock_info_support
|
/**
|
||||||
// setup the way we wish to interact with the menu
|
* clock_info_support: setup the way we wish to interact with the menu
|
||||||
// the hl property defines the color the of the info when the menu is selected after tapping on it
|
* the hl property defines the color the of the info when the menu is
|
||||||
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} );
|
* selected after tapping on it
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} );
|
||||||
|
|
||||||
// timeout used to update every minute
|
// Clear the screen once, at startup
|
||||||
var drawTimeout;
|
g.clear();
|
||||||
g.clear();
|
|
||||||
|
|
||||||
// Show launcher when middle button pressed, add updown button handlers
|
// Show launcher when middle button pressed
|
||||||
Bangle.setUI({
|
Bangle.setUI("clock");
|
||||||
mode : "clock",
|
// Load widgets
|
||||||
remove : function() {
|
Bangle.loadWidgets();
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
Bangle.drawWidgets();
|
||||||
drawTimeout = undefined;
|
draw();
|
||||||
// remove info menu
|
|
||||||
clockInfoMenu.remove();
|
|
||||||
delete clockInfoMenu;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load widgets
|
|
||||||
Bangle.loadWidgets();
|
|
||||||
draw();
|
|
||||||
setTimeout(Bangle.drawWidgets,0);
|
|
||||||
} // end of clock
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
"id": "simplestpp",
|
"id": "simplestpp",
|
||||||
"name": "Simplest++ Clock",
|
"name": "Simplest++ Clock",
|
||||||
"shortName": "Simplest++",
|
"shortName": "Simplest++",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "The simplest working clock, with fast load and clock_info, acts as a tutorial piece",
|
"description": "The simplest working clock, with clock_info, acts as a tutorial piece",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot3.png"}],
|
"screenshots": [{"url":"screenshot3.png"}],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Space Clock (Casio)
|
||||||
|

|
||||||
|
## Description
|
||||||
|
This watch face is inspired by the Casio Prototype, which was made by Khryzteen Nakamura from Clockology Fans on Facebook.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Time and Date
|
||||||
|
- Weekday
|
||||||
|
- Temperature
|
||||||
|
- HeartRate
|
||||||
|
- Battery
|
||||||
|
- Step Count
|
||||||
|
- Support of light and dark theme
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
Click on the heart icon to deactivate the heart rate monitor.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEewgbYhGIxGAC6wABBAcM5gACC5wYCCwgYLIwYcBhoWFDBQTBAofcC4/AGBIEDCw4wLJARdGAAfQGBYWJJBQwCC5SSLC6wwBC6owBwhgUDASQKC5UMpGIpgXU5koMRIXM4hiJC5gNBMRAXN5hiBDA0IC5oPBPYz+CABAQElAYFWgIYJC4h7BDAZeBHAJINPYQYC6DlCGBENRQwRBhgNCGBSjG4QxBBoYwQGIIrEPJS8GIgYADYZwIDDAgXJABQXXAAY"))
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,15 @@
|
||||||
|
{ "id": "spaceclock",
|
||||||
|
"name": "Space Clock (Casio Style)",
|
||||||
|
"shortName":"Space Clock",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Watch face in the style of Casio Prototype Space Resist",
|
||||||
|
"icon": "app-icon.png",
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock, casio, retro",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"spaceclock.app.js","url":"app.js"},
|
||||||
|
{"name":"spaceclock.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Stable Launch
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwIJGv//AAX+oEAwEBgECApuD4IFBocww1hAoXwxkxAoNB8GIiIFC4AFDofgCIYdFoEQFIZBRLLoFBHYkAI4YFBKYYFHCIodFLO8M4RHDh4FCKYMHwQFELIkPBYQdFFIVCLK8AAAg="))
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
//////////////////////////////
|
||||||
|
// Tic - Tac - Toe
|
||||||
|
// Stable Version 1.0 - 12/31/2022
|
||||||
|
// MissionMake
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// TODO:
|
||||||
|
// Implement Computer Player
|
||||||
|
// Beginning Screen (pick player to go first, pick one or two player)
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//create 3x3 array to log plays Xs defined as 1, Os defined as -1, blank is undefined, array is initialized undefined, player is which players turn is active (using 1,-1 definition to match matrix), active is if a game is being played
|
||||||
|
|
||||||
|
var arr1 = new Array(3);
|
||||||
|
var arr2 = new Array(3);
|
||||||
|
var arr3 = new Array(3);
|
||||||
|
var arr = new Array(arr1,arr2,arr3);
|
||||||
|
var val = 0;
|
||||||
|
var player;
|
||||||
|
var active = false;
|
||||||
|
var select = false;
|
||||||
|
var next = 0;
|
||||||
|
var winval =0;
|
||||||
|
var ex = require("heatshrink").decompress(atob("mEwwI63jACEngCEvwCEv4CB/wCBn+AgP8AoMf4ED/AFBh/gg/wAoIDBA4IFBB4ITBAoIbBD4I8C/wrCGAQuCGAQuCGAQuCGAQuCAo4RFDoopFGohBFJopZFMopxFPoqJFSoqhFVooA0A"));
|
||||||
|
var oh = require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCB/wCBBAU/AQIUCj8AgIzCh+AgYmCg/AgYyCAYIHBAoXgg+AAoMBApkPLgZKBAtBBRLIprDMoJxFPoqJFSoyhCAQStFXIrFFaIrdFdIwAVA"));
|
||||||
|
|
||||||
|
//calculates sum of rows, colums, and diagonals for a win condition. passes winner to win() and breaks out of calcs
|
||||||
|
function calcWin(){
|
||||||
|
winval = 0;
|
||||||
|
//sum of row
|
||||||
|
for(let i = 0; i<3; i++){
|
||||||
|
val=0;
|
||||||
|
for(let j = 0; j<3; j++){
|
||||||
|
val = arr[i][j]+val;
|
||||||
|
}
|
||||||
|
if (Math.abs(val)==3) {
|
||||||
|
winval = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//sum of columns
|
||||||
|
for(let j = 0; j<3; j++){
|
||||||
|
val=0;
|
||||||
|
for (let i = 0; i<3; i++){
|
||||||
|
val = arr[i][j]+val;
|
||||||
|
}
|
||||||
|
//if win set winval to val
|
||||||
|
if (Math.abs(val)==3) {
|
||||||
|
winval = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sum of ul to lr
|
||||||
|
val=0;
|
||||||
|
val = arr[0][0]+arr[1][1]+arr[2][2];
|
||||||
|
//if win set winval to val
|
||||||
|
if (Math.abs(val)==3) {
|
||||||
|
winval = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sum of ur to ll
|
||||||
|
val=0;
|
||||||
|
val = arr[0][2]+arr[1][1]+arr[2][0];
|
||||||
|
//if win set winval to val
|
||||||
|
if (Math.abs(val)==3){
|
||||||
|
winval = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw check
|
||||||
|
// drawChk is sum absolute value of array, if drawChk = 9 then there is a draw
|
||||||
|
drawChk = 0;
|
||||||
|
for(let i = 0; i<3; i++){
|
||||||
|
for(let j = 0; j<3; j++){
|
||||||
|
drawChk = drawChk + Math.abs(arr[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//checks for win cases and posts correct message, otherwise play
|
||||||
|
if (winval == 3){
|
||||||
|
active = false;
|
||||||
|
E.showAlert("Player X Wins").then(start);
|
||||||
|
} else if (winval == -3){
|
||||||
|
active = false;
|
||||||
|
E.showAlert("Player O Wins").then(start);
|
||||||
|
} else if (drawChk == 9) {
|
||||||
|
active = false;
|
||||||
|
E.showAlert("Draw").then(start);
|
||||||
|
}else{
|
||||||
|
//If no win then play
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw(){
|
||||||
|
g.clear();
|
||||||
|
if (player ==1){
|
||||||
|
playerIcon = "X";
|
||||||
|
} else if(player == -1){
|
||||||
|
playerIcon = "O";
|
||||||
|
}
|
||||||
|
//Banner Displays player turn
|
||||||
|
E.showMessage("","Player "+ playerIcon);
|
||||||
|
//drawboard
|
||||||
|
g.drawLine(62,24,62,176);
|
||||||
|
g.drawLine(112,24,112,176);
|
||||||
|
g.drawLine(12,74,164,74);
|
||||||
|
g.drawLine(12,124,164,124);
|
||||||
|
|
||||||
|
//loop through array and draw markers
|
||||||
|
for(let i = 0; i<3; i++){
|
||||||
|
for(let j = 0; j<3; j++){
|
||||||
|
if(arr[j][i] == -1){
|
||||||
|
g.drawImage(oh,i*50+12,j*50+24);//, {scale:1.05});
|
||||||
|
} else if (arr[j][i] == 1){
|
||||||
|
g.drawImage(ex,i*50+12,j*50+24);//, {scale:1.05});
|
||||||
|
} else {
|
||||||
|
//blank spot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select=false;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square locations
|
||||||
|
//12,24;62,24,112,24
|
||||||
|
//12,74;62,74,112,74
|
||||||
|
//12,124;62,124,112,124
|
||||||
|
|
||||||
|
function placeMarker(){
|
||||||
|
///Determine marker square
|
||||||
|
if (x <= 62) {
|
||||||
|
b = 0;
|
||||||
|
} else if (x <= 112){
|
||||||
|
b = 1;
|
||||||
|
} else {
|
||||||
|
b = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y <= 74) {
|
||||||
|
a = 0;
|
||||||
|
} else if (y <= 124){
|
||||||
|
a = 1;
|
||||||
|
} else {
|
||||||
|
a = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if empty
|
||||||
|
if( arr[a][b] == undefined){
|
||||||
|
//record in array
|
||||||
|
arr[a][b] = player;
|
||||||
|
player=player*-1;
|
||||||
|
select = false;
|
||||||
|
calcWin();
|
||||||
|
} else{ //if filled
|
||||||
|
// This could just do nothing
|
||||||
|
|
||||||
|
E.showAlert("SpaceFilled Try again").then(draw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Wait loop which is run until a tap is selected
|
||||||
|
function wait(){
|
||||||
|
//Terminal.println("wait");
|
||||||
|
if(select == true){
|
||||||
|
placeMarker();
|
||||||
|
} else {
|
||||||
|
setTimeout(wait,300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Starts new game
|
||||||
|
// Draws the start pattern, sets first player to x and goes to play
|
||||||
|
function start(){
|
||||||
|
//reset array to undefined
|
||||||
|
arr1.fill(undefined);
|
||||||
|
arr2.fill(undefined);
|
||||||
|
arr3.fill(undefined);
|
||||||
|
g.clear();
|
||||||
|
active =true;
|
||||||
|
player=1;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Looks for touch
|
||||||
|
Bangle.on('touch', function(zone,e) {
|
||||||
|
x = Object.values(e)[0];
|
||||||
|
y = Object.values(e)[1];
|
||||||
|
//if game is active
|
||||||
|
if(active == true){
|
||||||
|
g.fillCircle(x, y, 10);
|
||||||
|
select = true;
|
||||||
|
}
|
||||||
|
if(active == false){
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 594 B |
|
|
@ -0,0 +1,17 @@
|
||||||
|
{ "id": "tictactoe",
|
||||||
|
"name": "TicTacToe",
|
||||||
|
"shortName":"TicTacToe",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Tic Tac Toe for two players!",
|
||||||
|
"tags": "game",
|
||||||
|
"storage": [
|
||||||
|
{"name":"tictactoe.app.js","url":"app.js"},
|
||||||
|
{"name":"tictactoe.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"screenshots" : [
|
||||||
|
{ "url":"tttscreenshot.png" },
|
||||||
|
{ "url":"tttscreenshot2.png" }
|
||||||
|
],
|
||||||
|
"supports": ["BANGLEJS2"]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -136,7 +136,8 @@
|
||||||
<button class="btn" id="removeall" data-tooltip="Delete everything from your Bangle, leaving it blank">Remove all Apps</button>
|
<button class="btn" id="removeall" data-tooltip="Delete everything from your Bangle, leaving it blank">Remove all Apps</button>
|
||||||
<button class="btn" id="reinstallall" data-tooltip="Remove and re-install every app, leaving all other data intact">Reinstall apps</button>
|
<button class="btn" id="reinstallall" data-tooltip="Remove and re-install every app, leaving all other data intact">Reinstall apps</button>
|
||||||
<button class="btn" id="installdefault">Install default apps</button>
|
<button class="btn" id="installdefault">Install default apps</button>
|
||||||
<button class="btn" id="installfavourite" data-tooltip="Delete everything, install apps you've marked as favourites">Install favourite apps</button></p>
|
<button class="btn" id="installfavourite" data-tooltip="Delete everything, install apps you've marked as favourites">Install favourite apps</button>
|
||||||
|
<button class="btn" id="newGithubIssue" data-tooltip="Create a new issue on github">New issue on github</button></p>
|
||||||
<p><button class="btn tooltip tooltip-right" id="downloadallapps" data-tooltip="Download all Bangle.js files to a ZIP file">Backup</button>
|
<p><button class="btn tooltip tooltip-right" id="downloadallapps" data-tooltip="Download all Bangle.js files to a ZIP file">Backup</button>
|
||||||
<button class="btn tooltip tooltip-right" id="uploadallapps" data-tooltip="Restore Bangle.js from a ZIP file">Restore</button></p>
|
<button class="btn tooltip tooltip-right" id="uploadallapps" data-tooltip="Restore Bangle.js from a ZIP file">Restore</button></p>
|
||||||
<h3>Settings</h3>
|
<h3>Settings</h3>
|
||||||
|
|
|
||||||
|
|
@ -71,11 +71,15 @@ exports.load = function() {
|
||||||
bangleItems[2].emit("redraw");
|
bangleItems[2].emit("redraw");
|
||||||
}
|
}
|
||||||
function altUpdateHandler() {
|
function altUpdateHandler() {
|
||||||
|
try {
|
||||||
Bangle.getPressure().then(data=>{
|
Bangle.getPressure().then(data=>{
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
alt = Math.round(data.altitude) + "m";
|
alt = Math.round(data.altitude) + "m";
|
||||||
bangleItems[3].emit("redraw");
|
bangleItems[3].emit("redraw");
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print("Caught "+e+"\n in function altUpdateHandler in module clock_info");
|
||||||
|
bangleItems[3].emit('redraw');}
|
||||||
}
|
}
|
||||||
// actual menu
|
// actual menu
|
||||||
var menu = [{
|
var menu = [{
|
||||||
|
|
|
||||||