1
0
Fork 0

Merge branch 'espruino:master' into master

master
wagnerf42 2023-07-07 10:20:02 +02:00 committed by GitHub
commit 897838a096
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
173 changed files with 4950 additions and 1016 deletions

View File

@ -14,5 +14,6 @@
{"name":"90sclk.app.js","url":"app.js"}, {"name":"90sclk.app.js","url":"app.js"},
{"name":"90sclk.img","url":"app-icon.js","evaluate":true}, {"name":"90sclk.img","url":"app-icon.js","evaluate":true},
{"name":"90sclk.settings.js","url":"settings.js"} {"name":"90sclk.settings.js","url":"settings.js"}
] ],
"data": [{"name":"90sclk.setting.json"}]
} }

View File

@ -14,5 +14,6 @@
{"name":"activepedom.settings.js","url":"settings.js"}, {"name":"activepedom.settings.js","url":"settings.js"},
{"name":"activepedom.img","url":"app-icon.js","evaluate":true}, {"name":"activepedom.img","url":"app-icon.js","evaluate":true},
{"name":"activepedom.app.js","url":"app.js"} {"name":"activepedom.app.js","url":"app.js"}
] ],
"data":[{"name":"activepedom.settings.json"}]
} }

View File

@ -13,3 +13,4 @@
Added dynamic, short and range fields to clkinfo Added dynamic, short and range fields to clkinfo
0.12: Added color field and updating clkinfo periodically (running events) 0.12: Added color field and updating clkinfo periodically (running events)
0.13: Show day of the week in date 0.13: Show day of the week in date
0.14: Fixed "Today" and "Yesterday" wrongly displayed for allDay events on some time zones

View File

