Merge branch 'master' of github.com:espruino/BangleApps
46
apps.json
|
|
@ -1937,6 +1937,19 @@
|
||||||
{"name":"widmp.wid.js","url":"widget.js"}
|
{"name":"widmp.wid.js","url":"widget.js"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "widmpsh",
|
||||||
|
"name": "Moon Phase Widget Southern Hemisphere",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "Display the current moon phase in blueish for the southern hemisphere in eight phases",
|
||||||
|
"icon": "widget.png",
|
||||||
|
"type": "widget",
|
||||||
|
"tags": "widget,tools",
|
||||||
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"widmpsh.wid.js","url":"widget.js"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "minionclk",
|
"id": "minionclk",
|
||||||
"name": "Minion clock",
|
"name": "Minion clock",
|
||||||
|
|
@ -4387,7 +4400,7 @@
|
||||||
"id": "emojuino",
|
"id": "emojuino",
|
||||||
"name": "Emojuino",
|
"name": "Emojuino",
|
||||||
"shortName": "Emojuino",
|
"shortName": "Emojuino",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Emojis & Espruino: broadcast Unicode emojis via Bluetooth Low Energy.",
|
"description": "Emojis & Espruino: broadcast Unicode emojis via Bluetooth Low Energy.",
|
||||||
"icon": "emojuino.png",
|
"icon": "emojuino.png",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
|
|
@ -4593,9 +4606,10 @@
|
||||||
"version":"0.01",
|
"version":"0.01",
|
||||||
"description": "Simple app to power off your Bangle.js",
|
"description": "Simple app to power off your Bangle.js",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "poweroff, shutdown",
|
"tags": "tool, poweroff, shutdown",
|
||||||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"poweroff.app.js","url":"app.js"},
|
{"name":"poweroff.app.js","url":"app.js"},
|
||||||
{"name":"poweroff.img","url":"app-icon.js","evaluate":true}
|
{"name":"poweroff.img","url":"app-icon.js","evaluate":true}
|
||||||
|
|
@ -4605,9 +4619,17 @@
|
||||||
"id": "sensible",
|
"id": "sensible",
|
||||||
"name": "SensiBLE",
|
"name": "SensiBLE",
|
||||||
"shortName": "SensiBLE",
|
"shortName": "SensiBLE",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Collect, display and advertise real-time sensor data.",
|
"description": "Collect, display and advertise real-time sensor data.",
|
||||||
"icon": "sensible.png",
|
"icon": "sensible.png",
|
||||||
|
"screenshots": [
|
||||||
|
{ "url": "screenshot-top.png" },
|
||||||
|
{ "url": "screenshot-acc.png" },
|
||||||
|
{ "url": "screenshot-bar.png" },
|
||||||
|
{ "url": "screenshot-gps.png" },
|
||||||
|
{ "url": "screenshot-hrm.png" },
|
||||||
|
{ "url": "screenshot-mag.png" }
|
||||||
|
],
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool,sensors",
|
"tags": "tool,sensors",
|
||||||
"supports" : [ "BANGLEJS2" ],
|
"supports" : [ "BANGLEJS2" ],
|
||||||
|
|
@ -4800,6 +4822,22 @@
|
||||||
"screenshots":[
|
"screenshots":[
|
||||||
{ "url":"screenshot.png" }
|
{ "url":"screenshot.png" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ptlaunch",
|
||||||
|
"name": "Pattern Launcher",
|
||||||
|
"shortName": "Pattern Launcher",
|
||||||
|
"version": "0.02",
|
||||||
|
"description": "Directly launch apps from the clock screen with custom patterns.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "tools",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{ "name": "ptlaunch.app.js", "url": "app.js" },
|
||||||
|
{ "name": "ptlaunch.boot.js", "url": "boot.js" },
|
||||||
|
{ "name": "ptlaunch.img", "url": "app-icon.js", "evaluate": true }
|
||||||
|
],
|
||||||
|
"data": [{"name":"ptlaunch.patterns.json"}]
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Upgraded text to images, added welcome screen and subtitles.
|
0.02: Upgraded text to images, added welcome screen and subtitles.
|
||||||
|
0.03: Advertise app name as Espruino manufacturer data when idle.
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ const CYCLE_BUZZ_MILLISECONDS = 50;
|
||||||
const WELCOME_MESSAGE = 'Emojuino:\r\n\r\n< Swipe >\r\nto select\r\n\r\nTap\r\nto transmit';
|
const WELCOME_MESSAGE = 'Emojuino:\r\n\r\n< Swipe >\r\nto select\r\n\r\nTap\r\nto transmit';
|
||||||
|
|
||||||
// Non-user-configurable constants
|
// Non-user-configurable constants
|
||||||
|
const APP_ID = 'emojuino';
|
||||||
const IMAGE_INDEX = 0;
|
const IMAGE_INDEX = 0;
|
||||||
const CODE_POINT_INDEX = 1;
|
const CODE_POINT_INDEX = 1;
|
||||||
const EMOJI_PX = 96;
|
const EMOJI_PX = 96;
|
||||||
|
|
@ -40,12 +41,11 @@ const EMOJI_Y = (g.getHeight() - EMOJI_PX) / 2;
|
||||||
const TX_X = 68;
|
const TX_X = 68;
|
||||||
const TX_Y = 12;
|
const TX_Y = 12;
|
||||||
const FONT_SIZE = 24;
|
const FONT_SIZE = 24;
|
||||||
const BTN_WATCH_OPTIONS = { repeat: true, debounce: 20, edge: "falling" };
|
const ESPRUINO_COMPANY_CODE = 0x0590;
|
||||||
const UNICODE_CODE_POINT_ELIDED_UUID = [ 0x49, 0x6f, 0x49, 0x44, 0x55,
|
const UNICODE_CODE_POINT_ELIDED_UUID = [ 0x49, 0x6f, 0x49, 0x44, 0x55,
|
||||||
0x54, 0x46, 0x2d, 0x33, 0x32 ];
|
0x54, 0x46, 0x2d, 0x33, 0x32 ];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
let emojiIndex = 0;
|
let emojiIndex = 0;
|
||||||
let isToggleOn = false;
|
let isToggleOn = false;
|
||||||
|
|
@ -100,9 +100,22 @@ function transmitEmoji(image, codePoint, duration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transmit the app name under the Espruino company code to facilitate discovery
|
||||||
|
function transmitAppName() {
|
||||||
|
let options = {
|
||||||
|
showName: false,
|
||||||
|
manufacturer: ESPRUINO_COMPANY_CODE,
|
||||||
|
manufacturerData: JSON.stringify({ name: APP_ID }),
|
||||||
|
interval: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF.setAdvertising({}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Terminate the emoji transmission
|
// Terminate the emoji transmission
|
||||||
function terminateEmoji(displayIntervalId) {
|
function terminateEmoji(displayIntervalId) {
|
||||||
NRF.setAdvertising({ });
|
transmitAppName();
|
||||||
isTransmitting = false;
|
isTransmitting = false;
|
||||||
clearInterval(displayIntervalId);
|
clearInterval(displayIntervalId);
|
||||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX], false);
|
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX], false);
|
||||||
|
|
@ -169,3 +182,4 @@ g.setFontAlign(0, 0);
|
||||||
g.drawString(WELCOME_MESSAGE, g.getWidth() / 2, g.getHeight() / 2);
|
g.drawString(WELCOME_MESSAGE, g.getWidth() / 2, g.getHeight() / 2);
|
||||||
Bangle.on('touch', handleTouch);
|
Bangle.on('touch', handleTouch);
|
||||||
Bangle.on('drag', handleDrag);
|
Bangle.on('drag', handleDrag);
|
||||||
|
transmitAppName();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: Initial creation of the pattern launch app
|
||||||
|
0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Pattern Launcher
|
||||||
|
|
||||||
|
Directly launch apps from the clock screen with custom patterns.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Create patterns and link them to apps in the Pattern Launcher app.
|
||||||
|
|
||||||
|
Then launch the linked apps directly from the clock screen by simply drawing the desired pattern.
|
||||||
|
|
||||||
|
## Screenshots and detailed steps
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
From the main menu you can:
|
||||||
|
- Add a new pattern and link it to an app (first entry)
|
||||||
|
- To create a new pattern first select "Add Pattern"
|
||||||
|
- Now draw any pattern you like, this will later launch the linked app from the clock screen
|
||||||
|
- If you don't like the pattern, simply re-draw it. The previous pattern will be discarded.
|
||||||
|
- If you are happy with the pattern tap on screen or press the button to continue
|
||||||
|
- Now select the app you want to launch with the pattern.
|
||||||
|
- Note, you can bind multiple patterns to the same app.
|
||||||
|
- Remove linked patterns (second entry)
|
||||||
|
- To remove a pattern first select "Remove Pattern"
|
||||||
|
- You will now see a list of apps that have patterns linked to them
|
||||||
|
- Simply select the app that you want to unlink. This will remove the saved pattern, but not the app itself!
|
||||||
|
- Note, that you can not actually preview the patterns. This makes removing patterns that are linked to the same app annoying. sorry!
|
||||||
|
- Disable the lock screen on the clock screen from the settings (third entry)
|
||||||
|
- To launch the app from the pattern on the clock screen the watch must be unlocked.
|
||||||
|
- If this annoys you, you can disable the lock on the clock screen from the setting here
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
1) Nothing happens when I draw on the clock screen!
|
||||||
|
|
||||||
|
Please double-check if you actually have a pattern linked to an app.
|
||||||
|
|
||||||
|
2) I have a pattern linked to an app and still nothing happens when I draw on the clock screen!
|
||||||
|
|
||||||
|
Make sure the watch is unlocked before you start drawing. If this bothers you, you can permanently disable the watch-lock from within the Pattern Launcher app (via the Settings).
|
||||||
|
|
||||||
|
3) I have done all that and still nothing happens!
|
||||||
|
|
||||||
|
Please note that drawing on the clock screen will not visually show the pattern you drew. It will start the app as soon as the pattern was recognized - this might take 1 or 2 seconds! If still nothing happens, that might be a bug, sorry!
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkE//ziEAiM//4ACmMAgMvA4fziIIBCAUgEAUCBwXxFIYYDCAvyHIgPCGwIgFCA0wAYIRCh49BLQoXB+AHEgYUBgQaCE4JGEHAZGDAAMBAQMjC4UBEw0Aj5PFDIchC4Q/BC5CtIgIXUGwIXDI6EBiCaCCYJ3RIganTa5AnEgbXIewwPGn4ICCA8hgESAoQABmUQgI2CCBQA/AAvzbIRuD/8xNwMTCBTfDPwbYEPAaPDf4LnFB4T/EEAS/Fj7vFZ4LvBgMiFIQXBCAwmEE4Q3BiUikTnHJAQFEJ4XwgERHgI/CJ4oAIC4QYBiYXDCxgXDgUzLQQXIGwpHDLoRHJgJmFO4arCO5MCK4QACh6nCJ4poDCAbGFe4QnEY4IgGG4oOCc4ofCbAj3C/8hiMSAoQYCiMRMQQQKAH4AGkMAJwsyiEBL4wQER4Z+DR5AQFX4ooCX44QGVobvOgMREAUQBwg3B+IXFc4cTmYUBgIXFgImCAAkf/59BkERIgMBBwo/BC5AkDCgwXOAAIMGI5xFBBgR3SJYinXa5A4EfAQQHewoABJAgfCCA/zFAMRn4OC/8xIAIWDCAJGBgIQBA=="))
|
||||||
|
|
@ -0,0 +1,416 @@
|
||||||
|
var storage = require("Storage");
|
||||||
|
|
||||||
|
var DEBUG = false;
|
||||||
|
var log = (message) => {
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var CIRCLE_RADIUS = 25;
|
||||||
|
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||||
|
|
||||||
|
var CIRCLES = [
|
||||||
|
{ x: 25, y: 25, i: 0 },
|
||||||
|
{ x: 87, y: 25, i: 1 },
|
||||||
|
{ x: 150, y: 25, i: 2 },
|
||||||
|
{ x: 25, y: 87, i: 3 },
|
||||||
|
{ x: 87, y: 87, i: 4 },
|
||||||
|
{ x: 150, y: 87, i: 5 },
|
||||||
|
{ x: 25, y: 150, i: 6 },
|
||||||
|
{ x: 87, y: 150, i: 7 },
|
||||||
|
{ x: 150, y: 150, i: 8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var showMainMenu = () => {
|
||||||
|
log("loading patterns");
|
||||||
|
var storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {};
|
||||||
|
|
||||||
|
var mainmenu = {
|
||||||
|
"": {
|
||||||
|
title: "Pattern Launcher",
|
||||||
|
},
|
||||||
|
"< Back": () => {
|
||||||
|
log("cancel");
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
"Add Pattern": () => {
|
||||||
|
log("creating pattern");
|
||||||
|
createPattern().then((pattern) => {
|
||||||
|
log("got pattern");
|
||||||
|
log(pattern);
|
||||||
|
log(pattern.length);
|
||||||
|
|
||||||
|
var confirmPromise = new Promise((resolve) => resolve(true));
|
||||||
|
|
||||||
|
if (storedPatterns[pattern]) {
|
||||||
|
log("pattern already exists. show confirmation prompt");
|
||||||
|
confirmPromise = E.showPrompt("Pattern already exists\nOverwrite?", {
|
||||||
|
title: "Confirm",
|
||||||
|
buttons: { Yes: true, No: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmPromise.then((confirm) => {
|
||||||
|
log("confirmPromise resolved: " + confirm);
|
||||||
|
if (!confirm) {
|
||||||
|
showMainMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("selecting app");
|
||||||
|
getSelectedApp().then((app) => {
|
||||||
|
E.showMessage("Saving...");
|
||||||
|
log("got app");
|
||||||
|
log("saving pattern");
|
||||||
|
|
||||||
|
storedPatterns[pattern] = {
|
||||||
|
app: { name: app.name, src: app.src },
|
||||||
|
};
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
showMainMenu();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"Remove Pattern": () => {
|
||||||
|
log("selecting pattern through app");
|
||||||
|
getStoredPatternViaApp(storedPatterns).then((pattern) => {
|
||||||
|
E.showMessage("Deleting...");
|
||||||
|
delete storedPatterns[pattern];
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
showMainMenu();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Settings: () => {
|
||||||
|
var settings = storedPatterns["settings"] || {};
|
||||||
|
|
||||||
|
var settingsmenu = {
|
||||||
|
"": {
|
||||||
|
title: "Pattern Settings",
|
||||||
|
},
|
||||||
|
"< Back": () => {
|
||||||
|
log("cancel");
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.lockDisabled) {
|
||||||
|
settingsmenu["Enable lock"] = () => {
|
||||||
|
settings.lockDisabled = false;
|
||||||
|
storedPatterns["settings"] = settings;
|
||||||
|
Bangle.setOptions({ lockTimeout: 1000 * 30 });
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
settingsmenu["Disable lock"] = () => {
|
||||||
|
settings.lockDisabled = true;
|
||||||
|
storedPatterns["settings"] = settings;
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(settingsmenu);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
E.showMenu(mainmenu);
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawCircle = (circle) => {
|
||||||
|
g.fillCircle(circle.x, circle.y, CIRCLE_RADIUS);
|
||||||
|
};
|
||||||
|
|
||||||
|
var positions = [];
|
||||||
|
var createPattern = () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
E.showMenu();
|
||||||
|
g.clear();
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
CIRCLES.forEach((circle) => drawCircle(circle));
|
||||||
|
|
||||||
|
var pattern = [];
|
||||||
|
|
||||||
|
var isFinished = false;
|
||||||
|
var finishHandler = () => {
|
||||||
|
if (pattern.length === 0 || isFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log("Pattern is finished.");
|
||||||
|
isFinished = true;
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
Bangle.removeListener("tap", finishHandler);
|
||||||
|
resolve(pattern.join(""));
|
||||||
|
};
|
||||||
|
setWatch(() => finishHandler(), BTN);
|
||||||
|
setTimeout(() => Bangle.on("tap", finishHandler), 250);
|
||||||
|
|
||||||
|
var dragHandler = (position) => {
|
||||||
|
positions.push(position);
|
||||||
|
|
||||||
|
debounce().then(() => {
|
||||||
|
if (isFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
E.showMessage("Calculating...");
|
||||||
|
var t0 = Date.now();
|
||||||
|
|
||||||
|
log(positions.length);
|
||||||
|
|
||||||
|
var circlesClone = cloneCirclesArray();
|
||||||
|
pattern = [];
|
||||||
|
|
||||||
|
var step = Math.floor(positions.length / 100) + 1;
|
||||||
|
|
||||||
|
var p, a, b, circle;
|
||||||
|
|
||||||
|
for (var i = 0; i < positions.length; i += step) {
|
||||||
|
p = positions[i];
|
||||||
|
|
||||||
|
circle = circlesClone[0];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[1];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[2];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[3];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(3, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[4];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(4, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[5];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(5, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[6];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(6, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
circle = circlesClone[7];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(7, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[8];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(8, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tx = Date.now();
|
||||||
|
log(tx - t0);
|
||||||
|
positions = [];
|
||||||
|
var t1 = Date.now();
|
||||||
|
log(t1 - t0);
|
||||||
|
|
||||||
|
log("pattern:");
|
||||||
|
log(pattern);
|
||||||
|
|
||||||
|
log("redrawing");
|
||||||
|
g.clear();
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
CIRCLES.forEach((circle) => drawCircle(circle));
|
||||||
|
|
||||||
|
g.setColor(1, 1, 1);
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.setFont("6x8", 4);
|
||||||
|
pattern.forEach((circleIndex, patternIndex) => {
|
||||||
|
var circle = CIRCLES[circleIndex];
|
||||||
|
g.drawString(patternIndex + 1, circle.x, circle.y);
|
||||||
|
});
|
||||||
|
var t2 = Date.now();
|
||||||
|
log(t2 - t0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Bangle.on("drag", dragHandler);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getAppList = () => {
|
||||||
|
var appList = storage
|
||||||
|
.list(/\.info$/)
|
||||||
|
.map((appInfoFileName) => {
|
||||||
|
var appInfo = storage.readJSON(appInfoFileName, 1);
|
||||||
|
return (
|
||||||
|
appInfo && {
|
||||||
|
name: appInfo.name,
|
||||||
|
// type: appInfo.type,
|
||||||
|
// icon: appInfo.icon,
|
||||||
|
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; // do sortorder first
|
||||||
|
if (a.name < b.name) return -1;
|
||||||
|
if (a.name > b.name) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return appList;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getSelectedApp = () => {
|
||||||
|
E.showMessage("Loading apps...");
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
var selectAppMenu = {
|
||||||
|
"": {
|
||||||
|
title: "Select App",
|
||||||
|
},
|
||||||
|
"< Cancel": () => {
|
||||||
|
log("cancel");
|
||||||
|
showMainMenu();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var appList = getAppList();
|
||||||
|
appList.forEach((app) => {
|
||||||
|
selectAppMenu[app.name] = () => {
|
||||||
|
log("app selected");
|
||||||
|
log(app);
|
||||||
|
resolve(app);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
E.showMenu(selectAppMenu);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getStoredPatternViaApp = (storedPatterns) => {
|
||||||
|
E.showMessage("Loading patterns...");
|
||||||
|
log("getStoredPatternViaApp");
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
var selectPatternMenu = {
|
||||||
|
"": {
|
||||||
|
title: "Select App",
|
||||||
|
},
|
||||||
|
"< Cancel": () => {
|
||||||
|
log("cancel");
|
||||||
|
showMainMenu();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
log(storedPatterns);
|
||||||
|
var patterns = Object.keys(storedPatterns);
|
||||||
|
log(patterns);
|
||||||
|
|
||||||
|
patterns.forEach((pattern) => {
|
||||||
|
if (pattern) {
|
||||||
|
if (storedPatterns[pattern]) {
|
||||||
|
var app = storedPatterns[pattern].app;
|
||||||
|
if (!!app && !!app.name) {
|
||||||
|
var appName = app.name;
|
||||||
|
var i = 0;
|
||||||
|
while (appName in selectPatternMenu[app.name]) {
|
||||||
|
appName = app.name + i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
selectPatternMenu[appName] = () => {
|
||||||
|
log("pattern via app selected");
|
||||||
|
log(pattern);
|
||||||
|
log(app);
|
||||||
|
resolve(pattern);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
E.showMenu(selectPatternMenu);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
showMainMenu();
|
||||||
|
|
||||||
|
//////
|
||||||
|
// lib functions
|
||||||
|
//////
|
||||||
|
|
||||||
|
var debounceTimeoutId;
|
||||||
|
var debounce = (delay) => {
|
||||||
|
if (debounceTimeoutId) {
|
||||||
|
clearTimeout(debounceTimeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
debounceTimeoutId = setTimeout(() => {
|
||||||
|
debounceTimeoutId = undefined;
|
||||||
|
resolve();
|
||||||
|
}, delay || 500);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var cloneCirclesArray = () => {
|
||||||
|
var circlesClone = Array(CIRCLES.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < CIRCLES.length; i++) {
|
||||||
|
circlesClone[i] = CIRCLES[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return circlesClone;
|
||||||
|
};
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,202 @@
|
||||||
|
var DEBUG = true;
|
||||||
|
var log = (message) => {
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var CIRCLE_RADIUS = 25;
|
||||||
|
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||||
|
|
||||||
|
var CIRCLES = [
|
||||||
|
{ x: 25, y: 25, i: 0 },
|
||||||
|
{ x: 87, y: 25, i: 1 },
|
||||||
|
{ x: 150, y: 25, i: 2 },
|
||||||
|
{ x: 25, y: 87, i: 3 },
|
||||||
|
{ x: 87, y: 87, i: 4 },
|
||||||
|
{ x: 150, y: 87, i: 5 },
|
||||||
|
{ x: 25, y: 150, i: 6 },
|
||||||
|
{ x: 87, y: 150, i: 7 },
|
||||||
|
{ x: 150, y: 150, i: 8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var storedPatterns;
|
||||||
|
var positions = [];
|
||||||
|
var dragHandler = (position) => {
|
||||||
|
positions.push(position);
|
||||||
|
|
||||||
|
debounce().then(() => {
|
||||||
|
log(positions.length);
|
||||||
|
|
||||||
|
var circlesClone = cloneCirclesArray();
|
||||||
|
var pattern = [];
|
||||||
|
|
||||||
|
var step = Math.floor(positions.length / 100) + 1;
|
||||||
|
|
||||||
|
var p, a, b, circle;
|
||||||
|
|
||||||
|
for (var i = 0; i < positions.length; i += step) {
|
||||||
|
p = positions[i];
|
||||||
|
|
||||||
|
circle = circlesClone[0];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[1];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[2];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[3];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(3, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[4];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(4, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[5];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(5, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[6];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(6, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
circle = circlesClone[7];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(7, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[8];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(8, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positions = [];
|
||||||
|
|
||||||
|
pattern = pattern.join("");
|
||||||
|
|
||||||
|
if (pattern) {
|
||||||
|
if (storedPatterns[pattern]) {
|
||||||
|
var app = storedPatterns[pattern].app;
|
||||||
|
if (!!app && !!app.src) {
|
||||||
|
if (storedPatterns.settings) {
|
||||||
|
if (storedPatterns.settings.lockDisabled) {
|
||||||
|
Bangle.setLCDPower(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
load(app.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var debounceTimeoutId;
|
||||||
|
var debounce = (delay) => {
|
||||||
|
if (debounceTimeoutId) {
|
||||||
|
clearTimeout(debounceTimeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
debounceTimeoutId = setTimeout(() => {
|
||||||
|
debounceTimeoutId = undefined;
|
||||||
|
resolve();
|
||||||
|
}, delay || 500);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var cloneCirclesArray = () => {
|
||||||
|
var circlesClone = Array(CIRCLES.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < CIRCLES.length; i++) {
|
||||||
|
circlesClone[i] = CIRCLES[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return circlesClone;
|
||||||
|
};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var sui = Bangle.setUI;
|
||||||
|
Bangle.setUI = function (mode, cb) {
|
||||||
|
sui(mode, cb);
|
||||||
|
if (!mode) {
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
storedPatterns = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mode.startsWith("clock")) {
|
||||||
|
storedPatterns = {};
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var storage = require("Storage");
|
||||||
|
storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {};
|
||||||
|
if (Object.keys(storedPatterns).length > 0) {
|
||||||
|
Bangle.on("drag", dragHandler);
|
||||||
|
if (storedPatterns.settings) {
|
||||||
|
if (storedPatterns.settings.lockDisabled) {
|
||||||
|
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Corrected variable initialisation
|
0.02: Corrected variable initialisation
|
||||||
|
0.03: Advertise app name, added screenshots
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
// Non-user-configurable constants
|
// Non-user-configurable constants
|
||||||
const APP_ID = 'sensible';
|
const APP_ID = 'sensible';
|
||||||
|
const ESPRUINO_COMPANY_CODE = 0x0590;
|
||||||
|
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
|
|
@ -90,6 +91,19 @@ let magMenu = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Transmit the app name under the Espruino company code to facilitate discovery
|
||||||
|
function transmitAppName() {
|
||||||
|
let options = {
|
||||||
|
showName: false,
|
||||||
|
manufacturer: ESPRUINO_COMPANY_CODE,
|
||||||
|
manufacturerData: JSON.stringify({ name: APP_ID }),
|
||||||
|
interval: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF.setAdvertising({}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update acceleration
|
// Update acceleration
|
||||||
Bangle.on('accel', function(newAcc) {
|
Bangle.on('accel', function(newAcc) {
|
||||||
acc = newAcc;
|
acc = newAcc;
|
||||||
|
|
@ -155,6 +169,7 @@ Bangle.on('mag', function(newMag) {
|
||||||
|
|
||||||
// On start: enable sensors and display main menu
|
// On start: enable sensors and display main menu
|
||||||
g.clear();
|
g.clear();
|
||||||
|
transmitAppName();
|
||||||
Bangle.setBarometerPower(isBarEnabled, APP_ID);
|
Bangle.setBarometerPower(isBarEnabled, APP_ID);
|
||||||
Bangle.setGPSPower(isGpsEnabled, APP_ID);
|
Bangle.setGPSPower(isGpsEnabled, APP_ID);
|
||||||
Bangle.setHRMPower(isHrmEnabled, APP_ID);
|
Bangle.setHRMPower(isHrmEnabled, APP_ID);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Copied from widmp and flipped the phase directions!
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
WIDGETS["widmoonsh"] = { area: "tr", width: 24, draw: function() {
|
||||||
|
const MC = 29.5305882, NM = 694039.09;
|
||||||
|
var r = 11, mx = this.x + 12; my = this.y + 12;
|
||||||
|
|
||||||
|
function moonPhase(d) {
|
||||||
|
var tmp, month = d.getMonth(), year = d.getFullYear(), day = d.getDate();
|
||||||
|
if (month < 3) {year--; month += 12;}
|
||||||
|
tmp = ((365.25 * year + 30.6 * ++month + day - NM) / MC);
|
||||||
|
return Math.round(((tmp - (tmp | 0)) * 7)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLACK = g.theme.bg, MOON = 0x41f;
|
||||||
|
var moon = {
|
||||||
|
0: () => { g.reset().setColor(BLACK).fillRect(mx - r, my - r, mx + r, my + r);},
|
||||||
|
1: () => { moon[0](); g.setColor(MOON).drawCircle(mx, my, r);},
|
||||||
|
2: () => { moon[3](); g.setColor(BLACK).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);},
|
||||||
|
3: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r).setColor(BLACK).fillRect(mx, my - r, mx + r, my + r);},
|
||||||
|
4: () => { moon[3](); g.setColor(MOON).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);},
|
||||||
|
5: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r);},
|
||||||
|
6: () => { moon[7](); g.setColor(MOON).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);},
|
||||||
|
7: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r).setColor(BLACK).fillRect(mx - r, my - r, mx, my + r);},
|
||||||
|
8: () => { moon[7](); g.setColor(BLACK).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);}
|
||||||
|
};
|
||||||
|
moon[moonPhase(Date())]();
|
||||||
|
} };
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |