Merge branch 'espruino:master' into master

pull/780/head
awkirk71 2021-07-17 21:08:06 +01:00 committed by GitHub
commit 4c9436a5c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
185 changed files with 2978 additions and 1352 deletions

263
apps.json
View File

@ -1,14 +1,15 @@
[
{ "id": "boot",
"name": "Bootloader",
"tags": "tool,system",
"tags": "tool,system,b2",
"type":"bootloader",
"icon": "bootloader.png",
"version":"0.22",
"version":"0.27",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"storage": [
{"name":".boot0","url":"boot0.js"},
{"name":".bootcde","url":"bootloader.js"}
{"name":".bootcde","url":"bootloader.js"},
{"name":"bootupdate.js","url":"bootupdate.js"}
],
"sortorder" : -10
},
@ -41,21 +42,34 @@
"name": "Launcher (Default)",
"shortName":"Launcher",
"icon": "app.png",
"version":"0.04",
"version":"0.06",
"description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
"tags": "tool,system,launcher",
"tags": "tool,system,launcher,b2",
"type":"launch",
"storage": [
{"name":"launch.app.js","url":"app.js"}
],
"sortorder" : -10
},
{ "id": "launchb2",
"name": "Launcher (Bangle.js 2)",
"shortName":"Launcher",
"icon": "app.png",
"version":"0.01",
"description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications. It will not work on Bangle.js 1.0.",
"tags": "tool,system,launcher,b2,bno1",
"type":"launch",
"storage": [
{"name":"launchb2.app.js","url":"app.js"}
],
"sortorder" : -10
},
{ "id": "about",
"name": "About",
"icon": "app.png",
"version":"0.07",
"version":"0.08",
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
"tags": "tool,system",
"tags": "tool,system,b2",
"allow_emulator":true,
"storage": [
{"name":"about.app.js","url":"app.js"},
@ -67,7 +81,7 @@
"icon": "locale.png",
"version":"0.09",
"description": "Translations for different countries",
"tags": "tool,system,locale,translate",
"tags": "tool,system,locale,translate,b2",
"type": "locale",
"custom":"locale.html",
"readme": "README.md",
@ -104,7 +118,7 @@
{ "id": "welcome",
"name": "Welcome",
"icon": "app.png",
"version":"0.09",
"version":"0.10",
"description": "Appears at first boot and explains how to use Bangle.js",
"tags": "start,welcome",
"allow_emulator":true,
@ -171,13 +185,12 @@
{ "id": "setting",
"name": "Settings",
"icon": "settings.png",
"version":"0.24",
"version":"0.27",
"description": "A menu for setting up Bangle.js",
"tags": "tool,system",
"tags": "tool,system,b2",
"readme": "README.md",
"storage": [
{"name":"setting.app.js","url":"settings.js"},
{"name":"setting.boot.js","url":"boot.js"},
{"name":"setting.img","url":"settings-icon.js","evaluate":true}
],
"data": [
@ -191,7 +204,7 @@
"icon": "app.png",
"version":"0.11",
"description": "Set and respond to alarms",
"tags": "tool,alarm,widget",
"tags": "tool,alarm,widget,b2",
"storage": [
{"name":"alarm.app.js","url":"app.js"},
{"name":"alarm.boot.js","url":"boot.js"},
@ -206,9 +219,9 @@
{ "id": "wclock",
"name": "Word Clock",
"icon": "clock-word.png",
"version":"0.02",
"version":"0.03",
"description": "Display Time as Text",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -337,9 +350,9 @@
{ "id": "aclock",
"name": "Analog Clock",
"icon": "clock-analog.png",
"version": "0.14",
"version": "0.15",
"description": "An Analog Clock",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -350,9 +363,9 @@
{ "id": "clock2x3",
"name": "2x3 Pixel Clock",
"icon": "clock2x3.png",
"version":"0.04",
"version":"0.05",
"description": "This is a simple clock using minimalist 2x3 pixel numerical digits",
"tags": "clock",
"tags": "clock,b2",
"type": "clock",
"allow_emulator":true,
"storage": [
@ -363,9 +376,9 @@
{ "id": "geissclk",
"name": "Geiss Clock",
"icon": "clock.png",
"version":"0.02",
"version":"0.03",
"description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation",
"tags": "clock",
"tags": "clock,bno2",
"type":"clock",
"storage": [
{"name":"geissclk.app.js","url":"clock.js"},
@ -387,13 +400,17 @@
{ "id": "trex",
"name": "T-Rex",
"icon": "trex.png",
"version":"0.02",
"version":"0.03",
"description": "T-Rex game in the style of Chrome's offline game",
"tags": "game",
"allow_emulator":true,
"storage": [
{"name":"trex.app.js","url":"trex.js"},
{"name":"trex.img","url":"trex-icon.js","evaluate":true}
{"name":"trex.img","url":"trex-icon.js","evaluate":true},
{"name":"trex.settings.js","url":"settings.js"}
],
"data": [
{"name":"trex.score", "storageFile": true}
]
},
{ "id": "astroid",
@ -478,10 +495,11 @@
{ "id": "gpsrec",
"name": "GPS Recorder",
"icon": "app.png",
"version":"0.19",
"version":"0.22",
"interface": "interface.html",
"description": "Application that allows you to record a GPS track. Can run in background",
"tags": "tool,outdoors,gps,widget",
"readme": "README.md",
"storage": [
{"name":"gpsrec.app.js","url":"app.js"},
{"name":"gpsrec.img","url":"app-icon.js","evaluate":true},
@ -551,7 +569,7 @@
{ "id": "weather",
"name": "Weather",
"icon": "icon.png",
"version":"0.03",
"version":"0.05",
"description": "Show Gadgetbridge weather report",
"readme": "readme.md",
"tags": "widget,outdoors",
@ -596,14 +614,25 @@
{ "id": "widbat",
"name": "Battery Level Widget",
"icon": "widget.png",
"version":"0.05",
"version":"0.07",
"description": "Show the current battery level and charging status in the top right of the clock",
"tags": "widget,battery",
"tags": "widget,battery,b2",
"type":"widget",
"storage": [
{"name":"widbat.wid.js","url":"widget.js"}
]
},
{ "id": "widlock",
"name": "Lock Widget",
"icon": "widget.png",
"version":"0.03",
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
"tags": "widget,lock,b2",
"type":"widget",
"storage": [
{"name":"widlock.wid.js","url":"widget.js"}
]
},
{ "id": "widbatpc",
"name": "Battery Level Widget (with percentage)",
"shortName": "Battery Widget",
@ -641,9 +670,9 @@
{ "id": "widbt",
"name": "Bluetooth Widget",
"icon": "widget.png",
"version":"0.04",
"version":"0.05",
"description": "Show the current Bluetooth connection status in the top right of the clock",
"tags": "widget,bluetooth",
"tags": "widget,bluetooth,b2",
"type":"widget",
"storage": [
{"name":"widbt.wid.js","url":"widget.js"}
@ -679,7 +708,7 @@
{ "id": "hrm",
"name": "Heart Rate Monitor",
"icon": "heartrate.png",
"version":"0.03",
"version":"0.04",
"description": "Measure your heart rate and see live sensor data",
"tags": "health",
"storage": [
@ -861,9 +890,9 @@
{ "id": "sclock",
"name": "Simple Clock",
"icon": "clock-simple.png",
"version":"0.04",
"version":"0.05",
"description": "A Simple Digital Clock",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -871,10 +900,23 @@
{"name":"sclock.img","url":"clock-simple-icon.js","evaluate":true}
]
},
{ "id": "s7clk",
"name": "Simple 7 segment Clock",
"icon": "icon.png",
"version":"0.03",
"description": "A simple 7 segment Clock with date",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
{"name":"s7clk.app.js","url":"app.js"},
{"name":"s7clk.img","url":"icon.js","evaluate":true}
]
},
{ "id": "vibrclock",
"name": "Vibrate Clock",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "When BTN1 is pressed, vibrate out the time as a series of buzzes, one digit at a time. Hours, then Minutes. Zero is signified by one long buzz. Otherwise a simple digital clock.",
"tags": "clock",
"type":"clock",
@ -900,9 +942,9 @@
{ "id": "dclock",
"name": "Dev Clock",
"icon": "clock-dev.png",
"version":"0.09",
"version":"0.10",
"description": "A Digital Clock including timestamp (tst), beats(@), days in current month (dm) and days since new moon (l)",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1026,7 +1068,7 @@
{ "id": "bclock",
"name": "Binary Clock",
"icon": "clock-binary.png",
"version":"0.02",
"version":"0.03",
"description": "A simple binary clock watch face",
"tags": "clock",
"type":"clock",
@ -1102,9 +1144,9 @@
"name": "Large Digit Blob Clock",
"shortName" : "Blob Clock",
"icon": "clock-blob.png",
"version":"0.05",
"version":"0.06",
"description": "A clock with big digits",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1115,9 +1157,9 @@
{ "id": "boldclk",
"name": "Bold Clock",
"icon": "bold_clock.png",
"version":"0.03",
"version":"0.04",
"description": "Simple, readable and practical clock",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1128,7 +1170,7 @@
{ "id": "widclk",
"name": "Digital clock widget",
"icon": "widget.png",
"version":"0.04",
"version":"0.05",
"description": "A simple digital clock widget",
"tags": "widget,clock",
"type":"widget",
@ -1139,9 +1181,9 @@
{ "id": "widpedom",
"name": "Pedometer widget",
"icon": "widget.png",
"version":"0.12",
"version":"0.14",
"description": "Daily pedometer widget",
"tags": "widget",
"tags": "widget,b2",
"type":"widget",
"storage": [
{"name":"widpedom.wid.js","url":"widget.js"},
@ -1151,7 +1193,7 @@
{ "id": "berlinc",
"name": "Berlin Clock",
"icon": "berlin-clock.png",
"version":"0.03",
"version":"0.04",
"description": "Berlin Clock (see https://en.wikipedia.org/wiki/Mengenlehreuhr)",
"tags": "clock",
"type":"clock",
@ -1164,9 +1206,9 @@
{ "id": "ctrclk",
"name": "Centerclock",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "Watch-centered digital 24h clock with date in dd.mm.yyyy format.",
"tags": "clock",
"tags": "clock,bno2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1204,9 +1246,9 @@
"id": "pipboy",
"name": "Pipboy",
"icon": "app.png",
"version": "0.03",
"version": "0.04",
"description": "Pipboy themed clock",
"tags": "clock",
"tags": "clock,bno2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1285,9 +1327,9 @@
"name": "Commandline-Clock",
"shortName":"CLI-Clock",
"icon": "app.png",
"version":"0.11",
"version":"0.13",
"description": "Simple CLI-Styled Clock",
"tags": "clock,cli,command,bash,shell",
"tags": "clock,cli,command,bash,shell,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1309,10 +1351,11 @@
{ "id": "barclock",
"name": "Bar Clock",
"icon": "clock-bar.png",
"version":"0.05",
"version":"0.07",
"description": "A simple digital clock showing seconds as a bar",
"tags": "clock",
"type":"clock",
"readme": "README.md",
"allow_emulator":true,
"storage": [
{"name":"barclock.app.js","url":"clock-bar.js"},
@ -1322,9 +1365,9 @@
{ "id": "dotclock",
"name": "Dot Clock",
"icon": "clock-dot.png",
"version":"0.02",
"version":"0.03",
"description": "A Minimal Dot Analog Clock",
"tags": "clock",
"tags": "clock,b2",
"type":"clock",
"allow_emulator":true,
"storage": [
@ -1459,9 +1502,9 @@
"name": "OpenStreetMap",
"shortName":"OpenStMap",
"icon": "app.png",
"version":"0.05",
"version":"0.08",
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
"tags": "outdoors,gps",
"tags": "outdoors,gps,b2",
"custom": "custom.html",
"storage": [
{"name":"openstmap","url":"openstmap.js"},
@ -1526,9 +1569,9 @@
"name": "Dev Stopwatch",
"shortName":"Dev Stopwatch",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "Stopwatch with 5 laps supported (cyclically replaced)",
"tags": "stopwatch, chrono, timer, chronometer",
"tags": "stopwatch,chrono,timer,chronometer,b2",
"allow_emulator":true,
"storage": [
{"name":"devstopwatch.app.js","url":"app.js"},
@ -1567,7 +1610,7 @@
"name": "Numerals Clock",
"shortName": "Numerals Clock",
"icon": "numerals.png",
"version":"0.08",
"version":"0.09",
"description": "A simple big numerals clock",
"tags": "numerals,clock",
"type":"clock",
@ -1853,9 +1896,9 @@
{ "id": "calendar",
"name": "Calendar",
"icon": "calendar.png",
"version": "0.01",
"version": "0.02",
"description": "Simple calendar",
"tags": "calendar",
"tags": "calendar,b2",
"readme": "README.md",
"allow_emulator": true,
"storage": [
@ -1981,7 +2024,7 @@
"id": "beebclock",
"name": "Beeb Clock",
"icon": "beebclock.png",
"version":"0.03",
"version":"0.04",
"description": "Clock face that may be coincidentally familiar to BBC viewers",
"tags": "clock",
"type": "clock",
@ -2025,9 +2068,9 @@
"name": "Time Traveller's Chronometer",
"shortName": "Time Travel Clock",
"icon": "gallifr.png",
"version": "0.01",
"version": "0.02",
"description": "A clock for time travellers. The light pie segment shows the minutes, the black circle, the hour. The dial itself reads 'time' just in case you forget.",
"tags": "clock",
"tags": "clock,b2",
"readme": "README.md",
"type": "clock",
"allow_emulator":true,
@ -2109,7 +2152,7 @@
"name": "Binary Clock",
"shortName":"Binary Clock",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "A binary clock with hours and minutes. BTN1 toggles a digital clock.",
"tags": "clock,binary",
"type": "clock",
@ -2136,9 +2179,9 @@
"name": "Animated Clock",
"shortName":"Anim Clock",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "An animated clock face using Mark Ferrari's amazing 8 bit game art and palette cycling: http://www.markferrari.com/art/8bit-game-art",
"tags": "clock,animated",
"tags": "clock,animated,bno2",
"type": "clock",
"storage": [
{"name":"animclk.app.js","url":"app.js"},
@ -2152,9 +2195,9 @@
"name": "Analog Clock (Image background)",
"shortName":"Analog Clock",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "An analog clock with an image background",
"tags": "clock",
"tags": "clock,bno2",
"type": "clock",
"storage": [
{"name":"analogimgclk.app.js","url":"app.js"},
@ -2167,7 +2210,7 @@
"name": "Vertical watch face",
"shortName":"Vertical Face",
"icon": "app.png",
"version":"0.08",
"version":"0.09",
"description": "A simple vertical watch face with the date. Heart rate monitor is toggled with BTN1",
"tags": "clock",
"type":"clock",
@ -2285,7 +2328,7 @@
{ "id": "multiclock",
"name": "Multi Clock",
"icon": "multiclock.png",
"version":"0.12",
"version":"0.13",
"description": "Clock with multiple faces - Big, Analogue, Digital, Text, Time-Date.\n Switch between faces with BTN1 & BTN3",
"readme": "README.md",
"tags": "clock",
@ -2472,7 +2515,7 @@
"name": "World Clock - 4 time zones",
"shortName":"World Clock",
"icon": "app.png",
"version":"0.03",
"version":"0.04",
"description": "Current time zone plus up to four others",
"tags": "clock",
"type" : "clock",
@ -2490,9 +2533,9 @@
"name": "Digital Clock Face",
"shortName":"Digi Clock",
"icon": "digiclock.png",
"version":"0.01",
"version":"0.02",
"description": "A simple digital clock with the time, day, month, and year",
"tags": "clock",
"tags": "clock,bno2",
"type" : "clock",
"storage": [
{"name":"digiclock.app.js","url":"digiclock.js"},
@ -2750,7 +2793,7 @@
{ "id": "astral",
"name": "Astral Clock",
"icon": "app-icon.png",
"version":"0.02",
"version":"0.03",
"readme": "README.md",
"description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.",
"tags": "clock",
@ -2809,7 +2852,7 @@
"name": "De-Stress",
"shortName":"De-Stress",
"icon": "app.png",
"version":"0.01",
"version":"0.02",
"description": "Simple haptic heartbeat",
"storage": [
{"name":"de-stress.app.js","url":"app.js"},
@ -3046,7 +3089,7 @@
"version":"0.01",
"description": "Displays RGB565 and RGB888 colors, its name and code in screen.",
"readme": "README.md",
"tags": "Color, input,buttons,touch,UI",
"tags": "Color,input,buttons,touch,UI,bno2",
"storage": [
{"name":"color_catalog.app.js","url":"app.js"},
{"name":"color_catalog.img","url":"app-icon.js","evaluate":true}
@ -3136,7 +3179,7 @@
{ "id": "kitchen",
"name": "Kitchen Combo",
"icon": "kitchen.png",
"version":"0.10",
"version":"0.12",
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
"tags": "tool,outdoors,gps",
"type":"clock",
@ -3144,11 +3187,9 @@
"interface":"waypoints.html",
"storage": [
{"name":"kitchen.app.js","url":"kitchen.app.js"},
{"name":"stepo.kit.js","url":"stepo.kit.js"},
{"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"digi.kit.js","url":"digi.kit.js"},
{"name":"heart.kit.js","url":"heart.kit.js"},
{"name":"stepo2.kit.js","url":"stepo2.kit.js"},
{"name":"swatch.kit.js","url":"swatch.kit.js"},
{"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"compass.kit.js","url":"compass.kit.js"},
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
],
@ -3210,7 +3251,6 @@
"readme": "README.md",
"description": "An Omnitrix Showpiece",
"tags": "game",
"readme": "README.md",
"storage": [
{"name":"omnitrix.app.js","url":"omnitrix.app.js"},
{"name":"omnitrix.img","url":"omnitrix.icon.js","evaluate":true}
@ -3220,7 +3260,7 @@
"name": "Bat Clock",
"shortName":"Bat Clock",
"icon": "bat-clock.png",
"version":"0.01",
"version":"0.02",
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
"tags": "clock",
"type": "clock",
@ -3244,5 +3284,62 @@
{"name":"doztime.app.js","url":"app.js"},
{"name":"doztime.img","url":"app-icon.js","evaluate":true}
]
},
{ "id":"gbtwist",
"name":"Gadgetbridge Twist Control",
"shortName":"Twist Control",
"icon":"app.png",
"version":"0.01",
"description":"Shake your wrist to control your music app via Gadgetbridge",
"tags":"tools,bluetooth,gadgetbridge,music",
"type":"app",
"allow_emulator":false,
"readme": "README.md",
"storage": [
{"name":"gbtwist.app.js","url":"app.js"},
{"name":"gbtwist.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "thermom",
"name": "Thermometer",
"icon": "app.png",
"version":"0.02",
"description": "Displays the current temperature, updated every 20 seconds",
"tags": "tool",
"allow_emulator":true,
"storage": [
{"name":"thermom.app.js","url":"app.js"},
{"name":"thermom.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "mysticdock",
"name": "Mystic Dock",
"icon": "mystic-dock.png",
"version":"1.00",
"description": "A retro-inspired dockface that displays the current time and battery charge while plugged in, and which features an interactive mode that shows the time, date, and a rotating data display line.",
"tags": "dock",
"type":"dock",
"readme": "README.md",
"storage": [
{"name":"mysticdock.app.js","url":"mystic-dock-app.js"},
{"name":"mysticdock.boot.js","url":"mystic-dock-boot.js"},
{"name":"mysticdock.settings.js","url":"mystic-dock-settings.js"},
{"name":"mysticdock.img","url":"mystic-dock-icon.js","evaluate":true}
]
},
{ "id": "mysticclock",
"name": "Mystic Clock",
"icon": "mystic-clock.png",
"version":"1.00",
"description": "A retro-inspired watchface featuring time, date, and an interactive data display line.",
"tags": "clock",
"type":"clock",
"readme": "README.md",
"allow_emulator":true,
"storage": [
{"name":"mysticclock.app.js","url":"mystic-clock-app.js"},
{"name":"mysticclock.settings.js","url":"mystic-clock-settings.js"},
{"name":"mysticclock.img","url":"mystic-clock-icon.js","evaluate":true}
]
}
]

View File

@ -5,3 +5,4 @@
0.05: Actual pixels as of 27 Apr 2020
0.06: Actual pixels as of 12 Jun 2020
0.07: Pressing a button now exits immediately (fix #618)
0.08: Make about (mostly) work on non-240px screens

File diff suppressed because one or more lines are too long

View File

@ -9,3 +9,4 @@
0.12: Fix regression after 0.11
0.13: Fix broken date padding (fix #376)
0.14: Remove hardcoded hour buzz (you can install widchime if you miss it)
0.15: Add color scheme support

View File

@ -2,13 +2,16 @@
const locale = require('locale');
const p = Math.PI / 2;
const pRad = Math.PI / 180;
const faceWidth = 100; // watch face radius (240/2 - 24px for widget area)
const faceWidth = g.getWidth()/2 - 20; // watch face radius (240/2 - 24px for widget area)
const widgetHeight=24+1;
let timer = null;
let currentDate = new Date();
const centerX = g.getWidth() / 2;
const centerY = (g.getWidth() / 2) + widgetHeight/2;
g.theme.dark=false;
let colSecA = g.theme.dark ? "#00A" : "#58F"; // before the second
let colSecB = g.theme.dark ? "#58F" : "#00A"; // after the second
let colSec1 = g.theme.dark ? "#F83" : "#000"; // ON the second
const seconds = (angle) => {
const a = angle * pRad;
@ -46,40 +49,35 @@ const drawAll = () => {
// draw all secs
for (let i = 0; i < 60; i++) {
if (i > currentSec) {
g.setColor(0, 0, 0.6);
} else {
g.setColor(0.3, 0.3, 1);
}
g.setColor((i > currentSec) ? colSecA : colSecB);
seconds((360 * i) / 60);
}
onSecond();
};
const resetSeconds = () => {
g.setColor(0, 0, 0.6);
g.setColor(colSecA);
for (let i = 0; i < 60; i++) {
seconds((360 * i) / 60);
}
};
const onSecond = () => {
g.setColor(0.3, 0.3, 1);
g.setColor(colSecB);
seconds((360 * currentDate.getSeconds()) / 60);
if (currentDate.getSeconds() === 59) {
resetSeconds();
onMinute();
}
g.setColor(1, 0.7, 0.2);
g.setColor(colSec1);
currentDate = new Date();
seconds((360 * currentDate.getSeconds()) / 60);
g.setColor(1, 1, 1);
g.setColor(g.theme.fg);
};
const drawDate = () => {
g.reset();
g.setColor(1, 0, 0);
g.setColor("#f00");
g.setFont('6x8', 2);
const dayString = locale.dow(currentDate, true);
@ -89,7 +87,7 @@ const drawDate = () => {
// console.log(`${dayString}|${dateString}`);
// center date
const l = (g.getWidth() - g.stringWidth(dateDisplay)) / 2;
const t = centerY + 37;
const t = centerY + faceWidth*0.37;
g.drawString(dateDisplay, l, t, true);
// console.log(l, t);
};
@ -99,7 +97,7 @@ const onMinute = () => {
resetSeconds();
}
// clear existing hands
g.setColor(0, 0, 0);
g.setColor(g.theme.bg);
// Hour
hand((360 * (currentDate.getHours() + currentDate.getMinutes() / 60)) / 12, -8, faceWidth - 35);
// Minute
@ -107,10 +105,10 @@ const onMinute = () => {
// get new date, then draw new hands
currentDate = new Date();
g.setColor(1, 0.9, 0.9);
g.setColor(g.theme.fg);
// Hour
hand((360 * (currentDate.getHours() + currentDate.getMinutes() / 60)) / 12, -8, faceWidth - 35);
g.setColor(1, 1, 0.9);
g.setColor(g.theme.fg);
// Minute
hand((360 * currentDate.getMinutes()) / 60, -8, faceWidth - 10);
drawDate();
@ -137,8 +135,9 @@ g.clear();
resetSeconds();
startTimers();
drawAll();
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Add BTN2 -> launcher
0.03: Update to use setUI

View File

@ -114,5 +114,5 @@ if (g.drawImages) {
E.showMessage("Please update\nBangle.js firmware\nto use this clock","analogimgclk");
}
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Fix bug if image clock wasn't installed
0.03: Update to use setUI

View File

@ -102,5 +102,5 @@ if (g.drawImages) {
} else {
E.showMessage("Please update\nBangle.js firmware\nto use this clock","animclk");
}
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -36,6 +36,10 @@ charge.
*BTN3* - invokes calibration ( can be cancelled if pressed accidentally)
## Issues
* detect when calibration data is missing
## Acknowledgement
This app is based in the work done by [jeffmer](https://github.com/jeffmer/JeffsBangleAppsDev)

View File

@ -1,2 +1,3 @@
0.01: Create astral clock app
0.02: Fixed Whirlpool galaxy RA/DA, larger compass display, fixed moonphase overlapping battery widget
0.03: Update to use Bangle.setUI instead of setWatch

View File

@ -796,10 +796,11 @@ Bangle.on('lcdPower', on => {
Bangle.setCompassPower(1);
Bangle.setGPSPower(1);
// Buttons
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
// Show launcher when button pressed
Bangle.setClockMode();
setWatch(function () {
Bangle.setUI("clockupdown", btn => {
if (btn==0) {
if (!processing) {
if (!modeswitch) {
modeswitch = true;
@ -809,12 +810,11 @@ setWatch(function () {
else
modeswitch = false;
}
}, BTN3, { repeat: true });
setWatch(function () {
} else {
if (!processing)
ready_to_compute = true;
}, BTN1, { repeat: true });
}
});
setWatch(function () {
if (!astral_settings.astral_default) {

0
apps/banglerun/ChangeLog Executable file → Normal file
View File

View File

@ -3,3 +3,5 @@
0.03: Fix dates drawing over each other at midnight
0.04: Small bugfix
0.05: Clock does not start if app Languages is not installed
0.06: Improve accuracy
0.07: Update to use Bangle.setUI instead of setWatch

6
apps/barclock/README.md Normal file
View File

@ -0,0 +1,6 @@
# Bar Clock
A simple digital clock showing seconds as a horizontal bar.
| 24hr style | 12hr style |
| --- | --- |
| ![24-hour bar clock](screenshot.png) | ![12-hour bar clock with meridian](screenshot_pm.png) |

View File

@ -2,11 +2,10 @@
/**
* A simple digital clock showing seconds as a bar
**/
{
// Check settings for what type our clock should be
const is12Hour = (require('Storage').readJSON('setting.json', 1) || {})['12hour']
let locale = require('locale')
{ // add some more info to locale
// Check settings for what type our clock should be
const is12Hour = (require('Storage').readJSON('setting.json', 1) || {})['12hour']
let locale = require('locale')
{ // add some more info to locale
let date = new Date()
date.setFullYear(1111)
date.setMonth(1, 3) // februari: months are zero-indexed
@ -18,16 +17,16 @@
locale.hasMeridian = (locale.meridian(date) !== '')
}
}
const screen = {
}
const screen = {
width: g.getWidth(),
height: g.getWidth(),
middle: g.getWidth() / 2,
center: g.getHeight() / 2,
}
}
// hardcoded "settings"
const settings = {
// hardcoded "settings"
const settings = {
time: {
color: -1,
font: '6x8',
@ -52,11 +51,11 @@
top: 155, // just below time
thickness: 6, // matches 24h time "pixel" size
},
}
}
const SECONDS_PER_MINUTE = 60
const SECONDS_PER_MINUTE = 60
const timeText = function (date) {
const timeText = function (date) {
if (!is12Hour) {
return locale.time(date, true)
}
@ -68,20 +67,20 @@
date12.setHours(hours - 12)
}
return locale.time(date12, true)
}
const ampmText = function (date) {
}
const ampmText = function (date) {
return is12Hour ? locale.meridian(date) : ''
}
}
const dateText = function (date) {
const dateText = function (date) {
const dayName = locale.dow(date, true),
month = locale.month(date, true),
day = date.getDate()
const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}`
return `${dayName} ${dayMonth}`
}
}
const drawDateTime = function (date) {
const drawDateTime = function (date) {
const t = settings.time
g.setColor(t.color)
g.setFont(t.font, t.size)
@ -103,9 +102,9 @@
g.setFont(d.font, d.size)
g.setFontAlign(0, 0) // centered
g.drawString(dateText(date), d.center, d.middle, true)
}
}
const drawBar = function (date) {
const drawBar = function (date) {
const b = settings.bar
const seconds = date.getSeconds()
if (seconds === 0) {
@ -116,16 +115,16 @@
width = fraction * screen.width
g.setColor(b.color)
g.fillRect(0, b.top, width, b.top + b.thickness)
}
}
const clearScreen = function () {
const clearScreen = function () {
g.setColor(0)
const timeTop = settings.time.middle - (settings.time.size * 4)
g.fillRect(0, timeTop, screen.width, screen.height)
}
}
let lastSeconds
const tick = function () {
let lastSeconds, tTick
const tick = function () {
g.reset()
const date = new Date()
const seconds = date.getSeconds()
@ -136,36 +135,35 @@
}
// the bar only gets larger, so drawing on top of the previous one is fine
drawBar(date)
lastSeconds = seconds
}
// schedule next update
const millis = date.getMilliseconds()
tTick = setTimeout(tick, 1000-millis)
}
let iTick
const start = function () {
const start = function () {
lastSeconds = 99 // force redraw
tick()
iTick = setInterval(tick, 1000)
}
const stop = function () {
if (iTick) {
clearInterval(iTick)
iTick = undefined
}
}
const stop = function () {
if (tTick) {
clearTimeout(tTick)
tTick = undefined
}
}
// clean app screen
g.clear()
Bangle.loadWidgets()
Bangle.drawWidgets()
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat: false, edge: 'falling'})
// clean app screen
g.clear()
Bangle.loadWidgets()
Bangle.drawWidgets()
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.on('lcdPower', function (on) {
Bangle.on('lcdPower', function (on) {
if (on) {
start()
} else {
stop()
}
})
start()
}
})
start()

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1 +1,2 @@
0.01: App Created!
0.02: Update to use Bangle.setUI instead of setWatch

View File

@ -256,8 +256,5 @@ Bangle.drawWidgets();
timeInterval = setInterval(showTime, 1000);
showTime();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {
repeat: false,
edge: "falling"
});
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1 +1,2 @@
0.02: Modified for use with new bootloader and firmware
0.03: Update to use Bangle.setUI instead of setWatch

View File

@ -105,5 +105,5 @@ Bangle.loadWidgets();
Bangle.drawWidgets();
setInterval(() => { drawClock(); }, 1000);
drawClock();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,3 +1,4 @@
0.01: Initial commit. Not very efficient, and widgets not working for some reason.
0.02: Fixes; widget support
0.03: Remove hardcoded hour buzz (you can install widchime if you miss it)
0.04: Update to use Bangle.setUI instead of setWatch

View File

@ -51,64 +51,62 @@ Graphics.prototype.drawRotLine = function (sina, cosa, cx, cy, r1, r2) {
);
};
// Display modes
//
// 0: full-screen
// 1: with widgets
// 2: centred on Bangle (v.1), no widgets or time/date
// 3: centred with time above
// 4: centred with date above
// 5: centred with time and date above
let mode;
(function(g) {
// Display modes
//
// 0: full-screen
// 1: with widgets
// 2: centred on Bangle (v.1), no widgets or time/date
// 3: centred with time above
// 4: centred with date above
// 5: centred with time and date above
let mode;
// R1, R2: Outer and inner radii of hour marks
// RC1, RC2: Outer and inner radii of hub
// CX, CY: Centre location, relative to buffer (not screen, necessarily)
// HW2, MW2: Half-width of hour and minute hand
// HR, MR: Length of hour and minute hand, relative to CX,CY
// M: Half-width of gap in hour marks
// HSCALE: Half-width of hour mark as function(0<h<13)
let R1, R2, RC1, RC2, CX, CY, HW2, MW2, HR, MR, M, HSCALE;
// R1, R2: Outer and inner radii of hour marks
// RC1, RC2: Outer and inner radii of hub
// CX, CY: Centre location, relative to buffer (not screen, necessarily)
// HW2, MW2: Half-width of hour and minute hand
// HR, MR: Length of hour and minute hand, relative to CX,CY
// M: Half-width of gap in hour marks
// HSCALE: Half-width of hour mark as function(0<h<13)
let R1, R2, RC1, RC2, CX, CY, HW2, MW2, HR, MR, M, HSCALE;
// Screen size
const GW = g.getWidth();
const GH = g.getHeight();
// Screen size
const GW = g.getWidth();
const GH = g.getHeight();
// Top margin: the gap taken from the top of the buffer, except when
// in mode 0 (full screen)
let TM;
// Top margin: the gap taken from the top of the buffer, except when
// in mode 0 (full screen)
let TM;
// Buffer image. undefined means it needs regenerating
let faceImg;
// Buffer image. undefined means it needs regenerating
let faceImg;
// with_seconds flag determines whether the face is updated every
// second or every minute, and to draw the hand or not.
let with_seconds = true;
// with_seconds flag determines whether the face is updated every
// second or every minute, and to draw the hand or not.
let with_seconds = true;
// Display flags, determined from `mode` by setMode()
let with_widgets = false;
let with_digital_time = true;
let with_digital_date = true;
// Display flags, determined from `mode` by setMode()
let with_widgets = false;
let with_digital_time = true;
let with_digital_date = true;
// Create offscreen buffer for the once-per-minute face draw
const G1 = Graphics.createArrayBuffer(g.getWidth(), g.getHeight(), 1, {msb:true});
// Create offscreen buffer for the once-per-minute face draw
const G1 = Graphics.createArrayBuffer(g.getWidth(), g.getHeight(), 1, {msb:true});
// Precalculate sin/cos for the hour marks. Might be premature
// optimisation, but might as well.
let ss = [], cs = [];
for (let h=1; h<=12; h++) {
// Precalculate sin/cos for the hour marks. Might be premature
// optimisation, but might as well.
let ss = [], cs = [];
for (let h=1; h<=12; h++) {
const a = Math.PI * h / 6;
ss[h] = Math.sin(a);
cs[h] = Math.cos(a);
}
}
// Draw the face with hour and minute hand. Ideally, we'd separate
// the face from the hands and double-buffer, but memory is limited,
// so we buffer once and minute, and draw the second hand dynamically
// (with a bit of flicker)
const drawFace = (G) => {
// Draw the face with hour and minute hand. Ideally, we'd separate
// the face from the hands and double-buffer, but memory is limited,
// so we buffer once and minute, and draw the second hand dynamically
// (with a bit of flicker)
const drawFace = (G) => {
const fw = R1 * 2;
const fh = R1 * 2;
const fw2 = R1;
@ -148,30 +146,30 @@ Graphics.prototype.drawRotLine = function (sina, cosa, cx, cy, r1, r2) {
// widgets.
const img = {width:GW,height:GH-TM,buffer:G.buffer};
return img;
};
};
let hours, minutes, seconds, date;
let hours, minutes, seconds, date;
// Schedule event for calling at the start of the next second
const inOneSecond = (cb) => {
// Schedule event for calling at the start of the next second
const inOneSecond = (cb) => {
let now = new Date();
clearTimeout();
setTimeout(cb, 1000 - now.getMilliseconds());
};
};
// Schedule event for calling at the start of the next minute
const inOneMinute = (cb) => {
// Schedule event for calling at the start of the next minute
const inOneMinute = (cb) => {
let now = new Date();
clearTimeout();
setTimeout(cb, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds()));
};
};
// Draw a fat hour/minute hand
const drawHand = (G, a, w2, r1, r2) =>
// Draw a fat hour/minute hand
const drawHand = (G, a, w2, r1, r2) =>
G.fillRotRect(Math.sin(a), Math.cos(a), CX, CY, -w2, w2, r1, r2);
// Redraw function
const drawAll = (force) => {
// Redraw function
const drawAll = (force) => {
let now = new Date();
if (!faceImg) force = true;
@ -274,23 +272,18 @@ Graphics.prototype.drawRotLine = function (sina, cosa, cx, cy, r1, r2) {
inOneSecond(drawAll);
else
inOneMinute(drawAll);
};
};
const setButtons = () => {
const opts = { repeat: true, edge:'rising', debounce:30};
const setButtons = () => {
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=> {
if (btn==0) changeSeconds();
if (btn==1) { ++mode; setMode(); drawAll(true); }
});
};
// BTN1: enable/disable second hand
setWatch(changeSeconds, BTN1, opts);
// BTN2: return to launcher
setWatch(Bangle.showLauncher, BTN2, { repeat:false, edge:'falling' });
// BTN3: change display mode
setWatch(function () { ++mode; setMode(); drawAll(true); }, BTN3, opts);
};
// Load display parameters based on `mode`
const setMode = () => {
// Load display parameters based on `mode`
const setMode = () => {
// Normalize mode to 0 <= mode <= 5
mode = (6+mode) % 6;
@ -355,30 +348,30 @@ Graphics.prototype.drawRotLine = function (sina, cosa, cx, cy, r1, r2) {
// Clear the screen: we need to make sure all parts are cleaned off.
g.clear();
};
};
const changeSeconds = () => {
const changeSeconds = () => {
with_seconds = !with_seconds;
drawAll(true);
};
};
Bangle.loadWidgets();
Bangle.loadWidgets();
// Restore mode
try {
// Restore mode
try {
conf = storage.readJSON(filename);
mode = conf[0];
with_seconds = conf[1];
} catch (e) {
} catch (e) {
console.log(e);
mode = 1;
}
}
setButtons();
setMode();
drawAll();
setButtons();
setMode();
drawAll();
Bangle.on('lcdPower', (on) => {
Bangle.on('lcdPower', (on) => {
if (on) {
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -386,6 +379,4 @@ Graphics.prototype.drawRotLine = function (sina, cosa, cx, cy, r1, r2) {
} else {
clearTimeout();
}
});
})(g);
});

View File

@ -1,2 +1,3 @@
0.02: Modified for use with new bootloader and firmware
0.03: Shrinked size to avoid cut-off edges on the physical device. BTN3: show date. BTN1: show time in decimal.
0.04: Update to use Bangle.setUI instead of setWatch

View File

@ -100,9 +100,11 @@ g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
drawBerlinClock();
// Toggle date display, when BTN3 is pressed
setWatch(toggleTime,BTN1, { repeat : true, edge: "falling"});
// Toggle date display, when BTN3 is pressed
setWatch(toggleDate,BTN3, { repeat : true, edge: "falling"});
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
if (BTN3) {
// Toggle date display, when BTN3 is pressed
setWatch(toggleTime,BTN1, { repeat : true, edge: "falling"});
// Toggle date display, when BTN3 is pressed
setWatch(toggleDate,BTN3, { repeat : true, edge: "falling"});
}
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Fixed bug where screen didn't clear so incorrect time displayed.
0.03: Update to use Bangle.setUI instead of setWatch

View File

@ -167,12 +167,12 @@ Bangle.on('lcdPower',on=>{
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
setWatch(function() {
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=>{
if (btn!=1) return;
if(displayTime == 0){
displayTime = 1;
} else{
displayTime = 0;
}
}, BTN, {edge:"rising", debounce:50, repeat:true});
});

View File

@ -4,3 +4,4 @@
0.03: Modified for use with new bootloader and firmware
0.04: Modified to account for changes in the behavior of Graphics.fillPoly
0.05: Slight increase to draw speed after LCD on
0.06: Update to use Bangle.setUI instead of setWatch, allow themes and different size screens

View File

@ -1,4 +1,6 @@
const buf = Graphics.createArrayBuffer(144,200,1,{msb:true});
let big = g.getHeight() > 200;
const buf = Graphics.createArrayBuffer(big ? 144 : 120, big ? 180 : 150,1,{msb:true});
// TODO: convert these to Polys -> much faster and cleaner!
const NUMBERS = [
[1,1,1,1,3,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1],//0
[0,1,1,1,3,0,0,1,1,1,0,0,1,1,1,0,0,1,1,1,0,0,1,1,1],//1
@ -14,8 +16,10 @@ const NUMBERS = [
let intervalRef = null;
let digits = [-1,-1,-1,-1,-1,-1];
function flip() {
g.setColor(1,1,1);
g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer},55,26);
g.reset();
g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer},
(g.getWidth() - buf.getWidth())/2,
26 + (g.getHeight() - (buf.getHeight()+24))/2);
}
function drawPixel(ox,oy,x,y,r,p) {
let x1 = ox+x*(r*2);
@ -53,26 +57,31 @@ function redraw() {
let newDigits = [Math.floor(hours/10),hours%10,Math.floor(mins/10),mins%10,Math.floor(secs/10),secs%10];
let s = big?6:5; // size of main digits
let y2 = big?72:55;
let y3 = big?144:110;
for (var p = 0;p<25;p++) {
var px = p%5;
var py = Math.floor(p/5);
if (digits[0] === -1 || NUMBERS[newDigits[0]][p] !== NUMBERS[digits[0]][p] ) {
drawPixel(0,20,px,py,6,NUMBERS[newDigits[0]][p]);
drawPixel(0,0,px,py,s,NUMBERS[newDigits[0]][p]);
}
if (digits[1] === -1 || NUMBERS[newDigits[1]][p] !== NUMBERS[digits[1]][p] ) {
drawPixel(78,20,px,py,6,NUMBERS[newDigits[1]][p]);
drawPixel(13*s,0,px,py,s,NUMBERS[newDigits[1]][p]);
}
if (digits[2] === -1 || NUMBERS[newDigits[2]][p] !== NUMBERS[digits[2]][p] ) {
drawPixel(0,92,px,py,6,NUMBERS[newDigits[2]][p]);
drawPixel(0,y2,px,py,s,NUMBERS[newDigits[2]][p]);
}
if (digits[3] === -1 || NUMBERS[newDigits[3]][p] !== NUMBERS[digits[3]][p] ) {
drawPixel(78,92,px,py,6,NUMBERS[newDigits[3]][p]);
drawPixel(13*s,y2,px,py,s,NUMBERS[newDigits[3]][p]);
}
if (digits[4] === -1 || NUMBERS[newDigits[4]][p] !== NUMBERS[digits[4]][p] ) {
drawPixel(69,164,px,py,3,NUMBERS[newDigits[4]][p]);
drawPixel(17*s - 3*12,y3,px,py,3,NUMBERS[newDigits[4]][p]);
}
if (digits[5] === -1 || NUMBERS[newDigits[5]][p] !== NUMBERS[digits[5]][p] ) {
drawPixel(108,164,px,py,3,NUMBERS[newDigits[5]][p]);
drawPixel(17*s,y3,px,py,3,NUMBERS[newDigits[5]][p]);
}
}
digits = newDigits;
@ -99,5 +108,5 @@ Bangle.on('lcdPower',function(on) {
clearTimers();
}
});
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,2 +1,3 @@
0.02: Modified for use with new bootloader and firmware
0.03: Tweak for more efficient rendering, and firmware 2v06
0.04: Work with themes, smaller screens

View File

@ -12,9 +12,9 @@ var minute_hand = {
//g.fillRect(0,24,239,239); // Apps area
let intervalRef = null;
const p180 = Math.PI/180;
const clock_center = {x:Math.floor((240-1)/2), y:24+Math.floor((239-24)/2)};
const clock_center = {x:Math.floor((g.getWidth()-1)/2), y:24+Math.floor((g.getHeight()-25)/2)};
// ={ x: 119, y: 131 }
const radius = Math.floor((239-24+1)/2); // =108
const radius = Math.floor((g.getWidth()-24+1)/2); // =108
let tick0 = Graphics.createArrayBuffer(30,8,1,{msb:true});
tick0.fillRect(0,0,tick0.getWidth()-1, tick0.getHeight()-1);
@ -60,18 +60,15 @@ function hour_angle(date){
function draw_clock(){
//console.log("draw_clock");
let date = new Date();
//g.clear();
g.setBgColor(0,0,0);
g.setColor(0,0,0);
g.fillRect(0,24,239,239); // clear app area
g.setColor(1,1,1);
g.reset();
g.clearRect(0,24,239,239); // clear app area
// draw cross lines for testing
// g.setColor(1,0,0);
// g.drawLine(clock_center.x - radius, clock_center.y, clock_center.x + radius, clock_center.y);
// g.drawLine(clock_center.x, clock_center.y - radius, clock_center.x, clock_center.y + radius);
g.setColor(1,1,1);
g.setColor(g.theme.fg);
let ticks = [0, 90, 180, 270];
ticks.forEach((item)=>{
let agl = item+180;
@ -87,13 +84,13 @@ function draw_clock(){
let minute_agl = minute_angle(date);
g.drawImage(hour_hand, hour_pos_x(hour_agl), hour_pos_y(hour_agl), {rotate:hour_agl*p180}); //
g.drawImage(minute_hand, minute_pos_x(minute_agl), minute_pos_y(minute_agl), {rotate:minute_agl*p180}); //
g.setColor(1,1,1);
g.setColor(g.theme.fg);
g.fillCircle(clock_center.x, clock_center.y, 6);
g.setColor(0,0,0);
g.setColor(g.theme.bg);
g.fillCircle(clock_center.x, clock_center.y, 3);
// draw minute ticks. Takes long time to draw!
g.setColor(1,1,1);
g.setColor(g.theme.fg);
for (var i=0; i<60; i++){
let agl = i*6+180;
g.drawImage(tick1.asImage(), rotate_around_x(big_wheel_x(i*6), agl, tick1), rotate_around_y(big_wheel_y(i*6), agl, tick1), {rotate:agl*p180});
@ -141,5 +138,5 @@ g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
startTimers();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -21,3 +21,8 @@
0.20: Allow Gadgetbridge to work even with programmable:off
0.21: Handle echo off char from Gadgetbridge app when programmable:off (fix #558)
0.22: Stop LCD timeout being disabled on first run (when there is no settings.json)
0.23: Move to a precalculated .boot0 file which should speed up load time
0.24: Add Bangle.setUI polyfill
0.25: Fix error in 'no clock app' message
0.26: Remove buzz in setUI polyfill (#750)
0.27: Update polyfill for most recent changes

View File

@ -1,68 +1,2 @@
// This ALWAYS runs at boot
E.setFlags({pretokenise:1});
// Load settings...
var s = require('Storage').readJSON('setting.json',1)||{};
if (s.ble!==false) {
if (s.HID) { // Human interface device
if (s.HID=="joy") Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));
else if (s.HID=="kb") Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));
else /*kbmedia*/Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));
NRF.setServices({}, {uart:true, hid:Bangle.HID});
}
}
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
if (s.log) Terminal.setConsole(true); // if showing debug, force REPL onto terminal
else E.setConsole(null,{force:true}); // 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*/
Bluetooth.line="";
Bluetooth.on('data',function(d) {
var l = (Bluetooth.line + d).split("\n");
Bluetooth.line = l.pop();
l.forEach(n=>Bluetooth.emit("line",n));
});
Bluetooth.on('line',function(l) {
if (l.startsWith('\x10')) l=l.slice(1);
if (l.startsWith('GB({') && l.endsWith('})') && global.GB)
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
});
} else {
if (s.log && !NRF.getSecurityStatus().connected) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection)
else Bluetooth.setConsole(true); // else if no debug, force REPL to Bluetooth
}
// we just reset, so BLE should be on.
// Don't disconnect if something is already connected to us
if (s.ble===false && !NRF.getSecurityStatus().connected) NRF.sleep();
// Set time, vibrate, beep, etc
if (!Bangle.F_BEEPSET) {
if (!s.vibrate) Bangle.buzz=Promise.resolve;
if (s.beep===false) Bangle.beep=Promise.resolve;
else if (s.beep=="vib") Bangle.beep = function (time, freq) {
return new Promise(function(resolve) {
if ((0|freq)<=0) freq=4000;
if ((0|time)<=0) time=200;
if (time>5000) time=5000;
analogWrite(D13,0.1,{freq:freq});
setTimeout(function() {
digitalWrite(D13,0);
resolve();
}, time);
});
};
}
if (s.timeout!==undefined) Bangle.setLCDTimeout(s.timeout);
if (!s.timeout) Bangle.setLCDPower(1);
E.setTimeZone(s.timezone);
delete s;
// Draw out of memory errors onto the screen
E.on('errorFlag', function(errorFlags) {
g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip();
print("Interpreter error:", errorFlags);
E.getErrorFlags(); // clear flags so we get called next time
});
// stop users doing bad things!
global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }
// Load *.boot.js files
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
eval(require('Storage').read(bootFile));
});
// Initially this runs and rewrites itself
eval(require('Storage').read('bootupdate.js'));

View File

@ -14,11 +14,7 @@ if (!clockApp) {
if (clockApp)
clockApp = require("Storage").read(clockApp.src);
}
if (!clockApp) clockApp=`E.showMessage("No Clock Found");
setWatch(() => {
Bangle.showLauncher();
}, BTN2, {repeat:false,edge:"falling"});)
`;
if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, BTN2, {repeat:false,edge:"falling"});`;
// check to see if our clock is wrong - if it is use GPS time
if ((new Date()).getFullYear()<2000) {
E.showMessage("Searching for\nGPS time");

143
apps/boot/bootupdate.js Normal file
View File

@ -0,0 +1,143 @@
/* This rewrites boot0.js based on current settings. If settings changed then it
recalculates, but this avoids us doing a whole bunch of reconfiguration most
of the time. */
E.showMessage("Updating boot0...");
var s = require('Storage').readJSON('setting.json',1)||{};
var isB2 = process.env.HWVERSION; // Is Bangle.js 2
var boot = "";
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/));
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js'));} else {\n`;
boot += `E.setFlags({pretokenise:1});\n`;
if (s.ble!==false) {
if (s.HID) { // Human interface device
if (s.HID=="joy") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));`;
else if (s.HID=="kb") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));`
else /*kbmedia*/boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));`;
boot += `NRF.setServices({}, {uart:true, 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
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*/
boot += `
Bluetooth.line="";
Bluetooth.on('data',function(d) {
var l = (Bluetooth.line + d).split("\n");
Bluetooth.line = l.pop();
l.forEach(n=>Bluetooth.emit("line",n));
});
Bluetooth.on('line',function(l) {
if (l.startsWith('\x10')) l=l.slice(1);
if (l.startsWith('GB({') && l.endsWith('})') && global.GB)
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)
else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth
}
// we just reset, so BLE should be on.
// Don't disconnect if something is already connected to us
if (s.ble===false) boot += `if (!NRF.getSecurityStatus().connected) NRF.sleep();\n`;
// Set time
if (s.timeout!==undefined) boot += `Bangle.setLCDTimeout(${s.timeout});\n`;
if (!s.timeout) boot += `Bangle.setLCDPower(1);\n`;
boot += `E.setTimeZone(${s.timezone});`;
// Set vibrate, beep, etc IF on older firmwares
if (!Bangle.F_BEEPSET) {
if (!s.vibrate) boot += `Bangle.buzz=Promise.resolve;\n`
if (s.beep===false) boot += `Bangle.beep=Promise.resolve;\n`
else if (s.beep=="vib") boot += `Bangle.beep = function (time, freq) {
return new Promise(function(resolve) {
if ((0|freq)<=0) freq=4000;
if ((0|time)<=0) time=200;
if (time>5000) time=5000;
analogWrite(D13,0.1,{freq:freq});
setTimeout(function() {
digitalWrite(D13,0);
resolve();
}, time);
});
};\n`;
}
// Draw out of memory errors onto the screen
boot += `E.on('errorFlag', function(errorFlags) {
g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip();
print("Interpreter error:", errorFlags);
E.getErrorFlags(); // clear flags so we get called next time
});\n`;
// stop users doing bad things!
if (global.save) boot += `global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }\n`;
// Apply any settings-specific stuff
if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`;
if (s.quiet && s.qmOptions) boot+=`Bangle.setOptions(${E.toJS(s.qmOptions)});\n`;
if (s.quiet && s.qmBrightness) {
if (s.qmBrightness!=1) boot+=`Bangle.setLCDBrightness(${s.qmBrightness});\n`;
} else {
if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`;
}
if (s.quiet && s.qmTimeout) boot+=`Bangle.setLCDTimeout(${s.qmTimeout});\n`;
if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${s.passkey}, mitm:1, display:1});\n`;
if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`;
// Pre-2v10 firmwares without a theme/setUI
if (!g.theme) {
boot += `g.theme={fg:-1,bg:0,fg2:-1,bg2:7,fgH:-1,bgH:0x02F7,dark:true};\n`;
}
if (!Bangle.setUI) { // assume this is just for F18 - Q3 should already have it
boot += `Bangle.setUI=function(mode, cb) {
if (Bangle.btnWatches) {
Bangle.btnWatches.forEach(clearWatch);
delete Bangle.btnWatches;
}
if (Bangle.swipeHandler) {
Bangle.removeListener("swipe", Bangle.swipeHandler);
delete Bangle.swipeHandler;
}
if (Bangle.touchandler) {
Bangle.removeListener("touch", Bangle.touchHandler);
delete Bangle.touchHandler;
}
if (!mode) return;
else if (mode=="updown") {
Bangle.btnWatches = [
setWatch(function() { cb(-1); }, BTN1, {repeat:1}),
setWatch(function() { cb(1); }, BTN3, {repeat:1}),
setWatch(function() { cb(); }, BTN2, {repeat:1})
];
} else if (mode=="leftright") {
Bangle.btnWatches = [
setWatch(function() { cb(-1); }, BTN1, {repeat:1}),
setWatch(function() { cb(1); }, BTN3, {repeat:1}),
setWatch(function() { cb(); }, BTN2, {repeat:1})
];
Bangle.swipeHandler = d => {cb(d);};
Bangle.on("swipe", Bangle.swipeHandler);
Bangle.touchHandler = d => {cb();};
Bangle.on("touch", Bangle.touchHandler);
} else if (mode=="clock") {
Bangle.CLOCK=1;
Bangle.btnWatches = [
setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"})
];
} else if (mode=="clockupdown") {
Bangle.CLOCK=1;
Bangle.btnWatches = [
setWatch(function() { cb(-1); }, BTN1, {repeat:1}),
setWatch(function() { cb(1); }, BTN3, {repeat:1}),
setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"})
];
} else
throw new Error("Unknown UI mode");
};\n`;
}
// Append *.boot.js files
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+"\n";
});
boot += "}\n";// initial 'if'
var s = require('Storage').write('.boot0',boot);
delete boot;
E.showMessage("Reloading...");
eval(require('Storage').read('.boot0'));
eval(require('Storage').read('.bootcde'));

0
apps/buffgym/buffgym-scrn1.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

0
apps/buffgym/buffgym-scrn2.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

0
apps/buffgym/buffgym-scrn3.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

0
apps/buffgym/buffgym-scrn4.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

0
apps/buffgym/buffgym-scrn5.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

0
apps/buffgym/buffgym-scrn6.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

0
apps/buffgym/buffgym.app.js Executable file → Normal file
View File

0
apps/buffgym/buffgym.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -1 +1,2 @@
0.01: Basic calendar
0.02: Make Bangle 2 compatible

View File

@ -1,5 +1,6 @@
const maxX = 240;
const maxY = 240;
const maxX = g.getWidth();
const maxY = g.getHeight();
const fontSize = g.getWidth()>200?2:1;
const rowN = 7;
const colN = 7;
const headerH = maxY / 7;
@ -50,7 +51,7 @@ function drawCalendar(date) {
11: "December"
};
g.setFontAlign(0, 0);
g.setFont("6x8", 2);
g.setFont("6x8", fontSize);
g.setColor(white);
g.drawString(`${monthMap[month]} ${year}`, maxX / 2, headerH / 2);
g.drawPoly([10, headerH / 2, 20, 10, 20, headerH - 10], true);
@ -59,7 +60,7 @@ function drawCalendar(date) {
true
);
g.setFont("6x8", 2);
g.setFont("6x8", fontSize);
const dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
dowLbls.forEach((lbl, i) => {
g.drawString(lbl, i * colW + colW / 2, headerH + rowH / 2);
@ -135,26 +136,21 @@ const today = {
};
drawCalendar(date);
clearWatch();
setWatch(
() => {
Bangle.on("touch",area=>{
const month = date.getMonth();
const prevMonth = month > 0 ? month - 1 : 11;
let prevMonth;
if (area==1) {
let prevMonth = month > 0 ? month - 1 : 11;
if (prevMonth === 11) date.setFullYear(date.getFullYear() - 1);
date.setMonth(prevMonth);
drawCalendar(date);
},
BTN4,
{ repeat: true }
);
setWatch(
() => {
const month = date.getMonth();
const prevMonth = month < 11 ? month + 1 : 0;
} else {
let prevMonth = month < 11 ? month + 1 : 0;
if (prevMonth === 0) date.setFullYear(date.getFullYear() + 1);
date.setMonth(month + 1);
}
drawCalendar(date);
},
BTN5,
{ repeat: true }
);
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
});
// Show launcher when button pressed
Bangle.setUI("clock"); // TODO: ideally don't set 'clock' mode
// No space for widgets!

View File

@ -3,3 +3,5 @@
0.09: Add BTN1 status line with ID,Fw ver, mem %, battery %
0.10: Icon fixed for transparency
0.11: added Heart Rate Monitor status and ability to turn on/off
0.12: added support for different locales
0.13: Use setUI, work with smaller screens and themes

View File

@ -1,8 +1,8 @@
var fontsize = 3;
var fontsize = g.getWidth()>200 ? 3 : 2;
var fontheight = 10*fontsize;
var locale = require("locale");
var marginTop = 40;
var flag = false;
var WeekDays = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
var hrtOn = false;
var hrtStr = "Hrt: ??? bpm";
@ -26,41 +26,37 @@ function drawAll(){
}
function updateRest(now){
let date = locale.date(now,false);
writeLine(WeekDays[now.getDay()],1);
writeLine(date,2);
writeLine(locale.dow(now),1);
writeLine(locale.date(now,1),2);
drawInfo(5);
}
function updateTime(){
if (!Bangle.isLCDOn()) return;
let now = new Date();
let h = now.getHours();
let m = now.getMinutes();
h = h>=10?h:"0"+h;
m = m>=10?m:"0"+m;
writeLine(h+":"+m,0);
writeLine(locale.time(now,1),0);
writeLine(flag?" ":"_",3);
flag = !flag;
if(now.getMinutes() == 0)
updateRest(now);
}
function writeLineStart(line){
g.drawString(">",4,marginTop+line*30);
g.drawString(">",4,marginTop+line*fontheight);
}
function writeLine(str,line){
var y = marginTop+line*fontheight;
g.setFont("6x8",fontsize);
//g.setColor(0,1,0);
g.setColor(0,0x07E0,0);
g.setColor("#0f0");
g.setFontAlign(-1,-1);
g.clearRect(0,marginTop+line*30,((str.length+1)*20),marginTop+25+line*30);
g.clearRect(0,y,((str.length+1)*20),y+fontheight-1);
writeLineStart(line);
g.drawString(str,25,marginTop+line*30);
g.drawString(str,25,y);
}
function drawInfo(line) {
let val;
let str = "";
let col = 0x07E0; // green
let col = "#0f0"; // green
//console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode);
@ -68,7 +64,7 @@ function drawInfo(line) {
case NONE_FN_MODE:
break;
case HRT_FN_MODE:
col = 0x07FF; // cyan
col = "#0ff"; // cyan
str = "HRM: " + (hrtOn ? "ON" : "OFF");
drawModeLine(line,str,col);
return;
@ -76,7 +72,7 @@ function drawInfo(line) {
switch(infoMode) {
case NONE_MODE:
col = 0x0000;
col = "#fff";
str = "";
break;
case HRT_MODE:
@ -106,10 +102,11 @@ function drawInfo(line) {
function drawModeLine(line, str, col) {
g.setColor(col);
g.fillRect(0, marginTop-3+line*30, 239, marginTop+25+line*30);
g.setColor(0,0,0);
var y = marginTop+line*fontheight;
g.fillRect(0, y, 239, y+fontheight-1);
g.setColor(0);
g.setFontAlign(0, -1);
g.drawString(str, g.getWidth()/2, marginTop+line*30);
g.drawString(str, g.getWidth()/2, y);
}
function changeInfoMode() {
@ -191,10 +188,12 @@ Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();
Bangle.on('lcdPower',function(on) {
if (on)
drawAll();
if (on) drawAll();
});
var click = setInterval(updateTime, 1000);
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
setWatch(() => { changeInfoMode(); drawAll(); }, BTN1, {repeat: true});
setWatch(() => { changeFunctionMode(); drawAll(); }, BTN3, {repeat: true});
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=>{
if (btn==0) changeInfoMode();
if (btn==1) changeFunctionMode();
drawAll();
});

View File

@ -1,3 +1,4 @@
0.02: Modified for use with new bootloader and firmware
0.03: Added 'reset' so we don't get the font color from widgets
0.04: Changed name from clck3x2 to clock2x3
0.05: Use setUI, work with smaller screens and themes

View File

@ -1,8 +1,10 @@
const big = g.getWidth()>200;
const ox=10; // x offset
const oy=80;
const pw=20; // pixel width
const ps=5; // pixel spacing
const ds=10; // digit spacing
const oy=big ? 80 : 70;
const pw=big ? 20 : 14; // pixel width
const ps=big ? 5 : 3; // pixel spacing
const ds=big ? 10 : 8; // digit spacing
const ms=20; // middle space
const x00=ox; // digit 0, pixel 0, x position
@ -90,7 +92,7 @@ Bangle.on('lcdPower', function(on){
}
});
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
drawTime();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});

View File

@ -1 +1,2 @@
0.02: Modified for use with new bootloader and firmware
0.03: Changed setWatch to Bangle.setUI

View File

@ -46,26 +46,25 @@ function drawSegment (align, base, str) {
point = base + (maxSegmentWidth / 2) -
(g.stringWidth(str) / 2);
}
g.setColor(1, 1, 1);
g.setColor(g.theme.fg);
g.drawString(str, point, middleY - 4, false);
}
function drawDots (center) {
g.setColor(0xFD20);
g.setColor("#FA0");
g.fillCircle(center, middleY + 10, 2);
g.fillCircle(center, middleY + 40, 2);
}
function drawLines () {
g.setColor(0.5, 0.5, 0.5);
g.setColor("#777");
g.drawLine(middleX - lineLength, lineY1, middleX + lineLength, lineY1);
g.drawLine(middleX - lineLength, lineY2, middleX + lineLength, lineY2);
}
function drawDate (str) {
let maxSegmentWidth = 236;
g.setColor(0.5, 0.5, 0.5);
g.setColor(0.5, 0.5, 0.5);
g.setColor("#777");
g.drawString(str, (maxSegmentWidth) - (g.stringWidth(str)), middleY - 22,
false);
}
@ -149,6 +148,9 @@ function start () {
}
start();
// Show launcher when middle button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.on('lcdPower', function (on) {
@ -158,6 +160,3 @@ Bangle.on('lcdPower', function (on) {
stop();
}
});
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});

View File

@ -7,3 +7,4 @@
0.07: add days in current month (md) and days since new moon (l)
0.08: update icon
0.09: Use localised month and day of the week from locale
0.10: Changed setWatch to Bangle.setUI and allow small screen

View File

@ -1,17 +1,18 @@
var locale = require("locale");
/* jshint esversion: 6 */
const timeFontSize = 4;
const dateFontSize = 3;
const smallFontSize = 2;
const big = g.getWidth()>200;
const timeFontSize = big?4:3;
const dateFontSize = big?3:2;
const smallFontSize = big?2:1;
const font = "6x8";
const xyCenter = g.getWidth() / 2;
const yposTime = 50;
const yposDate = 85;
const yposTst = 115;
const yposDml = 170;
const yposDayMonth = 195;
const yposGMT = 220;
const yposDate = big?85:75;
const yposTst = big?115:95;
const yposDml = big?170:130;
const yposDayMonth = big?195:140;
const yposGMT = big?220:150;
// Check settings for what type our clock should be
var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
@ -99,6 +100,8 @@ Bangle.on('lcdPower', function(on) {
// clean app screen
g.clear();
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -107,6 +110,3 @@ setInterval(drawSimpleClock, 100);
// draw now
drawSimpleClock();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});

2
apps/de-stress/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: Adjust for different screen types and themes

View File

@ -1,29 +1,21 @@
g.setBgColor(0).clear();
//g.clear();
var img = {
width : 100, height : 100, bpp : 8,
transparent : 254,
buffer : require("heatshrink").decompress(atob("/wA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AglYAGE/44gHz4nnHKtPumez3C4WezFzp49ZLAgoCE4WeugnaHStPG4QAHzw9DE/Y6VHJQ9YMJwnEMk46CF4lzfglPug9WCAWYDQl0E4tzN4gLCMVC5Cp+YGoOezxBCubKHExxUEuiFCEoInECAhkjMQuYLArCFGwLLEHpjsGMIRpEfAsrMkwhBFAQ6BHJA9EKApDBHpAJBQYhPBE5iZBzAsDMb4gBEwg6MJhBkIYorgBPQiMMUAhkeHgkrug7ObI5qBHwhiGZYomOEojGeJQVzTx5kOMQ6JREAR3CDIJkcHobwEMiw+CAAYJEMSYWCAgRjcDgI4CYyiiDXopiFBooAWRwJkaHwjGVKwdzH4rADuhiXZAaICMbbtGHy2YBQ+YRDCiEMbidCULBkDLIwIIdqbmCp5jZPwIfDAYQAXXwNPzwACIQLQIACt0ZDIZBTwRFBHjWeHoSJCETlPc4ZjdAYYA7L4Jj/Mf5jCEYQDDAHhEEMbzH+McBfCMf4ADzxjep+YL/1PMb10MYQDCAHd0MYV0MbdzEYoA7UYdPMTBkCc4hj9leeAYRjbL4aIDAHN0UwRjeL4WYZHlPzBnCMbZkBQojtCAG6gEp5ibZARfCdwjG3ugDBzzGcMYTIEFAQA1ujGFMbjIFeAgA0HobGeZA9PAgYAyG4jGfZAyPBzBizf4LGjMggtCFAJpDAFw0FMURjCeAd0AgYAup90AgZjjMgWYFYZkwGImYMUhkDeYdPuZituZiDzximMYUrFwj6DAFF0TAg6CMcpkCSYpkqMQtPMVBkDuZktMQtzMVRkDLwZkoMQoFBMVZkJp5ijX4JizMhFPMkQjBMWpkHIAKjEADTrGMWZkDuY8DuZkdMQKKEMWpkDUIxFEACocGdoJi1MhCqBAwgATLYLkEMXJkDIY4GEAB+ep9PC4aDBMXRkJukruhiRCgxi+MglzJAtPMR7cGNIJi+MglPJYhSBzBhLzB0FzwWBMX5lGMghVGAAtzld0bwph/MhNzWYzKGNwR2Euhi/MhhTHA4ZrHA4Rh/Mp10YIlzA4JoCBQjE/ZTK8BA45i/MqtzX4jE/Mj0rYQjECBYZP/MrFPMoWep5h/ZT90ujE/MsZh/MsZB/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/ACg"))
};
var img = require("heatshrink").decompress(atob("plB4X/ln7A4OGmMs5dVAANa1WlAoQADBI9W1QAByoJNtWv4f61ISEtWrBI2q4EAgeqCQgJHq2sLoU6IYdaBIg5CrWAn2q2EDF4dq4EO1W8gQcCtUD1fP9cA9QSC1cA9f/9XADgWohxBBh2whQvBq2gAwMAh+wlQSB1k7IIXogYvBrXAlYJC9k6DYOw9gIChXA9JBC0AJCncOytWwAtBAAMDF4RBDAAMOgWVrXDwDjD9kKy2gIIcAgXD0taDYgvCRIJBDF4OA0trhwIDJgK2B4BKDAAXptcLA4kC4HrD4IJE8HptE4BAhfBLAJBEgEslISGL4JOBBAoSB1ksBIs6/70DBIgSHh+q+AIFnASIABASU0EgCR0IhWgEp4SBHCBxJLzusXowAIaBISLhYSO8EptcOCR2w9NagXACJkDwGlrXDwASMgXDCQOA2ASMh0C0tW2HgCRkLh2Vq2glASMlEKytV1iFN9k6qtVtEOORcD2EpCQNqOwJwL4GpCQNa4GgCRUKgelCQJyBlgSKnBwBCQWgnZdLOAQAB1BfKLoMqCIVVtYHBXZPA9ISDL4PsCRE7LoZfDnRKJLoYAC1kOTI8CDoIREJgMA1gSGFwJKEJgaGH9hKFTIaGGPQKVEAAeogbTFhXASogADtXAlYSE9cDeYRMG2A5EG4MOJQxMC1g5EG4M6JQ4AB1Q5E9ED1QRIHIatBG5Y5EVoM6G5Y5DnXDCwJvIHIsD32AG5Y5C1aUBgHqG5atDLwI3MHIReCG5hgD4aUKHI2+G5xgC1RcNAAdpBJA"))
function hr(){
Bangle.buzz(100,0.1).then(()=>{
Bangle.buzz(100,0.1).then(()=>{
g.clear();
return new Promise(resolve=>setTimeout(resolve,250)); // wait 250ms
}).then(()=>{
}).then(()=>{
return Bangle.buzz(150);
}).then(()=>{
g.drawImage(img, 25, 40, {scale:2});
});
}).then(()=>{
g.drawImage(img, g.getWidth()/2 - 76, g.getHeight()/2 - 65, {scale:2});
});
}
setInterval(hr, 2000);
g.flip();
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
// TODO - not clock but we still want a press to show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,2 +1,3 @@
0.01: App created
0.02: Persist state to storage to enable stopwatch to continue in the background
0.03: Modified to use setUI, theme and different screens

View File

@ -2,13 +2,16 @@ const EMPTY_LAP = '--:--:---';
const EMPTY_H = '00:00:000';
const MAX_LAPS = 6;
const XY_CENTER = g.getWidth() / 2;
const big = g.getWidth()>200;
const Y_CHRONO = 40;
const Y_HEADER = 80;
const Y_LAPS = 125;
const Y_BTN3 = 225;
const Y_HEADER = big?80:60;
const Y_LAPS = big?125:90;
const H_LAPS = big?15:8;
const Y_BTN3 = big?225:165;
const FONT = '6x8';
const CHRONO = '/* C H R O N O */';
var reset = false;
var currentLap = '';
var chronoInterval;
@ -22,9 +25,9 @@ var state = require("Storage").readJSON("devstopwatch.state.json",1) || {
laps: [EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP],
};
// Set laps.
setWatch(() => {
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=>{
if (btn==0) {
reset = false;
if (state.started) {
@ -34,13 +37,9 @@ setWatch(() => {
chronoInterval = setInterval(chronometer, 10);
}
}
}, BTN1, { repeat: true, edge: 'rising' });
// Reset chronometre.
setWatch(() => { resetChrono(); }, BTN3, { repeat: true, edge: 'rising' });
// Show launcher when middle button pressed.
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: 'falling' });
}
if (btn==1) resetChrono();
});
function resetChrono() {
state.laps = [EMPTY_H, EMPTY_H, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP];
@ -106,33 +105,33 @@ function printChrono() {
var print = '';
g.setFont(FONT, 2);
g.setFont(FONT, big?2:1);
print = CHRONO;
g.drawString(print, XY_CENTER, Y_CHRONO, true);
g.setColor(0, 220, 0);
g.setFont(FONT, 3);
g.setColor("#0e0");
g.setFont(FONT, big?3:2);
print = ` T ${state.laps[0]}\n`;
print += ` C ${state.laps[1]}\n`;
g.drawString(print, XY_CENTER, Y_HEADER, true);
g.setColor(255, 255, 255);
g.setFont(FONT, 2);
g.setColor(g.theme.fg);
g.setFont(FONT, big?2:1);
for (var i = 2; i < MAX_LAPS + 1; i++) {
g.setColor(255, 255, 255);
g.setColor(g.theme.fg);
let suffix = ' ';
if (state.currentLapIndex === i) {
let suffix = '*';
g.setColor(255, 200, 0);
g.setColor("#f70");
}
const lapLine = `L${i - 1} ${state.laps[i]} ${suffix}\n`;
g.drawString(lapLine, XY_CENTER, Y_LAPS + (15 * (i - 1)), true);
g.drawString(lapLine, XY_CENTER, Y_LAPS + (H_LAPS * (i - 1)), true);
}
g.setColor(255, 255, 255);
g.setColor(g.theme.fg);
g.setFont(FONT, 1);
print = 'Press 3 to reset';
g.drawString(print, XY_CENTER, Y_BTN3, true);

View File

@ -1 +1,2 @@
0.01: App Made!
0.02: Changed setWatch to Bangle.setUI, code tidy

View File

@ -10,104 +10,13 @@ function draw() {
var date = new Date();
var h = date.getHours();
var m = date.getMinutes();
var day = date.getDay();
var month = date.getMonth();
var day = require("locale").dow(date);
var month = require("locale").month(date,1);
var dateNum = date.getDate();
var year = date.getFullYear();
var half = "AM";
var time = (" " + h).substr(-2) + ":" + ("0" + m).substr(-2);
//convert day into string
switch (day) {
case 0:
day = "Sunday";
break;
case 1:
day = "Monday";
break;
case 2:
day = "Tuesday";
break;
case 3:
day = "Wednesday";
break;
case 4:
day = "Thursday";
break;
case 5:
day = "Friday";
break;
case 6:
day = "Saturday";
break;
default:
day = "ERROR";
break;
}
//convert month into String
switch(month) {
case 0:
month = "Jan";
break;
case 1:
month = "Feb";
break;
case 2:
month = "Mar";
break;
case 3:
month = "Apr";
break;
case 4:
month = "May";
break;
case 5:
month = "Jun";
break;
case 6:
month = "Jul";
break;
case 7:
month = "Aug";
break;
case 8:
month = "Sep";
break;
case 9:
month = "Oct";
break;
case 10:
month = "Nov";
break;
case 11:
month = "Dec";
break;
default:
month = "ERROR";
break;
}
if (h > 12) {
half = "PM";
h = h - 12;
@ -148,7 +57,7 @@ Bangle.on('lcdPower',on=>{
}
});
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
setWatch(Bangle.showLauncher, BTN2, {repeat : false, edge: "falling"});

View File

@ -1,2 +1,3 @@
0.01: Based on the Analog Clock app, minimal dot
0.02: Remove hardcoded hour buzz (you can install widchime if you miss it)
0.03: Use setUI, adjust for themes and different size screens

View File

@ -1,20 +1,22 @@
let g;
let Bangle;
const big = g.getWidth()>200;
const locale = require('locale');
const p = Math.PI / 2;
const pRad = Math.PI / 180;
const faceWidth = 100; // watch face radius
let timer = null;
let currentDate = new Date();
let hourRadius = 60;
let minRadius = 80;
const centerPx = g.getWidth() / 2;
const faceWidth = big?100:65; // watch face radius
let hourRadius = big?60:40;
let minRadius = big?80:55;
const centerX = g.getWidth() / 2;
const centerY = 24 + (g.getHeight()-24) / 2;
let colSecA = g.theme.dark ? "#00A" : "#58F"; // before the second
let colSecB = g.theme.dark ? "#58F" : "#00A"; // after the second
let colSec1 = g.theme.dark ? "#F83" : "#000"; // ON the second
const seconds = (angle) => {
const a = angle * pRad;
const x = centerPx + Math.sin(a) * faceWidth;
const y = centerPx - Math.cos(a) * faceWidth;
const x = centerX + Math.sin(a) * faceWidth;
const y = centerY - Math.cos(a) * faceWidth;
// if 15 degrees, make hour marker larger
const radius = (angle % 15) ? 2 : 4;
@ -23,15 +25,15 @@ const seconds = (angle) => {
const hourDot = (angle,radius) => {
const a = angle * pRad;
const x = centerPx + Math.sin(a) * hourRadius;
const y = centerPx - Math.cos(a) * hourRadius;
const x = centerX + Math.sin(a) * hourRadius;
const y = centerY - Math.cos(a) * hourRadius;
g.fillCircle(x, y, radius);
};
const minDot = (angle,radius) => {
const a = angle * pRad;
const x = centerPx + Math.sin(a) * minRadius;
const y = centerPx - Math.cos(a) * minRadius;
const x = centerX + Math.sin(a) * minRadius;
const y = centerY - Math.cos(a) * minRadius;
g.fillCircle(x, y, radius);
};
@ -45,54 +47,49 @@ const drawAll = () => {
// draw all secs
for (let i = 0; i < 60; i++) {
if (i > currentSec) {
g.setColor(0, 0, 0.6);
} else {
g.setColor(0.3, 0.3, 1);
}
g.setColor((i > currentSec) ? colSecA : colSecB);
seconds((360 * i) / 60);
}
onSecond();
};
const resetSeconds = () => {
g.setColor(0, 0, 0.6);
g.setColor(colSecA);
for (let i = 0; i < 60; i++) {
seconds((360 * i) / 60);
}
};
const drawMin = () => {
g.setColor(0.5, 0.5, 0.5);
g.setColor("#777");
for (let i = 0; i < 60; i++) {
minDot((360 * i) / 60,1);
}
};
const drawHour = () => {
g.setColor(0.5, 0.5, 0.5);
g.setColor("#777");
for (let i = 0; i < 12; i++) {
hourDot((360 * 5 * i) / 60,1);
}
};
const onSecond = () => {
g.setColor(0.3, 0.3, 1);
g.setColor(colSecB);
seconds((360 * currentDate.getSeconds()) / 60);
if (currentDate.getSeconds() === 59) {
resetSeconds();
onMinute();
}
g.setColor(1, 0.7, 0.2);
g.setColor(colSec1);
currentDate = new Date();
seconds((360 * currentDate.getSeconds()) / 60);
g.setColor(1, 1, 1);
g.setColor(g.theme.fg);
};
const drawDate = () => {
g.reset();
g.setColor(1, 1, 1);
g.setFont('6x8', 2);
g.setFont('6x8', big?2:1);
const dayString = locale.dow(currentDate, true);
// pad left date
@ -101,7 +98,7 @@ const drawDate = () => {
// console.log(`${dayString}|${dateString}`);
// center date
const l = (g.getWidth() - g.stringWidth(dateDisplay)) / 2;
const t = centerPx - 6 ;
const t = centerY - 6 ;
g.drawString(dateDisplay, l, t);
// console.log(l, t);
};
@ -111,7 +108,7 @@ const onMinute = () => {
resetSeconds();
}
// clear existing hands
g.setColor(0, 0, 0);
g.setColor(g.theme.bg);
hourDot((360 * currentDate.getHours()) / 12,4);
minDot((360 * currentDate.getMinutes()) / 60,3);
@ -125,7 +122,7 @@ const onMinute = () => {
g.setColor(1, 0, 0);
// Hour
hourDot((360 * currentDate.getHours()) / 12,4);
g.setColor(1, 0.9, 0.9);
g.setColor(g.theme.fg2);
// Minute
minDot((360 * currentDate.getMinutes()) / 60,3);
drawDate();
@ -152,8 +149,8 @@ g.clear();
resetSeconds();
startTimers();
drawAll();
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });

0
apps/dotmatrixclock/app.js Executable file → Normal file
View File

0
apps/dotmatrixclock/dotmatrix-clock-screen-shot.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

0
apps/dotmatrixclock/dotmatrixclock.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -1 +1,2 @@
0.01: First released version
0.02: Changed setWatch to Bangle.setUI

View File

@ -243,5 +243,5 @@ startTimers();
Bangle.loadWidgets();
drawAll();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1,4 +1,7 @@
(() => {
// Current shown notification, saved for dismissing.
var currentNot = null;
// Music handling
const state = {
music: "stop",
@ -151,15 +154,18 @@
global.GB = (event) => {
switch (event.t) {
case "notify":
case "notify-":
if (event.t === "notify") {
require("notify").show(prettifyNotificationEvent(event));
currentNot = prettifyNotificationEvent(event);
require("notify").show(currentNot);
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
Bangle.buzz();
}
} else { // notify-
break;
case "notify-":
currentNot.t = "notify";
currentNot.n = "DISMISS";
gbSend(currentNot);
currentNot = null;
require("notify").hide(event);
}
break;
case "musicinfo":
state.musicInfo = event;

1
apps/gbtwist/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Initial version

15
apps/gbtwist/README.md Normal file
View File

@ -0,0 +1,15 @@
# Gadgetbridge Twist Control
Control your music app (e.g. MortPlayer Music [a folder based, not tag based player] ) that handles multiple play-commands (same as using a single-button-headset's button to change songs) on your Gadgetbridge-connected phone.
- Activate counting for 4 seconds with a twist (beeps at start and end of counting)
- twist multiple times for:
play/pause (1),
next song (2),
prev. song (3),
next folder (4),
prev. folder (5),
reset counter (6)
- the command to be sent is shown in green
- Volume up/down is controlled by BTN1/BTN3 presses
![screenshot1](https://user-images.githubusercontent.com/84921310/119907374-65bb6180-bf50-11eb-9073-f29f7e333e00.jpg)

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

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwIYVhAFEjgFEh4FEg+AAocD4AME8ADCgPAvAFCj/8nkQAoN//8enAQB///44FBgYFB8f4FoIFB+IFBh/+n/4AocH/AXBj/+gP8FIIFDFwM//0x/wFDAIIFNv4FB/4FNEaIFFj/gn5HCj+AAoUEh4FBMgUP4AFDw/gv/wAoPDPoKhBjnxAoKtBjl4TYLICninBagUPWYLJPFoIADZIYABnj6KABIA="))

97
apps/gbtwist/app.js Normal file
View File

@ -0,0 +1,97 @@
// just a watch, to fill an empty screen
function drwClock() {
var d = new Date();
var h = d.getHours(), m = d.getMinutes();
var time = ("0"+h).substr(-2) + ":" + ("0"+m).substr(-2);
g.reset();
g.setFont('6x8',7);
g.setFontAlign(-1,-1);
g.drawString(time,20,80);
}
g.clear();
drwClock();
Bangle.loadWidgets();
Bangle.drawWidgets();
/////////////////////////////////////////////////////////////
// control music by twist/buttons
var counter = 0; //stores your counted your twists
var tstate = false; //are you ready to count the twists?
function playx() {
Bluetooth.println(JSON.stringify({t:"music", n:"play"}));
}
function volup() {
Bluetooth.println(JSON.stringify({t:"music", n:"volumeup"}));
}
function voldn() {
Bluetooth.println(JSON.stringify({t:"music", n:"volumedown"}));
}
function sendCmd() {
print (counter);
Bangle.beep(200,3000);
if (tstate==false && counter>0){
do {playx(); counter--;}
while (counter >= 1);
}
}
function twistctrl() {
if (tstate==false){
tstate=true;
setTimeout('tstate=false',4000);
setTimeout(sendCmd,4100);
Bangle.beep(200,3000);
}
else{
g.clearRect(10,140,230,200);
if (tstate==true){
if (counter < 5){
counter++;
drwCmd();
Bangle.buzz(100,2);
}
else {
counter = 0;
Bangle.buzz(400);
}
}
}
}
function drwCmd() {
g.setFont('6x8',6);
g.setColor(0.3,1,0.3);
g.clearRect(10,140,230,200);
switch (counter){
case 1:
g.drawString('play',50,150);
break;
case 2:
g.drawString('next',50,150);
break;
case 3:
g.drawString('prev',50,150);
break;
case 4:
g.drawString('nx f',50,150);
break;
case 5:
g.drawString('pr f',50,150);
break;
case 0:
g.clearRect(10,140,230,200);
break;
}
}
setWatch(volup,BTN1,{repeat:true});
setWatch(voldn,BTN3,{repeat:true});
Bangle.on('twist',twistctrl);
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});

BIN
apps/gbtwist/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen
0.03: Changed setWatch to Bangle.setUI

View File

@ -148,4 +148,5 @@ Bangle.drawWidgets();
iterate();
animInterval = setInterval(iterate, 50);
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -21,3 +21,6 @@
0.17: Disable recording if storage is full (fix #574)
0.18: Period counter now uses GPS time rather than counting packets (allows use with GPS Setup)
0.19: Fix memory usage issues inside track viewer app
0.20: Add documentation to explain time needed for getting a time fix
0.21: Fix issue where a period of 1s recorded every 2s, 5s every 6s, and so on
0.22: Ensure Bangle.setGPSPower uses 'gpsrec' as a tag

10
apps/gpsrec/README.md Normal file
View File

@ -0,0 +1,10 @@
# GPS Recorder
![icon](app.png)
This app allows you to record a GPS track. It can run in background. The data can later be exported as KML or GPX files via the BangleJS app store.
## Tips
When you turn on recording, a widget badge that looks like a satellite will appear immediately at the top of the screen. However, the recording does not begin immediately. It usually takes several minutes for the watch to get a [GPS fix](https://en.wikipedia.org/wiki/Time_to_first_fix). You will notice a blinking question mark at the lower left of the badge indicating currently getting a fix. The badge will change when a GPS fix is achieved and that is when the app actually starts writing data to the log file. You can [upload assistant files](https://banglejs.com/apps/#assisted%20gps%20update) to speed up the time spent on getting a GPS fix.

View File

@ -95,6 +95,7 @@ function getTrackList() {
Util.showModal("Loading Tracks...");
domTracks.innerHTML = "";
Puck.write(`\x10(function() {
Bluetooth.println("");
for (var n=0;n<36;n++) {
var f = require("Storage").open(".gpsrc"+n.toString(36),"r");
var l = f.readLine();

View File

@ -29,7 +29,7 @@
var period = 1000000;
if (lastFixTime!==undefined)
period = fix.time.getTime() - lastFixTime;
if (period > settings.period*1000) {
if (period+500 > settings.period*1000) { // round up
lastFixTime = fix.time.getTime();
try {
if (gpsTrack) gpsTrack.write([
@ -69,7 +69,7 @@
gpsTrack = undefined;
}
if (gOn != gpsOn) {
Bangle.setGPSPower(gOn);
Bangle.setGPSPower(gOn,"gpsrec");
gpsOn = gOn;
}
}

View File

@ -2,6 +2,9 @@
A configurable, low power GPS widget that runs in the background.
NOTE: This app has been superceded by [gpssetup](https://github.com/espruino/BangleApps/blob/master/apps/gpssetup/README.md)
## Goals
To develop a low power GPS widget that runs in the background and to

View File

@ -1,3 +1,4 @@
0.01: New App!
0.02: Use HRM data and calculations from Bangle.js (don't access hardware directly)
0.03: Fix timing issues, and use 1/2 scale to keep graph on screen
0.04: Update for new firmwares that have a 'HRM-raw' event

View File

@ -4,18 +4,25 @@ Bangle.setHRMPower(1);
var hrmInfo, hrmOffset = 0;
var hrmInterval;
function onHRM(h) {
// this is the first time we're called
if (counter!==undefined) {
// the first time we're called remove
// the countdown
counter = undefined;
g.clear();
}
hrmInfo = h;
hrmOffset = 0;
/* On 2v09 and earlier firmwares the only solution for realtime
HRM was to look at the 'raw' array that got reported. If you timed
it right you could grab the data pretty much as soon as it was written.
In new firmwares, '.raw' is not available. */
if (hrmInterval) clearInterval(hrmInterval);
hrmInterval = undefined;
if (hrmInfo.raw) {
hrmOffset = 0;
setTimeout(function() {
hrmInterval = setInterval(readHRM,41);
}, 40);
}
var px = g.getWidth()/2;
g.setFontAlign(0,0);
@ -28,13 +35,32 @@ function onHRM(h) {
g.drawString("BPM",px+15,45);
}
Bangle.on('HRM', onHRM);
/* On newer (2v10) firmwares we can subscribe to get
HRM events as they happen */
Bangle.on('HRM-raw', function(v) {
var a = v.raw;
hrmOffset++;
if (hrmOffset>g.getWidth()) {
hrmOffset=0;
g.clearRect(0,90,239,239);
g.moveTo(-100,0);
}
y = E.clip(170 - (v.raw*2),100,230);
g.setColor(1,1,1);
g.lineTo(hrmOffset, y);
});
// It takes 5 secs for us to get the first HRM event
var counter = 5;
function countDown() {
E.showMessage("Please wait...\n"+counter--);
if (counter) setTimeout(countDown, 1000);
if (counter) {
g.drawString(counter--,g.getWidth()/2,g.getHeight()/2, true);
setTimeout(countDown, 1000);
}
}
g.clear().setFont("6x8",2).setFontAlign(0,0);
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
countDown();

View File

@ -8,3 +8,5 @@
0.08: Improved error handling for missing firmware features, added template app.kit.js
0.09: Added heart rate monitor app
0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo
0.11: Detect when waypoints.json is not present, error E-WPT
0.12: Added stepo2 as a replacement for stepo and digi

View File

@ -41,9 +41,25 @@ The following buttons depend on which face is currently in use
- Waypointer : select next waypoint
## Stepo2
![](screenshot_stepo2.jpg)
- Requires one of the pedominter widgets to be installed
- Stepo2 is a combination of Stepo and Digi and now replaces them
- Displays the time in large font
- Display current step count in a doughnut gauge
- The gauge show percentage of steps out of a goal of 10000 steps
- When the battery is less than 25% the doughnut turns red
- Use BTN1 to switch to the Trip Counter, use long press to reset Trip Counter
- Use BTN1 to cycle through the displays of Day,Date, Trip Counter, Battery %, Mem % and Firmware
- Use BTN3 to switch to the next app
## Stepo
![](screenshot_stepo.jpg)
- now replaced by Stepo2 but still available if you install manually
- Requires one of the pedominter widgets to be installed
- Displays the time in large font
- Display current step count in a doughnut gauge
@ -62,11 +78,13 @@ The following buttons depend on which face is currently in use
## Digi
![](screenshot_digi.jpg)
- now replaced by Stepo2 but still available if you install manually
- Displays the time in large font
- Display day and date
- Use BTN1 to switch between display of battery and memory %.
- Use BTN3 to switch to the next app.
## Swatch
![](screenshot_swatch.jpg)
- A simple stopwatch
@ -76,6 +94,9 @@ The following buttons depend on which face is currently in use
## Heart
![](screenshot_heart.jpg)
- A simple heart rate monitor, at present the app is just showing the raw value from HRM.bpm
- This is an experimental app and not installed by default. The
heart.kit.js file can be uploaded via the Espruino IDE if you want
to try it out. Then reload the App.
- BTN1, long press, turn heart rate monitor on / off
## Waypointer
@ -226,12 +247,12 @@ I have settled on directly writing to the screen using the Graphics
object (g.) for the compass App. This creates a bit of flicker when
the arrow moves but is more reliable than using the ArrayBuffer.
v0.09: Since adding the heart rate monitor I have noticed that I can
sometimes can a memory error when switch through the Apps back to the
Stepo App. I think this can be cured by statically allocating the
ArrayBuffer for stepo rather than using new everytime you switch back
into the stepo watch face. The problem is that the bangle memory
management / defragmentation is quite slow to run.
v0.09: Since adding the heart rate monitor I have sometimes observed
a low memory error when switching through the Apps back to the Stepo
App. I think this can be cured by statically allocating the
ArrayBuffer for stepo rather than using 'new' everytime you switch
back into the stepo watch face. The problem is that the bangle
memory management / defragmentation is quite slow to run.
v0.10: Revisited having a display buffer for the stepo part of the App.
Now use direct screen writing as it means less memory allocation and
@ -241,16 +262,21 @@ reduces chance of getting a memory error on switching watch faces.
The following error codes will be displayed if one of the dependancies is not met.
* E-STEPS - no pedomintor widget has been installed, please install the widpedom or the activepedom widgets
* E-CALIB - no compass calibration data was found, see 'Compass Calibration'
* E-FW - require firmware 2v08.187 or later to detect gps and compass power status
* E-STEPS - no pedomintor widget has been installed, please install
the widpedom or the activepedom widgets
* E-CALIB - no compass calibration data was found, see 'Compass
Calibration'
* E-FW - require firmware 2v08.187 or later to detect gps and compass
power status
* E-WPT - missing waypoints.json file
### Issues / Future enhancements
* Add a settings app so that 'Kitchen' based clocks can be enabled/disabled
* GPS time display shows GMT and not BST, needs localising
* Occassional buzzing after 2-3 days of use, seems to disappear after
a reset to the launcher menu. Needs investigation
* Automatically switch the GPS power setting from Super-E to PSMOO 10
seconds after the LCD goes off. At present I just rely on using
the GPSSetup app and set the GPS power mode that I want.
* Add a small graph to the heart rate monitor app
* Add a facility to call the Arrow calibration process
* Maybe create waypoints.json file if missing

29
apps/kitchen/annex.js Normal file
View File

@ -0,0 +1,29 @@
// annexed code that might be worth keeping
/*****************************************************************************
Screen Buffer Object that can be shared between faces
Making into a Class like this means we allocate the memory once
and avoid fragmenting the memory when we switch in and out of faces
******************************************************************************/
function BUF() {
this.pal4color = new Uint16Array([0x0000,0xFFFF,0x7BEF,0xAFE5],0,2); // b,w,grey,greenyellow
this.pal4red = new Uint16Array([0x0000,0xFFFF,0xF800,0xAFE5],0,2); // b,w,red,greenyellow
this.buf = Graphics.createArrayBuffer(120,120,2,{msb:true});
}
BUF.prototype.flip = function(x,y) {
g.drawImage({width:120,height:120,bpp:2, buffer:this.buf.buffer, palette:this.pal4color}, x, y);
this.buf.clear();
}
BUF.prototype.flip_red = function(x,y) {
g.drawImage({width:120,height:120,bpp:2, buffer:this.buf.buffer, palette:this.pal4red}, x, y);
this.buf.clear();
}
let bufObj = new BUF();

View File

@ -17,13 +17,12 @@
}
function init(gps,sw, hrm) {
showMem("compass init() START");
gpsObject = gps;
intervalRefSec = undefined;
bearing = 0; // always point north if GPS is off
heading = 0;
oldHeading = 0;
previous = {hding:"-", bs:"-", dst:"-", wp_name:"-", course:999};
resetPrevious();
loc = require("locale");
CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
getWaypoint();
@ -34,12 +33,9 @@
*/
if (!Bangle.isCompassOn()) Bangle.setCompassPower(1);
gps.determineGPSState();
showMem("compass init() END");
}
function freeResources() {
showMem("compass freeResources() START");
gpsObject = undefined;
intervalRefSec = undefined;
previous = undefined;
@ -50,7 +46,6 @@
CALIBDATA = undefined;
wp = undefined;
if (Bangle.isCompassOn !== undefined && Bangle.isCompassOn()) Bangle.setCompassPower(0);
showMem("compass freeResources() END");
}
function startTimer() {
@ -67,12 +62,6 @@
if (Bangle.isCompassOn !== undefined && Bangle.isCompassOn()) Bangle.setCompassPower(0);
}
function showMem(msg) {
var val = process.memory();
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
log_debug(str);
}
function onButtonShort(btn) {
log_debug("onButtonShort()");
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;

View File

@ -2,7 +2,7 @@
var FACES = [];
var STOR = require("Storage");
STOR.list(/\.kit\.js$/).forEach(face=>FACES.push(eval(require("Storage").read(face))));
var iface = STOR.list(/\.kit\.js$/).indexOf("stepo.kit.js");
var iface = STOR.list(/\.kit\.js$/).indexOf("stepo2.kit.js");
var face = FACES[iface]();
var firstPress
var pressTimer;
@ -33,10 +33,10 @@ function nextFace(){
// when you feel the buzzer you know you have done a long press
function longPressCheck() {
Bangle.buzz();
debug_log("long PressCheck() buzz");
debug_log("BUZZ, long press");
if (pressTimer) {
clearInterval(pressTimer);
debug_log("clear pressTimer 2");
debug_log("CLEAR pressTimer 2");
pressTimer = undefined;
}
}
@ -48,10 +48,10 @@ function buttonPressed(btn) {
} else {
firstPress = getTime();
if (pressTimer) {
debug_log("clear pressTimer 1");
debug_log("CLEAR pressTimer 1");
clearInterval(pressTimer);
}
debug_log("set pressTimer 1");
debug_log("SET pressTimer 1");
pressTimer = setInterval(longPressCheck, 1500);
}
}
@ -60,7 +60,7 @@ function buttonPressed(btn) {
function buttonReleased(btn) {
var dur = getTime() - firstPress;
if (pressTimer) {
debug_log("clear pressTimer 3");
debug_log("CLEAR pressTimer 3");
clearInterval(pressTimer);
pressTimer = undefined;
}
@ -256,7 +256,7 @@ GPS.prototype.processFix = function(fix) {
this.gpsState = this.GPS_RUNNING;
if (!this.last_fix.fix && !(require("Storage").readJSON("setting.json", 1) || {}).quiet) {
Bangle.buzz(); // buzz on first position
debug_log("GPS fix buzz");
debug_log("BUZZ - gps fix");
}
this.last_fix = fix;
}
@ -303,7 +303,7 @@ GPS.prototype.getWPdistance = function() {
//log_debug(this.last_fix);
//log_debug(this.wp_current);
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return 0;
else
return this.calcDistance(this.last_fix, this.wp_current);
@ -313,14 +313,14 @@ GPS.prototype.getWPbearing = function() {
//log_debug(this.last_fix);
//log_debug(this.wp_current);
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return 0;
else
return this.calcBearing(this.last_fix, this.wp_current);
}
GPS.prototype.loadFirstWaypoint = function() {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
this.wp_index = 0;
this.wp_current = waypoints[this.wp_index];
log_debug(this.wp_current);
@ -332,7 +332,7 @@ GPS.prototype.getCurrentWaypoint = function() {
}
GPS.prototype.waypointHasLocation = function() {
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return false;
else
return true;
@ -340,12 +340,12 @@ GPS.prototype.waypointHasLocation = function() {
GPS.prototype.markWaypoint = function() {
if(this.wp_current.name === "NONE")
if(this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE")
return;
log_debug("GPS::markWaypoint()");
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
this.wp_current = waypoints[this.wp_index];
if (this.waypointHasLocation()) {
@ -360,7 +360,7 @@ GPS.prototype.markWaypoint = function() {
}
GPS.prototype.nextWaypoint = function(inc) {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
this.wp_index+=inc;
if (this.wp_index>=waypoints.length) this.wp_index=0;
if (this.wp_index<0) this.wp_index = waypoints.length-1;
@ -731,14 +731,14 @@ function TRIP() {
TRIP.prototype.resetTrip = function(steps) {
this.tripStart = (0 + steps);
console.log("resetTrip starting=" + this.tripStart);
log_debug("resetTrip starting=" + this.tripStart);
}
TRIP.prototype.getTrip = function(steps) {
let tripSteps = (0 + steps) - this.tripStart;
console.log("getTrip steps=" + steps);
console.log("getTrip tripStart=" + this.tripStart);
console.log("getTrip=" + tripSteps);
log_debug("getTrip steps=" + steps);
log_debug("getTrip tripStart=" + this.tripStart);
log_debug("getTrip=" + tripSteps);
return tripSteps;
}
@ -758,7 +758,6 @@ Debug Object
******************************************************************************/
/*
function DEBUG() {
this.logfile = require("Storage").open("debug.log","a");
}
@ -770,7 +769,6 @@ DEBUG.prototype.log = function(msg) {
}
debugObj = new DEBUG();
*/
function debug_log(m) {
//debugObj.log(m);

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

256
apps/kitchen/stepo2.kit.js Normal file
View File

@ -0,0 +1,256 @@
(() => {
function getFace(){
var intervalRefSec;
var trip;
var prevSteps;
var prevTopText1;
var prevTopText2;
var prevBottomText;
var prevMins;
var infoMode;
const INFO_DATE = 0;
const INFO_TRIP = 1;
const INFO_BATT = 2;
const INFO_MEM = 3;
const INFO_FW = 4;
function init(g,sw,hrm,tr) {
trip = tr;
infoMode = INFO_DATE;
forceRedraw();
}
function freeResources() {
trip = undefined;
}
function forceRedraw() {
prevStepsText = '';
prevSteps = -1;
prevTopText1 = '';
prevTopText2 = '';
prevBottomText = '';
prevMins = '';
}
function cycleInfoMode() {
switch(infoMode) {
case INFO_DATE:
infoMode = INFO_TRIP;
break;
case INFO_TRIP:
infoMode = INFO_BATT;
break;
case INFO_BATT:
infoMode = INFO_MEM
break;
case INFO_MEM:
infoMode = INFO_FW
break;
case INFO_FW:
default:
infoMode = INFO_DATE;
break;
}
}
function onButtonShort(btn) {
cycleInfoMode();
forceRedraw();
draw();
}
function onButtonLong(btn) {
trip.resetTrip(getSteps());
infoMode = INFO_TRIP;
forceRedraw();
draw();
}
function radians(a) {
return a*Math.PI/180;
}
function startTimer() {
draw();
intervalRefSec = setInterval(draw, 5000);
}
function stopTimer() {
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
}
function draw() {
var d = new Date();
var da = d.toString().split(" ");
var hh = da[4].substr(0,2);
var mm = da[4].substr(3,2);
var day = da[0];
var day_month = da[2] + " " + da[1];
g.setColor(1,1,1); // white
if (prevMins != mm) {
prevMins = mm;
// hours and minutes
g.clearRect(0, 24, 149, 239);
g.setFontAlign(-1, -1);
g.setFont("Vector", 104);
g.drawString(hh, 20, 30, true);
g.drawString(mm, 20, 120, true);
}
/*
* if our trip count is greater than todays steps then we have
* rolled over to the next day so we should reset the trip counter
*/
var steps = getSteps();
if (trip.getTrip(steps) < 0)
trip.resetTrip(steps);
drawTopText(day,day_month);
drawBottomText();
drawSteps();
}
function drawTopText(dy, dm) {
var topText1 = "";
var topText2 = "";
switch(infoMode) {
case INFO_DATE:
topText1 = dy.toUpperCase();
topText2 = dm.toUpperCase();
break;
case INFO_TRIP:
topText2 = "TRIP";
break;
case INFO_BATT:
topText2 = "BATT";
break;
case INFO_MEM:
topText2 = "MEM";
break;
case INFO_FW:
topText2 = "F/W";
break;
}
if (prevTopText1 !== topText1 || prevTopText2 !== topText2) {
prevTopText1 = topText1;
prevTopText2 = topText2;
// day, date
g.setFont("Vector", 24);
g.setFontAlign(0, -1);
g.clearRect(150, 30, 239, 75);
g.drawString(topText1, 195, 30, true);
g.drawString(topText2, 195, 55, true);
}
}
function drawBottomText() {
var bottomText = "";
var steps = getSteps();
switch(infoMode) {
case INFO_DATE:
bottomText = "" + steps;
break;
case INFO_TRIP:
bottomText = "" + trip.getTrip(steps);
break;
case INFO_BATT:
bottomText = "" + E.getBattery() + "%";
break;
case INFO_MEM:
var val = process.memory();
bottomText = "" + Math.round(val.usage*100/val.total) + "%";
break;
case INFO_FW:
bottomText = process.env.VERSION;
break;
}
if (prevBottomText !== bottomText) {
prevBottomText = bottomText;
g.clearRect(148, 190, 239, 239);
g.setColor(1,1,1); // white
g.setFont("Vector", 24);
g.setFontAlign(0, -1);
g.drawString(bottomText, 195, 190);
}
}
function drawSteps() {
var i = 0;
var cx = 150 + 45;
var cy = 130;
var r = 34;
var steps = getSteps();
if (trip.getTripState() == true)
steps = trip.getTrip(steps);
if (prevSteps == steps)
return;
prevSteps = steps;
var percent = steps / 10000;
if (percent > 1) percent = 1;
var startrot = 0 - 180;
var midrot = -180 - (360 * percent);
var endrot = -360 - 180;
g.setColor(0x07FF); // light cyan
// draw guauge
for (i = startrot; i > midrot; i -= 3) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
g.fillCircle(x,y,3);
}
// change the remaining color to RED if battery is below 25%
if (E.getBattery() > 25) {
//g.setColor(0x7BEF); // grey
g.setColor(0x000D); // dark navy
} else {
g.setColor(0xF800); // red
}
// draw remainder of guage in grey or red
for (i = midrot - 12; i > endrot + 12; i -= 3) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
g.fillCircle(x,y,3);
}
}
function getSteps() {
if (stepsWidget() === undefined)
return "E-STEPS";
return stepsWidget().getSteps();
}
function stepsWidget() {
if (WIDGETS.activepedom !== undefined) {
return WIDGETS.activepedom;
} else if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom;
}
return undefined;
}
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
}
return getFace;
})();

View File

@ -2,3 +2,5 @@
0.02: Only store relevant app data (saves RAM when many apps)
0.03: Allow scrolling to wrap around (fix #382)
0.04: Now displays widgets
0.05: Use g.theme for colours
0.06: Use Bangle.setUI for buttons

View File

@ -12,47 +12,44 @@ var menuScroll = 0;
var menuShowing = false;
function drawMenu() {
g.setFont("6x8",2);
g.setFontAlign(-1,0);
var n = 3;
g.reset().setFont("6x8",2).setFontAlign(-1,0);
var w = g.getWidth();
var h = g.getHeight();
var m = w/2;
var n = (h-48)/64;
if (selected>=n+menuScroll) menuScroll = 1+selected-n;
if (selected<menuScroll) menuScroll = selected;
// arrows
g.setColor(menuScroll ? -1 : 0);
g.fillPoly([120,6,106,20,134,20]);
g.setColor((apps.length>n+menuScroll) ? -1 : 0);
g.fillPoly([120,233,106,219,134,219]);
g.setColor(menuScroll ? g.theme.fg : g.theme.bg);
g.fillPoly([m,6,m-14,20,m+14,20]);
g.setColor((apps.length>n+menuScroll) ? g.theme.fg : g.theme.bg);
g.fillPoly([m,h-7,m-14,h-21,m+14,h-21]);
// draw
g.setColor(-1);
g.setColor(g.theme.fg);
for (var i=0;i<n;i++) {
var app = apps[i+menuScroll];
if (!app) break;
var y = 24+i*64;
if (i+menuScroll==selected) {
g.setColor(0.3,0.3,0.3);
g.fillRect(0,y,239,y+63);
g.setColor(1,1,1);
g.drawRect(0,y,239,y+63);
g.setColor(g.theme.bgH).fillRect(0,y,w-1,y+63);
g.setColor(g.theme.fgH).drawRect(0,y,w-1,y+63);
} else
g.clearRect(0,y,239,y+63);
g.clearRect(0,y,w-1,y+63);
g.drawString(app.name,64,y+32);
var icon=undefined;
if (app.icon) icon = s.read(app.icon);
if (icon) try {g.drawImage(icon,8,y+8);} catch(e){}
}
}
g.clear();
drawMenu();
setWatch(function() {
selected--;
Bangle.setUI("updown",dir=>{
if (dir) {
selected += dir;
if (selected<0) selected = apps.length-1;
drawMenu();
}, BTN1, {repeat:true});
setWatch(function() {
selected++;
if (selected>=apps.length) selected = 0;
drawMenu();
}, BTN3, {repeat:true});
setWatch(function() { // run
} else {
if (!apps[selected].src) return;
if (require("Storage").read(apps[selected].src)===undefined) {
E.showMessage("App Source\nNot found");
@ -61,6 +58,7 @@ setWatch(function() { // run
E.showMessage("Loading...");
load(apps[selected].src);
}
}, BTN2, {repeat:true,edge:"falling"});
}
});
Bangle.loadWidgets();
Bangle.drawWidgets();

1
apps/launchb2/ChangeLog Normal file
View File

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

67
apps/launchb2/app.js Normal file
View File

@ -0,0 +1,67 @@
var s = require("Storage");
var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type));
apps.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;
});
var APPH = 64;
var menuScroll = 0;
var menuShowing = false;
var w = g.getWidth();
var h = g.getHeight();
var n = Math.ceil((h-24)/APPH);
var menuScrollMax = APPH*apps.length - (h-24);
apps.forEach(app=>{
if (app.icon)
app.icon = s.read(app.icon); // should just be a link to a memory area
});
function drawApp(i) {
var y = 24+i*APPH-menuScroll;
var app = apps[i];
if (!app || y<-APPH || y>=g.getHeight()) return;
g.setFont("6x8",2).setFontAlign(-1,0).drawString(app.name,64,y+32);
if (app.icon) try {g.drawImage(app.icon,8,y+8);} catch(e){}
}
function drawMenu() {
g.reset().clearRect(0,24,w-1,h-1);
g.setClipRect(0,24,g.getWidth()-1,g.getHeight()-1);
for (var i=0;i<n;i++) drawApp(i);
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}
g.clear();
drawMenu();
Bangle.on('drag',e=>{
var dy = e.dy;
if (menuScroll - dy < 0)
dy = menuScroll;
if (menuScroll - dy > menuScrollMax)
dy = menuScroll - menuScrollMax;
if (!dy) return;
g.reset().setClipRect(0,24,g.getWidth()-1,g.getHeight()-1);
g.scroll(0,dy);
menuScroll -= dy;
if (e.dy < 0) drawApp(Math.floor((menuScroll+24)/APPH)+n-1);
else drawApp(Math.floor((menuScroll+24)/APPH));
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
});
Bangle.on("touch",(_,e)=>{
if (e.y<20) return;
var i = Math.floor((e.y+menuScroll-24) / APPH);
var app = apps[i];
if (!app) return;
if (!app.src || require("Storage").read(app.src)===undefined) {
E.showMessage("App Source\nNot found");
setTimeout(drawMenu, 2000);
} else {
E.showMessage("Loading...");
load(app.src);
}
});
Bangle.loadWidgets();
Bangle.drawWidgets();

BIN
apps/launchb2/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

0
apps/marioclock/mario-clock-screen-shot.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

0
apps/miclock/clock-mixed.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

0
apps/miclock2/clock-mixed.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

0
apps/minionclk/ChangeLog Executable file → Normal file
View File

0
apps/minionclk/app-icon.js Executable file → Normal file
View File

0
apps/minionclk/app.js Executable file → Normal file
View File

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