@ -38,13 +38,12 @@ function formatDay(date) {
if (!settings.useToday) { if (!settings.useToday) {
return formattedDate; return formattedDate;
} }
const dateformatted = date.toISOString().split('T')[0]; // yyyy-mm-dd const today = new Date(Date.now());
const today = new Date(Date.now()).toISOString().split('T')[0]; // yyyy-mm-dd if (date.getDay() == today.getDay() && date.getMonth() == today.getMonth())
if (dateformatted == today) {
return /*LANG*/"Today "; return /*LANG*/"Today ";
} else { else {
const tomorrow = new Date(Date.now() + 86400 * 1000).toISOString().split('T')[0]; // yyyy-mm-dd const tomorrow = new Date(Date.now() + 86400 * 1000);
if (dateformatted == tomorrow) { if (date.getDay() == tomorrow.getDay() && date.getMonth() == tomorrow.getMonth()) {
return /*LANG*/"Tomorrow "; return /*LANG*/"Tomorrow ";
} }
return formattedDate; return formattedDate;

View File

@ -1,7 +1,7 @@
{ {
"id": "agenda", "id": "agenda",
"name": "Agenda", "name": "Agenda",
"version": "0.13", "version": "0.14",
"description": "Simple agenda", "description": "Simple agenda",
"icon": "agenda.png", "icon": "agenda.png",
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}], "screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],

1
apps/alyxclock/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: first release

4
apps/alyxclock/README.md Normal file
View File

@ -0,0 +1,4 @@
# Half-Life Alyx Style clock
![](screenshot_alyxclock.png)

View File

@ -0,0 +1,174 @@
const icoH = [
[0,1,1,0,0,1,1,0],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[0,1,1,1,1,1,1,0],
[0,0,1,1,1,1,0,0],
[0,0,0,1,1,0,0,0],
[0,0,0,0,0,0,0,0],
]
const icoR = [
[0,0,0,0,1,1,1,1,0,0,0,0],
[0,0,1,1,0,0,0,0,1,1,0,0],
[0,1,1,1,1,0,0,1,1,0,1,0],
[0,1,1,0,0,0,0,0,0,0,1,0],
[1,1,1,1,1,1,1,1,0,0,0,1],
[1,1,0,0,1,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,0,1,1,0,1],
[1,1,1,1,1,1,0,0,0,0,1,1],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,0,1,1,1,1,1,1,1,1,0,0],
[0,0,0,0,1,1,1,1,0,0,0,0],
]
let idTimeout = null;
function icon (icon, x, y, size, gap) {
const color = g.getColor();
for (let r=0; r<icon.length; r++) {
for (let c=0; c<icon[r].length; c++) {
if (icon[r][c]===1){
g.setColor(color);
g.fillRect(c * size + x, r * size + y, (c+1) * size - gap + x, (r+1)*size - gap + y);
g.setColor('#fff');
g.drawLine(c * size + x + size/2 - 1, r * size + y + size/2 - 1, c * size + x + size/2 - 1, r * size + y + size/2 - 1, )
}
}
}
g.setColor(color);
}
function ohmA(x, y) {
g.setColor('#666');
g.fillRect(x, y, x+8, y+15);
g.setColor('#00f');
g.drawLine(x, y + 4, x + 8, y + 4);
g.setColor('#f00');
g.drawLine(x, y + 6, x + 8, y + 6);
g.setColor('#0f0');
g.drawLine(x, y + 8, x + 8, y + 8);
}
function ohmB(x, y) {
g.setColor('#666');
g.fillRect(x, y, x+15, y+8);
g.setColor('#00f');
g.drawLine(x + 4, y + 8, x + 4, y);
g.setColor('#f00');
g.drawLine(x + 6, y + 8, x + 6, y);
g.setColor('#0f0');
g.drawLine(x + 8, y + 8, x + 8, y);
}
function heart (x, y) {
g.setColor('#000');
g.fillRect(x-2, y-2, x + 32, y + 32)
g.setColor('#666');
g.drawRect(x-2, y-2, x + 32, y + 32)
g.setColor('#f00');
icon(icoH, x, y, 4, 2);
}
function resin() {
let d = Date();
let h = d.getHours();
let m = d.getMinutes();
const resinPosX = 25;
const resinPosY = 130;
g.setColor('#000');
g.fillRect(resinPosX - 3, resinPosY - 3, Bangle.appRect.w - resinPosX + 2, resinPosY + 40);
g.setColor('#666');
g.drawRect(resinPosX - 3, resinPosY - 3, Bangle.appRect.w - resinPosX + 2, resinPosY + 40);
g.setColor('#6ff');
icon(icoR, resinPosX, resinPosY, 3, 1);
g.setFont('6x8', 5);
g.setFontAlign(-1, -1);
g.drawString('_' + (m<10?'0':'')+m, resinPosX + 40, resinPosY - 5);
g.setFontAlign(1, -1);
g.setFont('6x8', 2);
g.drawString(h, resinPosX + 66, resinPosY);
}
function screw(x, y) {
g.setColor('#666').fillCircle(x, y, 4).setColor('#000').drawLine(x - 4, y, x + 4, y)
}
function led(x,y) {
g.setColor('#0f0').fillCircle(x, y, 8).setColor('#fff').fillCircle(x-3, y-3, 3);
}
function drawTime() {
const R = Bangle.appRect;
g.setBgColor('#000');
g.clear();
Bangle.drawWidgets();
g.reset();
// pcb
g.setColor('#030').fillRect(R.x, R.y, R.x2, R.y2);
screw(R.x + 8, R.y + 8)
screw(R.x2 - 8, R.y + 8)
screw(R.x + 8, R.y2 - 8)
screw(R.x2 - 8, R.y2 - 8)
for(let i=0; i<6; i++) {
g.setColor('#fff');
g.drawLine(24 + i * 9, 70, 24 + i * 9, 110);
g.drawLine(24 + i * 9, 110, 54 + i * 9, 140);
}
ohmA(29, 90);
ohmA(56, 90);
ohmB(80, 90);
screw(90, 110)
// led
led(50, R.y+10);
led(70, R.y+10);
ohmB(20, R.y + 10);
ohmB(90, R.y + 2);
ohmB(90, R.y + 14);
heart(10, 52);
heart(50, 52);
heart(90, 52);
g.setColor('#666');
for (let i=0; i<6; i++) {
g.fillCircle(110 + i*10, 80+10, 3);
g.fillCircle(110 + i*10, 110+10, 3);
}
g.setColor('#000');
g.fillRect(110, 80+10, 170, 110+10);
g.setColor('#666');
g.drawRect(110, 80+10, 170, 110+10);
g.setFont('6x8').setColor('#666').drawString('AH-118080\n0WT 18-001', 112, 85+10);
resin();
let d = Date();
let t = d.getSeconds()*1000 + d.getMilliseconds();
idTimeout = setTimeout(drawTime, 60000 - t); // time till next minute
}
// special function to handle display switch on
Bangle.on('lcdPower', function(on){
if (on) {
drawTime();
} else {
if(idTimeout) {
clearTimeout(idTimeout);
}
}
});
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
drawTime();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkAqoA/AHlUmYADC69FC601C600C60yC6x4SAANTPCgyFkowTSCgACkZ4TAAVDPCwvCPCaqEPCSnDPCZeDmc0Uyh4TqQXFPCBGEPCQWFPCClEPCSlDmjZDdiUlMYZ4NIwg0EPBpGEDoh4NIIYpCPCDqGMoUydh4oDPB5GGPCBGGG4h4CGQ6HDN4gIEqkjSY4+DBYreDSZINDHYpoDKYw9FTww5DC5BGJPAg8MQQw6DBpBGJWIsyCwtUkQABZhA7CkjYKfJIsGAB9UCqgA/ACQ"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

View File

@ -0,0 +1,17 @@
{
"id": "alyxclock",
"name": "Alyx Clock",
"version": "0.01",
"description": "A clock in the style of half-life alyx gravity gloves",
"icon": "alyxclock.png",
"screenshots": [{"url":"screenshot_alyxclock.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"alyxclock.app.js","url":"alyxclock.app.js"},
{"name":"alyxclock.img","url":"alyxclock.icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -26,3 +26,5 @@
0.25: Added option to 'ignore' an app from the message 0.25: Added option to 'ignore' an app from the message
0.26: Change handling of GPS status to depend on GPS events instead of connection events 0.26: Change handling of GPS status to depend on GPS events instead of connection events
0.27: Issue newline before GB commands (solves issue with console.log and ignored commands) 0.27: Issue newline before GB commands (solves issue with console.log and ignored commands)
0.28: Navigation messages no longer launch the Maps view unless they're new
0.29: Support for http request xpath return format

View File

@ -203,6 +203,8 @@
event.t="add"; event.t="add";
event.src="maps"; // for the icon event.src="maps"; // for the icon
event.title="Navigation"; event.title="Navigation";
if (require("messages").getMessages().find(m=>m.id=="nav"))
event.t = "modify";
} else { } else {
event.t="remove"; event.t="remove";
} }
@ -229,6 +231,7 @@
//send the request //send the request
var req = {t: "http", url:url, id:options.id}; var req = {t: "http", url:url, id:options.id};
if (options.xpath) req.xpath = options.xpath; if (options.xpath) req.xpath = options.xpath;
if (options.return) req.return = options.return; // for xpath
if (options.method) req.method = options.method; if (options.method) req.method = options.method;
if (options.body) req.body = options.body; if (options.body) req.body = options.body;
if (options.headers) req.headers = options.headers; if (options.headers) req.headers = options.headers;

View File

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

View File

@ -1,2 +1,5 @@
0.01: New App! 0.01: New App!
0.02: New config options such as step, meridian, short/long formats, custom prefix/suffix 0.02: New config options such as step, meridian, short/long formats, custom prefix/suffix
0.03: Allows showing the month in short or long format by setting `"shortMonth"` to true or false
0.04: Improves touchscreen drag handling for background apps such as Pattern Launcher
0.05: Fixes step count not resetting after a new day starts

View File

@ -18,7 +18,7 @@ Each box can be customized extensively via a simple JSON configuration. You can
## Config File Structure ## Config File Structure
Here's what an example configuration might look like: Here's an example of what a configuration might contain:
``` ```
{ {
@ -37,8 +37,9 @@ Here's what an example configuration might look like:
"boxPos": { "x": 0.5, "y": 0.5 }, "boxPos": { "x": 0.5, "y": 0.5 },
"prefix": "", // Adds a string to the beginning of the main string "prefix": "", // Adds a string to the beginning of the main string
"suffix": "", // Adds a string to the end of the main string "suffix": "", // Adds a string to the end of the main string
"disableSuffix": true, // Only used to remove the DayOfMonth suffix "disableSuffix": true, // Use to remove DayOfMonth suffix only
"short": false // Gets long format value of time, meridian, date, or DoW "short": false, // Use long format of time, meridian, date, or DoW
"shortMonth": false // Use long format of month within date
}, },
"bg": { // Can also be removed for no background "bg": { // Can also be removed for no background
@ -51,9 +52,9 @@ __Breakdown of Parameters:__
* **Box Name:** The name of your text box. Box Clock includes functional support for "time", "date", "meridian" (AM/PM), "dow" (Day of Week), "batt" (Battery), and "step" (Step count). You can add additional custom boxes with unique titles. * **Box Name:** The name of your text box. Box Clock includes functional support for "time", "date", "meridian" (AM/PM), "dow" (Day of Week), "batt" (Battery), and "step" (Step count). You can add additional custom boxes with unique titles.
* **string:** The text string to be displayed inside the box. * **string:** The text string to be displayed inside the box. This is only required for custom Box Names.
* **font:** The font name given to g.setFont() * **font:** The font name given to g.setFont().
* **fontSize:** The size of the font. * **fontSize:** The size of the font.
@ -75,17 +76,34 @@ __Breakdown of Parameters:__
* **suffix:** Adds a string to the end of the main string. For example, you can set "suffix": "%" to display "80%" for the battery percentage. * **suffix:** Adds a string to the end of the main string. For example, you can set "suffix": "%" to display "80%" for the battery percentage.
* **disableSuffix:** Applies only to the "date" box. Set to true to disable the DayOfMonth suffix. This is used to remove the "st","nd","rd", or "th" from the DayOfMonth number * **disableSuffix:** Applies only to the "date" box. Set to true to disable the DayOfMonth suffix. This is used to remove the "st","nd","rd", or "th" from the DayOfMonth number.
* **short:** Set to false to get the long format value of time, meridian, date, or DayOfWeek. Short formats are used by default, * **short:** Set to false to get the long format value of time, meridian, date, or DayOfWeek. Short formats are used by default if not specified.
* **shortMonth:** Applies only to the "date" box. Set to false to get the long format value of the month. Short format is used by default if not specified.
* **bg:** This specifies a custom background image, with the img property defining the name of the image file on the Bangle.js storage. * **bg:** This specifies a custom background image, with the img property defining the name of the image file on the Bangle.js storage.
## Multiple Configurations ## Multiple Configurations
The app includes a settings menu that allows you to switch between different configurations. The selected configuration is stored in the default JSON file alongside the other configuration data using the selectedConfig property. __Settings Menu:__
If the selectedConfig property is not present or is set to 0, the app will use the default configuration. To create additional configurations, create separate JSON files with the naming convention boxclk-N.json, where N is the configuration number. The settings menu will list all available configurations. The app includes a settings menu that allows you to switch between different configurations. The selected configuration is stored as a number in the default `boxclk.json` file using the selectedConfig property.
If the selectedConfig property is not present or is set to 0, the app will use the default configuration. To create additional configurations, create separate JSON files with the naming convention `boxclk-N.json`, where `N` is the configuration number. The settings menu will list all available configurations.
## Example Configs:
To easily try out other configs, download and place the JSON configs and/or background images from below onto your Bangle.js storage. Then go to the Box Clock settings menu to select the new config number. You can also modify them to suit your personal preferences.
__Space Theme:__
- **Config:** [boxclk-1.json](https://github.com/espruino/BangleApps/tree/master/apps/boxclk/boxclk-1.json)
- **Background:** [boxclk.space.img](https://github.com/espruino/BangleApps/tree/master/apps/boxclk/boxclk.space.img) ([Original Source](https://www.pixilart.com/art/fallin-from-outer-space-sr2e0c1a705749a))
__System Color Theme:__
- **Config:** [boxclk-2.json](https://github.com/espruino/BangleApps/tree/master/apps/boxclk/boxclk-2.json)
## Compatibility ## Compatibility

View File

@ -4,6 +4,7 @@
* 1. Module dependencies and initial configurations * 1. Module dependencies and initial configurations
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let storage = require("Storage"); let storage = require("Storage");
let locale = require("locale"); let locale = require("locale");
let widgets = require("widget_utils"); let widgets = require("widget_utils");
@ -30,6 +31,7 @@
* 2. Graphical and visual configurations * 2. Graphical and visual configurations
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let w = g.getWidth(); let w = g.getWidth();
let h = g.getHeight(); let h = g.getHeight();
let totalWidth, totalHeight; let totalWidth, totalHeight;
@ -40,6 +42,7 @@
* 3. Touchscreen Handlers * 3. Touchscreen Handlers
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let touchHandler; let touchHandler;
let dragHandler; let dragHandler;
let movementDistance = 0; let movementDistance = 0;
@ -49,6 +52,7 @@
* 4. Font loading function * 4. Font loading function
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let loadCustomFont = function() { let loadCustomFont = function() {
Graphics.prototype.setFontBrunoAce = function() { Graphics.prototype.setFontBrunoAce = function() {
// Actual height 23 (24 - 2) // Actual height 23 (24 - 2)
@ -66,6 +70,7 @@
* 5. Initial settings of boxes and their positions * 5. Initial settings of boxes and their positions
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
for (let key in boxesConfig) { for (let key in boxesConfig) {
if (key === 'bg' && boxesConfig[key].img) { if (key === 'bg' && boxesConfig[key].img) {
bgImage = storage.read(boxesConfig[key].img); bgImage = storage.read(boxesConfig[key].img);
@ -167,14 +172,15 @@
* 7. String forming helper functions * 7. String forming helper functions
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let isBool = function(val, defaultVal) { let isBool = function(val, defaultVal) {
return typeof val !== 'undefined' ? Boolean(val) : defaultVal; return typeof val !== 'undefined' ? Boolean(val) : defaultVal;
}; };
let getDate = function(short, disableSuffix) { let getDate = function(short, shortMonth, disableSuffix) {
const date = new Date(); const date = new Date();
const dayOfMonth = date.getDate(); const dayOfMonth = date.getDate();
const month = short ? locale.month(date, 0) : locale.month(date, 1); const month = shortMonth ? locale.month(date, 1) : locale.month(date, 0);
const year = date.getFullYear(); const year = date.getFullYear();
let suffix; let suffix;
if ([1, 21, 31].includes(dayOfMonth)) { if ([1, 21, 31].includes(dayOfMonth)) {
@ -211,6 +217,7 @@
* 8. Main draw function * 8. Main draw function
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let draw = (function() { let draw = (function() {
let updatePerMinute = true; // variable to track the state of time display let updatePerMinute = true; // variable to track the state of time display
@ -228,7 +235,12 @@
boxes.meridian.string = modString(boxes.meridian, locale.meridian(date, isBool(boxes.meridian.short, true))); boxes.meridian.string = modString(boxes.meridian, locale.meridian(date, isBool(boxes.meridian.short, true)));
} }
if (boxes.date) { if (boxes.date) {
boxes.date.string = modString(boxes.date, getDate(isBool(boxes.date.short, true), isBool(boxes.date.disableSuffix, false))); boxes.date.string = (
modString(boxes.date,
getDate(isBool(boxes.date.short, true),
isBool(boxes.date.shortMonth, true),
isBool(boxes.date.disableSuffix, false)
)));
} }
if (boxes.dow) { if (boxes.dow) {
boxes.dow.string = modString(boxes.dow, getDayOfWeek(date, isBool(boxes.dow.short, true))); boxes.dow.string = modString(boxes.dow, getDayOfWeek(date, isBool(boxes.dow.short, true)));
@ -237,7 +249,7 @@
boxes.batt.string = modString(boxes.batt, E.getBattery()); boxes.batt.string = modString(boxes.batt, E.getBattery());
} }
if (boxes.step) { if (boxes.step) {
boxes.step.string = modString(boxes.step, Bangle.getStepCount()); boxes.step.string = modString(boxes.step, Bangle.getHealthStatus("day").steps);
} }
boxKeys.forEach((boxKey) => { boxKeys.forEach((boxKey) => {
let boxItem = boxes[boxKey]; let boxItem = boxes[boxKey];
@ -267,6 +279,7 @@
* 9. Helper function for touch event * 9. Helper function for touch event
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let touchInText = function(e, boxItem, boxKey) { let touchInText = function(e, boxItem, boxKey) {
calcBoxSize(boxItem); calcBoxSize(boxItem);
const pos = calcBoxPos(boxKey); const pos = calcBoxPos(boxKey);
@ -291,6 +304,7 @@
* 10. Setup function to configure event handlers * 10. Setup function to configure event handlers
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
let setup = function() { let setup = function() {
// ------------------------------------ // ------------------------------------
// Define the touchHandler function // Define the touchHandler function
@ -338,6 +352,8 @@
// Define the dragHandler function // Define the dragHandler function
// ------------------------------------ // ------------------------------------
dragHandler = function(e) { dragHandler = function(e) {
// Check if any box is being dragged
if (!Object.values(isDragging).some(Boolean)) return;
// Calculate the movement distance // Calculate the movement distance
movementDistance += Math.abs(e.dx) + Math.abs(e.dy); movementDistance += Math.abs(e.dx) + Math.abs(e.dy);
// Check if the movement distance exceeds a threshold // Check if the movement distance exceeds a threshold
@ -391,8 +407,9 @@
* 11. Main execution part * 11. Main execution part
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
Bangle.loadWidgets(); Bangle.loadWidgets();
widgets.swipeOn(); widgets.swipeOn();
modSetColor(); modSetColor();
setup(); setup();
} }

88
apps/boxclk/boxclk-1.json Normal file
View File

@ -0,0 +1,88 @@
{
"time": {
"font": "6x8",
"fontSize": 3,
"outline": 2,
"color": "#0ff",
"outlineColor": "#00f",
"border": "#0f0",
"xPadding": -1,
"yPadding": -2.5,
"xOffset": 2,
"yOffset": 0,
"boxPos": {
"x": "0.33",
"y": "0.29"
}
},
"meridian": {
"font": "6x8",
"fontSize": 2,
"outline": 1,
"color": "#FF9900",
"outlineColor": "fg",
"border": "#0ff",
"xPadding": -0.5,
"yPadding": -1.5,
"xOffset": 2,
"yOffset": 1,
"boxPos": {
"x": "0.34",
"y": "0.46"
},
"short": false
},
"dow": {
"font": "6x8",
"fontSize": 2,
"outline": 1,
"color": "#000",
"outlineColor": "#fff",
"border": "#0f0",
"xPadding": -0.5,
"yPadding": -0.5,
"xOffset": 1,
"yOffset": 1,
"boxPos": {
"x": "0.5",
"y": "0.82"
}
},
"step": {
"font": "6x8",
"fontSize": 2,
"outline": 1,
"color": "#000",
"outlineColor": "#fff",
"border": "#0f0",
"xPadding": -0.5,
"yPadding": 0.5,
"xOffset": 1,
"yOffset": 1,
"boxPos": {
"x": "0.5",
"y": "0.71"
},
"prefix": "Steps: "
},
"batt": {
"font": "4x6",
"fontSize": 2,
"outline": 1,
"color": "#0ff",
"outlineColor": "#00f",
"border": "#0f0",
"xPadding": -0.5,
"yPadding": -0.5,
"xOffset": 1,
"yOffset": 1,
"boxPos": {
"x": "0.87",
"y": "0.87"
},
"suffix": "%"
},
"bg": {
"img": "boxclk.space.img"
}
}

87
apps/boxclk/boxclk-2.json Normal file
View File

@ -0,0 +1,87 @@
{
"time": {
"font": "6x8",
"fontSize": 5,
"outline": 3,
"color": "bgH",
"outlineColor": "fg",
"border": "#f0f",
"xPadding": -2,
"yPadding": -4.5,
"xOffset": 3,
"yOffset": 0,
"boxPos": {
"x": "0.5",
"y": "0.33"
}
},
"dow": {
"font": "6x8",
"fontSize": 3,
"outline": 1,
"color": "#5ccd73",
"outlineColor": "fg",
"border": "#f0f",
"xPadding": -1,
"yPadding": 0.5,
"xOffset": 2,
"yOffset": 0,
"boxPos": {
"x": "0.5",
"y": "0.57"
},
"short": false
},
"date": {
"font": "6x8",
"fontSize": 2,
"outline": 1,
"color": "#5ccd73",
"outlineColor": "fg",
"border": "#f0f",
"xPadding": -0.5,
"yPadding": 0.5,
"xOffset": 1,
"yOffset": 0,
"boxPos": {
"x": "0.5",
"y": "0.75"
},
"shortMonth": false,
"disableSuffix": true
},
"step": {
"font": "4x6",
"fontSize": 3,
"outline": 2,
"color": "bgH",
"outlineColor": "fg",
"border": "#f0f",
"xPadding": -1,
"yPadding": 0.5,
"xOffset": 2,
"yOffset": 1,
"boxPos": {
"x": "0.5",
"y": "0.92"
},
"prefix": "Steps: "
},
"batt": {
"font": "4x6",
"fontSize": 3,
"outline": 2,
"color": "bgH",
"outlineColor": "fg",
"border": "#f0f",
"xPadding": -1,
"yPadding": -1,
"xOffset": 2,
"yOffset": 2,
"boxPos": {
"x": "0.85",
"y": "0.08"
},
"suffix": "%"
}
}

Binary file not shown.

View File

@ -1,12 +1,13 @@
{ {
"id": "boxclk", "id": "boxclk",
"name": "Box Clock", "name": "Box Clock",
"version": "0.02", "version": "0.05",
"description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background", "description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background",
"icon": "app.png", "icon": "app.png",
"screenshots": [ "screenshots": [
{"url":"screenshot.png"}, {"url":"screenshot.png"},
{"url":"screenshot-1.png"} {"url":"screenshot-1.png"},
{"url":"screenshot-2.png"}
], ],
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,411 +1,413 @@
var __assign = Object.assign;
var Layout = require("Layout");
Bangle.loadWidgets();
Bangle.drawWidgets();
var HRM_MIN_CONFIDENCE = 75;
var services = ["0x180d", "0x181a", "0x1819"];
var acc;
var bar;
var gps;
var hrm;
var hrmAny;
var mag;
var btnsShown = false;
var prevBtnsShown = undefined;
var hrmAnyClear;
var settings = {
bar: false,
gps: false,
hrm: false,
mag: false,
};
var idToName = {
acc: "Acceleration",
bar: "Barometer",
gps: "GPS",
hrm: "HRM",
mag: "Magnetometer",
};
var infoFont = "6x8:2";
var colour = {
on: "#0f0",
off: "#fff",
};
var makeToggle = function (id) { return function () {
settings[id] = !settings[id];
var entry = btnLayout[id];
var col = settings[id] ? colour.on : colour.off;
entry.btnBorder = entry.col = col;
btnLayout.update();
btnLayout.render();
enableSensors();
}; };
var btnStyle = {
font: "Vector:14",
fillx: 1,
filly: 1,
col: g.theme.fg,
bgCol: g.theme.bg,
btnBorder: "#fff",
};
var btnLayout = new Layout({
type: "v",
c: [
{
type: "h",
c: [
__assign({ type: "btn", label: idToName.bar, id: "bar", cb: makeToggle('bar') }, btnStyle),
__assign({ type: "btn", label: idToName.gps, id: "gps", cb: makeToggle('gps') }, btnStyle),
]
},
{
type: "h",
c: [
__assign({ type: "btn", label: idToName.hrm, id: "hrm", cb: makeToggle('hrm') }, btnStyle),
__assign({ type: "btn", label: idToName.mag, id: "mag", cb: makeToggle('mag') }, btnStyle),
]
},
{
type: "h",
c: [
__assign(__assign({ type: "btn", label: idToName.acc, id: "acc", cb: function () { } }, btnStyle), { col: colour.on, btnBorder: colour.on }),
__assign({ type: "btn", label: "Back", cb: function () {
setBtnsShown(false);
} }, btnStyle),
]
}
]
}, {
lazy: true,
back: function () {
setBtnsShown(false);
},
});
var setBtnsShown = function (b) {
btnsShown = b;
hook(!btnsShown);
setIntervals();
redraw();
};
var drawInfo = function (force) {
var _a = Bangle.appRect, y = _a.y, x = _a.x, w = _a.w;
var mid = x + w / 2;
var drawn = false;
if (!force && !bar && !gps && !hrm && !mag)
return;
g.reset()
.clearRect(Bangle.appRect)
.setFont(infoFont)
.setFontAlign(0, -1);
if (bar) {
g.drawString("".concat(bar.altitude.toFixed(1), "m"), mid, y);
y += g.getFontHeight();
g.drawString("".concat(bar.pressure.toFixed(1), " hPa"), mid, y);
y += g.getFontHeight();
g.drawString("".concat(bar.temperature.toFixed(1), "C"), mid, y);
y += g.getFontHeight();
drawn = true;
}
if (gps) {
g.drawString("".concat(gps.lat.toFixed(4), " lat, ").concat(gps.lon.toFixed(4), " lon"), mid, y);
y += g.getFontHeight();
g.drawString("".concat(gps.alt, "m (").concat(gps.satellites, " sat)"), mid, y);
y += g.getFontHeight();
drawn = true;
}
if (hrm) {
g.drawString("".concat(hrm.bpm, " BPM (").concat(hrm.confidence, "%)"), mid, y);
y += g.getFontHeight();
drawn = true;
}
else if (hrmAny) {
g.drawString("~".concat(hrmAny.bpm, " BPM (").concat(hrmAny.confidence, "%)"), mid, y);
y += g.getFontHeight();
drawn = true;
if (!settings.hrm && !hrmAnyClear) {
hrmAnyClear = setTimeout(function () {
hrmAny = undefined;
hrmAnyClear = undefined;
}, 10000);
}
}
if (mag) {
g.drawString("".concat(mag.x, " ").concat(mag.y, " ").concat(mag.z), mid, y);
y += g.getFontHeight();
g.drawString("heading: ".concat(mag.heading.toFixed(1)), mid, y);
y += g.getFontHeight();
drawn = true;
}
if (!drawn) {
if (!force || Object.values(settings).every(function (x) { return !x; })) {
g.drawString("swipe to enable", mid, y);
}
else {
g.drawString("events pending", mid, y);
}
y += g.getFontHeight();
}
};
var onTap = function () {
setBtnsShown(true);
};
var redraw = function () {
if (btnsShown) {
if (!prevBtnsShown) {
prevBtnsShown = btnsShown;
Bangle.removeListener("swipe", onTap);
btnLayout.setUI();
btnLayout.forgetLazyState();
g.clearRect(Bangle.appRect);
}
btnLayout.render();
}
else {
if (prevBtnsShown) {
prevBtnsShown = btnsShown;
Bangle.setUI();
Bangle.on("swipe", onTap);
drawInfo(true);
}
else {
drawInfo();
}
}
};
var encodeHrm = function (hrm) {
return [0, hrm.bpm];
};
encodeHrm.maxLen = 2;
var encodePressure = function (data) {
return toByteArray(Math.round(data.pressure * 10), 4, false);
};
encodePressure.maxLen = 4;
var encodeElevation = function (data) {
return toByteArray(Math.round(data.altitude * 100), 3, true);
};
encodeElevation.maxLen = 3;
var encodeTemp = function (data) {
return toByteArray(Math.round(data.temperature * 10), 2, true);
};
encodeTemp.maxLen = 2;
var encodeGps = function (data) {
var speed = toByteArray(Math.round(1000 * data.speed / 36), 2, false);
var lat = toByteArray(Math.round(data.lat * 10000000), 4, true);
var lon = toByteArray(Math.round(data.lon * 10000000), 4, true);
var elevation = toByteArray(Math.round(data.alt * 100), 3, true);
var heading = toByteArray(Math.round(data.course * 100), 2, false);
return [
157,
2,
speed[0], speed[1],
lat[0], lat[1], lat[2], lat[3],
lon[0], lon[1], lon[2], lon[3],
elevation[0], elevation[1], elevation[2],
heading[0], heading[1]
];
};
encodeGps.maxLen = 17;
var encodeGpsHeadingOnly = function (data) {
var heading = toByteArray(Math.round(data.heading * 100), 2, false);
return [
16,
16,
heading[0], heading[1]
];
};
encodeGpsHeadingOnly.maxLen = 17;
var encodeMag = function (data) {
var x = toByteArray(data.x, 2, true);
var y = toByteArray(data.y, 2, true);
var z = toByteArray(data.z, 2, true);
return [x[0], x[1], y[0], y[1], z[0], z[1]];
};
encodeMag.maxLen = 6;
var toByteArray = function (value, numberOfBytes, isSigned) {
var byteArray = new Array(numberOfBytes);
if (isSigned && (value < 0)) {
value += 1 << (numberOfBytes * 8);
}
for (var index = 0; index < numberOfBytes; index++) {
byteArray[index] = (value >> (index * 8)) & 0xff;
}
return byteArray;
};
var enableSensors = function () {
Bangle.setBarometerPower(settings.bar, "btadv");
if (!settings.bar)
bar = undefined;
Bangle.setGPSPower(settings.gps, "btadv");
if (!settings.gps)
gps = undefined;
Bangle.setHRMPower(settings.hrm, "btadv");
if (!settings.hrm)
hrm = hrmAny = undefined;
Bangle.setCompassPower(settings.mag, "btadv");
if (!settings.mag)
mag = undefined;
};
var haveServiceData = function (serv) {
switch (serv) {
case "0x180d": return !!hrm;
case "0x181a": return !!(bar || mag);
case "0x1819": return !!(gps && gps.lat && gps.lon || mag);
}
};
var serviceToAdvert = function (serv, initial) {
var _a, _b, _c;
if (initial === void 0) { initial = false; }
switch (serv) {
case "0x180d":
if (hrm || initial) {
var o = {
maxLen: encodeHrm.maxLen,
readable: true,
notify: true,
};
if (hrm) {
o.value = encodeHrm(hrm);
hrm = undefined;
}
return _a = {}, _a["0x2a37"] = o, _a;
}
return {};
case "0x1819":
if (gps || initial) {
var o = {
maxLen: encodeGps.maxLen,
readable: true,
notify: true,
};
if (gps) {
o.value = encodeGps(gps);
gps = undefined;
}
return _b = {}, _b["0x2a67"] = o, _b;
}
else if (mag) {
var o = {
maxLen: encodeGpsHeadingOnly.maxLen,
readable: true,
notify: true,
value: encodeGpsHeadingOnly(mag),
};
return _c = {}, _c["0x2a67"] = o, _c;
}
return {};
case "0x181a": {
var o = {};
if (bar || initial) {
o["0x2a6c"] = {
maxLen: encodeElevation.maxLen,
readable: true,
notify: true,
};
o["0x2A1F"] = {
maxLen: encodeTemp.maxLen,
readable: true,
notify: true,
};
o["0x2a6d"] = {
maxLen: encodePressure.maxLen,
readable: true,
notify: true,
};
if (bar) {
o["0x2a6c"].value = encodeElevation(bar);
o["0x2A1F"].value = encodeTemp(bar);
o["0x2a6d"].value = encodePressure(bar);
bar = undefined;
}
}
if (mag || initial) {
o["0x2aa1"] = {
maxLen: encodeMag.maxLen,
readable: true,
notify: true,
};
if (mag) {
o["0x2aa1"].value = encodeMag(mag);
}
}
return o;
}
}
};
var getBleAdvert = function (map, all) {
if (all === void 0) { all = false; }
var advert = {};
for (var _i = 0, services_1 = services; _i < services_1.length; _i++) {
var serv = services_1[_i];
if (all || haveServiceData(serv)) {
advert[serv] = map(serv);
}
}
mag = undefined;
return advert;
};
var updateServices = function () {
var newAdvert = getBleAdvert(serviceToAdvert);
NRF.updateServices(newAdvert);
};
var onAccel = function (newAcc) { return acc = newAcc; };
var onPressure = function (newBar) { return bar = newBar; };
var onGPS = function (newGps) { return gps = newGps; };
var onHRM = function (newHrm) {
if (newHrm.confidence >= HRM_MIN_CONFIDENCE)
hrm = newHrm;
hrmAny = newHrm;
};
var onMag = function (newMag) { return mag = newMag; };
var hook = function (enable) {
if (enable) {
Bangle.on("accel", onAccel);
Bangle.on("pressure", onPressure);
Bangle.on("GPS", onGPS);
Bangle.on("HRM", onHRM);
Bangle.on("mag", onMag);
}
else {
Bangle.removeListener("accel", onAccel);
Bangle.removeListener("pressure", onPressure);
Bangle.removeListener("GPS", onGPS);
Bangle.removeListener("HRM", onHRM);
Bangle.removeListener("mag", onMag);
}
};
var setIntervals = function (locked, connected) {
if (locked === void 0) { locked = Bangle.isLocked(); }
if (connected === void 0) { connected = NRF.getSecurityStatus().connected; }
changeInterval(redrawInterval, locked ? 15000 : 5000);
if (connected) {
var interval = btnsShown ? 5000 : 1000;
if (bleInterval) {
changeInterval(bleInterval, interval);
}
else {
bleInterval = setInterval(updateServices, interval);
}
}
else if (bleInterval) {
clearInterval(bleInterval);
bleInterval = undefined;
}
};
var redrawInterval = setInterval(redraw, 1000);
Bangle.on("lock", function (locked) { return setIntervals(locked); });
var bleInterval;
NRF.on("connect", function () { return setIntervals(undefined, true); });
NRF.on("disconnect", function () { return setIntervals(undefined, false); });
setIntervals();
setBtnsShown(true);
enableSensors();
{ {
var ad = getBleAdvert(function (serv) { return serviceToAdvert(serv, true); }, true); var __assign = Object.assign;
var adServices = Object var Layout_1 = require("Layout");
.keys(ad) Bangle.loadWidgets();
.map(function (k) { return k.replace("0x", ""); }); Bangle.drawWidgets();
NRF.setServices(ad, { var HRM_MIN_CONFIDENCE_1 = 75;
advertise: adServices, var services_1 = ["0x180d", "0x181a", "0x1819"];
uart: false, var acc_1;
var bar_1;
var gps_1;
var hrm_1;
var hrmAny_1;
var mag_1;
var btnsShown_1 = false;
var prevBtnsShown_1 = undefined;
var hrmAnyClear_1;
var settings_1 = {
bar: false,
gps: false,
hrm: false,
mag: false,
};
var idToName = {
acc: "Acceleration",
bar: "Barometer",
gps: "GPS",
hrm: "HRM",
mag: "Magnetometer",
};
var infoFont_1 = "6x8:2";
var colour_1 = {
on: "#0f0",
off: "#fff",
};
var makeToggle = function (id) { return function () {
settings_1[id] = !settings_1[id];
var entry = btnLayout_1[id];
var col = settings_1[id] ? colour_1.on : colour_1.off;
entry.btnBorder = entry.col = col;
btnLayout_1.update();
btnLayout_1.render();
enableSensors_1();
}; };
var btnStyle = {
font: "Vector:14",
fillx: 1,
filly: 1,
col: g.theme.fg,
bgCol: g.theme.bg,
btnBorder: "#fff",
};
var btnLayout_1 = new Layout_1({
type: "v",
c: [
{
type: "h",
c: [
__assign({ type: "btn", label: idToName.bar, id: "bar", cb: makeToggle('bar') }, btnStyle),
__assign({ type: "btn", label: idToName.gps, id: "gps", cb: makeToggle('gps') }, btnStyle),
]
},
{
type: "h",
c: [
__assign({ type: "btn", label: idToName.hrm, id: "hrm", cb: makeToggle('hrm') }, btnStyle),
__assign({ type: "btn", label: idToName.mag, id: "mag", cb: makeToggle('mag') }, btnStyle),
]
},
{
type: "h",
c: [
__assign(__assign({ type: "btn", label: idToName.acc, id: "acc", cb: function () { } }, btnStyle), { col: colour_1.on, btnBorder: colour_1.on }),
__assign({ type: "btn", label: "Back", cb: function () {
setBtnsShown_1(false);
} }, btnStyle),
]
}
]
}, {
lazy: true,
back: function () {
setBtnsShown_1(false);
},
}); });
var setBtnsShown_1 = function (b) {
btnsShown_1 = b;
hook_1(!btnsShown_1);
setIntervals_1();
redraw_1();
};
var drawInfo_1 = function (force) {
var _a = Bangle.appRect, y = _a.y, x = _a.x, w = _a.w;
var mid = x + w / 2;
var drawn = false;
if (!force && !bar_1 && !gps_1 && !hrm_1 && !mag_1)
return;
g.reset()
.clearRect(Bangle.appRect)
.setFont(infoFont_1)
.setFontAlign(0, -1);
if (bar_1) {
g.drawString("".concat(bar_1.altitude.toFixed(1), "m"), mid, y);
y += g.getFontHeight();
g.drawString("".concat(bar_1.pressure.toFixed(1), " hPa"), mid, y);
y += g.getFontHeight();
g.drawString("".concat(bar_1.temperature.toFixed(1), "C"), mid, y);
y += g.getFontHeight();
drawn = true;
}
if (gps_1) {
g.drawString("".concat(gps_1.lat.toFixed(4), " lat, ").concat(gps_1.lon.toFixed(4), " lon"), mid, y);
y += g.getFontHeight();
g.drawString("".concat(gps_1.alt, "m (").concat(gps_1.satellites, " sat)"), mid, y);
y += g.getFontHeight();
drawn = true;
}
if (hrm_1) {
g.drawString("".concat(hrm_1.bpm, " BPM (").concat(hrm_1.confidence, "%)"), mid, y);
y += g.getFontHeight();
drawn = true;
}
else if (hrmAny_1) {
g.drawString("~".concat(hrmAny_1.bpm, " BPM (").concat(hrmAny_1.confidence, "%)"), mid, y);
y += g.getFontHeight();
drawn = true;
if (!settings_1.hrm && !hrmAnyClear_1) {
hrmAnyClear_1 = setTimeout(function () {
hrmAny_1 = undefined;
hrmAnyClear_1 = undefined;
}, 10000);
}
}
if (mag_1) {
g.drawString("".concat(mag_1.x, " ").concat(mag_1.y, " ").concat(mag_1.z), mid, y);
y += g.getFontHeight();
g.drawString("heading: ".concat(mag_1.heading.toFixed(1)), mid, y);
y += g.getFontHeight();
drawn = true;
}
if (!drawn) {
if (!force || Object.values(settings_1).every(function (x) { return !x; })) {
g.drawString("swipe to enable", mid, y);
}
else {
g.drawString("events pending", mid, y);
}
y += g.getFontHeight();
}
};
var onTap_1 = function () {
setBtnsShown_1(true);
};
var redraw_1 = function () {
if (btnsShown_1) {
if (!prevBtnsShown_1) {
prevBtnsShown_1 = btnsShown_1;
Bangle.removeListener("swipe", onTap_1);
btnLayout_1.setUI();
btnLayout_1.forgetLazyState();
g.clearRect(Bangle.appRect);
}
btnLayout_1.render();
}
else {
if (prevBtnsShown_1) {
prevBtnsShown_1 = btnsShown_1;
Bangle.setUI();
Bangle.on("swipe", onTap_1);
drawInfo_1(true);
}
else {
drawInfo_1();
}
}
};
var encodeHrm_1 = function (hrm) {
return [0, hrm.bpm];
};
encodeHrm_1.maxLen = 2;
var encodePressure_1 = function (data) {
return toByteArray_1(Math.round(data.pressure * 10), 4, false);
};
encodePressure_1.maxLen = 4;
var encodeElevation_1 = function (data) {
return toByteArray_1(Math.round(data.altitude * 100), 3, true);
};
encodeElevation_1.maxLen = 3;
var encodeTemp_1 = function (data) {
return toByteArray_1(Math.round(data.temperature * 10), 2, true);
};
encodeTemp_1.maxLen = 2;
var encodeGps_1 = function (data) {
var speed = toByteArray_1(Math.round(1000 * data.speed / 36), 2, false);
var lat = toByteArray_1(Math.round(data.lat * 10000000), 4, true);
var lon = toByteArray_1(Math.round(data.lon * 10000000), 4, true);
var elevation = toByteArray_1(Math.round(data.alt * 100), 3, true);
var heading = toByteArray_1(Math.round(data.course * 100), 2, false);
return [
157,
2,
speed[0], speed[1],
lat[0], lat[1], lat[2], lat[3],
lon[0], lon[1], lon[2], lon[3],
elevation[0], elevation[1], elevation[2],
heading[0], heading[1]
];
};
encodeGps_1.maxLen = 17;
var encodeGpsHeadingOnly_1 = function (data) {
var heading = toByteArray_1(Math.round(data.heading * 100), 2, false);
return [
16,
16,
heading[0], heading[1]
];
};
encodeGpsHeadingOnly_1.maxLen = 17;
var encodeMag_1 = function (data) {
var x = toByteArray_1(data.x, 2, true);
var y = toByteArray_1(data.y, 2, true);
var z = toByteArray_1(data.z, 2, true);
return [x[0], x[1], y[0], y[1], z[0], z[1]];
};
encodeMag_1.maxLen = 6;
var toByteArray_1 = function (value, numberOfBytes, isSigned) {
var byteArray = new Array(numberOfBytes);
if (isSigned && (value < 0)) {
value += 1 << (numberOfBytes * 8);
}
for (var index = 0; index < numberOfBytes; index++) {
byteArray[index] = (value >> (index * 8)) & 0xff;
}
return byteArray;
};
var enableSensors_1 = function () {
Bangle.setBarometerPower(settings_1.bar, "btadv");
if (!settings_1.bar)
bar_1 = undefined;
Bangle.setGPSPower(settings_1.gps, "btadv");
if (!settings_1.gps)
gps_1 = undefined;
Bangle.setHRMPower(settings_1.hrm, "btadv");
if (!settings_1.hrm)
hrm_1 = hrmAny_1 = undefined;
Bangle.setCompassPower(settings_1.mag, "btadv");
if (!settings_1.mag)
mag_1 = undefined;
};
var haveServiceData_1 = function (serv) {
switch (serv) {
case "0x180d": return !!hrm_1;
case "0x181a": return !!(bar_1 || mag_1);
case "0x1819": return !!(gps_1 && gps_1.lat && gps_1.lon || mag_1);
}
};
var serviceToAdvert_1 = function (serv, initial) {
var _a, _b, _c;
if (initial === void 0) { initial = false; }
switch (serv) {
case "0x180d":
if (hrm_1 || initial) {
var o = {
maxLen: encodeHrm_1.maxLen,
readable: true,
notify: true,
};
if (hrm_1) {
o.value = encodeHrm_1(hrm_1);
hrm_1 = undefined;
}
return _a = {}, _a["0x2a37"] = o, _a;
}
return {};
case "0x1819":
if (gps_1 || initial) {
var o = {
maxLen: encodeGps_1.maxLen,
readable: true,
notify: true,
};
if (gps_1) {
o.value = encodeGps_1(gps_1);
gps_1 = undefined;
}
return _b = {}, _b["0x2a67"] = o, _b;
}
else if (mag_1) {
var o = {
maxLen: encodeGpsHeadingOnly_1.maxLen,
readable: true,
notify: true,
value: encodeGpsHeadingOnly_1(mag_1),
};
return _c = {}, _c["0x2a67"] = o, _c;
}
return {};
case "0x181a": {
var o = {};
if (bar_1 || initial) {
o["0x2a6c"] = {
maxLen: encodeElevation_1.maxLen,
readable: true,
notify: true,
};
o["0x2A1F"] = {
maxLen: encodeTemp_1.maxLen,
readable: true,
notify: true,
};
o["0x2a6d"] = {
maxLen: encodePressure_1.maxLen,
readable: true,
notify: true,
};
if (bar_1) {
o["0x2a6c"].value = encodeElevation_1(bar_1);
o["0x2A1F"].value = encodeTemp_1(bar_1);
o["0x2a6d"].value = encodePressure_1(bar_1);
bar_1 = undefined;
}
}
if (mag_1 || initial) {
o["0x2aa1"] = {
maxLen: encodeMag_1.maxLen,
readable: true,
notify: true,
};
if (mag_1) {
o["0x2aa1"].value = encodeMag_1(mag_1);
}
}
return o;
}
}
};
var getBleAdvert_1 = function (map, all) {
if (all === void 0) { all = false; }
var advert = {};
for (var _i = 0, services_2 = services_1; _i < services_2.length; _i++) {
var serv = services_2[_i];
if (all || haveServiceData_1(serv)) {
advert[serv] = map(serv);
}
}
mag_1 = undefined;
return advert;
};
var updateServices_1 = function () {
var newAdvert = getBleAdvert_1(serviceToAdvert_1);
NRF.updateServices(newAdvert);
};
var onAccel_1 = function (newAcc) { return acc_1 = newAcc; };
var onPressure_1 = function (newBar) { return bar_1 = newBar; };
var onGPS_1 = function (newGps) { return gps_1 = newGps; };
var onHRM_1 = function (newHrm) {
if (newHrm.confidence >= HRM_MIN_CONFIDENCE_1)
hrm_1 = newHrm;
hrmAny_1 = newHrm;
};
var onMag_1 = function (newMag) { return mag_1 = newMag; };
var hook_1 = function (enable) {
if (enable) {
Bangle.on("accel", onAccel_1);
Bangle.on("pressure", onPressure_1);
Bangle.on("GPS", onGPS_1);
Bangle.on("HRM", onHRM_1);
Bangle.on("mag", onMag_1);
}
else {
Bangle.removeListener("accel", onAccel_1);
Bangle.removeListener("pressure", onPressure_1);
Bangle.removeListener("GPS", onGPS_1);
Bangle.removeListener("HRM", onHRM_1);
Bangle.removeListener("mag", onMag_1);
}
};
var setIntervals_1 = function (locked, connected) {
if (locked === void 0) { locked = Bangle.isLocked(); }
if (connected === void 0) { connected = NRF.getSecurityStatus().connected; }
changeInterval(redrawInterval_1, locked ? 15000 : 5000);
if (connected) {
var interval = btnsShown_1 ? 5000 : 1000;
if (bleInterval_1) {
changeInterval(bleInterval_1, interval);
}
else {
bleInterval_1 = setInterval(updateServices_1, interval);
}
}
else if (bleInterval_1) {
clearInterval(bleInterval_1);
bleInterval_1 = undefined;
}
};
var redrawInterval_1 = setInterval(redraw_1, 1000);
Bangle.on("lock", function (locked) { return setIntervals_1(locked); });
var bleInterval_1;
NRF.on("connect", function () { return setIntervals_1(undefined, true); });
NRF.on("disconnect", function () { return setIntervals_1(undefined, false); });
setIntervals_1();
setBtnsShown_1(true);
enableSensors_1();
{
var ad = getBleAdvert_1(function (serv) { return serviceToAdvert_1(serv, true); }, true);
var adServices = Object
.keys(ad)
.map(function (k) { return k.replace("0x", ""); });
NRF.setServices(ad, {
advertise: adServices,
uart: false,
});
}
} }

View File

@ -1,4 +1,5 @@
// ts helpers: {
// @ts-ignore helper
const __assign = Object.assign; const __assign = Object.assign;
const Layout = require("Layout"); const Layout = require("Layout");
@ -713,3 +714,4 @@ enableSensors();
}, },
); );
} }
}

View File

@ -18,5 +18,6 @@
{"name":"bthrm.settings.js","url":"settings.js"}, {"name":"bthrm.settings.js","url":"settings.js"},
{"name":"bthrm","url":"lib.js"}, {"name":"bthrm","url":"lib.js"},
{"name":"bthrm.default.json","url":"default.json"} {"name":"bthrm.default.json","url":"default.json"}
] ],
"data": [{"name":"bthrm.json"}]
} }

View File

@ -15,5 +15,6 @@
{"name":"bwclk.app.js","url":"app.js"}, {"name":"bwclk.app.js","url":"app.js"},
{"name":"bwclk.img","url":"app-icon.js","evaluate":true}, {"name":"bwclk.img","url":"app-icon.js","evaluate":true},
{"name":"bwclk.settings.js","url":"settings.js"} {"name":"bwclk.settings.js","url":"settings.js"}
] ],
"data":[{"name":"bwclk.setting.json"}]
} }

View File

@ -39,5 +39,10 @@
"name": "bwclklite.settings.js", "name": "bwclklite.settings.js",
"url": "settings.js" "url": "settings.js"
} }
],
"data": [
{
"name": "bwclklite.setting.json"
}
] ]
} }

View File

@ -12,7 +12,10 @@
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{ "name": "cassioWatch.app.js", "url": "app.js" }, { "name": "cassioWatch.app.js", "url": "app.js" },
{"name":"cassioWatch.settings.js","url":"settings.js"}, { "name": "cassioWatch.settings.js","url": "settings.js" },
{ "name": "cassioWatch.img", "url": "icon.js", "evaluate": true } { "name": "cassioWatch.img", "url": "icon.js", "evaluate": true }
],
"data": [
{ "name": "cassioWatch.settings.json" }
] ]
} }

View File

@ -2,3 +2,4 @@
0.02: Support BangleJS2 0.02: Support BangleJS2
0.03: Added threshold 0.03: Added threshold
0.04: Added notification 0.04: Added notification
0.05: Fixed boot

View File

@ -2,7 +2,7 @@
const pin = process.env.HWVERSION === 2 ? D3 : D30; const pin = process.env.HWVERSION === 2 ? D3 : D30;
var id; var id;
Bangle.on('charging', (charging) => { function gent(charging) {
if (charging) { if (charging) {
if (!id) { if (!id) {
var max = 0; var max = 0;
@ -37,5 +37,8 @@
require('notify').hide({id: 'chargent'}); require('notify').hide({id: 'chargent'});
} }
} }
}); }
Bangle.on('charging', gent);
if (Bangle.isCharging()) gent(true);
})(); })();

View File

@ -1,6 +1,6 @@
{ "id": "chargent", { "id": "chargent",
"name": "Charge Gently", "name": "Charge Gently",
"version": "0.04", "version": "0.05",
"description": "When charging, reminds you to disconnect the watch to prolong battery life.", "description": "When charging, reminds you to disconnect the watch to prolong battery life.",
"icon": "icon.png", "icon": "icon.png",
"type": "bootloader", "type": "bootloader",

View File

@ -11,5 +11,6 @@
"storage": [ "storage": [
{"name":"chargerot.boot.js","url":"boot.js"}, {"name":"chargerot.boot.js","url":"boot.js"},
{"name":"chargerot.settings.js","url":"settings.js"} {"name":"chargerot.settings.js","url":"settings.js"}
] ],
"data":[{"name":"chargerot.settings.json"}]
} }

1
apps/chess/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AEnPAEAv/wAAcF6XWAAYdFBQgLLF/4v/F/4vTFKoLGF/4v/F/4v/F4QpWBQov/F7UslgKBAYIABEgIDDF/4v/F4es1gvTdKoLBFYYAHF/4vTmQvuvQwJFxAvbAAOIxIAFFxIvzFKYLG6CMNF8GsF92BdpwvfqwvtRwgvQAC+IxIAJF5QAYF93OwEyRwqSPACwsJGEov/AH4A/AH4AwA="))

283
apps/chess/app.js Normal file
View File

@ -0,0 +1,283 @@
// Using p4wn chess engine: https://p4wn.sourceforge.net/ | https://github.com/douglasbagnall/p4wn
const engine = require("chessengine");
Bangle.loadWidgets(); // load before first appRect call
const FIELD_WIDTH = Bangle.appRect.w/8;
const FIELD_HEIGHT = Bangle.appRect.h/8;
const SETTINGS_FILE = "chess.json";
const DEFAULT_TIMEOUT = Bangle.getOptions().lockTimeout;
const ICON_SIZE=45;
const ICON_BISHOP = require("heatshrink").decompress(atob("lstwMB/4Ac/wFE4IED/kPAofgn4FDGon8j4QEBQgQE4EHBQcACwfAgF/BQYWD8EAHAX+NgI4C+AQEwAQDDYIhDDYMDCAQKBGQQsHHogKDCAJODCAI3CHoQKCHoIQDHoIQCFgoQBFgfgIQYmBEIQECKgIrCBYQKDC4OBg/8iCvEAC+AA="));
const ICON_PAWN = require("heatshrink").decompress(atob("lstwMB/4At/AFEGon4h4FDwE/AgX8CAngCAkAv4bDgYbECAf4gAhD4AhD/kAg4mDCAkACAYbBEIYQBG4gbDEII9DFhXAgEfBQYWDEwJUC/wKBGQXwCAgEBE4RCBCAYmBCAQmCCAQmBCAbdCCAIbCQ4gAYwA="));
const ICON_KING = require("heatshrink").decompress(atob("lstwMB/4Ac/wFE+4KEh4FD+F/AofvCwgKE+IKEg4bEj4FDwADC/k8g+HAoJhCC4PwAoQXBNod//AECgYfBAoUP/gQE8AQEBQcfCAaLBCAZmBEIZuBBQgyDJAIWCPgXAEAQWDBQRUCPgQnBHgJqBLwYhDOwRvDGQc/EIaSDCwLedwAA=="));
const ICON_QUEEN = require("heatshrink").decompress(atob("lstwMB/4Ac/l/AgXn4PzAgP+j0Ph4FB8FwuE///PgeDwPn/k8n0+j0f4Hz+Px8F+g/Px+fgf4vgACn/jAAf/x8Pj0en/8vAsB+P/+PBwcHj//w0MjEwJgMwsHBw5CBwMEhBDBPoR6B/gFCDYPgAoRZBAgUH//4AoQbB4AbDCAYbBCAZ1CAgJ7CwAKDGQQmBCAYmBEIQmC+AQEDYQQBDYQQCFgo3CXQIsFBYIEDACmAA="));
const ICON_ROOK = require("heatshrink").decompress(atob("lstwMB/4Ax/0HgPAAoPwnEOg4FBwBFBn///gEBI4XgAoMPAoJWCv4QDDYXwBQf/4AKD/wmDCARuDGQImCEIQbCGQMDCAQKBj4EB/AFBBQQsgDYQQCNQQhCOog3CCAQ3BEIRvCAoSRCE4IxCKgQmCKgYAZwA="));
const ICON_KNIGHT = require("heatshrink").decompress(atob("lstwMB/4Ann1/AgX48IKD4UPAgX+gEHAoXwgALDJQMfDYQFBEQWAgBSCBQQcC4AFBn///hnCBQPgAgMDGIQnDGIIQDAgQQBEwQQCGIIQCEwMECAQxBsAQBEwMPCAQmBAIJDB4EPDoM/CAIoBKgP4BQQQB/AzCKgJlIPgQ+COwJlCHoJlDJwJlDS4aBDDYQsCADOA"));
const settings = Object.assign({
state: engine.P4_INITIAL_BOARD,
computer_level: 0, // default to "stupid" which is the fastest
}, require("Storage").readJSON(SETTINGS_FILE,1) || {});
var ovr = Graphics.createArrayBuffer(Bangle.appRect.w,Bangle.appRect.h,2,{msb:true});
const curfield = [4*FIELD_WIDTH, 6*FIELD_HEIGHT]; // e2
const startfield = Array(2);
let piece_sel = 0;
let showmenu = false;
const writeSettings = () => {
settings.state = engine.p4_state2fen(state);
require('Storage').writeJSON(SETTINGS_FILE, settings);
};
const generateBgImage = () => {
var buf = Graphics.createArrayBuffer(Bangle.appRect.w,Bangle.appRect.h,1,{msb:true});
for(let idxrow=0; idxrow<8; idxrow++) {
for(let idxcol=0; idxcol<8; idxcol++) {
const bgCol = idxrow % 2 != idxcol % 2 ? 0 : 1;
const x = idxcol*FIELD_WIDTH;
const y = idxrow*FIELD_HEIGHT;
buf.setColor(bgCol).fillRect({x:x, y:y, w:FIELD_WIDTH, h:FIELD_HEIGHT});
}
}
return {width:buf.getWidth(), height:buf.getHeight(),
buffer:buf.buffer
};
};
const idx2Pos = (idxcol, idxrow) => {
"ram"
return 2*(1+8+1) + (7-idxrow)*(1+8+1) + idxcol + 1;
};
const drawPiece = (buf, x, y, piece) => {
let icon;
switch(piece & ~0x1) {
case engine.P4_PAWN:
icon = ICON_PAWN;
break;
case engine.P4_BISHOP:
icon = ICON_BISHOP;
break;
case engine.P4_KING:
icon = ICON_KING;
break;
case engine.P4_QUEEN:
icon = ICON_QUEEN;
break;
case engine.P4_ROOK:
icon = ICON_ROOK;
break;
case engine.P4_KNIGHT:
icon = ICON_KNIGHT;
break;
}
if (icon) {
const scale = FIELD_HEIGHT/ICON_SIZE;
buf.drawImage(icon, x+(FIELD_WIDTH-(ICON_SIZE*scale))/2, y, {scale: scale});
}
return buf;
};
const drawBoard = () => {
//console.log("Free: " + process.memory().free);
g.setBgColor("#555").setColor("#aaa").drawImage(bgImage, Bangle.appRect.x, Bangle.appRect.y);
for(let idxrow=0; idxrow<8; idxrow++) {
for(let idxcol=0; idxcol<8; idxcol++) {
const x = idxcol*FIELD_WIDTH+Bangle.appRect.x;
const y = idxrow*FIELD_HEIGHT+Bangle.appRect.y;
const pos = idx2Pos(idxcol, idxrow);
const field = state.board[pos];
if (field) {
const fgCol = field & 0x1 ? "#000" : "#fff";
drawPiece(g.setBgColor(fgCol), x, y, field);
}
}
}
};
const roundX = (x) => {
return Math.round(x/FIELD_WIDTH)*FIELD_WIDTH;
};
const roundY = (y) => {
return Math.round(y/FIELD_HEIGHT)*FIELD_HEIGHT;
};
const drawSelectedField = () => {
ovr.clear();
if (!showmenu) {
if (startfield[0] !== undefined && startfield[1] !== undefined) {
// remove piece from startfield
const x = startfield[0];
const y = startfield[1];
ovr.setColor(2).fillRect({x:x, y:y, w:FIELD_WIDTH, h:FIELD_HEIGHT});
}
const x = roundX(curfield[0]);
const y = roundY(curfield[1]);
ovr.setColor(piece_sel ? 1 : 2)
.drawRect({x:x+1, y:y, w:FIELD_WIDTH-2, h:FIELD_HEIGHT})
.drawRect({x:x+2, y:y+1, w:FIELD_WIDTH-4, h:FIELD_HEIGHT-2})
.drawRect({x:x+3, y:y+2, w:FIELD_WIDTH-6, h:FIELD_HEIGHT-4});
if (piece_sel) {
drawPiece(ovr.setBgColor(1), x, y, piece_sel);
ovr.setBgColor(0); // back to transparent
}
}
Bangle.setLCDOverlay({width:ovr.getWidth(), height:ovr.getHeight(),
bpp:2, transparent:0,
palette:new Uint16Array([0, g.toColor("#F00"), g.toColor("#0F0"), 0]),
buffer:ovr.buffer
},Bangle.appRect.x,Bangle.appRect.y);
};
const isInside = (rect, e) => {
return e.x>=rect.x && e.x<rect.x+rect.w
&& e.y>=rect.y && e.y<=rect.y+rect.h;
};
const showAlert = (msg) => {
showmenu = true;
drawSelectedField();
E.showAlert(msg).then(function() {
showmenu = false;
drawBoard();
drawSelectedField();
});
};
const move = (from,to) => {
const res = state.move(from, to);
//console.log(res);
if (!res.ok) {
showAlert("Illegal move");
} else {
if (res.flags & engine.P4_MOVE_FLAG_MATE) {
showAlert("Checkmate or stalemate");
} else if (res.flags & engine.P4_MOVE_FLAG_CHECK) {
showAlert("A king is in check");
} else if (res.flags & engine.P4_MOVE_FLAG_DRAW) {
showAlert("A draw is available");
}
}
return res;
};
const showMessage = (msg) => {
g.setColor("#f00").setFont("4x6:2").setFontAlign(-1,1).drawString(msg, 10, Bangle.appRect.y2-10);
};
// Run
g.reset();
const bgImage = generateBgImage();
let state = engine.p4_fen2state(settings.state);
drawBoard();
drawSelectedField();
Bangle.drawWidgets();
// drag selected field
Bangle.on('drag', (ev) => {
const newx = curfield[0]+ev.dx;
const newy = curfield[1]+ev.dy;
if (newx >= 0 && newx <= 7*FIELD_WIDTH) {
curfield[0] = newx;
}
if (newy >= 0 && newy <= 7*FIELD_HEIGHT) {
curfield[1] = newy;
}
drawSelectedField();
});
// touch to start/stop moving a piece
Bangle.on('touch', (button, xy) => {
if (isInside(Bangle.appRect, xy) && !showmenu) {
if (piece_sel === 0) {
startfield[0] = roundX(curfield[0]);
startfield[1] = roundY(curfield[1]);
const startpos = idx2Pos(startfield[0]/FIELD_WIDTH, startfield[1]/FIELD_HEIGHT);
piece_sel = state.board[startpos];
if (piece_sel === 0) {
startfield[0] = startfield[1] = undefined;
// nothing here, do nothing
return;
}
} else { // piece_sel === 0
const colTo = roundX(curfield[0]);
const rowTo = roundY(curfield[1]);
if (startfield[0] !== colTo || startfield[1] !== rowTo) {
showMessage(/*LANG*/"Moving..");
const posFrom = idx2Pos(startfield[0]/FIELD_WIDTH, startfield[1]/FIELD_HEIGHT);
const posTo = idx2Pos(colTo/FIELD_WIDTH, rowTo/FIELD_HEIGHT);
setTimeout(() => {
if (move(posFrom, posTo).ok) {
// human move ok, update
drawBoard();
drawSelectedField();
// do computer move
Bangle.setLCDTimeout(0.1); // this can take some time, turn off to save power
showMessage(/*LANG*/"Calculating..");
setTimeout(() => {
const compMove = state.findmove(settings.computer_level+1);
const result = move(compMove[0], compMove[1]);
writeSettings();
Bangle.setLCDPower(true);
Bangle.setLocked(false);
Bangle.setLCDTimeout(DEFAULT_TIMEOUT/1000); // restore
if (!showmenu) {
showAlert(result.string);
}
}, 200); // execute after display update
}
}, 100); // execute after display update
} // piece_sel === 0
startfield[0] = startfield[1] = undefined;
piece_sel = 0;
}
drawSelectedField();
}
});
// show menu on button
setWatch(() => {
showmenu = true;
drawSelectedField();
const closeMenu = () => {
showmenu = false;
E.showMenu();
drawBoard();
drawSelectedField();
};
E.showMenu({
"" : { title : /*LANG*/"Chess settings" },
"< Back" : () => closeMenu(),
/*LANG*/"New Game" : () => {
state = engine.p4_fen2state(engine.P4_INITIAL_BOARD);
writeSettings();
closeMenu();
},
/*LANG*/"Undo Move" : () => {
state.jump_to_moveno(-2);
closeMenu();
},
/*LANG*/'Level': {
value: settings.computer_level,
min: 0, max: 4,
format: v => [/*LANG*/'stupid', /*LANG*/'middling', /*LANG*/'default', /*LANG*/'slow', /*LANG*/'slowest'][v],
onchange: v => {
settings.computer_level = v;
writeSettings();
}
},
/*LANG*/"Exit" : () => load(),
});
}, BTN, { repeat: true, edge: "falling" });

BIN
apps/chess/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

1614
apps/chess/engine.js Normal file

File diff suppressed because it is too large Load Diff

17
apps/chess/metadata.json Normal file
View File

@ -0,0 +1,17 @@
{
"id": "chess",
"name": "Chess",
"shortName": "Chess",
"version": "0.01",
"description": "Chess game based on the [p4wn engine](https://p4wn.sourceforge.net/). Drag on the touchscreen to move the green cursor onto a piece, select it with a single touch and drag the now red cursor around. Release the piece with another touch to finish the move. The button opens a menu.",
"icon": "app.png",
"tags": "game",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"chess.app.js","url":"app.js"},
{"name":"chessengine","url":"engine.js"},
{"name":"chess.img","url":"app-icon.js","evaluate":true}
],
"data": [{"name":"chess.json"}],
"screenshots": [ {"url":"screenshot.png"} ]
}

BIN
apps/chess/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,3 +1,4 @@
0.01: Initial Creation 0.01: Initial Creation
0.02: Fixed some sleep bugs. Added a sleep mode toggle 0.02: Fixed some sleep bugs. Added a sleep mode toggle
0.03: Reduce busy-loop and code 0.03: Reduce busy-loop and code
0.04: Separate buzz-time and sleep-time

View File

@ -1,7 +1,7 @@
{ {
"id": "chimer", "id": "chimer",
"name": "Chimer", "name": "Chimer",
"version": "0.03", "version": "0.04",
"description": "A fork of Hour Chime that adds extra features such as: \n - Buzz or beep on every 60, 30 or 15 minutes. \n - Repeat Chime up to 3 times \n - Set hours to disable chime", "description": "A fork of Hour Chime that adds extra features such as: \n - Buzz or beep on every 60, 30 or 15 minutes. \n - Repeat Chime up to 3 times \n - Set hours to disable chime",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",

View File

@ -20,15 +20,16 @@
let count = settings.repeat; let count = settings.repeat;
const chime1 = () => { const chime1 = () => {
let p;
if (settings.type === 1) { if (settings.type === 1) {
Bangle.buzz(100); p = Bangle.buzz(100);
} else if (settings.type === 2) { } else if (settings.type === 2) {
Bangle.beep(); p = Bangle.beep();
} else { } else {
return; return;
} }
if (--count > 0) if (--count > 0)
setTimeout(chime1, 150); p.then(() => setTimeout(chime1, 150));
}; };
chime1(); chime1();

View File

@ -1,36 +0,0 @@
const enum StopWatchFormat {
HMS,
Colon,
}
type StopWatchSettings = {
format: StopWatchFormat,
};
(back => {
const SETTINGS_FILE = "clkinfostopw.setting.json";
const storage = require("Storage");
const settings: StopWatchSettings = Object.assign(
{ format: StopWatchFormat.HMS },
storage.readJSON(SETTINGS_FILE, true),
);
const save = () => {
storage.writeJSON(SETTINGS_FILE, settings)
};
E.showMenu({
"": { "title": "stopwatch" },
"< Back": back,
"Format": {
value: settings.format,
min: StopWatchFormat.HMS,
max: StopWatchFormat.Colon,
format: v => v === StopWatchFormat.HMS ? "12m34s" : "12:34",
onchange: v => {
settings.format = v;
save();
},
},
});
}) satisfies SettingsFunc

View File

@ -5,6 +5,7 @@
"icon": "app.png", "icon": "app.png",
"type": "clkinfo", "type": "clkinfo",
"tags": "clkinfo,sunrise", "tags": "clkinfo,sunrise",
"dependencies": {"mylocation":"app"},
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"storage": [ "storage": [
{"name":"sunrise.clkinfo.js","url":"clkinfo.js"} {"name":"sunrise.clkinfo.js","url":"clkinfo.js"}

View File

@ -3,4 +3,5 @@
0.03: Reported image for battery now reflects charge level 0.03: Reported image for battery now reflects charge level
0.04: On 2v18+ firmware, we can now stop swipe events from being handled by other apps 0.04: On 2v18+ firmware, we can now stop swipe events from being handled by other apps
eg. when a clockinfo is selected, swipes won't affect swipe-down widgets eg. when a clockinfo is selected, swipes won't affect swipe-down widgets
0.05: Reported image for battery is now transparent (2v18+) 0.05: Reported image for battery is now transparent (2v18+)
0.06: When >1 clockinfo, swiping one back tries to ensure they don't display the same thing

View File

@ -10,7 +10,12 @@ if (stepGoal == undefined) {
stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000; stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000;
} }
// Load the settings, with defaults /// How many times has addInteractive been called?
exports.loadCount = 0;
/// A list of all the instances returned by addInteractive
exports.clockInfos = [];
/// Load the settings, with defaults
exports.loadSettings = function() { exports.loadSettings = function() {
return Object.assign({ return Object.assign({
hrmOn : 0, // 0(Always), 1(Tap) hrmOn : 0, // 0(Always), 1(Tap)
@ -22,6 +27,7 @@ exports.loadSettings = function() {
); );
}; };
/// Load a list of ClockInfos - this does not cache and reloads each time
exports.load = function() { exports.load = function() {
var settings = exports.loadSettings(); var settings = exports.loadSettings();
delete settings.apps; // keep just the basic settings in memory delete settings.apps; // keep just the basic settings in memory
@ -63,7 +69,7 @@ exports.load = function() {
} else img=atob("GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA=="); } else img=atob("GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==");
return { return {
text : v + "%", v : v, min:0, max:100, img : img text : v + "%", v : v, min:0, max:100, img : img
} };
}, },
show : function() { this.interval = setInterval(()=>this.emit('redraw'), 60000); Bangle.on("charging", batteryUpdateHandler); batteryUpdateHandler(); }, show : function() { this.interval = setInterval(()=>this.emit('redraw'), 60000); Bangle.on("charging", batteryUpdateHandler); batteryUpdateHandler(); },
hide : function() { clearInterval(this.interval); delete this.interval; Bangle.removeListener("charging", batteryUpdateHandler); }, hide : function() { clearInterval(this.interval); delete this.interval; Bangle.removeListener("charging", batteryUpdateHandler); },
@ -73,7 +79,7 @@ exports.load = function() {
get : () => { let v = Bangle.getHealthStatus("day").steps; return { get : () => { let v = Bangle.getHealthStatus("day").steps; return {
text : v, v : v, min : 0, max : stepGoal, text : v, v : v, min : 0, max : stepGoal,
img : atob("GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA==") img : atob("GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA==")
}}, };},
show : function() { Bangle.on("step", stepUpdateHandler); stepUpdateHandler(); }, show : function() { Bangle.on("step", stepUpdateHandler); stepUpdateHandler(); },
hide : function() { Bangle.removeListener("step", stepUpdateHandler); }, hide : function() { Bangle.removeListener("step", stepUpdateHandler); },
}, },
@ -82,7 +88,7 @@ exports.load = function() {
get : () => { return { get : () => { return {
text : (hrm||"--") + " bpm", v : hrm, min : 40, max : 200, text : (hrm||"--") + " bpm", v : hrm, min : 40, max : 200,
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA==") img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA==")
}}, };},
run : function() { run : function() {
Bangle.setHRMPower(1,"clkinfo"); Bangle.setHRMPower(1,"clkinfo");
if (settings.hrmOn==1/*Tap*/) { if (settings.hrmOn==1/*Tap*/) {
@ -131,11 +137,11 @@ exports.load = function() {
require("Storage").list(/clkinfo.js$/).forEach(fn => { require("Storage").list(/clkinfo.js$/).forEach(fn => {
try{ try{
var a = eval(require("Storage").read(fn))(); var a = eval(require("Storage").read(fn))();
var b = menu.find(x => x.name === a.name) var b = menu.find(x => x.name === a.name);
if(b) b.items = b.items.concat(a.items); if(b) b.items = b.items.concat(a.items);
else menu = menu.concat(a); else menu = menu.concat(a);
} catch(e){ } catch(e){
console.log("Could not load clock info "+E.toJS(fn)) console.log("Could not load clock info "+E.toJS(fn));
} }
}); });
@ -204,11 +210,12 @@ exports.addInteractive = function(menu, options) {
if ("function" == typeof options) options = {draw:options}; // backwards compatibility if ("function" == typeof options) options = {draw:options}; // backwards compatibility
options.index = 0|exports.loadCount; options.index = 0|exports.loadCount;
exports.loadCount = options.index+1; exports.loadCount = options.index+1;
exports.clockInfos[options.index] = options;
options.focus = options.index==0 && options.x===undefined; // focus if we're the first one loaded and no position has been defined options.focus = options.index==0 && options.x===undefined; // focus if we're the first one loaded and no position has been defined
const appName = (options.app||"default")+":"+options.index; const appName = (options.app||"default")+":"+options.index;
// load the currently showing clock_infos // load the currently showing clock_infos
let settings = exports.loadSettings() let settings = exports.loadSettings();
if (settings.apps[appName]) { if (settings.apps[appName]) {
let a = settings.apps[appName].a|0; let a = settings.apps[appName].a|0;
let b = settings.apps[appName].b|0; let b = settings.apps[appName].b|0;
@ -259,6 +266,10 @@ exports.addInteractive = function(menu, options) {
//can happen for dynamic ones (alarms, events) //can happen for dynamic ones (alarms, events)
//in the worst case we come back to 0 //in the worst case we come back to 0
} while(menu[options.menuA].items.length==0); } while(menu[options.menuA].items.length==0);
// When we change, ensure we don't display the same thing as another clockinfo if we can avoid it
while ((options.menuB < menu[options.menuA].items.length) &&
exports.clockInfos.some(m => (m!=options) && m.menuA==options.menuA && m.menuB==options.menuB))
options.menuB++;
} }
if (oldMenuItem) { if (oldMenuItem) {
menuHideItem(oldMenuItem); menuHideItem(oldMenuItem);
@ -319,6 +330,7 @@ exports.addInteractive = function(menu, options) {
delete Bangle.CLKINFO_FOCUS; delete Bangle.CLKINFO_FOCUS;
menuHideItem(menu[options.menuA].items[options.menuB]); menuHideItem(menu[options.menuA].items[options.menuB]);
exports.loadCount--; exports.loadCount--;
delete exports.clockInfos[options.index];
}; };
options.redraw = function() { options.redraw = function() {
drawItem(menu[options.menuA].items[options.menuB]); drawItem(menu[options.menuA].items[options.menuB]);
@ -339,8 +351,7 @@ exports.addInteractive = function(menu, options) {
menuShowItem(menu[options.menuA].items[options.menuB]); menuShowItem(menu[options.menuA].items[options.menuB]);
return true; return true;
} };
delete settings; // don't keep settings in RAM - save space delete settings; // don't keep settings in RAM - save space
return options; return options;
}; };

View File

@ -1,7 +1,7 @@
{ "id": "clock_info", { "id": "clock_info",
"name": "Clock Info Module", "name": "Clock Info Module",
"shortName": "Clock Info", "shortName": "Clock Info",
"version":"0.05", "version":"0.06",
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)", "description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
"icon": "app.png", "icon": "app.png",
"type": "module", "type": "module",

View File

@ -6,3 +6,4 @@
0.06: Add button for force compass calibration 0.06: Add button for force compass calibration
0.07: Use 360-heading to output the correct heading value (fix #1866) 0.07: Use 360-heading to output the correct heading value (fix #1866)
0.08: Added adjustment for Bangle.js magnetometer heading fix 0.08: Added adjustment for Bangle.js magnetometer heading fix
0.09: use falling edge of button to reset compass (allows exit without compass reset)

View File

@ -68,7 +68,7 @@ g.clear(1);
g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"RESET", g.getWidth()-5, g.getHeight()/2); g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"RESET", g.getWidth()-5, g.getHeight()/2);
setWatch(function() { setWatch(function() {
Bangle.resetCompass(); Bangle.resetCompass();
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true}); }, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true, edge:"falling"});
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();

View File

@ -1,7 +1,7 @@
{ {
"id": "compass", "id": "compass",
"name": "Compass", "name": "Compass",
"version": "0.08", "version": "0.09",
"description": "Simple compass that points North", "description": "Simple compass that points North",
"icon": "compass.png", "icon": "compass.png",
"screenshots": [{"url":"screenshot_compass.png"}], "screenshots": [{"url":"screenshot_compass.png"}],

View File

@ -15,5 +15,6 @@
{"name":"contourclock.settings.js","url":"contourclock.settings.js"}, {"name":"contourclock.settings.js","url":"contourclock.settings.js"},
{"name":"contourclock","url":"lib.js"}, {"name":"contourclock","url":"lib.js"},
{"name":"contourclock.img","url":"app-icon.js","evaluate":true} {"name":"contourclock.img","url":"app-icon.js","evaluate":true}
] ],
"data": [{"name":"contourclock.json"}]
} }

View File

@ -13,5 +13,6 @@
{"name":"cprassist.app.js","url":"cprassist.js"}, {"name":"cprassist.app.js","url":"cprassist.js"},
{"name":"cprassist.img","url":"cprassist-icon.js","evaluate":true}, {"name":"cprassist.img","url":"cprassist-icon.js","evaluate":true},
{"name":"cprassist.settings.js","url":"settings.js"} {"name":"cprassist.settings.js","url":"settings.js"}
] ],
"data":[{"name":"cprassist.settings.json"}]
} }

View File

@ -12,5 +12,8 @@
{"name":"cscsensor.app.js","url":"cscsensor.app.js"}, {"name":"cscsensor.app.js","url":"cscsensor.app.js"},
{"name":"cscsensor.settings.js","url":"settings.js"}, {"name":"cscsensor.settings.js","url":"settings.js"},
{"name":"cscsensor.img","url":"cscsensor-icon.js","evaluate":true} {"name":"cscsensor.img","url":"cscsensor-icon.js","evaluate":true}
],
"data": [
{"name":"cscsensor.json"}
] ]
} }

