Merge branch 'master' of github.com:jeroenpeters1986/BangleApps

 Conflicts:
	apps.json
	apps/messages/ChangeLog
pull/1084/head
Jeroen Peters 2021-12-11 00:06:31 +01:00
commit f54c59bd02
61 changed files with 1369 additions and 151 deletions

110
apps.json
View File

@ -1,8 +1,8 @@
[
{
"id": "fwupdate",
"name": "Firmware Update (BETA)",
"version": "0.01",
"name": "Firmware Update",
"version": "0.02",
"description": "Uploads new Espruino firmwares to Bangle.js 2",
"icon": "app.png",
"type": "RAM",
@ -16,7 +16,7 @@
{
"id": "boot",
"name": "Bootloader",
"version": "0.37",
"version": "0.38",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"icon": "bootloader.png",
"type": "bootloader",
@ -57,7 +57,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.11",
"version": "0.12",
"description": "App to display notifications from iOS and Gadgetbridge",
"icon": "app.png",
"type": "app",
@ -79,7 +79,7 @@
"name": "Android Integration",
"shortName": "Android",
"version": "0.04",
"description": "(BETA) App to display notifications from Gadgetbridge on Android. This will eventually replace the Gadgetbridge widget.",
"description": "Display notifications/music/etc from Gadgetbridge on Android. This replaces the old Gadgetbridge widget.",
"icon": "app.png",
"tags": "tool,system,messages,notifications",
"dependencies": {"messages":"app"},
@ -96,7 +96,7 @@
"id": "ios",
"name": "iOS Integration",
"version": "0.07",
"description": "(BETA) App to display notifications from iOS devices",
"description": "Display notifications/music/etc from iOS devices",
"icon": "app.png",
"tags": "tool,system,ios,apple,messages,notifications",
"dependencies": {"messages":"app"},
@ -146,7 +146,7 @@
{
"id": "setting",
"name": "Settings",
"version": "0.35",
"version": "0.36",
"description": "A menu for setting up Bangle.js",
"icon": "settings.png",
"tags": "tool,system",
@ -283,7 +283,7 @@
"id": "gbridge",
"name": "Gadgetbridge",
"version": "0.24",
"description": "The default notification handler for Gadgetbridge notifications from Android. This will eventually be replaced by the 'Android' app.",
"description": "(NOT RECOMMENDED) Handles Gadgetbridge notifications from Android. This is now replaced by the 'Android' app.",
"icon": "app.png",
"type": "widget",
"tags": "tool,system,android,widget",
@ -727,7 +727,7 @@
{
"id": "gpsrec",
"name": "GPS Recorder",
"version": "0.26",
"version": "0.27",
"description": "Application that allows you to record a GPS track. Can run in background",
"icon": "app.png",
"tags": "tool,outdoors,gps,widget",
@ -1755,7 +1755,7 @@
"id": "cliock",
"name": "Commandline-Clock",
"shortName": "CLI-Clock",
"version": "0.14",
"version": "0.15",
"description": "Simple CLI-Styled Clock",
"icon": "app.png",
"screenshots": [{"url":"screenshot_cli.png"}],
@ -1937,6 +1937,19 @@
{"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",
"name": "Minion clock",
@ -2086,12 +2099,12 @@
"id": "numerals",
"name": "Numerals Clock",
"shortName": "Numerals Clock",
"version": "0.09",
"version": "0.10",
"description": "A simple big numerals clock",
"icon": "numerals.png",
"type": "clock",
"tags": "numerals,clock",
"supports": ["BANGLEJS"],
"supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true,
"screenshots": [{"url":"bangle1-numerals-screenshot.png"}],
"storage": [
@ -2393,7 +2406,7 @@
{
"id": "calendar",
"name": "Calendar",
"version": "0.02",
"version": "0.03",
"description": "Simple calendar",
"icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}],
@ -2403,8 +2416,10 @@
"allow_emulator": true,
"storage": [
{"name":"calendar.app.js","url":"calendar.js"},
{"name":"calendar.settings.js","url":"settings.js"},
{"name":"calendar.img","url":"calendar-icon.js","evaluate":true}
]
],
"data": [{"name":"calendar.json"}]
},
{
"id": "hidjoystick",
@ -2642,12 +2657,12 @@
"id": "widviz",
"name": "Widget Visibility Widget",
"shortName": "Viz Widget",
"version": "0.02",
"version": "0.03",
"description": "Swipe left to hide top bar widgets, swipe right to redisplay.",
"icon": "eye.png",
"type": "widget",
"tags": "widget",
"supports": ["BANGLEJS"],
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"widviz.wid.js","url":"widget.js"}
]
@ -3780,7 +3795,7 @@
"id": "gbmusic",
"name": "Gadgetbridge Music Controls",
"shortName": "Music Controls",
"version": "0.07",
"version": "0.08",
"description": "Control the music on your Gadgetbridge-connected phone",
"icon": "icon.png",
"screenshots": [{"url":"screenshot_v1.png"},{"url":"screenshot_v2.png"}],
@ -4080,7 +4095,7 @@
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true},
{"name":"carcrazy.settings.js","url":"settings.js"}
],
"data": [{"name":"app.json"}]
"data": [{"name":"CarCrazy.csv"}]
},
{
"id": "shortcuts",
@ -4387,7 +4402,7 @@
"id": "emojuino",
"name": "Emojuino",
"shortName": "Emojuino",
"version": "0.02",
"version": "0.03",
"description": "Emojis & Espruino: broadcast Unicode emojis via Bluetooth Low Energy.",
"icon": "emojuino.png",
"screenshots": [
@ -4409,7 +4424,7 @@
"id": "cliclockJS2Enhanced",
"name": "Commandline-Clock JS2 Enhanced",
"shortName": "CLI-Clock JS2",
"version": "0.02",
"version": "0.03",
"description": "Simple CLI-Styled Clock with enhancements. Modes that are hard to use and unneded are removed (BPM, battery info, memory ect) credit to hughbarney for the original code and design. Also added HID media controlls, just swipe on the clock face to controll the media! Gadgetbride support coming soon(hopefully) Thanks to t0m1o1 for media controls!",
"icon": "app.png",
"screenshots": [{"url":"screengrab.png"}],
@ -4522,7 +4537,7 @@
{"name":"schoolCalendar.img","url":"app-icon.js","evaluate":true}
],
"data": [
{"name":"app.json"}
{"name":"calendarItems.csv"}
]
},
{ "id": "timecal",
@ -4575,7 +4590,7 @@
"shortName":"93 Dub",
"icon": "93dub.png",
"screenshots": [{"url":"screenshot.png"}],
"version":"0.04",
"version":"0.05",
"description": "Fan recreation of orviwan's 91 Dub app for the Pebble smartwatch. Uses assets from his 91-Dub-v2.0 repo",
"tags": "clock",
"type": "clock",
@ -4593,9 +4608,10 @@
"version":"0.01",
"description": "Simple app to power off your Bangle.js",
"icon": "app.png",
"tags": "poweroff, shutdown",
"tags": "tool, poweroff, shutdown",
"supports" : ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"poweroff.app.js","url":"app.js"},
{"name":"poweroff.img","url":"app-icon.js","evaluate":true}
@ -4605,9 +4621,17 @@
"id": "sensible",
"name": "SensiBLE",
"shortName": "SensiBLE",
"version": "0.02",
"version": "0.03",
"description": "Collect, display and advertise real-time sensor data.",
"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",
"tags": "tool,sensors",
"supports" : [ "BANGLEJS2" ],
@ -4670,7 +4694,7 @@
"id": "pebble",
"name": "Pebble Clock",
"shortName": "Pebble",
"version": "0.03",
"version": "0.04",
"description": "A pebble style clock to keep the rebellion going",
"readme": "README.md",
"icon": "pebble.png",
@ -4800,6 +4824,38 @@
"screenshots":[
{ "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"}]
},
{ "id": "clicompleteclk",
"name": "CLI complete clock",
"shortName":"CLI cmplt clock",
"version":"0.02",
"description": "Command line styled clock with lots of information",
"icon": "app.png",
"allow_emulator": true,
"type": "clock",
"tags": "clock,cli,command,bash,shell,weather,hrt",
"supports" : ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"clicompleteclk.app.js","url":"app.js"},
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true}
]
}
]

