Merge branch 'espruino:master' into grandfather-clock

master
June Bennison 2025-02-11 16:26:17 +11:00 committed by GitHub
commit 0663076128
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
93 changed files with 797 additions and 142 deletions

View File

@ -270,7 +270,8 @@ and which gives information about the app for the Launcher.
// 'notify' - provides 'notify' library for showing notifications
// 'locale' - provides 'locale' library for language-specific date/distance/etc
// (a version of 'locale' is included in the firmware)
"tags": "", // comma separated tag list for searching
// 'defaultconfig' - a set of apps that will can be installed and will wipe out all previously installed apps
"tags": "", // comma separated tag list for searching (don't include uppercase or spaces)
// common types are:
// 'clock' - it's a clock
// 'widget' - it is (or provides) a widget

View File

@ -5,7 +5,7 @@
"version": "0.02",
"description": "A UI/UX for espruino smartwatches, displays dinamically calc. x,y coordinates.",
"icon": "app.png",
"tags": "Color,input,buttons,touch,UI",
"tags": "color,input,buttons,touch,ui",
"supports": ["BANGLEJS"],
"readme": "README.md",
"screenshots": [{"url":"UI4swatch_icon.png"},{"url":"UI4swatch_s1.png"}],

View File

@ -4,7 +4,7 @@
"version": "0.04",
"description": "Wrist mounted ukulele chords",
"icon": "app.png",
"tags": "uke, chords",
"tags": "uke,chords",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -6,7 +6,7 @@
"description": "A fully featured watch face with a playable game on the side.",
"readme":"README.md",
"type": "clock",
"tags": "clock, game",
"tags": "clock,game",
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"bblobface.app.js","url":"app.js"},

View File

@ -5,7 +5,7 @@
"version": "0.01",
"description": "Aerobic fitness test created by Léger & Lambert",
"icon": "beeptest.png",
"tags": "Health",
"tags": "health",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -6,7 +6,7 @@
"description": "Bangle version of a popular word search game",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"tags": "game, text",
"tags": "game,text",
"storage": [
{"name":"bordle.app.js","url":"bordle.app.js"},
{"name":"wordlencr.txt","url":"wordlencr.txt"},

View File

@ -33,3 +33,4 @@ clkinfo.addInteractive that would cause ReferenceError.
0.31: Use clock_info module as an app
0.32: Make the border of the clock_info box extend all the way to the right of the screen.
0.33: Fix issue rendering ClockInfos with for fg+bg color set to the same (#2749)
0.34: Support 12-hour time format

View File

@ -239,11 +239,9 @@ let drawTime = function() {
var y = y1;
var date = new Date();
var hours = String(date.getHours());
var minutes = date.getMinutes();
minutes = minutes < 10 ? String("0") + minutes : minutes;
var colon = settings.hideColon ? "" : ":";
var timeStr = hours + colon + minutes;
var timeStr = locale.time(date, 1);
if (settings.hideColon)
timeStr = timeStr.replace(":", "");
// Set y coordinates correctly
y += parseInt((H - y)/2) + 5;

View File

@ -1,7 +1,7 @@
{
"id": "bwclk",
"name": "BW Clock",
"version": "0.33",
"version": "0.34",
"description": "A very minimalistic clock.",
"readme": "README.md",
"icon": "app.png",

View File

@ -36,3 +36,4 @@ clkinfo.addInteractive that would cause ReferenceError.
Do an quick inital fillRect on theclock info area.
0.33: Make the border of the clock_info box extend all the way to the right of the screen.
0.34: Fix issue rendering ClockInfos with for fg+bg color set to the same (#2749)
0.35: Support 12-hour time format

View File

@ -199,11 +199,9 @@ let drawTime = function() {
let y = y1;
let date = new Date();
let hours = String(date.getHours());
let minutes = date.getMinutes();
minutes = minutes < 10 ? String("0") + minutes : minutes;
let colon = settings.hideColon ? "" : ":";
let timeStr = hours + colon + minutes;
var timeStr = locale.time(date, 1);
if (settings.hideColon)
timeStr = timeStr.replace(":", "");
// Set y coordinates correctly
y += parseInt((H - y)/2) + 5;

View File

@ -1,7 +1,7 @@
{
"id": "bwclklite",
"name": "BW Clock Lite",
"version": "0.34",
"version": "0.35",
"description": "A very minimalistic clock. This version of BW Clock is quicker at the cost of the custom font.",
"readme": "README.md",
"icon": "app.png",

View File

@ -6,7 +6,7 @@
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
"icon": "calclock.png",
"type": "clock",
"tags": "clock agenda",
"tags": "clock,agenda",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -19,3 +19,4 @@
Display Widgets in menus
0.17: Load holidays before events so the latter is not overpainted
0.18: Minor code improvements
0.19: Read events synchronized from Gadgetbridge

View File

@ -60,6 +60,12 @@ const loadEvents = () => {
date.setSeconds(time.s);
return {date: date, msg: a.msg, type: "e"};
}));
// all events synchronized from Gadgetbridge
events = events.concat((require("Storage").readJSON("android.calendar.json",1) || []).map(a => {
// timestamp is in seconds, Date requires milliseconds
const date = new Date(a.timestamp * 1000);
return {date: date, msg: a.title, type: "e"};
}));
};
const loadSettings = () => {
@ -221,8 +227,8 @@ const drawCalendar = function(date) {
}, []);
let i = 0;
g.setFont("8x12", fontSize);
for (y = 0; y < rowN - 1; y++) {
for (x = 0; x < colN; x++) {
for (let y = 0; y < rowN - 1; y++) {
for (let x = 0; x < colN; x++) {
i++;
const day = days[i];
const curMonth = day < 15 ? month+1 : day < 50 ? month-1 : month;

View File

@ -1,7 +1,7 @@
{
"id": "calendar",
"name": "Calendar",
"version": "0.18",
"version": "0.19",
"description": "Monthly calendar, displays holidays uploaded from the web interface and scheduled events.",
"icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}],

View File

@ -6,7 +6,7 @@
"icon": "app.png",
"version": "0.13",
"type": "clock",
"tags": "clock, weather, cassio, retro",
"tags": "clock,weather,cassio,retro",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",

View File

@ -3,7 +3,7 @@
"version":"0.01",
"description": "Record time active on a task, course, work or anything really.",
"icon": "app.png",
"tags": "logging, record, work, tasks",
"tags": "logging,record,work,tasks",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"screenshots" : [ { "url":"dump.png"}, { "url":"dump1.png" }, { "url":"dump2.png" }, { "url":"dump3.png" }, { "url":"dump4.png" }, { "url":"dump5.png" }, { "url":"dump6.png" } ],

View File

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

View File

@ -0,0 +1,10 @@
# Shortcuts
An app that allows you to create custom ClockInfos that act as shortcuts to your favourite apps.
## Create a Shortcut
After installing the app, you can open the app to add and manage existing shortcuts. If no shortcuts exist, the app will display a ``[+] Shortcuts`` button as a ClockInfo on your Clock (see screenshots), which when clicked will quickly launch into the app so you can add new shortcuts.
When adding a shortcut, first select the app you wish to use, then select the icon you wish to register to the shortcut. If a keyboard is installed, you'll get the option to rename the shortcut before saving.
Once created, your shortcut will appear on any Clocks with ClockInfo support, and tapping the shortcut will launch the chosen app

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UB/4ACBIM889VAHmqAAwKCrQLH0oLCuBoFhoLCtJ1HtILBtYLH9ILBtALHlILQlRFCwALSnWwgECBY8O1gLJgeoBaojLHZfqAQILIFwMDBY8CFwMO2QLGhwuBlWuBY0K4ED1aDHBYJUBlQLGnRUChQLIKgJHHBYJUBZY8KgZUBBZHCKgILHh2CBZMC0fABZC+CBZBSBC5JUB14MDcY2q1YLIDAILGtY4EAAXpBYNpBY9pBYNauAKFhulBYWqAAwLCqoLHBQQA6A="))

224
apps/clkshortcuts/app.js Normal file
View File

@ -0,0 +1,224 @@
var storage = require("Storage");
var keyboard = "textinput";
try {
keyboard = require(keyboard);
} catch (e) {
keyboard = null;
}
var icons = [
{name:"agenda", img :"GBiBAAAAAAA8AAB+AA/n8B/n+BgAGBgAGBn/mBn/mBgAGBgAGBn/mBn/+BgD/BgHDhn+Bhn8MxgMIxgMIx/8Ew/+BgAHDgAD/AAA8A=="},
{name:"alarm", img:"GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAOBwAcA4A4YcAwYMBgYGBgYGBgYGBgYGBgeGBgHGAwBMA4AcAcA4AOBwAH/gAB+AAAAAAAAAA=="},
{name:"mail", img:"GBiBAAAAAAAAAAAAAAAAAB//+D///DAADDgAHDwAPDcA7DPDzDDnDDA8DDAYDDAADDAADDAADDAADD///B//+AAAAAAAAAAAAAAAAA=="},
{name:"android", img: "GBiBAAAAAAEAgAD/AAD/AAHDgAGBgAMkwAMAwAP/wBv/2BsA2BsA2BsA2BsA2BsA2BsA2Bv/2AP/wADnAADnAADnAADnAADnAAAAAA=="},
{name:"add", img:"GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgYGBgYGBgYGBgYGBn/mBn/mBgYGBgYGBgYGBgYGBgAGBgAGB//+A//8AAAAAAAAAAAAA=="},
{name:"bangle", img:"GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA=="},
{name:"bike", img:"GBiBAAAAAAAAAAAAAAAD+AAD/AADjAABjAfhnAfjyAMDwAcGwB4O+H8M/GGZ5sD7c8fzM8fjM8DDA2GBhn8B/h4AeAAAAAAAAAAAAA=="},
{name:"map", img:"GBiBAAAAAAAAAAAAAADgGAf8+B//+BjHGBjDGBjDGBjDGBjDGBjDGBjDGBjDGBjDGBjDGBjDGBjjGB//+B8/4BgHAAAAAAAAAAAAAA=="},
{name:"play", img:"GBiBAAAAAAAAAAAAAA//8B//+BgAGBjAGBjwGBj8GBjeGBjHmBjB2BjB2BjHmBjeGBj8GBjwGBjAGBgAGB//+A//8AAAAAAAAAAAAA=="},
{name:"fast forward", img:"GBiBAAAAAAAAAAAAAH///v///8AAA8YYA8eeA8f/g8b7w8Y488YYO8YYO8Y488b7w8f/g8eeA8YYA8AAA////3///gAAAAAAAAAAAA=="},
{name:"rewind", img:"GBiBAAAAAAAAAAAAAH///v///8AAA8AYY8B548H/48PfY88cY9wYY9wYY88cY8PfY8H/48B548AYY8AAA////3///gAAAAAAAAAAAA=="},
{name:"timer", img:"GBiBAAAAAAB+AAB+AAAAMAB+OAH/nAOByAcA4A4YcAwYMBgYGBgYGBgYGBgYGBgAGBgAGAwAMA4AcAcA4AOBwAH/gAB+AAAAAAAAAA=="},
{name:"connected", img:"GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBngGBn4GBgcGBgOGBnHGBnzGBgxmBgZmBmZmBmZmBgAGBgAGB//+A//8AAAAAAAAAAAAA=="},
{name:"lock", img:"GBiBAAAAAAA8AAD/AAHDgAGBgAMAwAMAwAMAwAf/4A//8AwAMAwAMAwAMAwYMAw8MAw8MAwYMAwAMAwAMAwAMA//8Af/4AAAAAAAAA=="},
{name:"battery", img:"GBiBAAAAAAAAAAB+AAB+AAHngAPnwAMAwAMAwAMIwAMIwAMYwAM4wAM+wAN8wAMcwAMYwAMQwAMQwAMAwAMAwAP/wAH/gAAAAAAAAA=="},
{name:"game", img:"GBiBAAAAAAAAAAAAAAA8AAB+AABmAABmAAB+AAA8AAAYAAAYAAAYAAMYAA//8B//+BgAGBgAGBgAGBgAGB//+A//8AAAAAAAAAAAAA=="},
{name:"dice", img:"GBiBAAAAAB//8D//+HAAPGMDHmeHnmeHnmMDHmAAHmMDHmeHnmeHnmMDHmAAHmMDHmeHnmeHnmMDHnAAPn///j///h///g///AAAAA=="},
{name:"gear", img:"GBiBAAAAAAAAAAA8AAB+AABmAA3nsA/D8B8A+Dg8HBx+OAznMAzDMAzDMAznMBx+ODg8HB8A+A/D8A3nsABmAAB+AAA8AAAAAAAAAA=="},
{name:"wrench", img:"GBiBAAAAAAAAAAAAAAAHgAAfwAA7gAAzEABjOABj+ABh+ABgGADgMAHAcAOP4AcfgA44AB9wADHgADHAADGAAB8AAA4AAAAAAAAAAA=="},
{name:"calendar", img:"FhgBDADAMAMP/////////////////////8AADwAAPAAA8AADwAAPAAA8AADwAAPAAA8AADwAAPAAA8AADwAAP///////"},
{name:"power", img:"GBiBAAAAAAAAAAB+AAH/gAeBwA4YcAwYMBjbGBnbmDGZjDMYzDMYzDMAzDMAzDGBjBnDmBj/GAw8MA4AcAeB4AH/gAB+AAAAAAAAAA=="},
{name:"terminal", img:"GBiBAAAAAAAAAAAAAA//8B//+B//+B//+B//+BgAGBgAGBgAGBmAGBjAGBhgGBhgGBjAGBmPmBgAGBgAGB//+A//8AAAAAAAAAAAAA=="},
{name:"camera", img:"GBiBAAAAAAAAAAD/AAH/gAMAwD8A/H8A/mA8BmD/BmHDhmGBhmMAxmMAxmMAxmMAxmGBhmHDhmD/BmA8BmAABn///j///AAAAAAAAA=="},
{name:"phone", img:"GBiBAAAAAAAAAAOAAA/AABzgADBgADBgADBgABjgABjAABzAAAxgAA5wAAc58AMf+AGHHADgDABwDAA8GAAfGAAH8AAA4AAAAAAAAA=="},
{name:"two prong plug", img:"GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA=="},
{name:"steps", img:"GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA=="},
{name:"graph", img:"GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA=="},
{name:"hills", img:"GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA=="},
{name:"sun", img:"GBiBAAAYAAAYAAAYAAgAEBwAOAx+MAD/AAHDgAMAwAcA4AYAYOYAZ+YAZwYAYAcA4AMAwAHDgAD/AAx+MBwAOAgAEAAYAAAYAAAYAA=="},
{name:"home", img:"GBiBAAAAAAAAAAAAAAH/gAP/wAdg4A5wYA44MBwf+DgP/BgAGBgAGBgAGBnnmBnnmBnnmBnnmBngGBngGB//+B//+AAAAAAAAAAAAA=="},
{name:"bell", img:"GBiBAAAAAAAAAAAfgAB/2ADw+AHAMAOAGAcAGD4ADHgADDgADBwADA4AHAcAGAOAOAHAcAPg4ANxwAM5gAP/AAHvAAAHAAACAAAAAA=="},
{name:"bin", img:"GBiBAAAAAAAAAAB+AB//+B//+AwAMAwAMAxmMAZmYAZmYAZmYAZmYAZmYAZmYAZmYAZmYAZmYANmwAMAwAMAwAP/wAH/gAAAAAAAAA=="},
];
let storedApps;
var showMainMenu = () => {
storedApps = storage.readJSON("clkshortcuts.json", 1) || {};
var mainMenu = {
"": {
title: "Shortcuts",
},
"< Back": () => {
load();
},
"New": () => {
// Select the app
getSelectedApp().then((app) => {
getSelectedIcon().then((icon) => {
promptForRename(app.name).then((name) => {
E.showMessage("Saving...");
storedApps[app.src] = {
name: name, src: app.src, icon: icon
};
storage.writeJSON("clkshortcuts.json", storedApps);
showMainMenu();
}).catch(() => {
E.showMessage("Saving...");
storedApps[app.src] = {
name: app.name, src: app.src, icon: icon
};
storage.writeJSON("clkshortcuts.json", storedApps);
showMainMenu();
} );
}).catch(() => {showMainMenu();});
}).catch(() => {showMainMenu();});
},
};
getStoredAppsArray(storedApps).forEach((app) => {
mainMenu[app.name] = {
onchange: () => {
showEditMenu(app).then((dirty) => {
if (dirty) {
E.showMessage("Saving...");
storage.writeJSON("clkshortcuts.json", storedApps);
}
showMainMenu();
});
},
format: v=>"\0" + atob(app.icon)
};
});
E.showMenu(mainMenu);
};
var showEditMenu = (app) => {
return new Promise((resolve, reject) => {
var editMenu = {
"": {
title: "Edit " + app.name,
},
"< Back": () => {
resolve(false);
},
"Name":{
onchange: () => {
promptForRename(app.name).then((name) => {
storedApps[app.src].name = name;
resolve(true);
}).catch();
},
format: v=>app.name.substring(0, 7)
},
"Icon": {
onchange: () => {
getSelectedIcon().then((icon) => {
storedApps[app.src].icon = icon;
resolve(true);
}).catch(() => resolve(false));
},
format: v=>"\0" + atob(app.icon)
},
"Delete": {
onchange: () => {
delete storedApps[app.src]
resolve(true);
},
format: v=>"\0" + atob("GBiBAAAAAAAAAAB+AB//+B//+AwAMAwAMAxmMAZmYAZmYAZmYAZmYAZmYAZmYAZmYAZmYAZmYANmwAMAwAMAwAP/wAH/gAAAAAAAAA==")
}
};
E.showMenu(editMenu);
});
};
var promptForRename = (name) => {
return new Promise((resolve, reject) => {
if (!keyboard) { reject("No textinput is available"); }
else {
return require("textinput").input({text:name}).then((result) => resolve(result));
}
});
};
var getStoredAppsArray = (apps) => {
var appList = Object.keys(apps);
var storedAppArray = [];
for (var i = 0; i < appList.length; i++) {
var app = "" + appList[i];
storedAppArray.push(
apps[app]
);
}
return storedAppArray;
};
var getSelectedIcon = () => {
return new Promise((resolve, reject) => {
var iconMenu = {
"": {
title: "Select Icon",
},
"< Back": () => {
reject("The user cancelled the operation");
},
};
icons.forEach((icon) => {
iconMenu["\0" + atob(icon.img) + " " + icon.name] = () => {
resolve(icon.img);
};
});
E.showMenu(iconMenu);
});
};
var getAppList = () => {
var appList = storage
.list(/\.info$/)
.map((appInfoFileName) => {
var appInfo = storage.readJSON(appInfoFileName, 1);
return (
appInfo && {
name: appInfo.name,
sortorder: appInfo.sortorder,
src: appInfo.src,
}
);
})
.filter((app) => app && !!app.src);
appList.sort((a, b) => {
var n = (0 | a.sortorder) - (0 | b.sortorder);
if (n) return n;
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
return appList;
};
var getSelectedApp = () => {
return new Promise((resolve, reject) => {
E.showMessage("Loading apps...");
var selectAppMenu = {
"": {
title: "Select App",
},
"< Back": () => {
reject("The user cancelled the operation");
},
};
var appList = getAppList();
appList.forEach((app) => {
selectAppMenu[app.name] = () => {
resolve(app);
};
});
E.showMenu(selectAppMenu);
});
};
showMainMenu();

BIN
apps/clkshortcuts/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,45 @@
(function() {
var storage = require("Storage");
var storedApps = storage.readJSON("clkshortcuts.json", 1) || {};
var items = [];
if (Object.keys(storedApps).length !== 0) {
for (var key in storedApps) {
var source = {
name: storedApps[key].name,
img: storedApps[key].icon,
src: storedApps[key].src,
get : function() {
return {
text : this.name,
img : atob(this.img)
}
},
run: function() { load(this.src);},
show : function() {},
hide : function() {},
}
items.push(source);
}
}
else {
var source = {
name: "Shortcuts",
img: "GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgYGBgYGBgYGBgYGBn/mBn/mBgYGBgYGBgYGBgYGBgAGBgAGB//+A//8AAAAAAAAAAAAA==",
src: "clkshortcuts.app.js",
get : function() {
return {
text : this.name,
img : atob(this.img)
}
},
run: function() { load(this.src);},
show : function() {},
hide : function() {},
};
items = [source];
}
return {
name: "Shortcuts",
items: items
};
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

View File

@ -0,0 +1,17 @@
{ "id": "clkshortcuts",
"name": "Shortcuts",
"shortName": "Shortcuts",
"version": "0.01",
"description": "Add shortcuts to launch your favourite apps straight from the Clock",
"icon": "app.png",
"screenshots": [{"url":"add_shortcuts_screenshot.png"}, {"url":"example_shortcuts_screenshot.png"}],
"tags": "clkinfo,clockinfo",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{ "name": "clkshortcuts.app.js", "url": "app.js" },
{ "name": "clkshortcuts.img", "url": "app-icon.js", "evaluate": true },
{"name":"clkshortcuts.clkinfo.js","url":"clkinfo.js"}
],
"data": [{"name":"clkshortcuts.json"}]
}

View File

@ -13,3 +13,4 @@
0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle
0.13: Cache loaded ClockInfos so if we have clockInfoWidget and a clock, we don't load them twice (saves ~300ms)
0.14: Check for .clkinfocache and use that if exists (from boot 0.64)
0.15: Fix error when displaying a category with only one clockinfo (fix #3728)

View File

@ -283,7 +283,7 @@ exports.addInteractive = function(menu, options) {
//in the worst case we come back to 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) &&
while ((options.menuB < menu[options.menuA].items.length-1) &&
exports.clockInfos.some(m => (m!=options) && m.menuA==options.menuA && m.menuB==options.menuB))
options.menuB++;
}

View File

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

View File

@ -5,7 +5,7 @@
"version": "0.03",
"description": "Displays RGB565 and RGB888 colors, its name and code in screen.",
"icon": "app.png",
"tags": "Color,input,buttons,touch,UI",
"tags": "color,input,buttons,touch,ui",
"supports": ["BANGLEJS"],
"readme": "README.md",
"storage": [

View File

@ -0,0 +1,6 @@
0.01: New App!
0.02: update to my current preferences.
0.03: update app list
0.04: change app name "mysetup" -> "anotherconf"
0.05: remove apps that are not "core" to the experience.
0.06: change name "anotherconf" -> "confthyttan"

View File

@ -0,0 +1,31 @@
# Thyttan's Default Config
A different default set of apps and configurations. Brings many quality of life improvements. Opinionated based on the creators taste. Read more below before installing.
## Usage
Before installing do this:
1. Backup your current setup (via the "More..." tab of the App Loader) so you can restore it later if you want.
2. Install this app (you'll be prompted about all data being removed from your Bangle)
3. Try it out, switch out apps to your favorites and tweak to your liking!
## Features
There will not be a trace of a "Thyttan's Default Config" app on your watch after installation. Only the apps it installed and the configurations.
On the clock face:
- Swipe right on the screen to open the launcher (Desktop Launcher) - or press the hardware button.
- Swipe left to open a flashlight app.
- Swipe up to open the messages.
- Swipe down for quick access to music and podcast controls.
- (Do a subsequent left or right swipe to enter the listed apps)
- (Check out the "Quick Launch" app readme for more info)
## Requests
Add to the espruino/BangleApps issue tracker and mention @thyttan for bug reports and suggestions.
## Creator
thyttan

BIN
apps/confthyttan/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
{"mode":0,"apps":[{"name":"Run+","src":"runplus.app.js","files":"runplus.info,runplus.app.js,runplus.img,runplus.settings.js,runplus_karvonen"}],"timeout":10}

View File

@ -0,0 +1 @@
{"mode":0,"apps":[{"name":"Calculator","src":"calculator.app.js"},{"name":"SleepLog","src":"sleeplog.app.js"},{"name":"Messages","sortorder":-9,"src":"messagegui.app.js"},{"name":"Messages","sortorder":-9,"src":"messagegui.app.js","files":"messagegui.info,messagegui,messagegui.app.js,messagegui.new.js,messagegui.boot.js,messagegui.img"}],"standardNumSwipeHandlers":5,"standardNumDragHandlers":1}

View File

@ -0,0 +1 @@
{"showClocks":true,"showLaunchers":true,"direct":false,"swipeExit":false,"timeOut":"15s"}

View File

@ -0,0 +1 @@
{"buzzOnCharge":true,"monthFirst":true,"twentyFourH":true,"showAmPm":false,"showSeconds":true,"showWeather":false,"stepGoal":10000,"stepBar":true,"weekBar":true,"mondayFirst":true,"dayBar":true,"redrawOnStep":false}

View File

@ -0,0 +1 @@
{useAppHistory:true,disregardQuicklaunch:true,hideLoading:true}

View File

@ -0,0 +1 @@
{"colors":"011","image":"heart","touchOn":"always","oversize":7,"dragDelay":500,"minValue":0.01,"tapToLock":false,"unlockSide":"","tapSide":"","tapOn":"always","tOut":2000,"minFlash":0.2,"isOn":true,"value":0.9109}

View File

@ -0,0 +1 @@
{vibrateTimeout:10,vibrate:":",vibrateCalls:":::",flash:false}

View File

@ -0,0 +1,68 @@
{ "id": "confthyttan",
"name": "Thyttan's Default Config",
"version":"0.06",
"description": "A different default set of apps and configurations. Brings many quality of life improvements. Opinionated based on the creators taste. Read more below before installing.",
"icon": "app.png",
"type": "defaultconfig",
"tags": "system,configuration,config,anotherconfig,thyttan",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"dependencies" : {
"sched":"app",
"kbmulti":"app",
"messageicons":"app",
"widmsggrid":"app",
"msgwakefup":"app",
"delaylock":"app",
"notify":"app",
"health":"app",
"widminbate":"app",
"podadrem":"app",
"spotrem":"app",
"android":"app",
"widanclk":"app",
"backswipe":"app",
"torch":"app",
"calculator":"app",
"widbt_notify":"app",
"smpltmr":"app",
"clkinfostopw":"app",
"runplus":"app",
"dtlaunch":"app",
"quicklaunch":"app",
"kineticscroll":"app",
"alarm":"app",
"recorder":"app",
"agenda":"app",
"edgeclk":"app",
"autoreset":"app",
"chargent":"app",
"setting":"app",
"fastload":"app",
"boot":"app",
"ateatimer":"app",
"drained":"app"
},
"storage": [
{"name":"backswipe.json",
"url":"backswipe.json"},
{"name":"autoreset.json",
"url":"autoreset.json"},
{"name":"dtlaunch.json",
"url":"dtlaunch.json"},
{"name":"fastload.json",
"url":"fastload.json"},
{"name":"quicklaunch.json",
"url":"quicklaunch.json"},
{"name":"messages.settings.json",
"url":"messages.settings.json"},
{"name":"widbt_notify.json",
"url":"widbt_notify.json"},
{"name":"recorder.json",
"url":"recorder.json"},
{"name":"edgeclk.settings.json",
"url":"edgeclk.settings.json"},
{"name":"setting.json",
"url":"setting.json"}
]
}

View File

@ -0,0 +1 @@
{lapp:{name:"Show Launcher",sortorder:-12,src:"no source"},rapp:{name:"torch",type:"app",sortorder:-11,src:"torch.app.js"},uapp:{name:"Messages",sortorder:-9,src:"messagegui.app.js"},dapp:{name:"Extension",type:"app",sortorder:-11,src:"quicklaunch.app.js"},tapp:{name:""},dlapp:{name:"PA Remote",src:"podadrem.app.js"},drapp:{name:"Remote for Spotify",src:"spotrem.app.js"},duapp:{name:""},ddapp:{name:"Extension",type:"app",sortorder:-11,src:"quicklaunch.app.js"},dtapp:{name:""},ddlapp:{name:"Alarms",src:"alarm.app.js"},ddrapp:{name:"Run+",src:"runplus.app.js"},dduapp:{name:""},dddapp:{name:"Agenda",src:"agenda.app.js"},ddtapp:{name:""},rlapp:{name:""},rrapp:{name:""},ruapp:{name:""},rdapp:{name:""},rtapp:{name:""},trace:"dr"}

View File

@ -0,0 +1 @@
{recording:false,period:10,record:["gps","hrm","steps"],file:"recorder.log0.csv"}

View File

@ -0,0 +1 @@
{ble:true,blerepl:true,log:false,quiet:0,timeout:10,vibrate:true,beep:true,timezone:2,HID:false,clock:"edgeclk.app.js","12hour":false,firstDayOfWeek:1,brightness:0.5,options:{wakeOnBTN1:true,wakeOnBTN2:true,wakeOnBTN3:true,wakeOnFaceUp:false,wakeOnTouch:false,wakeOnTwist:false,twistThreshold:819.2,twistMaxY:-800,twistTimeout:1000,btnLoadTimeout:700},theme:{fg:65535,bg:0,fg2:65535,bg2:8,fgH:65535,bgH:31,dark:true},clockHasWidgets:true,launcher:"dtlaunch.app.js",touch:{x1:6,y1:14,x2:197,y2:178}}

View File

@ -0,0 +1 @@
{showWidget:true,buzzOnConnect:false,buzzOnLoss:false,hideConnected:false,showMessage:false,nextBuzz:30000}

View File

@ -2,3 +2,4 @@
0.02: Added Settings & readme
0.03: Fix lint warnings
0.04: Fix lint warnings
0.05: Fix on not reading counter defaults in Settings

View File

@ -1,7 +1,7 @@
{
"id": "counter2",
"name": "Counter2",
"version": "0.04",
"version": "0.05",
"description": "Dual Counter",
"readme":"README.md",
"icon": "counter2-icon.png",

View File

@ -18,7 +18,7 @@
"": { "title": "Counter2" },
"< Back": () => back(),
'Default C1': {
value: settings[0],
value: settings.max0,
min: -99, max: 99,
onchange: v => {
settings.max0 = v;
@ -26,7 +26,7 @@
}
},
'Default C2': {
value: settings[2],
value: settings.max1,
min: -99, max: 99,
onchange: v => {
settings.max1 = v;

View File

@ -3,7 +3,7 @@
"version":"0.01",
"description": "Delay the locking of the screen to 5 seconds after the backlight turns off.",
"icon": "app.png",
"tags": "settings, configuration, backlight, touchscreen, screen",
"tags": "settings,configuration,backlight,touchscreen,screen",
"type": "bootloader",
"supports" : ["BANGLEJS2"],
"readme": "README.md",

View File

@ -6,7 +6,7 @@
"icon": "app.png",
"version": "0.01",
"type": "clock",
"tags": "clock, weather, dino, trex, chrome",
"tags": "clock,weather,dino,trex,chrome",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",

View File

@ -6,7 +6,7 @@
"version": "0.02",
"description": "Follow The Recipe (FTR) is a bangle.js app to follow a recipe step by step",
"type": "app",
"tags": "tool, tools, cook",
"tags": "tool,tools,cook",
"supports": [
"BANGLEJS2"
],

View File

@ -5,7 +5,7 @@
"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",
"tags": "tool,voice,tasker",
"supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": false,
"storage": [

View File

@ -4,7 +4,7 @@
"version":"0.01",
"description": "Disconnect from your android device by running this app. The app will forward you to your clock face immediately after triggering the command. (Gadgetbridge nightly required until version 82 is released)",
"icon": "app.png",
"tags": "android, gadgetbridge, bluetooth, bt",
"tags": "android,gadgetbridge,bluetooth,bt",
"supports" : ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"gbdiscon.app.js","url":"app.js"},

View File

@ -3,7 +3,7 @@
"version": "0.03",
"description": "This app will provide you with on course data to support your golf game!",
"icon": "golfview.png",
"tags": "outdoors, gps",
"tags": "outdoors,gps",
"allow_emulator": true,
"supports" : ["BANGLEJS2"],
"readme": "README.md",

View File

@ -4,7 +4,7 @@
"version": "0.03",
"description": "Wrist mounted guitar chords",
"icon": "app.png",
"tags": "guitar, chords",
"tags": "guitar,chords",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -5,7 +5,7 @@
"description": "Songs lyrics and guitar chords",
"icon": "app.png",
"screenshots": [{"url": "screenshot.png"}],
"tags": "guitar, song, lyrics, chords",
"tags": "guitar,song,lyrics,chords",
"supports" : ["BANGLEJS2"],
"interface": "manage_songs.html",
"readme": "README.md",

View File

@ -7,7 +7,7 @@
/* eslint-env node */
var imageconverter = require("../../../webtools/imageconverter.js").imageconverter;
var imageconverter = require("../../../webtools/imageconverter.js");
var icons = JSON.parse(require("fs").readFileSync(__dirname+"/icon_names.json"));
const imgOptions = {
mode : "1bit",

View File

@ -5,7 +5,7 @@
"icon": "app.png",
"version":"0.01",
"description": "Interval Timer for workouts, HIIT, or whatever else.",
"tags": "timer, interval, hiit, workout",
"tags": "timer,interval,hiit,workout",
"readme":"README.md",
"supports":["BANGLEJS2"],
"storage": [

View File

@ -5,7 +5,7 @@
"version": "0.04",
"description": "A clock that displays the great wave of kanagawa (image from wikipedia) with seconds in active mode.",
"icon": "app.png",
"tags": "clock, kanagawa, wave",
"tags": "clock,kanagawa,wave",
"type": "clock",
"supports" : ["BANGLEJS2"],
"readme": "README.md",

View File

@ -261,7 +261,7 @@ var locales = {
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%d %B %Y", "1": "%d/%m/%Y" }, // 1 mars 2020 // 01/03/2020
abmonth: "janv,févr,mars,avril,mai,juin,juil,août,sept,oct,nov,déc",
abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc",
month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre",
abday: "dim,lun,mar,mer,jeu,ven,sam",
day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi",
@ -423,7 +423,7 @@ var locales = {
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%A %B %d %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20
abmonth: "janv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.",
abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc",
month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre",
abday: "dim,lun,mar,mer,jeu,ven,sam",
day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi",
@ -452,7 +452,7 @@ var locales = {
speed: "kmh",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "vorm", 1: " nachm" },
ampm: { 0: "vorm", 1: "nachm" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%Y" }, // Sonntag, 1. März 2020 // 1.3.2020
abmonth: "Jan,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
@ -471,7 +471,7 @@ var locales = {
ampm: { 0: "AM", 1: "PM" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%A %d %B %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20
abmonth: "janv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.",
abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc",
month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre",
abday: "dim,lun,mar,mer,jeu,ven,sam",
day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi",
@ -567,7 +567,7 @@ var locales = {
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%A %d %B de %Y", "1": "%d/%m/%Y" }, // dimenge 1 de març de 2020 // 01/03/2020
abmonth: "gen.,febr.,març,abril,mai,junh,julh,ago.,set.,oct.,nov.,dec.",
abmonth: "gen,febr,març,abril,mai,junh,julh,ago,set,oct,nov,dec",
month: "genièr,febrièr,març,abril,mai,junh,julhet,agost,setembre,octòbre,novembre,decembre",
abday: "dg,dl,dm,dc,dj,dv,ds",
day: "dimenge,diluns,dimars,dimècres,dijòus,divendres,dissabte",
@ -612,10 +612,10 @@ var locales = {
speed: "km/h",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "dop.", 1: "pop." },
ampm: { 0: "dop", 1: "pop" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%-d. %b %Y", 1: "%-d.%-m.%Y" }, // "3. jan. 2020" // "3.1.2020"(short)
abmonth: "sij.,velj.,ožu.,tra.,svi,lip.,srp.,kol.,ruj.,lis.,stu.,pro.",
abmonth: "sij,velj,ožu,tra,svi,lip,srp,kol,ruj,lis,stu,pro",
month: "siječanj,veljača,ožujak,travanj,svibanj,lipanj,srpanj,kolovoz,rujan,listopad,studeni,prosinac",
abday: "ned.,pon.,uto.,sri.,čet.,pet.,sub.",
day: "nedjelja,ponedjeljak,utorak,srijeda,četvrtak,petak,subota",
@ -628,7 +628,7 @@ var locales = {
speed: "km/h",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "dop.", 1: "pop." },
ampm: { 0: "dop", 1: "pop" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%-d. %b %Y", 1: "%-d.%-m.%Y" }, // "3. jan. 2020" // "3.1.2020"(short)
abmonth: "jan.,feb.,mar.,apr.,maj,jun.,jul.,avg.,sep.,okt.,nov.,dec.",
@ -728,7 +728,7 @@ var locales = {
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%d %B %Y", "1": "%d/%m/%y" },
abmonth: "gen.,febr.,març,abr.,maig,juny,jul.,ag.,set.,oct.,nov.,des.",
abmonth: "gen,febr,març,abr,maig,juny,jul,ag,set,oct,nov,des",
month: "gener,febrer,març,abril,maig,juny,juliol,agost,setembre,octubre,novembre,desembre",
abday: "dg.,dl.,dt.,dc.,dj.,dv.,ds.",
day: "diumenge,dilluns,dimarts,dimecres,dijous,divendres,dissabte",

View File

@ -15,6 +15,6 @@
{"name":"messagesoverlay.settings.js","url":"settings.js"},
{"name":"messagesoverlay.default.json","url":"default.json"}
],
"data": [{"name":"bthrm.json"}],
"data":[{"name":"messagesoverlay.json"}],
"screenshots": [{"url":"screen_call.png"} ,{"url":"screen_message.png"} ]
}

View File

@ -30,3 +30,8 @@
1.28: increased vibration strength, added some comments, & some QOL
1.29: changed image
1.30: changed image, again
1.40: added various settings for controlling when & how to throw dice
1.41: fixed dumb mistake
1.42: okay maby I should've read the *whole* error log
1.43: playing whackamole with ESLint
1.44: fixed (?) settings app

View File

@ -1,12 +1,20 @@
var menu = true; // default to have the selection menu open
var settings = Object.assign({
vibrate: true,
shake: true,
screen: false,
shake_timeout: 200,
shake_duration: 100,
}, require('Storage').readJSON("multidice.json", true) || {});
var menu = true; // defaults to having the menu open
const DICE_ARRAY = [0, 4, 6, 8, 10, 12, 20, 100]; // 0 means nothing selected
const SELECTION_ARRAY = [6, 0, 0, 0, 0, 0, 0, 0]; // default to selecting a single d20
// function to draw the selection menu
function drawMenu() {
stringArr = new Array ("", "", "", "", "", "", "", "");
for (i = 0; i < 8; i++) {
var stringArr = new Array ("", "", "", "", "", "", "", "");
for (var i = 0; i < 8; i++) {
if (SELECTION_ARRAY [i] != 0) {
@ -41,6 +49,7 @@ function touchHandler (button, xy) {
return;
}
var selection;
if (xy.x <= 87) { // left
if (xy.y <= 43) { // first
@ -84,15 +93,30 @@ function touchHandler (button, xy) {
drawMenu();
}
var shaken = false;
var last_shaken = null;
function accelHandler (xyz) {
// if the screen should be on *and* it isn't, return
if (settings.screen && ! Bangle.isBacklightOn()) {
return;
}
if (xyz.diff >= 0.3) {
menu = false;
mutex (rollDice).catch (() => {
shaken = true;
last_shaken = Date.now();
} else if (shaken && last_shaken !== null) {
return; // not necessary, but prevents spamming the logs
});
if (Date.now() - last_shaken > settings.shake_timeout) {
last_shaken = null;
shaken = false;
menu = false;
mutex (rollDice).catch (() => { return; });
}
}
}
@ -123,8 +147,8 @@ function mutex (functionRef) {
// function to roll all selected dice, and display them
function rollDice() {
resultsArr = new Uint8Array (8);
for (i = 0; i < 8; i++) {
var resultsArr = new Uint8Array (8);
for (var i = 0; i < 8; i++) {
if (SELECTION_ARRAY [i] != 0) {
@ -135,7 +159,7 @@ function rollDice() {
g.clear();
g.setFont ("Vector", 40);
for (i = 0; i < 4; i++) {
for (var i = 0; i < 4; i++) {
if (SELECTION_ARRAY [i] != 0) {
@ -143,7 +167,7 @@ function rollDice() {
}
}
for (i = 4; i < 8; i++) {
for (var i = 4; i < 8; i++) {
if (SELECTION_ARRAY [i] != 0) {
@ -157,14 +181,19 @@ function rollDice() {
// triggers the vibration, then pauses before returning
function vibrate() {
if (! settings.vibrate) {
return (Promise.resolve (0));
}
return new Promise ((resolve, reject) => {
return Bangle.buzz (50, 1).then ((value) => {
return Bangle.buzz (settings.shake_duration, 1).then ((value) => {
setTimeout (() => {
resolve (value);
}, 200);
}, 2 * settings.shake_duration + settings.shake_timeout);
});
});
}
@ -177,7 +206,7 @@ function random (max) {
drawMenu();
Bangle.on ('touch', touchHandler);
Bangle.on ('accel', accelHandler);
if (settings.shake) { Bangle.on ('accel', accelHandler); }
setWatch (function() {
menu = false;

View File

@ -1,7 +1,7 @@
{ "id": "multidice",
"name": "multiple dice roller",
"shortName":"multidice",
"version":"1.30",
"version":"1.44",
"description": "roll anywhere from 1-8 dice at the same time",
"icon": "app.png",
"tags": "tool,game",
@ -10,6 +10,8 @@
"allow_emulator": true,
"storage": [
{"name":"multidice.app.js","url":"app.js"},
{"name":"multidice.settings.js","url":"settings.js"},
{"name":"multidice.img","url":"app-icon.js","evaluate":true}
]
],
"data": [{"name": "multidice.json"}]
}

View File

@ -0,0 +1,58 @@
(function(back) {
var settings = Object.assign({
vibrate: true,
shake: true,
screen: false,
shake_timeout: 200,
shake_duration: 100,
}, require('Storage').readJSON("multidice.json", true) || {});
function writeSettings() {
require('Storage').writeJSON("multidice.json", settings);
}
// Show the menu
E.showMenu({
"" : { "title" : "multi dice roll" },
"< Back" : () => back(),
'vibrate on roll?': {
value: !!settings.vibrate,
onchange: v => {
settings.vibrate = v;
writeSettings();
}
},
'allow shaking?': {
value: !!settings.shake,
onchange: v => {
settings.shake = v;
writeSettings();
}
},
'screen on to shake?': {
value: !!settings.screen,
onchange: v => {
settings.screen = v;
writeSettings();
}
},
'shake timeout': {
value: settings.shake_timeout / 5,
min: 10, max: 40,
format: v => v * 5,
onchange: v => {
settings.shake_timeout = v * 5;
writeSettings();
}
},
'shake duration': {
value: settings.shake_duration / 5,
min: 10, max: 40,
format: v => v * 5,
onchange: v => {
settings.shake_duration = v * 5;
writeSettings();
}
},
});
})

View File

@ -6,7 +6,7 @@
"description": "A smart and simple calculator for Ohm's Law calculations, designed specifically for Bangle.js 2 smartwatches. Handles voltage, current, resistance, and power calculations with smart logic to prevent invalid inputs.",
"icon": "app.png",
"type": "app",
"tags": "calculator, utilities, electric",
"tags": "calculator,utilities,electric",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,

View File

@ -8,7 +8,7 @@
"dependencies": {"mylocation":"app","weather":"app"},
"screenshots": [{"url":"screenshot_pastel.png"}, {"url":"weather_icons.png"}],
"type": "clock",
"tags": "clock, weather, tool",
"tags": "clock,weather,tool",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -4,7 +4,7 @@
"version": "0.05",
"description": "Simple app to power off your Bangle.js",
"icon": "app.png",
"tags": "tool, poweroff, shutdown",
"tags": "tool,poweroff,shutdown",
"supports" : ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,

View File

@ -5,7 +5,7 @@
"version": "0.16",
"description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice. Configurations can be accessed through Settings->Apps.",
"type": "bootloader",
"tags": "tools, system",
"tags": "tools,system",
"readme": "README.md",
"supports": ["BANGLEJS2"],
"storage": [

View File

@ -10,7 +10,7 @@
{"url": "screenshot-2.png"}
],
"description": "An intuitive, easy-to-use app that aids in the interpretation of resistor color codes and calculation of resistance values.",
"tags": "app, tool, electricity, ohms, converter",
"tags": "app,tool,electricity,ohms,converter",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,

View File

@ -49,7 +49,7 @@ Alarms are stored in an array in `sched.json`, and take the form:
// e.g. repeat every 2 months: { interval: "month", num: 2 }.
// Supported intervals: day, week, month, year
vibrate : "...", // OPTIONAL pattern of '.', '-' and ' ' to use for when buzzing out this alarm (defaults to '..' if not set)
hidden : false, // OPTIONAL if false, the widget should not show an icon for this alarm
hidden : false, // OPTIONAL if true, the widget should not show an icon for this alarm
as : false, // auto snooze
timer : 5*60*1000, // OPTIONAL - if set, this is a timer and it's the time in ms
del : false, // OPTIONAL - if true, delete the timer after expiration

View File

@ -6,7 +6,7 @@
"description": "A keyboard on your wrist! Swipe right for space, left for delete.",
"icon": "app.png",
"allow_emulator": true,
"tags": "tools, keyboard, text, scribble",
"tags": "tools,keyboard,text,scribble",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -5,7 +5,7 @@
"description": "A basic implementation of the famous consice workout",
"icon": "icon.png",
"type":"app",
"tags": "Training, Workout",
"tags": "training,workout",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"allow_emulator":true,

View File

@ -5,7 +5,7 @@
"description": "Watch face in the style of Casio Prototype Space Resist",
"icon": "app-icon.png",
"type": "clock",
"tags": "clock, casio, retro",
"tags": "clock,casio,retro",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -5,7 +5,7 @@
"description": "Navigate between clock and launcher with Swipe action",
"icon": "swiperclocklaunch.png",
"type": "bootloader",
"tags": "tools, system",
"tags": "tools,system",
"supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"swiperclocklaunch.boot.js","url":"boot.js"},

View File

@ -7,7 +7,7 @@
"description": "Tamagotchi WatchApp",
"icon": "app.png",
"allow_emulator": true,
"tags": "clock, watch, virtual pet",
"tags": "clock,watch,virtualpet",
"supports": [
"BANGLEJS2"
],

View File

@ -5,7 +5,7 @@
"version": "0.08",
"description": "App to test the bangle.js input interface. It displays the user action in text, circle buttons or on/off switch UI elements.",
"icon": "app.png",
"tags": "input,interface,buttons,touch,UI",
"tags": "input,interface,buttons,touch,ui",
"supports": ["BANGLEJS"],
"readme": "README.md",
"storage": [

View File

@ -7,7 +7,7 @@
"description": "Conveniently record a series of date/time stamps",
"screenshots": [ {"url": "screenshot.png" } ],
"readme": "README.md",
"tags": "timestamp, log",
"tags": "timestamp,log",
"supports": ["BANGLEJS2"],
"interface": "interface.html",
"storage": [

View File

@ -6,7 +6,7 @@
"description": "Draw stuff in your wrist",
"icon": "app.png",
"allow_emulator": true,
"tags": "tools, keyboard, text, scribble",
"tags": "tools,keyboard,text,scribble",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [

1
apps/txtreader/ChangeLog Normal file
View File

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

20
apps/txtreader/README.md Normal file
View File

@ -0,0 +1,20 @@
# txtreader
Very basic text reader with an integrated file selector.
## Features
- select files from storage (.txt)
- display their contents
- browse pages
## Controls
Bangle.js 2
- tap the right side of the screen to flip to the next page
- tap the left side of the screen to flip to the previous page
- exit by pressing the physical button
## Creator
<https://topkekker.rip/>

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkCkQA/AH4A/AAcosd2m9jw04AQlyC5WL1e63YCG3lUC5WPj/x/8f+H/h4CBj/3jYXLv/3/4CCt4FC/IXM/4AI/YXk/WZzOfCgWfAoIXN/F3u9/C4V/AoIvUI6H3F4p3oC6/73e734XTR4wXQ/vd7vfC6f85gAG55HQAAppBC5n5xAAGz4vmzIAGF/33F49/C5v6F4+vC5rrFd6JHCv4XTI4WfF6lms1vF6mq1WvC6Z3WxfdABHVugXKlFUogAHoVCC5QA/AH4A6A"))

88
apps/txtreader/app.js Normal file
View File

@ -0,0 +1,88 @@
function showFileSelector() {
let files = require("Storage").list().filter(f => f.endsWith('.txt'));
let menuItems = {};
files.forEach(file => {
menuItems[file] = () => {
E.showPrompt(`Select ${file}?`).then(confirm => {
if (confirm) {
onFileSelected(file);
} else {
showFileSelector();
}
});
};
});
menuItems['< Back'] = () => { load(); };
E.showMenu(menuItems);
}
function onFileSelected(file) {
const chunkSize = 1024;
let currentOffset = 0;
let currentPage = 1;
let history = [];
function displayText(offset, pageNumber) {
g.clear();
g.setFont("6x8", 1);
g.setColor(g.theme.fg);
g.drawString("Page " + pageNumber, 10, 2);
//g.drawString("Offset " + offset, 60, 2);
g.drawString(file, g.getWidth() - file.length * 6, 2);
var text = require("Storage").read(file, offset, chunkSize);
var lines = text.split("\n");
var y = 15; // Text start, top row reserved for page number
var linesDisplayed = 0; // Lines per page
var totalCharsDisplayed = 0; // Total characters per page
for (var i = 0; i < lines.length; i++) {
var wrappedLines = g.wrapString(lines[i], g.getWidth() - 20);
for (var j = 0; j < wrappedLines.length; j++) {
g.drawString(wrappedLines[j], 10, y);
y += 10; // Move down for the next line
linesDisplayed++;
totalCharsDisplayed += wrappedLines[j].length + (j < wrappedLines.length - 1 ? 0 : 1); // Add newline character for the last wrapped line
if (y >= g.getHeight() - 10) {
// If we run out of space, stop drawing
return { nextOffset: offset + totalCharsDisplayed, linesDisplayed: linesDisplayed };
}
}
}
return null; // No more lines to display
}
// Initial display
var result = displayText(currentOffset, currentPage);
history.push({ offset: currentOffset, linesDisplayed: result.linesDisplayed });
// Handle touch events
Bangle.on('touch', function(button) {
if (button === 2) { // Right side of the screen (next page)
var nextOffset = displayText(currentOffset, currentPage + 1);
if (nextOffset !== null) {
currentOffset = nextOffset.nextOffset;
currentPage++;
history.push({ offset: currentOffset, linesDisplayed: nextOffset.linesDisplayed });
displayText(currentOffset, currentPage);
} else {
currentOffset = 0;
currentPage = 1;
history = [{ offset: currentOffset, linesDisplayed: result.linesDisplayed }];
displayText(currentOffset, currentPage);
}
} else if (button === 1) { // Left side of the screen (previous page)
if (currentPage > 1) {
history.pop(); // Remove current page from history
var previousPage = history[history.length - 1];
currentOffset = previousPage.offset;
currentPage--;
displayText(currentOffset, currentPage);
}
}
});
}
showFileSelector();

View File

@ -0,0 +1,16 @@
{
"id": "txtreader",
"name": "txtreader",
"shortName": "txtreader",
"version": "0.01",
"description": "Basic text reader with pages and a file selector.",
"icon": "txtreader.png",
"screenshots": [{"url":"screenshot_txtreader.png"}],
"tags": "app,tool",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"txtreader.app.js","url":"app.js"},
{"name":"txtreader.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -8,7 +8,7 @@
"dependencies": {"weather":"app"},
"screenshots": [{"url":"screens/screen1.png"}],
"type": "clock",
"tags": "clock, weather",
"tags": "clock,weather",
"supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",

View File

@ -5,7 +5,7 @@
"description": "Lock a Bangle 2 screen by tapping a widget.",
"icon": "widget.png",
"type": "widget",
"tags": "widget, screenlock",
"tags": "widget,screenlock",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -2,6 +2,31 @@
/* Checks for any obvious problems in apps.json
*/
var BASEDIR = __dirname+"/../";
var APPSDIR_RELATIVE = "apps/";
var APPSDIR = BASEDIR + APPSDIR_RELATIVE;
var showAllErrors = process.argv.includes("--show-all");
if (process.argv.includes("--help")) {
console.log(`BangleApps Sanity Check
------------------------
Checks apps in this repository for common issues that might
cause problems.
USAGE:
bin/sanitycheck.js
- default, runs all tests (hides known errors)
bin/sanitycheck.js --show-all
- show all warnings/errors (including known ones)
bin/sanitycheck.js --help
- show this message
`);
process.exit(0);
}
var fs = require("fs");
var vm = require("vm");
var heatshrink = require("../webtools/heatshrink");
@ -27,40 +52,42 @@ var jsparse = (() => {
return str => acorn.parse(str, { ecmaVersion: 2020 });
})();
var BASEDIR = __dirname+"/../";
var APPSDIR_RELATIVE = "apps/";
var APPSDIR = BASEDIR + APPSDIR_RELATIVE;
var knownWarningCount = 0;
var knownErrorCount = 0;
var warningCount = 0;
var errorCount = 0;
var warningList = [];
var errorList = [];
function ERROR(msg, opt) {
// file=app.js,line=1,col=5,endColumn=7
opt = opt||{};
errorList.push(msg);
if (KNOWN_ERRORS.includes(msg)) {
console.log(`Known error : ${msg}`);
knownErrorCount++;
} else {
console.log(`::error${Object.keys(opt).length?" ":""}${Object.keys(opt).map(k=>k+"="+opt[k]).join(",")}::${msg}`);
errorCount++;
if (!showAllErrors) return;
msg += " (KNOWN)"
}
console.log(`::error${Object.keys(opt).length?" ":""}${Object.keys(opt).map(k=>k+"="+opt[k]).join(",")}::${msg}`);
errorCount++;
}
function WARN(msg, opt) {
// file=app.js,line=1,col=5,endColumn=7
opt = opt||{};
warningList.push(msg);
if (KNOWN_WARNINGS.includes(msg)) {
console.log(`Known warning : ${msg}`);
knownWarningCount++;
} else {
console.log(`::warning${Object.keys(opt).length?" ":""}${Object.keys(opt).map(k=>k+"="+opt[k]).join(",")}::${msg}`);
warningCount++;
if (!showAllErrors) return;
msg += " (KNOWN)"
}
console.log(`::warning${Object.keys(opt).length?" ":""}${Object.keys(opt).map(k=>k+"="+opt[k]).join(",")}::${msg}`);
warningCount++;
}
/* These are errors that we temporarily allow */
var KNOWN_ERRORS = [
"In locale en_CA, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)",
"In locale fr_FR, long date output must be shorter than 15 characters (10 septembre 2024 -> 17)",
"In locale fr_FR, short month must be shorter than 5 characters",
"In locale sv_SE, speed must be shorter than 5 characters",
"In locale en_SE, long date output must be shorter than 15 characters (September 10 2024 -> 17)",
"In locale en_NZ, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)",
@ -69,51 +96,23 @@ var KNOWN_ERRORS = [
"In locale en_IL, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)",
"In locale es_ES, long date output must be shorter than 15 characters (miércoles, 10 de septiembre de 2024 -> 35)",
"In locale fr_BE, long date output must be shorter than 15 characters (dimanche septembre 10 2024 -> 26)",
"In locale fr_BE, short month must be shorter than 5 characters",
"In locale fr_BE, short month must be shorter than 5 characters",
"In locale fr_BE, short month must be shorter than 5 characters",
"In locale fr_BE, short month must be shorter than 5 characters",
"In locale fr_BE, short month must be shorter than 5 characters",
"In locale fi_FI, long date output must be shorter than 15 characters (keskiviikkona 10. maaliskuuta 2024 -> 34)",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale fi_FI, short month must be shorter than 5 characters",
"In locale de_CH, meridian must be shorter than 4 characters",
"In locale de_CH, meridian must be shorter than 4 characters",
"In locale de_CH, long date output must be shorter than 15 characters (Donnerstag, 10. September 2024 -> 30)",
"In locale fr_CH, long date output must be shorter than 15 characters (dimanche 10 septembre 2024 -> 26)",
"In locale fr_CH, short month must be shorter than 5 characters",
"In locale fr_CH, short month must be shorter than 5 characters",
"In locale fr_CH, short month must be shorter than 5 characters",
"In locale fr_CH, short month must be shorter than 5 characters",
"In locale fr_CH, short month must be shorter than 5 characters",
"In locale wae_CH, long date output must be shorter than 15 characters (Sunntag, 10. Herbštmánet 2024 -> 29)",
"In locale tr_TR, long date output must be shorter than 15 characters (10 Haziran 2024 Pazartesi -> 25)",
"In locale hu_HU, long date output must be shorter than 15 characters (2024 Szep 10, Csütörtök -> 23)",
"In locale oc_FR, long date output must be shorter than 15 characters (divendres 10 setembre de 2024 -> 29)",
"In locale oc_FR, short month must be shorter than 5 characters",
"In locale oc_FR, short month must be shorter than 5 characters",
"In locale hr_HR, meridian must be shorter than 4 characters",
"In locale hr_HR, meridian must be shorter than 4 characters",
"In locale hr_HR, short month must be shorter than 5 characters",
"In locale sl_SI, meridian must be shorter than 4 characters",
"In locale sl_SI, meridian must be shorter than 4 characters",
"In locale ca_ES, long date output must be shorter than 15 characters (10 setembre 2024 -> 16)",
"In locale ca_ES, short month must be shorter than 5 characters",
];
/* These are warnings we know about but don't want in our output */
var KNOWN_WARNINGS = [
"App gpsrec data file wildcard .gpsrc? does not include app ID",
"App owmweather data file weather.json is also listed as data file for app weather",
"App messagegui storage file messagegui is also listed as storage file for app messagelist",
"App carcrazy has a setting file but no corresponding data entry (add `\"data\":[{\"name\":\"carcrazy.settings.json\"}]`)",
"App loadingscreen has a setting file but no corresponding data entry (add `\"data\":[{\"name\":\"loadingscreen.settings.json\"}]`)",
"App trex has a setting file but no corresponding data entry (add `\"data\":[{\"name\":\"trex.settings.json\"}]`)",
@ -169,7 +168,7 @@ const APP_KEYS = [
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports', 'noOverwrite'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","scheduler","notify","locale","settings","textinput","module","clkinfo"]; // values allowed for "type" field
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","scheduler","notify","locale","settings","textinput","module","clkinfo","defaultconfig"]; // values allowed for "type" field - listed in README.md
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
@ -207,6 +206,10 @@ apps.forEach((app,appIdx) => {
if (!app.name) ERROR(`App ${app.id} has no name`, {file:metadataFile});
var isApp = !app.type || app.type=="app";
var appTags = app.tags ? app.tags.split(",") : [];
if (appTags.some(tag => tag!=tag.trim()))
WARN(`App ${app.id} 'tag' list contains whitespace ("${app.tags}")`, {file:metadataFile});
if (appTags.some(tag => tag!=tag.toLowerCase()))
WARN(`App ${app.id} 'tag' list contains uppercase ("${app.tags}")`, {file:metadataFile});
if (app.name.length>20 && !app.shortName && isApp) ERROR(`App ${app.id} has a long name, but no shortName`, {file:metadataFile});
if (app.type && !METADATA_TYPES.includes(app.type))
ERROR(`App ${app.id} 'type' is one one of `+METADATA_TYPES, {file:metadataFile});
@ -296,7 +299,8 @@ apps.forEach((app,appIdx) => {
if (INTERNAL_FILES_IN_APP_TYPE[app.type].includes(file.name))
fileInternal = true;
}
allFiles.push({app: app.id, file: file.name, internal:fileInternal});
if (!app.type=="defaultconfig")
allFiles.push({app: app.id, file: file.name, internal:fileInternal});
if (file.url) if (!fs.existsSync(appDir+file.url)) ERROR(`App ${app.id} file ${file.url} doesn't exist`, {file:metadataFile});
if (!file.url && !file.content && !app.custom) ERROR(`App ${app.id} file ${file.name} has no contents`, {file:metadataFile});
var fileContents = "";
@ -494,7 +498,7 @@ while(fileA=allFiles.pop()) {
if (isGlob(nameA)||isGlob(nameB))
ERROR(`App ${fileB.app} ${typeB} file ${nameB} matches app ${fileA.app} ${typeB} file ${nameA}`);
else if (fileA.app != fileB.app && (!fileA.internal) && (!fileB.internal))
WARN(`App ${fileB.app} ${typeB} file ${nameB} is also listed as ${typeA} file for app ${fileA.app}`);
WARN(`App ${fileB.app} ${typeB} file ${nameB} is also listed as ${typeA} file for app ${fileA.app}`, {file:APPSDIR_RELATIVE+fileB.app+"/metadata.json"});
}
})
}
@ -520,8 +524,17 @@ function sanityCheckLocales(){
}
promise.then(function() {
KNOWN_ERRORS.forEach(msg => {
if (!errorList.includes(msg))
WARN(`Known error '${msg}' no longer occurs`);
});
KNOWN_WARNINGS.forEach(msg => {
if (!warningList.includes(msg))
WARN(`Known warning '${msg}' no longer occurs`);
});
console.log("==================================");
console.log(`${errorCount} errors, ${warningCount} warnings (and ${knownErrorCount} known errors, ${knownWarningCount} known warnings)`);
console.log(`${errorCount} errors, ${warningCount} warnings`);
console.log(`${knownErrorCount} known errors, ${knownWarningCount} known warnings${(knownErrorCount||knownWarningCount)?", run with --show-all to see them":""}`);
console.log("==================================");
if (errorCount) {
process.exit(1);

2
core

@ -1 +1 @@
Subproject commit bf08b484830ef4e811faf67ec663ebf839b5d09b
Subproject commit 3ec8e289a26a545d0d0c50f6945978584fb3d7f8

View File

@ -14,4 +14,4 @@ type AppInfo = {
type AppType = "app" | "clock" | "widget" | "module" | "bootloader" |
"settings" | "clkinfo" | "RAM" | "launch" | "textinput" | "scheduler" |
"notify" | "locale";
"notify" | "locale" | "defaultconfig";