View File

@ -13,5 +13,8 @@
{"name":"cycling.settings.js","url":"settings.js"}, {"name":"cycling.settings.js","url":"settings.js"},
{"name":"blecsc","url":"blecsc.js"}, {"name":"blecsc","url":"blecsc.js"},
{"name":"cycling.img","url":"cycling.icon.js","evaluate": true} {"name":"cycling.img","url":"cycling.icon.js","evaluate": true}
],
"data": [
{"name":"cycling.json"}
] ]
} }

View File

@ -6,10 +6,13 @@
"type":"textinput", "type":"textinput",
"tags": "keyboard", "tags": "keyboard",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"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"} {"name":"dragboard.settings.js","url":"settings.js"}
],
"data": [
{"name":"dragboard.json"}
] ]
} }

View File

@ -6,10 +6,13 @@
"type":"textinput", "type":"textinput",
"tags": "keyboard", "tags": "keyboard",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"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":"draguboard.settings.js","url":"settings.js"} {"name":"draguboard.settings.js","url":"settings.js"}
],
"data": [
{"name":"draguboard.json"}
] ]
} }

View File

@ -12,5 +12,6 @@
{"name":"f9lander.app.js","url":"app.js"}, {"name":"f9lander.app.js","url":"app.js"},
{"name":"f9lander.img","url":"app-icon.js","evaluate":true}, {"name":"f9lander.img","url":"app-icon.js","evaluate":true},
{"name":"f9lander.settings.js", "url":"settings.js"} {"name":"f9lander.settings.js", "url":"settings.js"}
] ],
"data":[{"name":"f9settings.json"}]
} }

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Allow redirection of loads to the launcher 0.02: Allow redirection of loads to the launcher
0.03: Allow hiding the fastloading info screen 0.03: Allow hiding the fastloading info screen
0.04: (WIP) Allow use of app history when going back (`load()` or `Bangle.load()` calls without specified app).