View File

@ -2,3 +2,4 @@
0.02: DiscoMinotaur's adjustments (removed battery and adjusted spacing)
0.03: Code style cleanup
0.04: Set 00:00 to 12:00 for 12 hour time
0.05: Display time, even on Thursday

View File

@ -5,8 +5,8 @@
Uses many portions from Espruino documentation, example watchfaces, and the waveclk app. It also sourced from Jon Barlow's 91 Dub v2.0 source code and resources and adapted for Bangle.js 2's screen. Time, date and the battery display works. It is not pixel perfect to the original.
Contributors:
Leer10
Orviwan (original watchface and assets)
Gordon Williams (Bangle.js, watchapps for reference code and documentation)
DiscoMinotaur (adjustments)
Ray Holder (minor 12 hour time rendering adjustment)
* Leer10
* Orviwan (original watchface and assets)
* Gordon Williams (Bangle.js, watchapps for reference code and documentation)
* DiscoMinotaur (adjustments)
* Ray Holder (minor 12 hour time rendering adjustment, fix Thursdays)

View File

@ -93,7 +93,7 @@ function draw(){
if (w == 1) {imgW = imgMon;}
if (w == 2) {imgW = imgTue;}
if (w == 3) {imgW = imgWed;}
if (w == 4) {imgW = imgThr;}
if (w == 4) {imgW = imgThu;}
if (w == 5) {imgW = imgFri;}
if (w == 6) {imgW = imgSat;}
g.drawImage(imgW, 85, 63);

View File

@ -41,3 +41,4 @@
Don't set beep vibration up on Bangle.js 2 (built in)
0.36: Add comments to .boot0 to make debugging a bit easier
0.37: Remove Quiet Mode settings: now handled by Quiet Mode Schedule app
0.38: Option to log to file if settings.log==2

View File

@ -23,8 +23,14 @@ if (s.ble!==false) {
boot += `bleServiceOptions.hid=Bangle.HID;\n`;
}
}
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal
if (s.log==2) { // logging to file
boot += `_DBGLOG=require("Storage").open("log.txt","a");
`;
} if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
if (s.log==2) boot += `_DBGLOG=require("Storage").open("log.txt","a");
LoopbackB.on('data',function(d) {_DBGLOG.write(d);Terminal.write(d);});
LoopbackA.setConsole(true);\n`;
else if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal
else boot += `E.setConsole(null,{force:true});\n`; // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
/* If not programmable add our own handler for Bluetooth data
to allow Gadgetbridge commands to be received*/
@ -41,7 +47,10 @@ Bluetooth.on('line',function(l) {
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
});\n`;
} else {
if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection)
if (s.log==2) boot += `_DBGLOG=require("Storage").open("log.txt","a");
LoopbackB.on('data',function(d) {_DBGLOG.write(d);Terminal.write(d);});
if (!NRF.getSecurityStatus().connected) LoopbackA.setConsole();\n`;
else if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection)
else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth
}
// we just reset, so BLE should be on.

View File

@ -1,2 +1,3 @@
0.01: Basic calendar
0.02: Make Bangle 2 compatible
0.03: Add setting to start week on Sunday

View File

@ -6,3 +6,8 @@ Basic calendar
- Use `BTN4` (left screen tap) to go to the previous month
- Use `BTN5` (right screen tap) to go to the next month
## Settings
- Starts on Sunday: whether the calendar should start on Sunday (default is Monday).

View File

@ -18,6 +18,10 @@ const gray2 = "#888888";
const gray3 = "#bbbbbb";
const red = "#d41706";
let settings = require('Storage').readJSON("calendar.json", true) || {};
if (settings.startOnSun === undefined)
settings.startOnSun = false;
function drawCalendar(date) {
g.setBgColor(color4);
g.clearRect(0, 0, maxX, maxY);
@ -61,13 +65,18 @@ function drawCalendar(date) {
);
g.setFont("6x8", fontSize);
const dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
let dowLbls;
if (settings.startOnSun) {
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
} else {
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
}
dowLbls.forEach((lbl, i) => {
g.drawString(lbl, i * colW + colW / 2, headerH + rowH / 2);
});
date.setDate(1);
const dow = date.getDay();
const dow = date.getDay() + (settings.startOnSun ? 1 : 0);
const dowNorm = dow === 0 ? 7 : dow;
const monthMaxDayMap = {

24
apps/calendar/settings.js Normal file
View File

@ -0,0 +1,24 @@
(function(back) {
var FILE = "calendar.json";
var settings = require('Storage').readJSON(FILE, true) || {};
if (settings.startOnSun === undefined)
settings.startOnSun = true;
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
E.showMenu({
"" : { "title" : "Calendar" },
"< Back" : () => back(),
'Start on Sunday': {
value: settings.startOnSun,
format: v => v?"Yes":"No",
onchange: v => {
settings.startOnSun = v;
writeSettings();
}
},
});
})

View File

@ -1,2 +1,3 @@
0.01: Submitted to App Loader
0.02: Removed unneded code, added HID controlls thanks to t0m1o1 for his code :p
0.03: Load widgets after Bangle.setUI to ensure widgets know if they're on a clock or not (fix #970)

View File

@ -144,14 +144,15 @@ function writeLine(str,line){
}
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();
Bangle.on('lcdPower',function(on) {
if (on) drawAll();
});
var click = setInterval(updateTime, 1000);
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=>{
drawAll();
drawAll(); // why do we redraw here??
});
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();

View File

@ -0,0 +1,2 @@
0.01: New clock!
0.02: Load steps from Health Tracking app (if installed)

View File

@ -0,0 +1,22 @@
# Command line complete clock
Command line styled clock with lots of information:
It can show the following (depending on availability) information:
* Time
* Day of week
* Date
* Weather conditions and temperature (requires app [Weather](https://banglejs.com/apps/#weather))
* Steps (requires app [Health Tracking](https://banglejs.com/apps/#health%20tracking) or a step widget)
* Heart rate (when screen is on and unlocked)
## TODO
* Make time font bigger
* Show progress of steps (if any goal is set)
* Show trend of HRM out of history data
## Creator
Marco ([myxor](https://github.com/myxor))
## Icon
Icon taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgI/8/4ACAqYv/F/PwAqgA6A=="))

195
apps/clicompleteclk/app.js Normal file
View File

@ -0,0 +1,195 @@
const storage = require('Storage');
const locale = require("locale");
const font = "12x20";
const fontsize = 1;
const fontheight = 19;
const marginTop = 10;
const marginLeftTopic = 3; // margin of topics
const marginLeftData = 68; // margin of data values
const topicColor = g.theme.dark ? "#fff" : "#000";
const textColor = g.theme.dark ? "#0f0" : "#080";
let hrtValue;
let hrtValueIsOld = false;
let localTempValue;
let weatherTempString;
let lastHeartRateRowIndex;
// timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
drawAll(false);
}, 60000 - (Date.now() % 60000));
}
function drawAll(drawInfoToo){
let now = new Date();
updateTime(now);
if (drawInfoToo) {
drawInfo(now);
}
queueDraw();
}
function updateTime(now){
if (!Bangle.isLCDOn()) return;
writeLineTopic("TIME", 1);
writeLine(locale.time(now,1),1);
if(now.getMinutes() == 0)
drawInfo(now);
}
function drawInfo(now) {
if (now == undefined)
now = new Date();
let i = 2;
writeLineTopic("DOWK", i);
writeLine(locale.dow(now),i);
i++;
writeLineTopic("DATE", i);
writeLine(locale.date(now,1),i);
i++;
/*
writeLineTopic("BAT", i);
const b = E.getBattery();
writeLine(b + "%", i); // TODO make bars
i++;
*/
// weather
const weatherJson = getWeather();
if(weatherJson && weatherJson.weather){
const currentWeather = weatherJson.weather;
const weatherTempValue = locale.temp(currentWeather.temp-273.15);
weatherTempString = weatherTempValue;
writeLineTopic("WTHR", i);
writeLine(currentWeather.txt,i);
i++;
writeLineTopic("TEMP", i);
writeLine(weatherTempValue,i);
i++;
}
// steps
const steps = getSteps();
if (steps != undefined) {
writeLineTopic("STEP", i);
writeLine(steps, i);
i++;
}
drawHeartRate(i);
}
function drawHeartRate(i) {
if (i == undefined)
i = lastHeartRateRowIndex;
writeLineTopic("HRTM", i);
if (hrtValue != undefined) {
if (!hrtValueIsOld)
writeLine(hrtValue,i);
else
writeLine(hrtValue,i, topicColor);
}
lastHeartRateRowIndex = i;
}
function writeLineTopic(str, line) {
var y = marginTop+line*fontheight;
g.setFont(font,fontsize);
g.setColor(topicColor).setFontAlign(-1,-1);
g.clearRect(0,y,g.getWidth(),y+fontheight-1);
g.drawString("[" + str + "]",marginLeftTopic,y);
}
function writeLine(str,line,pColor){
if (pColor == undefined)
pColor = textColor;
var y = marginTop+line*fontheight;
g.setFont(font,fontsize);
g.setColor(pColor).setFontAlign(-1,-1);
g.drawString(str,marginLeftData,y);
}
function getSteps() {
var steps = 0;
let health;
try {
health = require("health");
} catch (e) {
// Module health not found
}
if (health != undefined) {
health.readDay(new Date(), h=>steps+=h.steps);
} else if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
} else if (WIDGETS.activepedom !== undefined) {
return WIDGETS.activepedom.getSteps();
}
return steps;
}
function getWeather() {
let jsonWeather = storage.readJSON('weather.json');
return jsonWeather;
}
// EVENTS:
// turn on HRM when the LCD is unlocked
Bangle.on('lock', function(isLocked) {
if (!isLocked) {
Bangle.setHRMPower(1,"clicompleteclk");
if (hrtValue == undefined)
hrtValue = "...";
else
hrtValueIsOld = true;
} else {
hrtValueIsOld = true;
Bangle.setHRMPower(0,"clicompleteclk");
}
drawHeartRate();
});
Bangle.on('lcdPower',function(on) {
if (on) {
drawAll(true);
} else {
hrtValueIsOld = true;
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
Bangle.on('HRM', function(hrm) {
//if(hrm.confidence > 90){
hrtValueIsOld = false;
hrtValue = hrm.bpm;
if (Bangle.isLCDOn())
drawHeartRate();
//} else {
// hrtValue = undefined;
//}
});
g.clear();
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll(true);

BIN
apps/clicompleteclk/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

View File

@ -7,3 +7,4 @@
0.13: Use setUI, work with smaller screens and themes
0.14: Fix BTN1 (fix #853)
Add light/dark theme support
0.15: Load widgets after Bangle.setUI to ensure widgets know if they're on a clock or not (fix #970)

View File

@ -183,9 +183,6 @@ Bangle.on('HRM', function(hrm) {
});
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();
Bangle.on('lcdPower',function(on) {
if (on) drawAll();
});
@ -196,3 +193,6 @@ Bangle.setUI("clockupdown", btn=>{
if (btn>0) changeFunctionMode();
drawAll();
});
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Upgraded text to images, added welcome screen and subtitles.
0.03: Advertise app name as Espruino manufacturer data when idle.

View File

@ -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';
// Non-user-configurable constants
const APP_ID = 'emojuino';
const IMAGE_INDEX = 0;
const CODE_POINT_INDEX = 1;
const EMOJI_PX = 96;
@ -40,12 +41,11 @@ const EMOJI_Y = (g.getHeight() - EMOJI_PX) / 2;
const TX_X = 68;
const TX_Y = 12;
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,
0x54, 0x46, 0x2d, 0x33, 0x32 ];
// Global variables
let emojiIndex = 0;
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
function terminateEmoji(displayIntervalId) {
NRF.setAdvertising({ });
transmitAppName();
isTransmitting = false;
clearInterval(displayIntervalId);
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX], false);
@ -169,3 +182,4 @@ g.setFontAlign(0, 0);
g.drawString(WELCOME_MESSAGE, g.getWidth() / 2, g.getHeight() / 2);
Bangle.on('touch', handleTouch);
Bangle.on('drag', handleDrag);
transmitAppName();

View File

@ -1 +1,4 @@
0.01: Initial version
0.02: Add support for ZIPs
Find and download ZIPs direct from the Espruino website
Take 'beta' tag off

View File

@ -3,29 +3,44 @@
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<p><b>THIS IS CURRENTLY BETA - PLEASE USE THE NORMAL FIRMWARE UPDATE
INSTRUCTIONS FOR <a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">BANGLE.JS</a> 1 AND <a href="https://www.espruino.com/Bangle.js2#firmware-updates" target="_blank">BANGLE.JS 2</a></b></p>
<div id="fw-unknown">
<p>Firmware updates using the App Loader are only possible on
<p><b>Firmware updates using the App Loader are only possible on
Bangle.js 2. For firmware updates on Bangle.js 1 please
<a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">see the Bangle.js 1 instructions</a></p>
<a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">see the Bangle.js 1 instructions</a></b></p>
</div>
<p>Your current firmware version is <span id="fw-version" style="font-weight:bold">unknown</span></p>
<div id="fw-ok" style="display:none">
<p>Please upload a hex file here. This file should be the <code>.app_hex</code>
<div id="latest-firmware" style="display:none">
<p>The currently available Espruino firmware releases are:</p>
<ul id="latest-firmware-list">
</ul>
<p>To update, click the link and then click the 'Upload' button that appears.</p>
</div>
<p>Or you can upload a hex or zip file here. This file should be an <code>.app_hex</code>
file, *not* the normal <code>.hex</code> (as that contains the bootloader as well).</p>
<input class="form-input" type="file" id="fileLoader" accept=".hex,.app_hex"/><br>
<p><button id="upload" class="btn btn-primary">Upload</button></p>
<input class="form-input" type="file" id="fileLoader" accept=".hex,.app_hex,.zip"/><br>
<p><button id="upload" class="btn btn-primary" style="display:none">Upload</button></p>
</div>
<p>Firmware updates via this tool work differently to the NRF Connect method mentioned on
<a href="https://www.espruino.com/Bangle.js2#firmware-updates">the Bangle.js page</a>. Firmware
is uploaded to a file on the Bangle. Once complete the Bangle reboots and the bootloader copies
the new firmware into internal Storage.</p>
<pre id="log"></pre>
<script src="../../core/lib/customize.js"></script>
<script src="../../core/lib/espruinotools.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.js"></script>
<script>
var hex;
var hexJS; // JS to upload hex
var HEADER_LEN = 16; // size of app flash header
var APP_START = 0x26000;
var APP_MAX_LENGTH = 0xda000; // from linker file - the max size the app can be, for sanity check!
var MAX_ADDRESS = 0x1000000; // discount anything in hex file above this
var VERSION = 0x12345678; // VERSION! Use this to test firmware in JS land
var DEBUG = false;
@ -37,6 +52,8 @@ function log(t) {
function onInit(device) {
console.log(device);
if (device && device.version)
document.getElementById("fw-version").innerText = device.version;
if (device && device.id=="BANGLEJS2") {
document.getElementById("fw-unknown").style = "display:none";
document.getElementById("fw-ok").style = "";
@ -44,7 +61,7 @@ function onInit(device) {
}
function checkForFileOnServer() {
/*function getURL(url, callback) {
function getURL(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = callback;
baseURL = url;
@ -55,6 +72,7 @@ function checkForFileOnServer() {
function getFilesFromURL(url, regex, callback) {
getURL(url, function() {
console.log(this.responseXML)
var files = [];
var elements = this.responseXML.getElementsByTagName("a");
for (var i=0;i<elements.length;i++) {
@ -67,35 +85,78 @@ function checkForFileOnServer() {
});
}
var regex = new RegExp("_bangle2");
var regex = new RegExp("_banglejs2.*zip$");
var domFirmwareList = document.getElementById("latest-firmware-list");
var domFirmware = document.getElementById("latest-firmware");
console.log("Checking server...");
var domFirmware = document.getElementById("latest-firmware");
getFilesFromURL("https://www.espruino.com/binaries/", regex, function(releaseFiles) {
releaseFiles.sort().reverse().forEach(function(f) {
releaseFiles.sort().reverse().forEach(function(f) {
var name = f.substr(f.substr(0,f.length-1).lastIndexOf('/')+1);
domFirmware.innerHTML += 'Release: <a href="'+f+'">'+name+'</a><br/>';
console.log("Found "+name);
domFirmwareList.innerHTML += '<li>Release: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
domFirmware.style = "";
});
getFilesFromURL("https://www.espruino.com/binaries/travis/master/",regex, function(travisFiles) {
travisFiles.forEach(function(f) {
var name = f.substr(f.lastIndexOf('/')+1);
domFirmware.innerHTML += 'Cutting Edge build: <a href="'+f+'">'+name+'</a><br/>';
console.log("Found "+name);
domFirmwareList.innerHTML += '<li>Cutting Edge build: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
domFirmware.style = "";
});
document.getElementById("checking-server").style = "display:none";
document.getElementById("main-ui").style = "";
console.log("Finished check for firmware files...");
var fwlinks = document.querySelectorAll(".fw-link");
for (var i=0;i<fwlinks.length;i++)
fwlinks[i].addEventListener("click", e => {
e.preventDefault();
var url = e.target.href;
downloadZipFile(url).then(info=>{
document.getElementById("upload").style = ""; // show upload
});
});
});
});*/
});
}
function downloadFile() {
/*response = await fetch(APP_HEX_PATH+"readlink.php?link="+APP_HEX_FILE, {
method: 'GET',
cache: 'no-cache',
});
if (response.ok) {
blob = await response.blob();
data = await blob.text();
document.getElementById("latest-firmware").innerHTML="(<b>"+data.toString()+"</b>)";
}*/
function downloadZipFile(url) {
return new Promise((resolve,reject) => {
Espruino.Core.Utils.getBinaryURL(url, (err, binary) => {
if (err) return reject("Unable to download "+url);
resolve(binary);
});
}).then(convertZipFile);
}
function convertZipFile(binary) {
var info = {};
Promise.resolve(binary).then(binary => {
info.binary = binary;
return JSZip.loadAsync(binary)
}).then(function(zipFile) {
info.zipFile = zipFile;
return info.zipFile.file("manifest.json").async("string");
}).then(function(content) {
info.manifest = JSON.parse(content).manifest;
}).then(function(content) {
console.log(info.manifest);
return info.zipFile.file(info.manifest.application.dat_file).async("arraybuffer");
}).then(function(content) {
info.dat_file = content;
}).then(function(content) {
console.log(info.manifest);
return info.zipFile.file(info.manifest.application.bin_file).async("arraybuffer");
}).then(function(content) {
info.bin_file = content;
if (info.bin_file.byteLength > APP_MAX_LENGTH) throw new Error("Firmware file is too big!");
info.storageContents = new Uint8Array(info.bin_file.byteLength + HEADER_LEN)
info.storageContents.set(new Uint8Array(info.bin_file), HEADER_LEN);
createJS_app(info.storageContents, APP_START, APP_START+info.bin_file.byteLength);
log("Download complete");
console.log("Download complete",info);
document.getElementById("upload").style = ""; // show upload
return info;
}).catch(err => log("ERROR:" + err));
}
function handleFileSelect(event) {
@ -103,13 +164,24 @@ function handleFileSelect(event) {
log("More than one file selected!");
return;
}
var file = event.target.files[0];
var reader = new FileReader();
reader.onload = function(event) {
hex = event.target.result.split("\n");
document.getElementById("upload").style = ""; // show upload
fileLoaded();
};
reader.readAsText(event.target.files[0]);
if (file.name.endsWith(".hex") || file.name.endsWith(".app_hex")) {
reader.onload = function(event) {
hex = event.target.result.split("\n");
document.getElementById("upload").style = ""; // show upload
fileLoaded();
};
reader.readAsText(event.target.files[0]);
} else if (file.name.endsWith(".zip")) {
reader.onload = function(event) {
convertZipFile(event.target.result);
};
reader.readAsArrayBuffer(event.target.files[0]);
} else {
log("Unknown file extension for "+file.name);
}
};
@ -174,14 +246,16 @@ function btoa(input) {
return out;
}
// To upload the app, we write to external flash
function createJS_app(binary, bin32, startAddress, endAddress, HEADER_LEN) {
/* To upload the app, we write to external flash,
binary = Uint8Array of data to flash. Should include HEADER_LEN header, then bytes to flash */
function createJS_app(binary, startAddress, endAddress) {
/* typedef struct {
uint32_t address;
uint32_t size;
uint32_t CRC;
uint32_t version;
} FlashHeader; */
var bin32 = new Uint32Array(binary.buffer);
bin32[0] = startAddress;
bin32[1] = endAddress - startAddress;
bin32[2] = CRC32(new Uint8Array(binary.buffer, HEADER_LEN));
@ -189,7 +263,8 @@ function createJS_app(binary, bin32, startAddress, endAddress, HEADER_LEN) {
console.log("CRC 0x"+bin32[2].toString(16));
hexJS = "";//`\x10if (E.CRC32(E.memoryArea(${startAddress},${endAddress-startAddress}))==${bin32[2]}) { print("FIRMWARE UP TO DATE!"); load();}\n`;
hexJS += '\x10var s = require("Storage");\n';
var CHUNKSIZE = 1024;
hexJS += '\x10s.erase(".firmware");\n';
var CHUNKSIZE = 2048;
for (var i=0;i<binary.length;i+=CHUNKSIZE) {
var l = binary.length-i;
if (l>CHUNKSIZE) l=CHUNKSIZE;
@ -243,10 +318,8 @@ function fileLoaded() {
});
console.log(`// Data from 0x${startAddress.toString(16)} to 0x${endAddress.toString(16)} (${endAddress-startAddress} bytes)`);
// Work out data
var HEADER_LEN = 16;
var binary = new Uint8Array(HEADER_LEN + endAddress-startAddress);
binary.fill(0); // actually seems to assume a block is filled with 0 if not complete
var bin32 = new Uint32Array(binary.buffer);
parseLines(function(addr, data) {
if (addr>MAX_ADDRESS) return; // ignore data out of range
var binAddr = HEADER_LEN + addr - startAddress;
@ -260,7 +333,7 @@ function fileLoaded() {
createJS_bootloader(new Uint8Array(binary.buffer, HEADER_LEN), startAddress, endAddress);
} else {
console.log("App - Writing to external flash");
createJS_app(binary, bin32, startAddress, endAddress);
createJS_app(binary, startAddress, endAddress);
}
}
@ -279,7 +352,7 @@ function handleUpload() {
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
document.getElementById("upload").addEventListener("click", handleUpload);
checkForFileOnServer();
setTimeout(checkForFileOnServer, 10);
</script>
</body>

View File

@ -5,3 +5,4 @@
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
0.06: Bangle.js 2 support
0.07: Fix "previous" button image
0.08: Fix scrolling title background color

View File

@ -91,7 +91,7 @@ function rScroller(l) {
y = l.y+l.h/2;
l.offset = l.offset%w;
g.setClipRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
.setColor(l.col)
.setColor(l.col).setBgColor(l.bgCol) // need to set colors: iScroll calls this function outside Layout
.setFontAlign(-1, 0) // left center
.clearRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
.drawString(l.label, l.x-l.offset+40, y)

View File

@ -28,3 +28,4 @@
0.24: Better support for Bangle.js 2, avoid widget area for Graphs, smooth graphs more
0.25: Fix issue where if Bangle.js 2 got a GPS fix but no reported time, errors could be caused by the widget (fix #935)
0.26: Multiple bugfixes
0.27: Map drawing with light theme (fix #1023)

View File

@ -197,15 +197,14 @@ function plotTrack(info) {
g.setColor(1,0.5,0.5);
g.setFont("Vector",16);
g.drawString("Track"+info.fn.toString()+" - Loading",10,220);
g.setColor(0,0,0);
g.setColor(g.theme.bg);
g.fillRect(0,220,239,239);
if (!info.qOSTM) {
g.setColor(1, 0, 0);
g.fillRect(9,80,11,120);
g.fillPoly([9,60,19,80,0,80]);
g.setColor(1,1,1);
g.setColor(g.theme.fg);
g.drawString("N",2,40);
g.setColor(1,1,1);
} else {
osm.lat = info.lat;
osm.lon = info.lon;
@ -228,7 +227,7 @@ function plotTrack(info) {
g.setColor(0,1,0);
g.fillCircle(mp.x,mp.y,5);
if (info.qOSTM) g.setColor(1,0,0.55);
else g.setColor(1,1,1);
else g.setColor(g.theme.fg);
l = f.readLine(f);
while(l!==undefined) {
c = l.split(",");
@ -248,11 +247,11 @@ function plotTrack(info) {
g.setColor(1,0,0);
g.fillCircle(ox,oy,5);
if (info.qOSTM) g.setColor(0, 0, 0);
else g.setColor(1,1,1);
else g.setColor(g.theme.fg);
g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20);
g.setFont("6x8",2);
g.setFontAlign(0,0,3);
g.drawString("Back",g.getWidth() - 10, g.getHeight() - 40);
g.drawString("Back",g.getWidth() - 10, g.getHeight()/2);
setWatch(function() {
viewTrack(info.fn, info);
}, global.BTN3||BTN1);

View File

@ -13,4 +13,5 @@
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
Fix phone icon (#1014)
0.10: Respect the 'new' attribute if it was set from iOS integrations
0.11: New notification icons, more app icons
0.11: Open app when touching the widget (Bangle.js 2 only)
0.12: New notification icons, more app icons

View File

@ -1,4 +1,5 @@
WIDGETS["messages"]={area:"tl",width:0,draw:function() {
Bangle.removeListener('touch', this.touch);
if (!this.width) return;
var c = (Date.now()-this.t)/1000;
g.reset().setBgColor((c&1) ? "#0f0" : "#030").setColor((c&1) ? "#000" : "#fff");
@ -12,6 +13,7 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
WIDGETS["messages"].buzz(); // buzz every 4 seconds
}
setTimeout(()=>WIDGETS["messages"].draw(), 1000);
if (process.env.HWVERSION>1) Bangle.on('touch', this.touch);
},show:function(quiet) {
WIDGETS["messages"].t=Date.now(); // first time
WIDGETS["messages"].l=Date.now()-10000; // last buzz
@ -33,6 +35,10 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100));
}
b();
},touch:function(b,c) {
var w=WIDGETS["messages"];
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+23) return;
load("messages.app.js");
}};
/* We might have returned here if we were in the Messages app for a
message but then the watch was never viewed. In that case we don't

View File

@ -7,3 +7,4 @@
0.07: Add date on touch and some improvements (see settings and readme)
0.08: Add new draw styles, tidy up draw functionality
0.09: Tweak for faster rendering
0.10: Enhance for use with Bangle2, insert new draw mode 'thickfill'

View File

@ -7,14 +7,20 @@ Settings can be accessed through the app/widget settings menu of the Bangle.js
### Color:
* rnd - shows numerals in different color combinations every time the watches wakes
* r/g - red/green
* y/w - yellow/white
* o/c - orange/cyan
* b/y - blue/yellow'ish
* r/g - red/green (Bangle1/Bangle2)
* y/w - yellow/white (Bangle1 only)
* o/c - orange/cyan (Bangle1 only)
* b/y - blue/yellow'ish (Bangle1 only)
* r/g - red/green (Bangle2 only)
* g/b - green/blue (Bangle2 only)
* r/c - red/cyan (Bangle2 only)
* m/g - magenta/green (Bangle2 only)
### Draw mode
* fill - fill numerals
* frame - only shows outline of numerals
* framefill - frame with lighter color fill
* thickfill - thick frame in theme foreground color
### Menu button
* choose button to start launcher menu with

View File

@ -6,7 +6,7 @@
* + see README.md for details
*/
var numerals = {
var numerals = {
0:[[9,1,82,1,90,9,90,92,82,100,9,100,1,92,1,9],[30,25,61,25,69,33,69,67,61,75,30,75,22,67,22,33]],
1:[[50,1,82,1,90,9,90,92,82,100,73,100,65,92,65,27,50,27,42,19,42,9]],
2:[[9,1,82,1,90,9,90,53,82,61,21,61,21,74,82,74,90,82,90,92,82,100,9,100,1,92,1,48,9,40,70,40,70,27,9,27,1,19,1,9]],
@ -19,8 +19,8 @@ var numerals = {
9:[[9,1,82,1,90,9,90,92,82,100,9,100,1,92,1,82,9,74,69,74,69,61,9,61,1,53,1,9],[22,27,69,27,69,41,22,41]],
};
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
var _hCol = ["#ff5555","#ffff00","#FF9901","#2F00FF"];
var _mCol = ["#55ff55","#ffffff","#00EFEF","#FFBF00"];
var _hCol = [];
var _mCol = [];
var _rCol = 0;
var scale = g.getWidth()/240;
var interval = 0;
@ -42,15 +42,23 @@ var drawFuncs = {
},
thickframe : function(poly,isHole){
g.drawPoly(poly,true);
g.drawPoly(translate(1,0,poly),true);
g.drawPoly(translate(1,1,poly),true);
g.drawPoly(translate(0,1,poly),true);
g.drawPoly(translate(1,0,poly,1),true);
g.drawPoly(translate(1,1,poly,1),true);
g.drawPoly(translate(0,1,poly,1),true);
},
thickfill : function(poly,isHole){
if (isHole) g.setColor(g.theme.bg);
g.fillPoly(poly,true);
g.setColor(g.theme.fg);
g.drawPoly(translate(1,0,poly,1),true);
g.drawPoly(translate(1,1,poly,1),true);
g.drawPoly(translate(0,1,poly,1),true);
}
};
function translate(tx, ty, p){
function translate(tx, ty, p, ascale){
//return p.map((x, i)=> x+((i&1)?ty:tx));
return g.transformVertices(p, {x:tx,y:ty,scale:scale});
return g.transformVertices(p, {x:tx,y:ty,scale:ascale==undefined?scale:ascale});
}
@ -99,6 +107,18 @@ function setUpdateInt(set){
if (set) interval=setInterval(draw, REFRESH_RATE);
}
function setUp(){
if (process.env.HWVERSION==1){
_hCol = ["#ff5555","#ffff00","#FF9901","#2F00FF"];
_mCol = ["#55ff55","#ffffff","#00EFEF","#FFBF00"];
} else {
_hCol = ["#ff0000","#00ff00","#ff0000","#ff00ff"];
_mCol = ["#00ff00","#0000ff","#00ffff","#00ff00"];
}
if (settings.color==0) _rCol = Math.floor(Math.random()*_hCol.length);
}
setUp();
g.clear(1);
// Show launcher when button pressed
Bangle.setUI("clock");
@ -111,11 +131,12 @@ if (settings.showDate) {
}
Bangle.on('lcdPower', function(on){
if (on){
if (settings.color==0) _rCol = Math.floor(Math.random()*_hCol.length);
setUp();
draw();
setUpdateInt(1);
} else setUpdateInt(0);
});
Bangle.on('lock', () => setUp());
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -12,8 +12,8 @@
}
let numeralsSettings = storage.readJSON('numerals.json',1);
if (!numeralsSettings) resetSettings();
let dm = ["fill","frame","framefill","thickframe"];
let col = ["rnd","r/g","y/w","o/c","b/y"];
let dm = ["fill","frame","framefill","thickframe","thickfill"];
let col = process.env.HWVERSION==1?["rnd","r/g","y/w","o/c","b/y"]:["rnd","r/g","g/b","r/c","m/g"];
let btn = [[24,"BTN1"],[22,"BTN2"],[23,"BTN3"],[11,"BTN4"],[16,"BTN5"]];
var menu={
"" : { "title":"Numerals"},

View File

@ -1,3 +1,4 @@
0.01: first release
0.02: included deployment of pebble.settings.js in apps.json
0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot
0.04: Fix widget hiding code (fix #1046)

View File

@ -111,9 +111,10 @@ g.clear();
Bangle.loadWidgets();
/*
* we are not drawing the widgets as we are taking over the whole screen
* so we will blank out the draw() functions of each widget
* so we will blank out the draw() functions of each widget and change the
* area to the top bar doesn't get cleared.
*/
for (let wd of WIDGETS) {wd.draw=()=>{};}
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
loadSettings();
setInterval(draw, 15000); // refresh every 15s
draw();

2
apps/ptlaunch/ChangeLog Normal file
View File

@ -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

46
apps/ptlaunch/README.md Normal file
View File

@ -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
![](main_menu.png)
![](add_pattern.png)
![](select_app.png)
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!

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkE//ziEAiM//4ACmMAgMvA4fziIIBCAUgEAUCBwXxFIYYDCAvyHIgPCGwIgFCA0wAYIRCh49BLQoXB+AHEgYUBgQaCE4JGEHAZGDAAMBAQMjC4UBEw0Aj5PFDIchC4Q/BC5CtIgIXUGwIXDI6EBiCaCCYJ3RIganTa5AnEgbXIewwPGn4ICCA8hgESAoQABmUQgI2CCBQA/AAvzbIRuD/8xNwMTCBTfDPwbYEPAaPDf4LnFB4T/EEAS/Fj7vFZ4LvBgMiFIQXBCAwmEE4Q3BiUikTnHJAQFEJ4XwgERHgI/CJ4oAIC4QYBiYXDCxgXDgUzLQQXIGwpHDLoRHJgJmFO4arCO5MCK4QACh6nCJ4poDCAbGFe4QnEY4IgGG4oOCc4ofCbAj3C/8hiMSAoQYCiMRMQQQKAH4AGkMAJwsyiEBL4wQER4Z+DR5AQFX4ooCX44QGVobvOgMREAUQBwg3B+IXFc4cTmYUBgIXFgImCAAkf/59BkERIgMBBwo/BC5AkDCgwXOAAIMGI5xFBBgR3SJYinXa5A4EfAQQHewoABJAgfCCA/zFAMRn4OC/8xIAIWDCAJGBgIQBA=="))

416
apps/ptlaunch/app.js Normal file
View File

@ -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;
};

BIN
apps/ptlaunch/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

202
apps/ptlaunch/boot.js Normal file
View File

@ -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 });
}
}
}
};
})();

BIN
apps/ptlaunch/main_menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Corrected variable initialisation
0.03: Advertise app name, added screenshots

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -6,6 +6,7 @@
// Non-user-configurable constants
const APP_ID = 'sensible';
const ESPRUINO_COMPANY_CODE = 0x0590;
// 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
Bangle.on('accel', function(newAcc) {
acc = newAcc;
@ -155,6 +169,7 @@ Bangle.on('mag', function(newMag) {
// On start: enable sensors and display main menu
g.clear();
transmitAppName();
Bangle.setBarometerPower(isBarEnabled, APP_ID);
Bangle.setGPSPower(isGpsEnabled, APP_ID);
Bangle.setHRMPower(isHrmEnabled, APP_ID);

View File

@ -38,3 +38,4 @@
0.33: Really fix 'beep' menu on Bangle.js 2 this time
0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app
0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen
0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js

View File

@ -2,27 +2,26 @@
This is Bangle.js's settings menu
* **Make Connectable** regardless of the current Bluetooth settings, makes Bangle.js so you can connect to it (while the window is up)
* **App/Widget Settings** settings specific to installed applications
* **BLE** Bluetooth Settings menu - see below.
* **Debug Info** should debug info be shown on the watch's screen or not?
* **Beep** most Bangle.js do not have a speaker inside, but they can use the vibration motor to beep in different pitches. You can change the behaviour here to use a Piezo speaker if one is connected
* **Vibration** enable/disable the vibration motor
* **Quiet Mode** prevent notifications/alarms from vibrating/beeping/turning the screen on - see below
* **Locale** set time zone/whether the clock is 12/24 hour (for supported clocks)
* **Select Clock** if you have more than one clock face, select the default one
* **HID** When Bluetooth is enabled, Bangle.js can appear as a Bluetooth Keyboard/Joystick/etc to send keypresses to a connected device.
* **NOTE:** on some platforms enabling HID can cause you problems when trying to connect to Bangle.js to upload apps.
* **Set Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader
* **LCD** Configure settings about the screen. How long it stays on, how bright it is, and when it turns on - see below.
* **Theme** Adjust the colour scheme
* **Reset Settings** Reset the settings to defaults
* **Utils** Utilities - including resetting settings (see below)
* **Turn Off** Turn Bangle.js off
## BLE - Bluetooth Settings
* **Make Connectable** regardless of the current Bluetooth settings, makes Bangle.js so you can connect to it (while the window is up)
* **BLE** is Bluetooth LE enabled and the watch connectable?
* **Programmable** if BLE is on, can the watch be connected to in order to program/upload apps? As long as your watch firmware is up to date, Gadgetbridge will work even with `Programmable` set to `Off`.
* **HID** When Bluetooth is enabled, Bangle.js can appear as a Bluetooth Keyboard/Joystick/etc to send keypresses to a connected device.
* **NOTE:** on some platforms enabling HID can cause you problems when trying to connect to Bangle.js to upload apps.
* **Passkey BETA** allows you to set a passkey that is required to connect and pair to Bangle.js. **Note:** This is Beta and you will almost certainly encounter issues connecting with Web Bluetooth using this option.
* **Whitelist** allows you to specify only specific devices that you will let connect to your Bangle.js. Simply choose the menu item, then `Add Device`, and then connect to Bangle.js with the device you want to add. If you are already connected you will have to disconnect first. Changes will take effect when you exit the `Settings` app.
* **NOTE:** iOS devices and newer Android devices often implement Address Randomisation and change their Bluetooth address every so often. If you device's address changes, you will be unable to connect until you update the whitelist again.
@ -45,3 +44,15 @@ The exact effects depend on the app. In general the watch will not wake up by i
- Alarms: Stops notifications, but "alarm" apps will still work
- Silent: Blocks even alarms
## Utils
* **Debug Info** should debug info be shown on the watch's screen or not?
* `Hide` (default) do not show debug information
* `Show` Show on the Bangle's screen (when not connected to Bluetooth or `Programmable:off`)
* `Log` Show on the Bangle's screen **and** write to a file called `log.txt` on Storage (when not connected to Bluetooth or `Programmable:off`). Warning - this file is appended to so may grow to be large if this is left enabled.
* **Compact Storage** Removes deleted/old files from Storage - this will speed up your Bangle.js
* **Rewrite Settings** Should not normally be required, but if `.boot0` has been deleted/corrupted (and so no settings are being loaded) this will fix it.
* **Flatten Battery** Turns on all devices and draws as much power as possible, attempting to flatten the Bangle.js battery. This can still take 5+ hours.
* **Reset Settings** Reset the settings (as set in this app) to defaults. Does not reset settings for other apps.
* **Factory Reset** (not available on Bangle.js 1) - wipe **everything** and return to a factory state

View File

@ -95,17 +95,8 @@ function showMainMenu() {
const mainmenu = {
'': { 'title': 'Settings' },
'< Back': ()=>load(),
'Make Connectable': ()=>makeConnectable(),
'App Settings': ()=>showAppSettingsMenu(),
'BLE': ()=>showBLEMenu(),
'Debug Info': {
value: settings.log,
format: v => v ? "Show" : "Hide",
onchange: () => {
settings.log = !settings.log;
updateSettings();
}
},
'Beep': beepMenuItem,
'Vibration': {
value: settings.vibrate,
@ -134,7 +125,7 @@ function showMainMenu() {
'Set Time': ()=>showSetTimeMenu(),
'LCD': ()=>showLCDMenu(),
'Theme': ()=>showThemeMenu(),
'Reset Settings': ()=>showResetMenu(),
'Utils': ()=>showUtilMenu(),
'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
};
@ -146,6 +137,7 @@ function showBLEMenu() {
var hidN = ["Off", "Kbrd & Media", "Kbrd","Joystick"];
E.showMenu({
'< Back': ()=>showMainMenu(),
'Make Connectable': ()=>makeConnectable(),
'BLE': {
value: settings.ble,
format: boolFormat,
@ -476,21 +468,63 @@ function showLocaleMenu() {
return E.showMenu(localemenu);
}
function showResetMenu() {
const resetmenu = {
'': { 'title': 'Reset' },
function showUtilMenu() {
var menu = {
'': { 'title': 'Utilities' },
'< Back': ()=>showMainMenu(),
'Debug Info': {
value: E.clip(0|settings.log,0,2),
format: v => ["Hide","Show","Log"][E.clip(0|v,0,2)],
onchange: v => {
settings.log = v;
updateSettings();
}
},
'Compact Storage': () => {
E.showMessage("Compacting...\nTakes approx\n1 minute",{title:"Storage"});
require("Storage").compact();
showUtilMenu();
},
'Rewrite Settings': () => {
require("Storage").write(".boot0","eval(require('Storage').read('bootupdate.js'));");
load("setting.app.js");
},
'Flatten Battery': () => {
E.showMessage('Flattening battery - this can take hours.\nLong-press button to cancel.');
Bangle.setLCDTimeout(0);
Bangle.setLCDPower(1);
if (Bangle.setGPSPower) Bangle.setGPSPower(1,"flat");
if (Bangle.setHRMPower) Bangle.setHRMPower(1,"flat");
if (Bangle.setCompassPower) Bangle.setCompassPower(1,"flat");
if (Bangle.setBarometerPower) Bangle.setBarometerPower(1,"flat");
if (Bangle.setHRMPower) Bangle.setGPSPower(1,"flat");
setInterval(function() {
var i=1000;while (i--);
}, 1);
},
'Reset Settings': () => {
E.showPrompt('Reset Settings?').then((v) => {
E.showPrompt('Reset to Defaults?',{title:"Settings"}).then((v) => {
if (v) {
E.showMessage('Resetting');
resetSettings();
}
setTimeout(showMainMenu, 50);
setTimeout(showMainMenu, 50);
} else showUtilMenu();
});
}
};
return E.showMenu(resetmenu);
if (Bangle.factoryReset) {
menu['Factory Reset'] = ()=>{
E.showPrompt('This will remove everything!',{title:"Factory Reset"}).then((v) => {
if (v) {
E.showMessage();
Terminal.setConsole();
Bangle.factoryReset();
} else showUtilMenu();
});
}
}
return E.showMenu(menu);
}
function makeConnectable() {

1
apps/widmpsh/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Copied from widmp and flipped the phase directions!

25
apps/widmpsh/widget.js Normal file
View File

@ -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())]();
} };

BIN
apps/widmpsh/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,3 +1,3 @@
0.01: New Widget
0.02: swipe left,right update
0.01: New Widget
0.02: swipe left,right update
0.03: Fix widget visibility code to the top bar isn't cleared by drawWidgets

View File

@ -6,16 +6,21 @@
if (!Bangle.isLCDOn() || saved) return;
saved = [];
for (var wd of WIDGETS) {
saved.push(wd.draw);
saved.push({d:wd.draw,a:wd.area});
wd.draw=()=>{};
wd.area="";
}
g.setColor(0,0,0);
g.fillRect(0,0,239,23);
g.fillRect(0,0,g.getWidth(),23);
}
function reveal(){
if (!Bangle.isLCDOn() || !saved) return;
for (var wd of WIDGETS) wd.draw = saved.shift();
for (var wd of WIDGETS) {
var o = saved.shift();
wd.draw = o.d;
wd.area = o.a;
}
Bangle.drawWidgets();
saved=null;
}

View File

@ -207,10 +207,10 @@ apps.forEach((app,appIdx) => {
}
});
// prefer "appid.json" over "appid.settings.json" (TODO: change to ERROR once all apps comply?)
if (dataNames.includes(app.id+".settings.json") && !dataNames.includes(app.id+".json"))
/* if (dataNames.includes(app.id+".settings.json") && !dataNames.includes(app.id+".json"))
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'} instead of ${app.id+'.json'}`)
else if (dataNames.includes(app.id+".settings.json"))
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'}`)
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'}`)*/
// settings files should be listed under data, not storage (TODO: change to ERROR once all apps comply?)
if (fileNames.includes(app.id+".settings.json"))
WARN(`App ${app.id} uses storage file ${app.id+'.settings.json'} instead of data file`)