View File

@ -8,9 +8,16 @@ This allows fast loading of all apps with two conditions:
## Settings ## Settings
* Activate app history and navigate back through recent apps instead of immediately loading the clock face
* If Quick Launch is installed it can be excluded from app history
* Allows to redirect all loads usually loading the clock to the launcher instead * Allows to redirect all loads usually loading the clock to the launcher instead
* The "Fastloading..." screen can be switched off * The "Fastloading..." screen can be switched off
## App history
* Long press of hardware button clears the app history and loads the clock face
* Installing the 'Fast Reset' app allows doing fastloads directly to the clock face by pressing the hardware button for one second. Useful if there are many apps in the history and the user want to access the clock quickly.
## Technical infos ## Technical infos
This is still experimental but it uses the same mechanism as `.bootcde` does. This is still experimental but it uses the same mechanism as `.bootcde` does.
@ -19,3 +26,6 @@ It checks the app to be loaded for widget use and stores the result of that and
# Creator # Creator
[halemmerich](https://github.com/halemmerich) [halemmerich](https://github.com/halemmerich)
# Contributors
[thyttan](https://github.com/thyttan)

View File

@ -1,5 +1,6 @@
{ {
const SETTINGS = require("Storage").readJSON("fastload.json") || {}; const s = require("Storage");
const SETTINGS = s.readJSON("fastload.json") || {};
let loadingScreen = function(){ let loadingScreen = function(){
g.reset(); g.reset();
@ -16,26 +17,26 @@ let loadingScreen = function(){
g.flip(true); g.flip(true);
}; };
let cache = require("Storage").readJSON("fastload.cache") || {}; let cache = s.readJSON("fastload.cache") || {};
let checkApp = function(n){ let checkApp = function(n){
// no widgets, no problem // no widgets, no problem
if (!global.WIDGETS) return true; if (!global.WIDGETS) return true;
let app = require("Storage").read(n); let app = s.read(n);
if (cache[n] && E.CRC32(app) == cache[n].crc) if (cache[n] && E.CRC32(app) == cache[n].crc)
return cache[n].fast return cache[n].fast;
cache[n] = {}; cache[n] = {};
cache[n].fast = app.includes("Bangle.loadWidgets"); cache[n].fast = app.includes("Bangle.loadWidgets");
cache[n].crc = E.CRC32(app); cache[n].crc = E.CRC32(app);
require("Storage").writeJSON("fastload.cache", cache); s.writeJSON("fastload.cache", cache);
return cache[n].fast; return cache[n].fast;
} };
global._load = load; global._load = load;
let slowload = function(n){ let slowload = function(n){
global._load(n); global._load(n);
} };
let fastload = function(n){ let fastload = function(n){
if (!n || checkApp(n)){ if (!n || checkApp(n)){
@ -50,17 +51,40 @@ let fastload = function(n){
}; };
global.load = fastload; global.load = fastload;
let appHistory, resetHistory, recordHistory;
if (SETTINGS.useAppHistory){
appHistory = s.readJSON("fastload.history.json",true)||[];
resetHistory = ()=>{appHistory=[];s.writeJSON("fastload.history.json",appHistory);};
recordHistory = ()=>{s.writeJSON("fastload.history.json",appHistory);};
}
Bangle.load = (o => (name) => { Bangle.load = (o => (name) => {
if (Bangle.uiRemove && !SETTINGS.hideLoading) loadingScreen(); if (Bangle.uiRemove && !SETTINGS.hideLoading) loadingScreen();
if (SETTINGS.useAppHistory){
if (name && name!=".bootcde" && !(name=="quicklaunch.app.js" && SETTINGS.disregardQuicklaunch)) {
// store the name of the app to launch
appHistory.push(name);
} else if (name==".bootcde") { // when Bangle.showClock is called
resetHistory();
} else if (name=="quicklaunch.app.js" && SETTINGS.disregardQuicklaunch) {
// do nothing with history
} else {
// go back in history
appHistory.pop();
name = appHistory[appHistory.length-1];
}
}
if (SETTINGS.autoloadLauncher && !name){ if (SETTINGS.autoloadLauncher && !name){
let orig = Bangle.load; let orig = Bangle.load;
Bangle.load = (n)=>{ Bangle.load = (n)=>{
Bangle.load = orig; Bangle.load = orig;
fastload(n); fastload(n);
} };
Bangle.showLauncher(); Bangle.showLauncher();
Bangle.load = orig; Bangle.load = orig;
} else } else
o(name); o(name);
})(Bangle.load); })(Bangle.load);
if (SETTINGS.useAppHistory) E.on('kill', ()=>{if (!BTN.read()) recordHistory(); else resetHistory();}); // Usually record history, but reset it if long press of HW button was used.
} }

View File

@ -1,7 +1,7 @@
{ "id": "fastload", { "id": "fastload",
"name": "Fastload Utils", "name": "Fastload Utils",
"shortName" : "Fastload Utils", "shortName" : "Fastload Utils",
"version": "0.03", "version": "0.04",
"icon": "icon.png", "icon": "icon.png",
"description": "Enable experimental fastloading for more apps", "description": "Enable experimental fastloading for more apps",
"type":"bootloader", "type":"bootloader",

View File

@ -1,7 +1,8 @@
(function(back) { (function(back) {
var FILE="fastload.json"; var FILE="fastload.json";
var settings; var settings;
var isQuicklaunchPresent = !!require('Storage').read("quicklaunch.app.js", 0, 1);
function writeSettings(key, value) { function writeSettings(key, value) {
var s = require('Storage').readJSON(FILE, true) || {}; var s = require('Storage').readJSON(FILE, true) || {};
s[key] = value; s[key] = value;
@ -12,25 +13,52 @@
function readSettings(){ function readSettings(){
settings = require('Storage').readJSON(FILE, true) || {}; settings = require('Storage').readJSON(FILE, true) || {};
} }
readSettings(); readSettings();
function buildMainMenu(){ function buildMainMenu(){
var mainmenu = { var mainmenu = {};
'': { 'title': 'Fastload', back: back },
'Force load to launcher': { mainmenu[''] = { 'title': 'Fastload', back: back };
mainmenu['Activate app history'] = {
value: !!settings.useAppHistory,
onchange: v => {
writeSettings("useAppHistory",v);
if (v && settings.autoloadLauncher) {
writeSettings("autoloadLauncher",!v); // Don't use app history and load to launcher together.
setTimeout(()=>E.showMenu(buildMainMenu()), 0); // Update the menu so it can be seen if a value was automatically set to false (app history vs load launcher).
}
}
};
if (isQuicklaunchPresent) {
mainmenu['Exclude Quick Launch from history'] = {
value: !!settings.disregardQuicklaunch,
onchange: v => {
writeSettings("disregardQuicklaunch",v);
}
};
}
mainmenu['Force load to launcher'] = {
value: !!settings.autoloadLauncher, value: !!settings.autoloadLauncher,
onchange: v => { onchange: v => {
writeSettings("autoloadLauncher",v); writeSettings("autoloadLauncher",v);
if (v && settings.useAppHistory) {
writeSettings("useAppHistory",!v);
setTimeout(()=>E.showMenu(buildMainMenu()), 0); // Update the menu so it can be seen if a value was automatically set to false (app history vs load launcher).
} // Don't use app history and load to launcher together.
} }
}, };
'Hide "Fastloading..."': {
mainmenu['Hide "Fastloading..."'] = {
value: !!settings.hideLoading, value: !!settings.hideLoading,
onchange: v => { onchange: v => {
writeSettings("hideLoading",v); writeSettings("hideLoading",v);
} }
} };
};
return mainmenu; return mainmenu;
} }

1
apps/fastreset/ChangeLog Normal file
View File

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

32
apps/fastreset/README.md Normal file
View File

@ -0,0 +1,32 @@
# Fast Reset
Reset the watch by holding the hardware button for half a second. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.
Fast Reset was developed with the app history feature of 'Fastload Utils' in mind. If many apps are in the history stack, the user may want a fast way to exit directly to the clock face without using the firmwares reset function.
## Usage
Just install and it will run as boot code.
## Features
If 'Fastload Utils' is installed fastloading will be used when possible. Otherwise a standard `load(.bootcde)` is used.
If the hardware button is held for longer the standard reset functionality of the firmware is executed as well (total 1.5 seconds). And eventually the watchdog will be kicked.
## Controls
Hold the hardware button for half a second to feel the buzz, loading the clock face.
## Requests
Mention @[thyttan](https://github.com/thyttan) in an issue to the official [BangleApps repository](https://github.com/espruino/BangleApps/issues) for feature requests and bug reports.
## Acknowledgements
<a target="_blank" href="https://icons8.com/icon/15165/rewind">Rewind</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
## Creator
[thyttan](https://github.com/thyttan)

BIN
apps/fastreset/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

5
apps/fastreset/boot.js Normal file
View File

@ -0,0 +1,5 @@
{let buzzTimeout;
setWatch((e)=>{
if (e.state) buzzTimeout = setTimeout(()=>{Bangle.buzz(80,0.40);Bangle.showClock();}, 500);
if (!e.state && buzzTimeout) clearTimeout(buzzTimeout);},
BTN,{repeat:true, edge:'both' });}

View File

@ -0,0 +1,14 @@
{ "id": "fastreset",
"name": "Fast Reset",
"shortName":"Fast Reset",
"version":"0.01",
"description": "Reset the watch by holding the hardware button for half a second. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.",
"icon": "app.png",
"type": "bootloader",
"tags": "system",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"fastreset.boot.js","url":"boot.js"}
]
}

View File

@ -14,5 +14,6 @@
{"name":"game1024.app.js","url":"app.js"}, {"name":"game1024.app.js","url":"app.js"},
{"name":"game1024.settings.js","url":"settings.js"}, {"name":"game1024.settings.js","url":"settings.js"},
{"name":"game1024.img","url":"app-icon.js","evaluate":true} {"name":"game1024.img","url":"app-icon.js","evaluate":true}
] ],
"data":[{"name":"game1024.settings.json"}]
} }

1
apps/gassist/ChangeLog Normal file
View File

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

11
apps/gassist/app.js Normal file
View File

@ -0,0 +1,11 @@
Bluetooth.println("");
Bluetooth.println(JSON.stringify({
t:"intent",
target:"activity",
action:"android.intent.action.VOICE_COMMAND",
flags:["FLAG_ACTIVITY_NEW_TASK"]
}));
setTimeout(function() {
Bangle.showClock();
}, 0);

BIN
apps/gassist/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

21
apps/gassist/boot.js Normal file
View File

@ -0,0 +1,21 @@
// load settings
var settings = Object.assign({
enableTap: true
}, require("Storage").readJSON("gassist.json", true) || {});
if (settings.enableTap) {
Bangle.on("tap", function(e) {
if (e.dir=="front" && e.double) {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({
t:"intent",
target:"activity",
action:"android.intent.action.VOICE_COMMAND",
flags:["FLAG_ACTIVITY_NEW_TASK"]
}));
}
});
}
// clear variable
settings = undefined;

1
apps/gassist/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4ALiAAFFtoxmFpQxjFxwwfFyAwdFyQwcF9wuUGDQvuFywwYF/4vUnAABF9YuCGBAv/F/6/PGC4bE3QACG5YvdFoYxSLzAvuFw4wjLxbCidhAvVGB4UFF7QxMCZAuaGJIRKF7oATFtoA/AEPMAAQttGNQuHGE4vuFxIwlF/4v/d/4vwGBAumGIwtpAH4A/AEIA=="))

View File

@ -0,0 +1,18 @@
{
"id": "gassist",
"name": "Google Assist",
"version": "0.01",
"description": "A simple way to initiate Google Assistant on Android. Intents must be enabled in Gadgetbridge.",
"icon": "app.png",
"type": "app",
"tags": "tool, voice, tasker",
"supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": false,
"storage": [
{"name":"gassist.boot.js","url":"boot.js"},
{"name":"gassist.app.js","url":"app.js"},
{"name":"gassist.settings.js","url":"settings.js"},
{"name":"gassist.img","url":"icon.js","evaluate":true}
],
"data": [{"name":"gassist.json"}]
}

33
apps/gassist/settings.js Normal file
View File

@ -0,0 +1,33 @@
(function (back) {
let storage = require('Storage');
let file = "gassist.json";
// Load and set default settings
let appSettings = Object.assign({
enableTap : true
}, storage.readJSON(file, true) || {});
// Save settings to storage
function writeSettings() {
storage.writeJSON(file, appSettings);
}
function showMenu() {
E.showMenu({
"": {
"title": "Google Assist"
},
"< Back": () => back(),
'Front Tap:': {
value: (appSettings.enableTap === true),
format: v => v ? "On" : "Off",
onchange: v => {
appSettings.enableTap = v;
writeSettings();
}
},
});
}
// Initially show the menu
showMenu();
});

View File

@ -2,3 +2,4 @@
0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen 0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen
0.03: Changed setWatch to Bangle.setUI 0.03: Changed setWatch to Bangle.setUI
0.04: Tell clock widgets to hide. 0.04: Tell clock widgets to hide.
0.05: Making geissclock work on Bangle.js 2 (but only animate when unlocked!)

View File

@ -1,4 +1,7 @@
var W = 79, H = 64; var W = 79, H = 64;
// if screen is always on, only animate when unlocked
var isScreenAlwaysOn = process.env.BOARD=="BANGLEJS2";
/*var compiled = E.compiledC(` /*var compiled = E.compiledC(`
// void transl(int, int, int ) // void transl(int, int, int )
int transl(unsigned char *map, unsigned char *imgfrom, unsigned char *imgto) { int transl(unsigned char *map, unsigned char *imgfrom, unsigned char *imgto) {
@ -46,6 +49,7 @@ var map = new Uint8Array(W*H);
var pal = new Uint16Array(256); var pal = new Uint16Array(256);
var PALETTES = 3; var PALETTES = 3;
var MAPS = 6; var MAPS = 6;
var animInterval;
// If we're missing any maps, compute them! // If we're missing any maps, compute them!
(function() { (function() {
@ -65,6 +69,8 @@ function randomPalette() {
var n = (0|Math.random()*200000) % PALETTES; var n = (0|Math.random()*200000) % PALETTES;
var p = new Uint8Array(pal.buffer); var p = new Uint8Array(pal.buffer);
p.set(require("Storage").readArrayBuffer("geissclk."+n+".pal")); p.set(require("Storage").readArrayBuffer("geissclk."+n+".pal"));
if (!g.theme.dark) // if not dark, invert colors
E.mapInPlace(pal,pal,x=>x^0xFFFF);
} }
function randomMap() { function randomMap() {
@ -93,7 +99,7 @@ var im = {
}; };
var lastSeconds = -1; var lastSeconds = -1;
function iterate() { "ram" function iterate(clearBuf) { "ram"
var d = new Date(); var d = new Date();
var time = require("locale").time(d,1); var time = require("locale").time(d,1);
var seconds = d.getSeconds().toString().padStart(2,0); var seconds = d.getSeconds().toString().padStart(2,0);
@ -108,27 +114,59 @@ function iterate() { "ram"
gfx.buffer = dataa.buffer; gfx.buffer = dataa.buffer;
} }
var x,y,n,t = getTime()/10; var x,y,n,t = getTime()/10;
var amt = 100*Bangle.getAccel().diff; if (clearBuf) {
for (var i=0;i<amt;i++) { gfx.clear();
//x = Math.round((W/2) + 20*Math.sin(t)); } else { // do geiss animation
//y = Math.round((H/2) + 20*Math.cos(t)); var amt = 100*Bangle.getAccel().diff;
//t += 0.628; for (var i=0;i<amt;i++) {
x = 1+(Math.random()*(W-2))|0; //x = Math.round((W/2) + 20*Math.sin(t));
y = 1+(Math.random()*(H-2))|0; //y = Math.round((H/2) + 20*Math.cos(t));
dataa[x + y*W] = 240; //t += 0.628;
x = 1+(Math.random()*(W-2))|0;
y = 1+(Math.random()*(H-2))|0;
dataa[x + y*W] = 240;
}
compiled.transl(addrmap, addra, addrb);
} }
compiled.transl(addrmap, addra, addrb);
x = 8; x = 8;
gfx.setFont("5x9Numeric7Seg",2); gfx.setFont("5x9Numeric7Seg",2);
gfx.drawString(time, x, 20); gfx.drawString(time, x, 20);
gfx.setFont("5x9Numeric7Seg"); if (!clearBuf) { // don't draw seconds if not animating
gfx.drawString(seconds, x+55, 30); gfx.setFont("5x9Numeric7Seg");
gfx.drawString(seconds, x+55, 30);
}
// firmwares pre-2v09 wouldn't accelerate a 3x blit if it went right to the RHS - hence we're 79px not 80 // firmwares pre-2v09 wouldn't accelerate a 3x blit if it went right to the RHS - hence we're 79px not 80
g.drawImage(im,1,24,{scale:3}); if (g.getWidth()==176) // Bangle.js 2
g.drawImage(im,8,24,{scale:2});
else
g.drawImage(im,3,24,{scale:3});
} }
if (isScreenAlwaysOn) {
Bangle.on('lock',function(on) {
if (animInterval) {
clearInterval(animInterval);
animInterval = undefined;
}
if (!on) { // not locked - animate!
randomMap();
randomPalette();
iterate();
animInterval = setInterval(iterate, 50);
} else {
iterate(true); // just clear
animInterval = setTimeout(function() {
iterate(true);
animInterval = setInterval(function() {
iterate(true);
}, 60000);
}, 60000 - (Date.now() % 60000));
}
});
}
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
if (animInterval) { if (animInterval) {
@ -144,11 +182,15 @@ Bangle.on('lcdPower',function(on) {
}); });
// Show launcher when button pressed // Show launcher when button pressed
Bangle.setUI("clock");g.clear(); Bangle.setUI("clock");
g.clear(1);
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
iterate(); iterate(true);
animInterval = setInterval(iterate, 50); if (Bangle.isLCDOn() && (!isScreenAlwaysOn || !Bangle.isLocked())) {
console.log("Starting");
animInterval = setInterval(iterate, 50);
}

View File

@ -1,12 +1,12 @@
{ {
"id": "geissclk", "id": "geissclk",
"name": "Geiss Clock", "name": "Geiss Clock",
"version": "0.04", "version": "0.05",
"description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation", "description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation",
"icon": "clock.png", "icon": "clock.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"geissclk.app.js","url":"clock.js"}, {"name":"geissclk.app.js","url":"clock.js"},
{"name":"geissclk.precompute.js","url":"precompute.js"}, {"name":"geissclk.precompute.js","url":"precompute.js"},

View File

@ -1,6 +1,6 @@
// PALETTES --------------------------- // PALETTES ---------------------------
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n0 / 3"); E.showMessage("Precomputing\npalettes\n\nPlease wait...\n0 / 3");
(function() { // fire (function() { "jit" // fire
for (var i=0;i<256;i++) { for (var i=0;i<256;i++) {
var r = Math.min(i*6,240); var r = Math.min(i*6,240);
var g = Math.min(i*3,240); var g = Math.min(i*3,240);
@ -10,7 +10,7 @@ E.showMessage("Precomputing\npalettes\n\nPlease wait...\n0 / 3");
})() })()
require("Storage").write("geissclk.0.pal",pal.buffer); require("Storage").write("geissclk.0.pal",pal.buffer);
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n1 / 3"); E.showMessage("Precomputing\npalettes\n\nPlease wait...\n1 / 3");
(function() { // gunge (function() { "jit" // gunge
for (var i=0;i<256;i++) { for (var i=0;i<256;i++) {
var r = 0; var r = 0;
var g = Math.min(i*3,255); var g = Math.min(i*3,255);
@ -20,7 +20,7 @@ E.showMessage("Precomputing\npalettes\n\nPlease wait...\n1 / 3");
})() })()
require("Storage").write("geissclk.1.pal",pal.buffer); require("Storage").write("geissclk.1.pal",pal.buffer);
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n2 / 3"); E.showMessage("Precomputing\npalettes\n\nPlease wait...\n2 / 3");
(function() { // rainbow (function() { "jit" // rainbow
for (var i=0;i<256;i++) { for (var i=0;i<256;i++) {
var cl = E.HSBtoRGB((48+i)/128,1,Math.min(i/16,0.9),true); var cl = E.HSBtoRGB((48+i)/128,1,Math.min(i/16,0.9),true);
var r = cl[0]; var r = cl[0];
@ -35,7 +35,7 @@ require("Storage").write("geissclk.2.pal",pal.buffer);
// MAPS ---------------------------------------------- // MAPS ----------------------------------------------
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n0 / 5"); E.showMessage("Precomputing\nmaps\n\nPlease wait...\n0 / 5");
// straight out // straight out
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) { (function() { "jit"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) { for (var x=0;x<W;x++) {
var dx = x-(W/2); var dx = x-(W/2);
var dy = y-(H/2); var dy = y-(H/2);
@ -49,7 +49,7 @@ E.showMessage("Precomputing\nmaps\n\nPlease wait...\n0 / 5");
require("Storage").write("geissclk.0.map",map); require("Storage").write("geissclk.0.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n1 / 5"); E.showMessage("Precomputing\nmaps\n\nPlease wait...\n1 / 5");
// ripple out // ripple out
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) { (function() { "jit"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) { for (var x=0;x<W;x++) {
var dx = x-(W/2); var dx = x-(W/2);
var dy = y-(H/2); var dy = y-(H/2);
@ -63,7 +63,7 @@ E.showMessage("Precomputing\nmaps\n\nPlease wait...\n1 / 5");
require("Storage").write("geissclk.1.map",map); require("Storage").write("geissclk.1.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n2 / 5"); E.showMessage("Precomputing\nmaps\n\nPlease wait...\n2 / 5");
// twisty outwards // twisty outwards
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) { (function() { "jit"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) { for (var x=0;x<W;x++) {
var dx = x-(W/2); var dx = x-(W/2);
var dy = y-(H/2); var dy = y-(H/2);
@ -76,7 +76,7 @@ E.showMessage("Precomputing\nmaps\n\nPlease wait...\n2 / 5");
require("Storage").write("geissclk.2.map",map); require("Storage").write("geissclk.2.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n3 / 5"); E.showMessage("Precomputing\nmaps\n\nPlease wait...\n3 / 5");
// spiral // spiral
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) { (function() { "jit"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) { for (var x=0;x<W;x++) {
var dx = x-(W/2); var dx = x-(W/2);
var dy = y-(H/2); var dy = y-(H/2);
@ -89,7 +89,7 @@ E.showMessage("Precomputing\nmaps\n\nPlease wait...\n3 / 5");
require("Storage").write("geissclk.3.map",map); require("Storage").write("geissclk.3.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n4 / 5"); E.showMessage("Precomputing\nmaps\n\nPlease wait...\n4 / 5");
// blur down // blur down
(function() { "ram"; var n=0; for (var y=0;y<H;y++) { (function() { "jit"; var n=0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) { for (var x=0;x<W;x++) {
map[n++] = 136 - 6*16 + (y&1)*8-4; map[n++] = 136 - 6*16 + (y&1)*8-4;
} }
@ -97,7 +97,7 @@ E.showMessage("Precomputing\nmaps\n\nPlease wait...\n4 / 5");
require("Storage").write("geissclk.4.map",map); require("Storage").write("geissclk.4.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n5 / 5"); E.showMessage("Precomputing\nmaps\n\nPlease wait...\n5 / 5");
// twisty // twisty
(function() { "ram"; var n=0; for (var y=0;y<H;y++) { (function() { "jit"; var n=0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) { for (var x=0;x<W;x++) {
dx = Math.sin(y*0.2); dx = Math.sin(y*0.2);
dy = Math.cos(x*0.2); dy = Math.cos(x*0.2);

View File

@ -14,5 +14,6 @@
{"name":"getup.app.js","url":"app.js"}, {"name":"getup.app.js","url":"app.js"},
{"name":"getup.settings.js","url":"settings.js"}, {"name":"getup.settings.js","url":"settings.js"},
{"name":"getup.img","url":"app-icon.js","evaluate":true} {"name":"getup.img","url":"app-icon.js","evaluate":true}
] ],
"data":[{"name":"getup.settings.json"}]
} }

View File

@ -20,5 +20,6 @@
{"name":"happyclk.app.js","url":"happyclk.app.js"}, {"name":"happyclk.app.js","url":"happyclk.app.js"},
{"name":"happyclk.img","url":"happyclk.icon.js","evaluate":true}, {"name":"happyclk.img","url":"happyclk.icon.js","evaluate":true},
{"name":"happyclk.settings.js","url":"happyclk.settings.js"} {"name":"happyclk.settings.js","url":"happyclk.settings.js"}
] ],
"data":[{"name":"happyclk.setting.json"}]
} }

View File

@ -4,7 +4,7 @@
"shortName":"HR Alarm", "shortName":"HR Alarm",
"version":"0.02", "version":"0.02",
"description": "This invisible widget vibrates whenever the heart rate gets close to the upper limit or goes over or under the configured limits", "description": "This invisible widget vibrates whenever the heart rate gets close to the upper limit or goes over or under the configured limits",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget", "tags": "widget",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
@ -12,5 +12,8 @@
"storage": [ "storage": [
{"name":"hralarm.wid.js","url":"widget.js"}, {"name":"hralarm.wid.js","url":"widget.js"},
{"name":"hralarm.settings.js","url":"settings.js"} {"name":"hralarm.settings.js","url":"settings.js"}
],
"data": [
{"name":"hralarm.json"}
] ]
} }

View File

@ -15,5 +15,8 @@
{"name":"imageclock.app.js","url":"app.js"}, {"name":"imageclock.app.js","url":"app.js"},
{"name":"imageclock.settings.js","url":"settings.js"}, {"name":"imageclock.settings.js","url":"settings.js"},
{"name":"imageclock.img","url":"app-icon.js","evaluate":true} {"name":"imageclock.img","url":"app-icon.js","evaluate":true}
],
"data": [
{"name":"imageclock.json"}
] ]
} }

View File

@ -12,5 +12,6 @@
{"name":"textinput","url":"lib.js"}, {"name":"textinput","url":"lib.js"},
{"name":"kbtouch.settings.js","url":"settings.js"} {"name":"kbtouch.settings.js","url":"settings.js"}
], ],
"sortorder":-1 "sortorder":-1,
"data":[{"name":"kbtouch.settings.json"}]
} }

View File

@ -24,4 +24,6 @@
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. 0.25: Add more colors to the settings and add the ability to disable the data charts+Markup.
0.26: Use widget_utils. 0.26: Use widget_utils.
0.27: Report latest HRM rather than HRM 10 minutes ago (fix #2395) 0.27: Report latest HRM rather than HRM 10 minutes ago (fix #2395)
0.28: Battery Vref implemented correctly.
0.29: Support fastload.

View File

@ -25,7 +25,8 @@ the "sched" app must be installed on your device.
## Data that can be configured ## Data that can be configured
* Steps - Steps loaded via the wpedom app. * Steps - Steps loaded via the wpedom app.
* Battery - Current battery level in % * Battery - Current battery level in %
* VREF - Voltage of battery * BattVolt - Voltage of battery
* VREF - Internal Voltage Reference
* HRM - Last measured HRM * HRM - Last measured HRM
* Temp - Weather temperature loaded via the weather module + gadgetbridge * Temp - Weather temperature loaded via the weather module + gadgetbridge
* Humidity - Humidity loaded via the weather module + gadgetbridge * Humidity - Humidity loaded via the weather module + gadgetbridge

View File

@ -1,7 +1,8 @@
{
const TIMER_IDX = "lcars"; const TIMER_IDX = "lcars";
const SETTINGS_FILE = "lcars.setting.json"; const SETTINGS_FILE = "lcars.setting.json";
const locale = require('locale'); const locale = require('locale');
const storage = require('Storage') const storage = require('Storage');
const widget_utils = require('widget_utils'); const widget_utils = require('widget_utils');
let settings = { let settings = {
alarm: -1, alarm: -1,
@ -18,7 +19,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];
} }
/* /*
@ -36,10 +37,10 @@ let cGrey = "#424242";
*/ */
let lcarsViewPos = 0; let lcarsViewPos = 0;
// let hrmValue = 0; // let hrmValue = 0;
var plotMonth = false; let plotMonth = false;
function convert24to16(input) let convert24to16 = function(input)
{ {
let RGB888 = parseInt(input.replace(/^#/, ''), 16); let RGB888 = parseInt(input.replace(/^#/, ''), 16);
let r = (RGB888 & 0xFF0000) >> 16; let r = (RGB888 & 0xFF0000) >> 16;
@ -55,17 +56,17 @@ function convert24to16(input)
RGB565 = RGB565 | b; RGB565 = RGB565 | b;
return "0x"+RGB565.toString(16); return "0x"+RGB565.toString(16);
} };
var color1C = convert24to16(color1);//Converting colors to the correct format. let color1C = convert24to16(color1);//Converting colors to the correct format.
var color2C = convert24to16(color2); let color2C = convert24to16(color2);
var color3C = convert24to16(color3); let color3C = convert24to16(color3);
/* /*
* Requirements and globals * Requirements and globals
*/ */
var colorPalette = new Uint16Array([//Used to change the color of the image if the user selects a color that is diffrent than the default. let 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
@ -84,73 +85,73 @@ var colorPalette = new Uint16Array([//Used to change the color of the image if t
0x0000 // not used 0x0000 // not used
],0,1); ],0,1);
var bgLeftFullscreen = { let bgLeftFullscreen = {
width : 27, height : 176, bpp : 3, width : 27, height : 176, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAoUP7y/KH5QGDoQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyDCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4"))), buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAoUP7y/KH5QGDoQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyDCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4"))),
palette: colorPalette palette: colorPalette
}; };
var bgLeftNotFullscreen = { let bgLeftNotFullscreen = {
width : 27, height : 152, bpp : 3, width : 27, height : 152, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=="))), buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=="))),
palette: colorPalette palette: colorPalette
}; };
var bgRightFullscreen = { let bgRightFullscreen = {
width : 27, height : 176, bpp : 3, width : 27, height : 176, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAnUP7y/KH4yGeVYAJ0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkp4CS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A=="))), buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAnUP7y/KH4yGeVYAJ0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkp4CS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A=="))),
palette: colorPalette palette: colorPalette
}; };
var bgRightNotFullscreen = { let bgRightNotFullscreen = {
width : 27, height : 152, bpp : 3, width : 27, height : 152, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAx0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A="))), buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAx0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A="))),
palette: colorPalette palette: colorPalette
}; };
var bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen; let bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen;
var bgRight= settings.fullscreen ? bgRightFullscreen : bgRightNotFullscreen; let bgRight= settings.fullscreen ? bgRightFullscreen : bgRightNotFullscreen;
var iconEarth = { let iconEarth = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
buffer : require("heatshrink").decompress(atob("AFtx48ECBsDwU5k/yhARLjgjBjlzAQMQEZcIkOP/fn31IEZgCBnlz58cEpM4geugEgwU/8+WNZJHDuHHvgmBCQ8goEOnVgJoMnyV58mACItHI4X8uAFBuVHnnz4BuGxk4////Egz3IkmWvPgNw8f/prB//BghTC+AjE7848eMjNnzySBwUJkmf/BuGuPDAQIjBiPHhhTCSQnjMo0ITANJn44Dg8MuFBggCCiFBcAJ0Bv5xEh+ITo2OhHkyf/OIQdBWwVHhgjBNwUE+fP/5EEgePMoYLBhMgyVJk/+BQQdC688I4XxOIc8v//NAvr+QEBj/5NwKVBy1/QYUciPBhk1EAJrC+KeC489QYaMBgU/8BNB9+ChEjz1Jkn/QYMBDQIgCcYTCCiP/nlzJQmenMAgV4//uy/9wRaB/1J8iVCcAfHjt9TYYICnhKCgRKBw159/v//r927OIeeoASBDQccvv3791KYVDBYPLJQeCnPnz//AAP6ocEjEkXgMgJQtz79fLAP8KYkccAcJ8Gf/f/xu/cAMQ4eP5MlyQRCMolx40YsOGBAPfnnzU4KVDpKMBvz8Dh0/8me7IICgkxJQXPIgZTD58sEgcJk+eNoONnFBhk4/5uB/pcDg5KD+4mEv4CBXISVDhEn31/8/+mH7x//JQK5CAAMB4JBCnnxJQf/+fJEgkAa4L+CAQOOjMn/1bXIRxDJQXx58f//Hhlz/88EgsChMgz/Zs/+nfkyV/8huDOI6SD498NwoACi1Z8+S/Plz17/+QCI7jC+ZxBmfPnojIAAMDcYWSp//2wRJEwq2GABECjMgNYwAmA=")) buffer : require("heatshrink").decompress(atob("AFtx48ECBsDwU5k/yhARLjgjBjlzAQMQEZcIkOP/fn31IEZgCBnlz58cEpM4geugEgwU/8+WNZJHDuHHvgmBCQ8goEOnVgJoMnyV58mACItHI4X8uAFBuVHnnz4BuGxk4////Egz3IkmWvPgNw8f/prB//BghTC+AjE7848eMjNnzySBwUJkmf/BuGuPDAQIjBiPHhhTCSQnjMo0ITANJn44Dg8MuFBggCCiFBcAJ0Bv5xEh+ITo2OhHkyf/OIQdBWwVHhgjBNwUE+fP/5EEgePMoYLBhMgyVJk/+BQQdC688I4XxOIc8v//NAvr+QEBj/5NwKVBy1/QYUciPBhk1EAJrC+KeC489QYaMBgU/8BNB9+ChEjz1Jkn/QYMBDQIgCcYTCCiP/nlzJQmenMAgV4//uy/9wRaB/1J8iVCcAfHjt9TYYICnhKCgRKBw159/v//r927OIeeoASBDQccvv3791KYVDBYPLJQeCnPnz//AAP6ocEjEkXgMgJQtz79fLAP8KYkccAcJ8Gf/f/xu/cAMQ4eP5MlyQRCMolx40YsOGBAPfnnzU4KVDpKMBvz8Dh0/8me7IICgkxJQXPIgZTD58sEgcJk+eNoONnFBhk4/5uB/pcDg5KD+4mEv4CBXISVDhEn31/8/+mH7x//JQK5CAAMB4JBCnnxJQf/+fJEgkAa4L+CAQOOjMn/1bXIRxDJQXx58f//Hhlz/88EgsChMgz/Zs/+nfkyV/8huDOI6SD498NwoACi1Z8+S/Plz17/+QCI7jC+ZxBmfPnojIAAMDcYWSp//2wRJEwq2GABECjMgNYwAmA="))
}; };
var iconSaturn = { let iconSaturn = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 1, transparent : 1,
buffer : require("heatshrink").decompress(atob("AH4A/AEkQuPHCJ0ChEAwARNjAjBjgjOhs06Q2OEYVx4ARMhEggUMkANIDoIgBoEEgEBNxJEC6ZrBAAMwNxAjDNYcHNxIjB7dtEwIHBwRoKj158+cuPEjlwCRAjC23bpu0wRNDAAsHEYWeEwaSJ6YjCAQUNSRQjEzxQBWZMNEYlsmg2JWAIjCz95SoJuJggjDtuw6dMG5JKCz998wFBJRVNEYW0yaVBJRNhJQN9+4pCzhKJmBKC4YpB/fINxIgCzFxSoQ3J4ENm3CAQPb98wbpEcAQMYWwKYBNxMDXgc2/fv3g2IEAOAgAjBjy5CEhEMfYICBgfPnjdLjj+CgMHiC3JknDhhoINw4jCAB0IJQIANR4QjPAH4A/AFA")) buffer : require("heatshrink").decompress(atob("AH4A/AEkQuPHCJ0ChEAwARNjAjBjgjOhs06Q2OEYVx4ARMhEggUMkANIDoIgBoEEgEBNxJEC6ZrBAAMwNxAjDNYcHNxIjB7dtEwIHBwRoKj158+cuPEjlwCRAjC23bpu0wRNDAAsHEYWeEwaSJ6YjCAQUNSRQjEzxQBWZMNEYlsmg2JWAIjCz95SoJuJggjDtuw6dMG5JKCz998wFBJRVNEYW0yaVBJRNhJQN9+4pCzhKJmBKC4YpB/fINxIgCzFxSoQ3J4ENm3CAQPb98wbpEcAQMYWwKYBNxMDXgc2/fv3g2IEAOAgAjBjy5CEhEMfYICBgfPnjdLjj+CgMHiC3JknDhhoINw4jCAB0IJQIANR4QjPAH4A/AFA"))
}; };
var iconMoon = { let iconMoon = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 1, transparent : 1,
buffer : require("heatshrink").decompress(atob("AH4AQjlx44CCCZsg8eOkHDwAQKEYgmPhEgEQM48AOIgMHEYoCB4ATI8UAmH/x04JoRuJsImHuBKLn37EwZuIgEQOI8cEpXj/yYBhE8+YNGgkYoJxITBUPnAaC///nC+FjBuIOJZEB8YeCh/8AoYACoMEEAnEjhQDPQJKJ/DCDAoi5DoLdHAoMQgLjFWYPOnngh02IwXzwDjEgPGEYS8BI4MBYoSVG4fP/nghkAgZrDkngJQqSG4gvBg4sBQgkImHihEAWwP8ZBMBEYl5/+cSoVAGQIUFh04weJn///0gj/OEw5KEz45BzhuCTYQAEgePB4IACAoJuBnAQEa4XHjxKB//xFgWHJQsCRgMDEonipwjENwUBDQNx8+evvn/hTDLw3igE+EgZxB8UOXIvEJQUfEYOfv53DEQkgga5BJQvzx84cAj+CDoNh8/eEYJKDuCSEcocnEon+/7xEgFBIIcfB4Mf/IICXI2DgDdBAAn758gCIq5Dv4zBvJuIOIfjEgvP/ARHgwdCB4P3AoTdFAAk4EYk8SQgAFTALaDSQwAGh08//vnDmBABYmEEZYAzA==")) buffer : require("heatshrink").decompress(atob("AH4AQjlx44CCCZsg8eOkHDwAQKEYgmPhEgEQM48AOIgMHEYoCB4ATI8UAmH/x04JoRuJsImHuBKLn37EwZuIgEQOI8cEpXj/yYBhE8+YNGgkYoJxITBUPnAaC///nC+FjBuIOJZEB8YeCh/8AoYACoMEEAnEjhQDPQJKJ/DCDAoi5DoLdHAoMQgLjFWYPOnngh02IwXzwDjEgPGEYS8BI4MBYoSVG4fP/nghkAgZrDkngJQqSG4gvBg4sBQgkImHihEAWwP8ZBMBEYl5/+cSoVAGQIUFh04weJn///0gj/OEw5KEz45BzhuCTYQAEgePB4IACAoJuBnAQEa4XHjxKB//xFgWHJQsCRgMDEonipwjENwUBDQNx8+evvn/hTDLw3igE+EgZxB8UOXIvEJQUfEYOfv53DEQkgga5BJQvzx84cAj+CDoNh8/eEYJKDuCSEcocnEon+/7xEgFBIIcfB4Mf/IICXI2DgDdBAAn758gCIq5Dv4zBvJuIOIfjEgvP/ARHgwdCB4P3AoTdFAAk4EYk8SQgAFTALaDSQwAGh08//vnDmBABYmEEZYAzA=="))
}; };
var iconMars = { let iconMars = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 1, transparent : 1,
buffer : require("heatshrink").decompress(atob("AH4ATjlwCJ+Dh0wwAQMg0cuPHjFhCZkDps0yVJkmQCBMEjFx42atOmzQmLhMkEYQCCCREQoOGEYmmzB0IEY4CBkARGoJKBEYQCEzgSGkGSpAjDyYCCphuGiFhJQgCD8ASFgRHGAQKbB6BuHJRGeOIsINxEk6dNmARDgMEjQjHAQPnVQojIyZKB6YSDNwK5FAQt54BuDXJIjBEwK5EgxKKXgq5BJRdgXIojJAQJKMcAM0EwM2JUApDoCVFExa7FkGCgAmIkAREEwUEjAmHCIgABhEggQmFpACBCIojBEwRQCzVhwkQU4YADgQmBwQCCI4IFBCAojFAQojGJQQjDAQgRGEZICBEo4gFyUIkilFJQUYEAZrBAQMYNw5KDSQSbCNwwABgOGEwgCBsPACQ5xGwdNnARJcAVh48evvnCJK8Chs+/fv33gCRcB48cuPHCBYA/ADAA==")) buffer : require("heatshrink").decompress(atob("AH4ATjlwCJ+Dh0wwAQMg0cuPHjFhCZkDps0yVJkmQCBMEjFx42atOmzQmLhMkEYQCCCREQoOGEYmmzB0IEY4CBkARGoJKBEYQCEzgSGkGSpAjDyYCCphuGiFhJQgCD8ASFgRHGAQKbB6BuHJRGeOIsINxEk6dNmARDgMEjQjHAQPnVQojIyZKB6YSDNwK5FAQt54BuDXJIjBEwK5EgxKKXgq5BJRdgXIojJAQJKMcAM0EwM2JUApDoCVFExa7FkGCgAmIkAREEwUEjAmHCIgABhEggQmFpACBCIojBEwRQCzVhwkQU4YADgQmBwQCCI4IFBCAojFAQojGJQQjDAQgRGEZICBEo4gFyUIkilFJQUYEAZrBAQMYNw5KDSQSbCNwwABgOGEwgCBsPACQ5xGwdNnARJcAVh48evvnCJK8Chs+/fv33gCRcB48cuPHCBYA/ADAA=="))
}; };
var iconSatellite = { let iconSatellite = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 2, transparent : 2,
buffer : require("heatshrink").decompress(atob("pMkyQC/ATGXhIRPyNl0gmPjlwCJ9ly1aCJ1c+fHJR1Hy1ZJR1I+fPnlx6QRLpe+/JKBr5KMuYjBJQMdCJce/fvJQW0CJUlEYQCBSpvvJQbXJjl0NwnzNxGQwEOnHhgF78+WqQyIrFx48cAQXz4ShJgAABh0+8cP//9LJEhg4jDuP3//0LhGQgYlBgeAn///5cIy8MuAmDCIP/9I4HkmCEYMOgHfCQWkCI0cuBuDgF/CIP+CI1Ny1IkeAgHANwIAB/QRFrj7BhkxEwQRC/4RFpbXDgSVBg4RCSorXDI4MJAQMfCIP8cwImDn37fwN58+kwHgLgSVFub7CI4NyBAJKDLgkuEYX78+evKtCLg0jEYRKC58JMoRcFkwjDJQTFDl65EkojEAQMdcwn/+gFC3YjEJQLXEpYRDWwQmEdI6SHAQO0CJUkx4jDF4gCIJQgRMXIjCEARIjCCJ2XEYPKCJqJBJQIROcAUpCJ0kybaDARtdCKAC2kAA=")) buffer : require("heatshrink").decompress(atob("pMkyQC/ATGXhIRPyNl0gmPjlwCJ9ly1aCJ1c+fHJR1Hy1ZJR1I+fPnlx6QRLpe+/JKBr5KMuYjBJQMdCJce/fvJQW0CJUlEYQCBSpvvJQbXJjl0NwnzNxGQwEOnHhgF78+WqQyIrFx48cAQXz4ShJgAABh0+8cP//9LJEhg4jDuP3//0LhGQgYlBgeAn///5cIy8MuAmDCIP/9I4HkmCEYMOgHfCQWkCI0cuBuDgF/CIP+CI1Ny1IkeAgHANwIAB/QRFrj7BhkxEwQRC/4RFpbXDgSVBg4RCSorXDI4MJAQMfCIP8cwImDn37fwN58+kwHgLgSVFub7CI4NyBAJKDLgkuEYX78+evKtCLg0jEYRKC58JMoRcFkwjDJQTFDl65EkojEAQMdcwn/+gFC3YjEJQLXEpYRDWwQmEdI6SHAQO0CJUkx4jDF4gCIJQgRMXIjCEARIjCCJ2XEYPKCJqJBJQIROcAUpCJ0kybaDARtdCKAC2kAA="))
}; };
var iconCharging = { let iconCharging = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 5, transparent : 5,
buffer : require("heatshrink").decompress(atob("23btugAwUBtoICARG0h048eODQYCJ6P/AAUCCJfbo4SDxYRLtEcuPHjlwgoRJ7RnIloUHoYjDAQfAExEAwUIkACEkSAIEYwCBhZKH6EIJI0CJRFHEY0BJRWBSgf//0AJRYSE4BKLj4SE8BKLv4RD/hK/JS2AXY0gXwRKG4cMmACCJQMAg8csEFJQsBAwfasEAm379u0gFbcBfHzgFBz1xMQZKBjY/D0E2+BOChu26yVEEYdww+cgAFCg+cgIfB6RKF4HbgEIkGChEAthfCJQ0eEAIjBBAMxk6GCJQtgtyVBwRKBAQMbHAJKGXIIFCgACBhl54qVG2E+EAJKBJoWAm0WJQ6SCXgdxFgMLJQvYjeAEAUwFIUitEtJQ14NwUHgEwKYZKGwOwNYX7XgWCg3CJQ5rB4MevPnAoPDJRJrCgEG/ECAoNsJRUwoEesIIBiJKI3CVDti/CJRKVDiJHBSo0YsOGjED8AjBcAcIgdhcAXAPIUAcAYIBcA4dBAQUG8BrBgBuCgOwcBEeXIK2BBAIFBgRqBGoYAChq8CcYUE4FbUYOACQsHzgjDgwFBCIImBAQsDtwYD7cAloRI22B86YBw5QBgoRJ7dAgYEDCJaeBJoMcsARMAQNoJIIRE6A")) buffer : require("heatshrink").decompress(atob("23btugAwUBtoICARG0h048eODQYCJ6P/AAUCCJfbo4SDxYRLtEcuPHjlwgoRJ7RnIloUHoYjDAQfAExEAwUIkACEkSAIEYwCBhZKH6EIJI0CJRFHEY0BJRWBSgf//0AJRYSE4BKLj4SE8BKLv4RD/hK/JS2AXY0gXwRKG4cMmACCJQMAg8csEFJQsBAwfasEAm379u0gFbcBfHzgFBz1xMQZKBjY/D0E2+BOChu26yVEEYdww+cgAFCg+cgIfB6RKF4HbgEIkGChEAthfCJQ0eEAIjBBAMxk6GCJQtgtyVBwRKBAQMbHAJKGXIIFCgACBhl54qVG2E+EAJKBJoWAm0WJQ6SCXgdxFgMLJQvYjeAEAUwFIUitEtJQ14NwUHgEwKYZKGwOwNYX7XgWCg3CJQ5rB4MevPnAoPDJRJrCgEG/ECAoNsJRUwoEesIIBiJKI3CVDti/CJRKVDiJHBSo0YsOGjED8AjBcAcIgdhcAXAPIUAcAYIBcA4dBAQUG8BrBgBuCgOwcBEeXIK2BBAIFBgRqBGoYAChq8CcYUE4FbUYOACQsHzgjDgwFBCIImBAQsDtwYD7cAloRI22B86YBw5QBgoRJ7dAgYEDCJaeBJoMcsARMAQNoJIIRE6A"))
}; };
var iconWarning = { let iconWarning = {
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 1, transparent : 1,
buffer : require("heatshrink").decompress(atob("kmSpIC/AWMyoQIFsmECJFJhMmA4QXByVICIwODAQ4RRFIQGD5JVLkIGDzJqMyAGDph8MiRKGyApEAoZKFyYIDQwMkSQNkQZABBhIIOOJRuEL5gRIAUKACVQMhmUSNYNDQYJTBBwYFByGTkOE5FJWYNMknCAQKYCiaSCpmGochDoSYBhMwTAZrChILBhmEzKPBF4ImBTAREBDoMmEwJVDoYjBycJFgWEJQRuLJQ1kmQCCjJlCBYbjCagaDBwyDBmBuBF4TjJAUQKINBChCDQxZBcZIIQF4NIgEAgKSDiQmEVQKMBoARBAAMCSQLLBVoxqKL4gaCChVCNwoRKOIo4CJIgABBoSMHpIRFgDdJOIJUBCAUJRgJuEAQb+DIIgRIAX4C/ASOQA")) buffer : require("heatshrink").decompress(atob("kmSpIC/AWMyoQIFsmECJFJhMmA4QXByVICIwODAQ4RRFIQGD5JVLkIGDzJqMyAGDph8MiRKGyApEAoZKFyYIDQwMkSQNkQZABBhIIOOJRuEL5gRIAUKACVQMhmUSNYNDQYJTBBwYFByGTkOE5FJWYNMknCAQKYCiaSCpmGochDoSYBhMwTAZrChILBhmEzKPBF4ImBTAREBDoMmEwJVDoYjBycJFgWEJQRuLJQ1kmQCCjJlCBYbjCagaDBwyDBmBuBF4TjJAUQKINBChCDQxZBcZIIQF4NIgEAgKSDiQmEVQKMBoARBAAMCSQLLBVoxqKL4gaCChVCNwoRKOIo4CJIgABBoSMHpIRFgDdJOIJUBCAUJRgJuEAQb+DIIgRIAX4C/ASOQA"))
@ -172,26 +173,26 @@ Graphics.prototype.setFontAntonioLarge = function(scale) {
/* /*
* Draw watch face * Draw watch face
*/ */
var drawTimeout; let drawTimeout;
function queueDraw() { let queueDraw = function() {
// Faster updates during alarm to ensure that it is // Faster updates during alarm to ensure that it is
// shown correctly... // shown correctly...
var timeout = isAlarmEnabled() ? 10000 : 60000; let timeout = isAlarmEnabled() ? 10000 : 60000;
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() { drawTimeout = setTimeout(function() {
drawTimeout = undefined; drawTimeout = undefined;
draw(); draw();
}, timeout - (Date.now() % timeout)); }, timeout - (Date.now() % timeout));
} };
/** /**
* This function plots a data row in LCARS style. * This function plots a data row in LCARS style.
* Note: It can be called async and therefore, the text alignment and * Note: It can be called async and therefore, the text alignment and
* font is set each time the function is called. * font is set each time the function is called.
*/ */
function printRow(text, value, y, c){ let printRow = function(text, value, y, c){
g.setFontAntonioMedium(); g.setFontAntonioMedium();
g.setFontAlign(-1,-1,0); g.setFontAlign(-1,-1,0);
@ -210,23 +211,23 @@ function printRow(text, value, y, c){
g.setColor(c); g.setColor(c);
g.setFontAlign(1,-1,0); g.setFontAlign(1,-1,0);
g.drawString(value, 126, y); g.drawString(value, 126, y);
} };
function drawData(key, y, c){ let drawData = function(key, y, c){
try{ try{
_drawData(key, y, c); _drawData(key, y, c);
} catch(ex){ } catch(ex){
// Show last error - next try hopefully works. // Show last error - next try hopefully works.
} }
} };
function _drawData(key, y, c){ let _drawData = function(key, y, c){
key = key.toUpperCase() key = key.toUpperCase()
var text = key; let text = key;
var value = "ERR"; let value = "ERR";
var should_print= true; let should_print= true;
if(key == "STEPS"){ if(key == "STEPS"){
text = "STEP"; text = "STEP";
@ -239,21 +240,24 @@ function _drawData(key, y, c){
} else if (key == "VREF"){ } else if (key == "VREF"){
value = E.getAnalogVRef().toFixed(2) + "V"; value = E.getAnalogVRef().toFixed(2) + "V";
} else if (key =="BATTVOLT" ) {
text = "BATV";
value = (E.getAnalogVRef()*analogRead(3)*4).toFixed(2) + "V";
} else if(key == "HRM"){ } else if(key == "HRM"){
value = Math.round(Bangle.getHealthStatus().bpm||Bangle.getHealthStatus("last").bpm); value = Math.round(Bangle.getHealthStatus().bpm||Bangle.getHealthStatus("last").bpm);
} else if (key == "TEMP"){ } else if (key == "TEMP"){
var weather = getWeather(); let weather = getWeather();
value = weather.temp; value = weather.temp;
} else if (key == "HUMIDITY"){ } else if (key == "HUMIDITY"){
text = "HUM"; text = "HUM";
var weather = getWeather(); let weather = getWeather();
value = weather.hum; value = weather.hum;
} else if (key == "WIND"){ } else if (key == "WIND"){
text = "WND"; text = "WND";
var weather = getWeather(); let weather = getWeather();
value = weather.wind; value = weather.wind;
} else if (key == "ALTITUDE"){ } else if (key == "ALTITUDE"){
@ -277,18 +281,18 @@ function _drawData(key, y, c){
if(should_print){ if(should_print){
printRow(text, value, y, c); printRow(text, value, y, c);
} }
} };
function drawHorizontalBgLine(color, x1, x2, y, h){ let drawHorizontalBgLine = function(color, x1, x2, y, h){
g.setColor(color); g.setColor(color);
for(var i=0; i<h; i++){ for(let i=0; i<h; i++){
g.drawLine(x1, y+i, x2,y+i); g.drawLine(x1, y+i, x2,y+i);
} }
} };
function drawInfo(){ let drawInfo = function(){
if(lcarsViewPos != 0 || !settings.fullscreen){ if(lcarsViewPos != 0 || !settings.fullscreen){
return; return;
} }
@ -310,9 +314,9 @@ function drawInfo(){
g.setColor(color3); g.setColor(color3);
g.drawString("LOCK", 128, 53); g.drawString("LOCK", 128, 53);
} }
} };
function drawState(){ let drawState = function(){
if(lcarsViewPos != 0){ if(lcarsViewPos != 0){
return; return;
} }
@ -322,11 +326,11 @@ function drawState(){
g.setFontAntonioMedium(); g.setFontAntonioMedium();
if(!isAlarmEnabled()){ if(!isAlarmEnabled()){
var bat = E.getBattery(); let bat = E.getBattery();
var flash = storage.getFree() / process.env.STORAGE; let flash = storage.getFree() / process.env.STORAGE;
var current = new Date(); let current = new Date();
var hours = current.getHours(); let hours = current.getHours();
var iconMsg = let iconMsg =
Bangle.isCharging() ? { icon: iconCharging, text: "STATUS" } : Bangle.isCharging() ? { icon: iconCharging, text: "STATUS" } :
bat < 30 ? { icon: iconWarning, text: "BAT" } : bat < 30 ? { icon: iconWarning, text: "BAT" } :
flash < 0.1 ? { icon: iconWarning, text: "DISK" } : flash < 0.1 ? { icon: iconWarning, text: "DISK" } :
@ -348,12 +352,12 @@ function drawState(){
} }
g.setFontAlign(-1, -1, 0); g.setFontAlign(-1, -1, 0);
} };
function drawPosition0(){ let drawPosition0 = function(){
// Draw background image // Draw background image
var offset = settings.fullscreen ? 0 : 24; let offset = settings.fullscreen ? 0 : 24;
g.drawImage(bgLeft, 0, offset); g.drawImage(bgLeft, 0, offset);
drawHorizontalBgLine(color1, 25, 120, offset, 4); drawHorizontalBgLine(color1, 25, 120, offset, 4);
drawHorizontalBgLine(color1, 130, 176, offset, 4); drawHorizontalBgLine(color1, 130, 176, offset, 4);
@ -363,13 +367,13 @@ function drawPosition0(){
drawHorizontalBgLine(color2, 120, 176, 87, 4); drawHorizontalBgLine(color2, 120, 176, 87, 4);
// The last line is a battery indicator too // The last line is a battery indicator too
var bat = E.getBattery() / 100.0; let bat = E.getBattery() / 100.0;
var batStart = 19; let batStart = 19;
var batWidth = 172 - batStart; let batWidth = 172 - batStart;
var batX2 = parseInt(batWidth * bat + batStart); let batX2 = parseInt(batWidth * bat + batStart);
drawHorizontalBgLine(color2, batStart, batX2, 171, 5); drawHorizontalBgLine(color2, batStart, batX2, 171, 5);
drawHorizontalBgLine(cGrey, batX2, 172, 171, 5); drawHorizontalBgLine(cGrey, batX2, 172, 171, 5);
for(var i=0; i+batStart<=172; i+=parseInt(batWidth/4)){ for(let i=0; i+batStart<=172; i+=parseInt(batWidth/4)){
drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8) drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8)
} }
@ -379,8 +383,8 @@ function drawPosition0(){
// Write time // Write time
g.setFontAlign(-1, -1, 0); g.setFontAlign(-1, -1, 0);
g.setColor(cWhite); g.setColor(cWhite);
var currentDate = new Date(); let currentDate = new Date();
var timeStr = locale.time(currentDate,1); let timeStr = locale.time(currentDate,1);
g.setFontAntonioLarge(); g.setFontAntonioLarge();
if(settings.fullscreen){ if(settings.fullscreen){
g.drawString(timeStr, 27, 10); g.drawString(timeStr, 27, 10);
@ -392,13 +396,13 @@ function drawPosition0(){
g.setColor(cWhite); g.setColor(cWhite);
g.setFontAntonioMedium(); g.setFontAntonioMedium();
if(settings.fullscreen){ if(settings.fullscreen){
var dayStr = locale.dow(currentDate, true).toUpperCase(); let dayStr = locale.dow(currentDate, true).toUpperCase();
dayStr += " " + currentDate.getDate(); dayStr += " " + currentDate.getDate();
dayStr += " " + locale.month(currentDate, 1).toUpperCase(); dayStr += " " + locale.month(currentDate, 1).toUpperCase();
g.drawString(dayStr, 30, 56); g.drawString(dayStr, 30, 56);
} else { } else {
var dayStr = locale.dow(currentDate, true).toUpperCase(); let dayStr = locale.dow(currentDate, true).toUpperCase();
var date = currentDate.getDate(); let date = currentDate.getDate();
g.drawString(dayStr, 128, 35); g.drawString(dayStr, 128, 35);
g.drawString(date, 128, 55); g.drawString(date, 128, 55);
} }
@ -412,11 +416,11 @@ function drawPosition0(){
// Draw state // Draw state
drawState(); drawState();
} };
function drawPosition1(){ let drawPosition1 = function(){
// Draw background image // Draw background image
var offset = settings.fullscreen ? 0 : 24; let offset = settings.fullscreen ? 0 : 24;
g.drawImage(bgRight, 149, offset); g.drawImage(bgRight, 149, offset);
if(settings.fullscreen){ if(settings.fullscreen){
drawHorizontalBgLine(color1, 0, 140, offset, 4); drawHorizontalBgLine(color1, 0, 140, offset, 4);
@ -444,8 +448,8 @@ function drawPosition1(){
// Plot HRM graph // Plot HRM graph
if(plotMonth){ if(plotMonth){
var data = new Uint16Array(32); let data = new Uint16Array(32);
var cnt = new Uint8Array(32); let cnt = new Uint8Array(32);
health.readDailySummaries(new Date(), h=>{ health.readDailySummaries(new Date(), h=>{
data[h.day]+=h.bpm; data[h.day]+=h.bpm;
if (h.bpm) cnt[h.day]++; if (h.bpm) cnt[h.day]++;
@ -462,9 +466,9 @@ function drawPosition1(){
}); });
// Plot step graph // Plot step graph
var data = new Uint16Array(32); data = new Uint16Array(32);
health.readDailySummaries(new Date(), h=>data[h.day]+=h.steps/1000); health.readDailySummaries(new Date(), h=>data[h.day]+=h.steps/1000);
var gridY = parseInt(Math.max.apply(Math, data)/2); let gridY = parseInt(Math.max.apply(Math, data)/2);
gridY = gridY <= 0 ? 1 : gridY; gridY = gridY <= 0 ? 1 : gridY;
require("graph").drawBar(g, data, { require("graph").drawBar(g, data, {
axes : true, axes : true,
@ -490,8 +494,8 @@ function drawPosition1(){
// Plot day // Plot day
} else { } else {
var data = new Uint16Array(24); let data = new Uint16Array(24);
var cnt = new Uint8Array(24); let cnt = new Uint8Array(24);
health.readDay(new Date(), h=>{ health.readDay(new Date(), h=>{
data[h.hr]+=h.bpm; data[h.hr]+=h.bpm;
if (h.bpm) cnt[h.hr]++; if (h.bpm) cnt[h.hr]++;
@ -508,9 +512,9 @@ function drawPosition1(){
}); });
// Plot step graph // Plot step graph
var data = new Uint16Array(24); data = new Uint16Array(24);
health.readDay(new Date(), h=>data[h.hr]+=h.steps); health.readDay(new Date(), h=>data[h.hr]+=h.steps);
var gridY = parseInt(Math.max.apply(Math, data)/1000)*1000; let gridY = parseInt(Math.max.apply(Math, data)/1000)*1000;
gridY = gridY <= 0 ? 1000 : gridY; gridY = gridY <= 0 ? 1000 : gridY;
require("graph").drawBar(g, data, { require("graph").drawBar(g, data, {
axes : true, axes : true,
@ -534,9 +538,9 @@ function drawPosition1(){
g.drawString("DAY", 154, 115); g.drawString("DAY", 154, 115);
} }
} }
} };
function draw(){ let draw = function(){
// Queue draw first to ensure that its called in one minute again. // Queue draw first to ensure that its called in one minute again.
queueDraw(); queueDraw();
@ -557,14 +561,14 @@ function draw(){
} else { } else {
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
} };
/* /*
* Step counter via widget * Step counter via widget
*/ */
function getSteps() { let getSteps = function() {
var steps = 0; let steps = 0;
try{ try{
if (WIDGETS.wpedom !== undefined) { if (WIDGETS.wpedom !== undefined) {
steps = WIDGETS.wpedom.getSteps(); steps = WIDGETS.wpedom.getSteps();
@ -578,15 +582,15 @@ function getSteps() {
} }
return steps; return steps;
} };
function getWeather(){ let getWeather = function(){
var weatherJson; let weatherJson;
try { try {
weatherJson = storage.readJSON('weather.json'); weatherJson = storage.readJSON('weather.json');
var weather = weatherJson.weather; let weather = weatherJson.weather;
// Temperature // Temperature
weather.temp = locale.temp(weather.temp-273.15); weather.temp = locale.temp(weather.temp-273.15);
@ -596,7 +600,7 @@ function getWeather(){
// Wind // Wind
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/); const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934; let speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934;
weather.wind = Math.round(wind[1] * speedFactor); weather.wind = Math.round(wind[1] * speedFactor);
return weather return weather
@ -613,15 +617,15 @@ function getWeather(){
wdir: " ? ", wdir: " ? ",
wrose: " ? " wrose: " ? "
}; };
} };
/* /*
* Handle alarm * Handle alarm
*/ */
function isAlarmEnabled(){ let isAlarmEnabled = function(){
try{ try{
var alarm = require('sched'); let alarm = require('sched');
var alarmObj = alarm.getAlarm(TIMER_IDX); let alarmObj = alarm.getAlarm(TIMER_IDX);
if(alarmObj===undefined || !alarmObj.on){ if(alarmObj===undefined || !alarmObj.on){
return false; return false;
} }
@ -630,35 +634,35 @@ function isAlarmEnabled(){
} catch(ex){ } } catch(ex){ }
return false; return false;
} };
function getAlarmMinutes(){ let getAlarmMinutes = function(){
if(!isAlarmEnabled()){ if(!isAlarmEnabled()){
return -1; return -1;
} }
var alarm = require('sched'); let alarm = require('sched');
var alarmObj = alarm.getAlarm(TIMER_IDX); let alarmObj = alarm.getAlarm(TIMER_IDX);
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
} };
function increaseAlarm(){ let increaseAlarm = function(){
try{ try{
var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0; let minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
var alarm = require('sched') let alarm = require('sched')
alarm.setAlarm(TIMER_IDX, { alarm.setAlarm(TIMER_IDX, {
timer : (minutes+5)*60*1000, timer : (minutes+5)*60*1000,
}); });
alarm.reload(); alarm.reload();
} catch(ex){ } } catch(ex){ }
} };
function decreaseAlarm(){ let decreaseAlarm = function(){
try{ try{
var minutes = getAlarmMinutes(); let minutes = getAlarmMinutes();
minutes -= 5; minutes -= 5;
var alarm = require('sched') let alarm = require('sched')
alarm.setAlarm(TIMER_IDX, undefined); alarm.setAlarm(TIMER_IDX, undefined);
if(minutes > 0){ if(minutes > 0){
@ -669,13 +673,13 @@ function decreaseAlarm(){
alarm.reload(); alarm.reload();
} catch(ex){ } } catch(ex){ }
} };
/* /*
* Listeners * Listeners
*/ */
Bangle.on('lcdPower',on=>{ let onLcdPower = on=>{
if (on) { if (on) {
// Whenever we connect to Gadgetbridge, reading data from // Whenever we connect to Gadgetbridge, reading data from
// health failed. Therefore, we update only partially... // health failed. Therefore, we update only partially...
@ -685,31 +689,34 @@ Bangle.on('lcdPower',on=>{
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined; drawTimeout = undefined;
} }
}); };
Bangle.on('lcdPower', onLcdPower);
Bangle.on('lock', function(isLocked) { let onLock = function(isLocked) {
drawInfo(); drawInfo();
}); };
Bangle.on('lock', onLock);
Bangle.on('charging',function(charging) {
let onCharge = function(charging) {
drawState(); drawState();
}); };
Bangle.on('charging', onCharge);
function feedback(){ let feedback = function(){
Bangle.buzz(40, 0.3); Bangle.buzz(40, 0.3);
} };
// Touch gestures to control clock. We don't use swipe to be compatible with the bangle ecosystem let onTouch = function(btn, e){
Bangle.on('touch', function(btn, e){ let left = parseInt(g.getWidth() * 0.2);
var left = parseInt(g.getWidth() * 0.2); let right = g.getWidth() - left;
var right = g.getWidth() - left; let upper = parseInt(g.getHeight() * 0.2);
var upper = parseInt(g.getHeight() * 0.2); let lower = g.getHeight() - upper;
var lower = g.getHeight() - upper;
var is_left = e.x < left; let is_left = e.x < left;
var is_right = e.x > right; let is_right = e.x > right;
var is_upper = e.y < upper; let is_upper = e.y < upper;
var is_lower = e.y > lower; let is_lower = e.y > lower;
if(!settings.disableData){ if(!settings.disableData){
if(is_left && lcarsViewPos == 1){ if(is_left && lcarsViewPos == 1){
@ -744,16 +751,30 @@ Bangle.on('touch', function(btn, e){
draw(); draw();
return; return;
} }
}); };
// Touch gestures to control clock. We don't use swipe to be compatible with the bangle ecosystem
Bangle.on('touch', onTouch);
let themeBefore = g.theme;
/* /*
* Lets start widgets, listen for btn etc. * Lets start widgets, listen for btn etc.
*/ */
// Show launcher when middle button pressed // Show launcher when middle button pressed
Bangle.setUI("clock"); Bangle.setUI({mode:"clock",remove:function() {
if (drawTimeout) clearTimeout(drawTimeout);
delete Graphics.prototype.setFontAntonioMedium;
delete Graphics.prototype.setFontAntonioLarge;
Bangle.removeListener("lcdPower",onLcdPower);
Bangle.removeListener("lock",onLock);
Bangle.removeListener("charging",onCharge);
Bangle.removeListener("touch",onTouch);
require('sched').setAlarm(TIMER_IDX, undefined);
g.setTheme(themeBefore);
widget_utils.cleanup();
}});
Bangle.loadWidgets(); Bangle.loadWidgets();
// Clear the screen once, at startup and draw clock // Clear the screen once, at startup and draw clock
g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear(); g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear();
draw(); draw();
}

View File

@ -26,7 +26,7 @@
} }
var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"]; var dataOptions = ["Steps", "Battery", "BattVolt", "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','Purple','Pink','Light Green','Dark Green']; 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','#FF00FF','#6C00FF','#99FF00','#556B2F']; var bg_code = ['#00ff00','#FF9900','#0094FF','#FF00DC','#ff0000','#0000ff','#ffef00','#FFFFFF','#FF00FF','#6C00FF','#99FF00','#556B2F'];

View File

@ -3,7 +3,7 @@
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",
"version":"0.27", "version":"0.29",
"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.",
@ -16,5 +16,6 @@
{"name":"lcars.app.js","url":"lcars.app.js"}, {"name":"lcars.app.js","url":"lcars.app.js"},
{"name":"lcars.img","url":"lcars.icon.js","evaluate":true}, {"name":"lcars.img","url":"lcars.icon.js","evaluate":true},
{"name":"lcars.settings.js","url":"lcars.settings.js"} {"name":"lcars.settings.js","url":"lcars.settings.js"}
] ],
"data":[{"name":"lcars.setting.json"}]
} }

View File

@ -13,5 +13,8 @@
{"name":"limelight.app.js","url":"limelight.app.js"}, {"name":"limelight.app.js","url":"limelight.app.js"},
{"name":"limelight.settings.js","url":"limelight.settings.js"}, {"name":"limelight.settings.js","url":"limelight.settings.js"},
{"name":"limelight.img","url":"limelight.icon.js","evaluate":true} {"name":"limelight.img","url":"limelight.icon.js","evaluate":true}
],
"data": [
{"name":"limelight.json"}
] ]
} }

View File

@ -14,5 +14,6 @@
{"name":"linuxclock.app.js","url":"app.js"}, {"name":"linuxclock.app.js","url":"app.js"},
{"name":"linuxclock.img","url":"app-icon.js","evaluate":true}, {"name":"linuxclock.img","url":"app-icon.js","evaluate":true},
{"name":"linuxclock.settings.js","url":"settings.js"} {"name":"linuxclock.settings.js","url":"settings.js"}
] ],
"data":[{"name":"linuxclock.setting.json"}]
} }

View File

@ -10,5 +10,6 @@
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"loadingscreen.settings.js","url":"settings.js"} {"name":"loadingscreen.settings.js","url":"settings.js"}
] ],
"data":[{"name":".loading"}]
} }

View File

@ -96,4 +96,7 @@
Nav messages with '/' now get split on newlines Nav messages with '/' now get split on newlines
0.70: Handle nav messages from newer Gadgetbridge builds that output distance as a String 0.70: Handle nav messages from newer Gadgetbridge builds that output distance as a String
If we receive a 'music' message and we're in the messages app (but not showing a message) show music (#2814) If we receive a 'music' message and we're in the messages app (but not showing a message) show music (#2814)
0.71: Cancel buzzing when watch unlocked, or when different messages viewed 0.71: Cancel buzzing when watch unlocked, or when different messages viewed
On 2v18.64+ firmware, 'No Messages' now has a 'back' button
0.72: Nav message updastes don't automatically launch navigation menu unless they're new
0.73: Add sharp left+right nav icons

View File

@ -70,6 +70,8 @@ var onMessagesModified = function(type,msg) {
if (msg.state && msg.state!="play") openMusic = false; // no longer playing music to go back to if (msg.state && msg.state!="play") openMusic = false; // no longer playing music to go back to
if ((active!=undefined) && (active!="list") && (active!="music")) return; // don't open music over other screens (but do if we're in the main menu) if ((active!=undefined) && (active!="list") && (active!="music")) return; // don't open music over other screens (but do if we're in the main menu)
} }
if (msg && msg.id=="nav" && msg.t=="modify" && active!="map")
return; // don't show an updated nav message if we're just in the menu
showMessage(msg&&msg.id); showMessage(msg&&msg.id);
}; };
Bangle.on("message", onMessagesModified); Bangle.on("message", onMessagesModified);
@ -102,6 +104,8 @@ function showMapMessage(msg) {
case "right": img = "GhcBAABgAAA8AAAPgAAB8AAAPgAAB8D///j///9///+/AAPPAAHjgAD44AB8OAA+DgAPA4ABAOAAADgAAA4AAAOAAADgAAA4AAAOAAAA";break; case "right": img = "GhcBAABgAAA8AAAPgAAB8AAAPgAAB8D///j///9///+/AAPPAAHjgAD44AB8OAA+DgAPA4ABAOAAADgAAA4AAAOAAADgAAA4AAAOAAAA";break;
case "left_slight": img = "ERgB//B/+D/8H4AP4Af4A74Bz4Dj4HD4OD4cD4AD4ADwADwADgAHgAPAAOAAcAA4ABwADgAH";break; case "left_slight": img = "ERgB//B/+D/8H4AP4Af4A74Bz4Dj4HD4OD4cD4AD4ADwADwADgAHgAPAAOAAcAA4ABwADgAH";break;
case "right_slight": img = "ERgBB/+D/8H/4APwA/gD/APuA+cD44Phw+Dj4HPgAeAB4ADgAPAAeAA4ABwADgAHAAOAAcAA";break; case "right_slight": img = "ERgBB/+D/8H/4APwA/gD/APuA+cD44Phw+Dj4HPgAeAB4ADgAPAAeAA4ABwADgAHAAOAAcAA";break;
case "left_sharp": img = "GBaBAAAA+AAB/AAH/gAPjgAeBwA8BwB4B+DwB+HgB+PAB+eAB+8AB+4AB/wAB/gAB//gB//gB//gBwAABwAABwAABwAABw=="; break;
case "right_sharp": img = "GBaBAB8AAD+AAH/gAHHwAOB4AOA8AOAeAOAPB+AHh+ADx+AB5+AA9+AAd+AAP+AAH+AH/+AH/+AH/+AAAOAAAOAAAOAAAA==";break;
case "keep_left": img = "ERmBAACAAOAB+AD+AP+B/+H3+PO+8c8w4wBwADgAHgAPAAfAAfAAfAAfAAeAAeAAcAA8AA4ABwADgA==";break; case "keep_left": img = "ERmBAACAAOAB+AD+AP+B/+H3+PO+8c8w4wBwADgAHgAPAAfAAfAAfAAfAAeAAeAAcAA8AA4ABwADgA==";break;
case "keep_right": img = "ERmBAACAAOAA/AD+AP+A//D/fPueeceY4YBwADgAPAAeAB8AHwAfAB8ADwAPAAcAB4ADgAHAAOAAAA==";break; case "keep_right": img = "ERmBAACAAOAA/AD+AP+A//D/fPueeceY4YBwADgAPAAeAB8AHwAfAB8ADwAPAAcAB4ADgAHAAOAAAA==";break;
case "uturn_left": img = "GRiBAAAH4AAP/AAP/wAPj8APAfAPAHgHgB4DgA8BwAOA4AHAcADsOMB/HPA7zvgd9/gOf/gHH/gDh/gBwfgA4DgAcBgAOAAAHAAADgAABw==";break; case "uturn_left": img = "GRiBAAAH4AAP/AAP/wAPj8APAfAPAHgHgB4DgA8BwAOA4AHAcADsOMB/HPA7zvgd9/gOf/gHH/gDh/gBwfgA4DgAcBgAOAAAHAAADgAABw==";break;
@ -414,8 +418,9 @@ function checkMessages(options) {
if (!options.clockIfNoMsg) return E.showPrompt(/*LANG*/"No Messages",{ if (!options.clockIfNoMsg) return E.showPrompt(/*LANG*/"No Messages",{
title:/*LANG*/"Messages", title:/*LANG*/"Messages",
img:require("heatshrink").decompress(atob("kkk4UBrkc/4AC/tEqtACQkBqtUDg0VqAIGgoZFDYQIIM1sD1QAD4AIBhnqA4WrmAIBhc6BAWs8AIBhXOBAWz0AIC2YIC5wID1gkB1c6BAYFBEQPqBAYXBEQOqBAnDAIQaEnkAngaEEAPDFgo+IKA5iIOhCGIAFb7RqAIGgtUBA0VqobFgNVA")), img:require("heatshrink").decompress(atob("kkk4UBrkc/4AC/tEqtACQkBqtUDg0VqAIGgoZFDYQIIM1sD1QAD4AIBhnqA4WrmAIBhc6BAWs8AIBhXOBAWz0AIC2YIC5wID1gkB1c6BAYFBEQPqBAYXBEQOqBAnDAIQaEnkAngaEEAPDFgo+IKA5iIOhCGIAFb7RqAIGgtUBA0VqobFgNVA")),
buttons : {/*LANG*/"Ok":1} buttons : {/*LANG*/"Ok":1},
}).then(() => { load() }); back: () => load()
}).then(() => load());
return load(); return load();
} }
// we have >0 messages // we have >0 messages

View File

@ -2,7 +2,7 @@
"id": "messagegui", "id": "messagegui",
"name": "Message UI", "name": "Message UI",
"shortName": "Messages", "shortName": "Messages",
"version": "0.71", "version": "0.73",
"description": "Default app to display notifications from iOS and Gadgetbridge/Android", "description": "Default app to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -1,6 +1,6 @@
# Messages library # Messages library
This library handles the passing of messages. It can storess a list of messages This library handles the passing of messages. It can stores a list of messages
and allows them to be retrieved by other apps. and allows them to be retrieved by other apps.
## Example ## Example
@ -37,18 +37,10 @@ myMessageListener = Bangle.on("message", (type, message)=>{
}); });
``` ```
Apps can launch the full GUI by calling `require("messages").openGUI()`, if you Apps can launch the currently installed Message GUI by calling `require("messages").openGUI()`.
want to write your own GUI, it should include boot code that listens for If you want to write your own GUI, it should include a library called `messagegui`
`"messageGUI"` events: with a method called `open` that will cause it to be opened, with the
optionally supplied message. See `apps/messagegui/lib.js` for an example.
```js
Bangle.on("messageGUI", message=>{
if (message.handled) return; // another app already opened it's GUI
message.handled = true; // prevent other apps form launching
Bangle.load("my_message_gui.app.js");
})
```
## Requests ## Requests

View File

@ -107,7 +107,7 @@ exports.dismiss = function(msg) {
}; };
/** /**
* Emit a "type=openGUI" event, to open GUI app * Open the Messages GUI app
* *
* @param {object} [msg={}] Message the app should show * @param {object} [msg={}] Message the app should show
*/ */
@ -215,7 +215,7 @@ exports.buzz = function(msgSrc) {
let repeat = msgSettings.repeat; let repeat = msgSettings.repeat;
if (repeat===undefined) repeat = 4; // repeat may be zero if (repeat===undefined) repeat = 4; // repeat may be zero
if (repeat) if (repeat)
{ {
exports.buzzInterval = setInterval(() => require("buzz").pattern(pattern), repeat*1000); exports.buzzInterval = setInterval(() => require("buzz").pattern(pattern), repeat*1000);
let vibrateTimeout = msgSettings.vibrateTimeout; let vibrateTimeout = msgSettings.vibrateTimeout;

View File

@ -13,5 +13,6 @@
{"name":"metronome.app.js","url":"metronome.js"}, {"name":"metronome.app.js","url":"metronome.js"},
{"name":"metronome.img","url":"metronome-icon.js","evaluate":true}, {"name":"metronome.img","url":"metronome-icon.js","evaluate":true},
{"name":"metronome.settings.js","url":"settings.js"} {"name":"metronome.settings.js","url":"settings.js"}
] ],
"data":[{"name":"metronome.settings.json"}]
} }

View File

@ -1,3 +1,4 @@
0.01: Initial version 0.01: Initial version
0.02: Update for time_utils module 0.02: Update for time_utils module
0.03: Use default Bangle formatter for booleans 0.03: Use default Bangle formatter for booleans
0.04: Remove copied sched alarm.js & import newer features (oneshot alarms)

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