Merge branch 'master' into development
|
@ -1,11 +1,8 @@
|
|||
apps/animclk/V29.LBM.js
|
||||
apps/banglerun/rollup.config.js
|
||||
apps/schoolCalendar/fullcalendar/main.js
|
||||
apps/authentiwatch/qr_packed.js
|
||||
apps/qrcode/qr-scanner.umd.min.js
|
||||
apps/gipy/pkg/gps.js
|
||||
apps/health/chart.min.js
|
||||
*.test.js
|
||||
|
||||
# typescript/generated files
|
||||
apps/btadv/*.js
|
||||
# Needs to be ignored because it uses ESM export/import
|
||||
apps/gipy/pkg/gps.js
|
||||
apps/gipy/pkg/gps.d.ts
|
||||
apps/gipy/pkg/gps_bg.wasm.d.ts
|
||||
|
||||
# Needs to be ignored because it includes broken JS
|
||||
apps/health/chart.min.js
|
||||
|
|
|
@ -1,7 +1,32 @@
|
|||
{
|
||||
const lintExemptions = require("./apps/lint_exemptions.js");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
function findGeneratedJS(roots) {
|
||||
function* listFiles(dir, allow) {
|
||||
for (const f of fs.readdirSync(dir)) {
|
||||
const filepath = path.join(dir, f);
|
||||
const stat = fs.statSync(filepath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
yield* listFiles(filepath, allow);
|
||||
} else if(allow(filepath)) {
|
||||
yield filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return roots.flatMap(root =>
|
||||
[...listFiles(root, f => f.endsWith(".ts"))]
|
||||
.map(f => f.replace(/\.ts$/, ".js"))
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
"env": {
|
||||
// TODO: "espruino": false
|
||||
// TODO: "banglejs": false
|
||||
// For a prototype of the above, see https://github.com/espruino/BangleApps/pull/3237
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
|
@ -23,10 +48,8 @@
|
|||
"Flash": "readonly",
|
||||
"Float32Array": "readonly",
|
||||
"Float64Array": "readonly",
|
||||
"fs": "readonly",
|
||||
"Function": "readonly",
|
||||
"Graphics": "readonly",
|
||||
"heatshrink": "readonly",
|
||||
"I2C": "readonly",
|
||||
"Int16Array": "readonly",
|
||||
"Int32Array": "readonly",
|
||||
|
@ -46,11 +69,9 @@
|
|||
"RegExp": "readonly",
|
||||
"Serial": "readonly",
|
||||
"SPI": "readonly",
|
||||
"Storage": "readonly",
|
||||
"StorageFile": "readonly",
|
||||
"String": "readonly",
|
||||
"SyntaxError": "readonly",
|
||||
"tensorflow": "readonly",
|
||||
"TFMicroInterpreter": "readonly",
|
||||
"TypeError": "readonly",
|
||||
"Uint16Array": "readonly",
|
||||
|
@ -58,8 +79,10 @@
|
|||
"Uint32Array": "readonly",
|
||||
"Uint8Array": "readonly",
|
||||
"Uint8ClampedArray": "readonly",
|
||||
"Unistroke": "readonly",
|
||||
"Waveform": "readonly",
|
||||
// Methods and Fields at https://banglejs.com/reference
|
||||
"__FILE__": "readonly",
|
||||
"analogRead": "readonly",
|
||||
"analogWrite": "readonly",
|
||||
"arguments": "readonly",
|
||||
|
@ -89,6 +112,7 @@
|
|||
"getSerial": "readonly",
|
||||
"getTime": "readonly",
|
||||
"global": "readonly",
|
||||
"globalThis": "readonly",
|
||||
"HIGH": "readonly",
|
||||
"I2C1": "readonly",
|
||||
"Infinity": "readonly",
|
||||
|
@ -129,7 +153,43 @@
|
|||
"VIBRATE": "readonly",
|
||||
// Aliases and not defined at https://banglejs.com/reference
|
||||
"g": "readonly",
|
||||
"WIDGETS": "readonly"
|
||||
"WIDGETS": "readonly",
|
||||
"module": "readonly",
|
||||
"exports": "writable",
|
||||
"D0": "readonly",
|
||||
"D1": "readonly",
|
||||
"D2": "readonly",
|
||||
"D3": "readonly",
|
||||
"D4": "readonly",
|
||||
"D5": "readonly",
|
||||
"D6": "readonly",
|
||||
"D7": "readonly",
|
||||
"D8": "readonly",
|
||||
"D9": "readonly",
|
||||
"D10": "readonly",
|
||||
"D11": "readonly",
|
||||
"D12": "readonly",
|
||||
"D13": "readonly",
|
||||
"D14": "readonly",
|
||||
"D15": "readonly",
|
||||
"D16": "readonly",
|
||||
"D17": "readonly",
|
||||
"D18": "readonly",
|
||||
"D19": "readonly",
|
||||
"D20": "readonly",
|
||||
"D21": "readonly",
|
||||
"D22": "readonly",
|
||||
"D23": "readonly",
|
||||
"D24": "readonly",
|
||||
"D25": "readonly",
|
||||
"D26": "readonly",
|
||||
"D27": "readonly",
|
||||
"D28": "readonly",
|
||||
"D29": "readonly",
|
||||
"D30": "readonly",
|
||||
"D31": "readonly",
|
||||
|
||||
"bleServiceOptions": "writable", // available in boot.js code that's called ad part of bootupdate
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 11
|
||||
|
@ -142,22 +202,49 @@
|
|||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"no-case-declarations": "off",
|
||||
"no-constant-condition": "off",
|
||||
"no-delete-var": "off",
|
||||
"no-empty": "off",
|
||||
"no-empty": ["warn", { "allowEmptyCatch": true }],
|
||||
"no-global-assign": "off",
|
||||
"no-inner-declarations": "off",
|
||||
"no-octal": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-unreachable": "warn",
|
||||
"no-cond-assign": "warn",
|
||||
"no-useless-catch": "warn",
|
||||
// TODO: "no-undef": "warn",
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-undef": "warn",
|
||||
"no-unused-vars": ["warn", { "args": "none" } ],
|
||||
"no-useless-escape": "off",
|
||||
"no-control-regex" : "off"
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.ts"],
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
rules: {
|
||||
"no-delete-var": "off",
|
||||
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||
"no-prototype-builtins": "off",
|
||||
"prefer-const": "off",
|
||||
"prefer-rest-params": "off",
|
||||
"no-control-regex" : "off",
|
||||
"@typescript-eslint/no-delete-var": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-this-alias": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
}
|
||||
},
|
||||
...Object.entries(lintExemptions).map(([filePath, {rules}]) => ({
|
||||
files: [filePath],
|
||||
rules: Object.fromEntries(rules.map(rule => [rule, "off"])),
|
||||
})),
|
||||
],
|
||||
ignorePatterns: findGeneratedJS(["apps/", "modules/"]),
|
||||
reportUnusedDisableDirectives: true,
|
||||
}
|
|
@ -11,10 +11,10 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Use Node.js 16.x
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
- name: Install testing dependencies
|
||||
run: npm ci
|
||||
- name: Test all apps and widgets
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
Contributing to BangleApps
|
||||
==========================
|
||||
|
||||
https://github.com/espruino/BangleApps?tab=readme-ov-file#getting-started
|
||||
has some links to tutorials on developing for Bangle.js.
|
||||
|
||||
Please check out the Wiki to get an idea what sort of things
|
||||
we'd like to see for contributed apps: https://github.com/espruino/BangleApps/wiki/App-Contribution
|
||||
|
29
README.md
|
@ -6,6 +6,8 @@ Bangle.js App Loader (and Apps)
|
|||
* Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps)
|
||||
* Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/)
|
||||
|
||||
The release version is manually refreshed with regular intervals while the development version is continuously updated as new code is committed to this repository.
|
||||
|
||||
**All software (including apps) in this repository is MIT Licensed - see [LICENSE](LICENSE)** By
|
||||
submitting code to this repository you confirm that you are happy with it being MIT licensed,
|
||||
and that it is not licensed in another way that would make this impossible.
|
||||
|
@ -278,6 +280,7 @@ and which gives information about the app for the Launcher.
|
|||
// 'bluetooth' - uses Bluetooth LE
|
||||
// 'system' - used by the system
|
||||
// 'clkinfo' - provides or uses clock_info module for data on your clock face or clocks that support it (see apps/clock_info/README.md)
|
||||
// 'health' - e.g. heart rate monitors or step counting
|
||||
"supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS or BANGLEJS2
|
||||
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above)
|
||||
"dependencies" : { "messages":"app" } // optional, depend on a specific app ID
|
||||
|
@ -402,7 +405,7 @@ in an iframe.
|
|||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<script src="../../lib/interface.js"></script>
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<div id="t">Loading...</div>
|
||||
<script>
|
||||
function onInit() {
|
||||
|
@ -553,6 +556,30 @@ You can use `g.setColor(r,g,b)` OR `g.setColor(16bitnumber)` - some common 16 bi
|
|||
| GreenYellow | 0xAFE5 |
|
||||
| Pink | 0xF81F |
|
||||
|
||||
## Fonts
|
||||
|
||||
A recent addition to Bangle.js is the ability to use extra fonts with support for more characters.
|
||||
For example [all regions](https://banglejs.com/apps/?id=fontall) or [extended](https://banglejs.com/apps/?id=fontext) fonts.
|
||||
|
||||
Once installed, these apps cause a new font, `Intl` to be added to `Graphics`, which can be used with just `g.setFont("Intl")`.
|
||||
|
||||
There is also a `font` library - this is not implemented yet, but more information about the planned implementation
|
||||
is available at https://github.com/espruino/BangleApps/issues/3109
|
||||
|
||||
For now, to make your app work with the international font, you can check if `Graphics.prototype.setFontIntl` exists,
|
||||
and if so you can change the font you plan on using:
|
||||
|
||||
```JS
|
||||
myFont = "6x8:2";
|
||||
if (Graphics.prototype.setFontIntl)
|
||||
myFont = "Intl";
|
||||
```
|
||||
|
||||
Any new Font library must contain the metadata `"icon": "app.png", "tags": "font", "type": "module", "provides_modules" : ["fonts"],`
|
||||
and should provide a `font` library, as well as a `boot.js` that adds `Graphics.prototype.setFontIntl`. If you plan on
|
||||
making a new library it's best to just copy one of the existing ones for now.
|
||||
|
||||
|
||||
## API Reference
|
||||
|
||||
[Reference](http://www.espruino.com/Reference#software)
|
||||
|
|
21
android.html
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8,maximum-scale=0.8, minimum-scale=0.8, shrink-to-fit=no">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||
<link rel="stylesheet" href="css/spectre.min.css">
|
||||
<link rel="stylesheet" href="css/spectre-exp.min.css">
|
||||
<link rel="stylesheet" href="css/spectre-icons.min.css">
|
||||
|
@ -22,7 +22,7 @@
|
|||
<body>
|
||||
<header class="navbar-primary navbar">
|
||||
<section class="navbar-section" >
|
||||
<a href="https://banglejs.com" target="_blank" class="navbar-brand mr-2" ><img src="img/banglejs-logo-sml.png" alt="Bangle.js">
|
||||
<a href="https://banglejs.com" target="_blank" class="navbar-brand mr-2"><img src="img/banglejs-logo-small.svg" alt="Bangle.js">
|
||||
<div>App Loader</div></a>
|
||||
<!-- <a href="#" class="btn btn-link">...</a> -->
|
||||
</section>
|
||||
|
@ -84,6 +84,7 @@
|
|||
<label class="chip tooltip" filterid="outdoors" data-tooltip="For outdoor use">Outdoors</label>
|
||||
<label class="chip tooltip" filterid="ram" data-tooltip="Apps that don't save anything to flash memory">Online</label>
|
||||
<label class="chip tooltip" filterid="clkinfo" data-tooltip="Info displayed on clocks, or clocks with info">Clock Info</label>
|
||||
<label class="chip tooltip" filterid="health" data-tooltip="Apps for your health">Health</label>
|
||||
<label class="chip tooltip" filterid="favourites" data-tooltip="Apps that you've liked ❤️">Favourites</label>
|
||||
</div>
|
||||
<div class="sort-nav hidden">
|
||||
|
@ -176,9 +177,13 @@
|
|||
</label>
|
||||
<label class="form-switch">
|
||||
<input type="checkbox" id="settings-alwaysAllowUpdate">
|
||||
<i class="form-icon"></i> Always allow to reinstall apps in place regardless of the version
|
||||
<i class="form-icon"></i> Always show "reinstall app" button <i class="icon icon-refresh"></i> regardless of the version
|
||||
</label>
|
||||
<button class="btn" id="defaultsettings">Reset to default App Loader settings</button>
|
||||
<label class="form-switch">
|
||||
<input type="checkbox" id="settings-autoReload">
|
||||
<i class="form-icon"></i> Automatically reload watch after app App Loader actions (removes "Hold button" prompt)
|
||||
</label>
|
||||
<button class="btn" id="defaultsettings">Reset App Loader settings to defaults</button>
|
||||
</details>
|
||||
</div>
|
||||
<div id="more-deviceinfo" style="display:none">
|
||||
|
@ -243,12 +248,14 @@ if (typeof Android!=="undefined") {
|
|||
hadData : false,
|
||||
handlers : []
|
||||
}
|
||||
connection.on("data", function(d) {
|
||||
connection.received += d;
|
||||
connection.hadData = true;
|
||||
if (connection.cb) connection.cb(d);
|
||||
});
|
||||
|
||||
function bangleRx(data) {
|
||||
// document.getElementById("status").innerText = "RX:"+data;
|
||||
connection.received += data;
|
||||
connection.hadData = true;
|
||||
if (connection.cb) connection.cb(data);
|
||||
// call data event
|
||||
if (connection.handlers["data"])
|
||||
connection.handlers["data"](data);
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New Widget!
|
||||
0.02: Minor code improvements
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "1button",
|
||||
"name": "One-Button-Tracker",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A widget that turns BTN1 into a tracker, records time of button press/release.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
console.log("Button let go");
|
||||
digitalWrite(LED2,0);
|
||||
var unpress_time = new Date();
|
||||
recFile = require("Storage").open("one_button_presses.csv","a");
|
||||
const recFile = require("Storage").open("one_button_presses.csv","a");
|
||||
recFile.write([press_time.getTime(),unpress_time.getTime()].join(",")+"\n");
|
||||
}, BTN1, { repeat: true, edge: 'falling', debounce: 50 });
|
||||
|
||||
|
|
|
@ -122,13 +122,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
function swipeHandler() {
|
||||
/*function swipeHandler() {
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
function buttonHandler() {
|
||||
/*function buttonHandler() {
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
var twok = new TwoK();
|
||||
twok.addDigit(); twok.addDigit();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New app!
|
||||
0.02: Better support for watch themes
|
||||
0.03: Workaround minifier bug
|
||||
0.04: Minor code improvements
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "2047pp",
|
||||
"shortName":"2047pp",
|
||||
"icon": "app.png",
|
||||
"version":"0.03",
|
||||
"version": "0.04",
|
||||
"description": "Bangle version of a tile shifting game",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Minor code improvements
|
||||
|
|
|
@ -80,7 +80,7 @@ function refreshBattery() {
|
|||
}
|
||||
|
||||
// Only update displayed battery level every minute as it fluctuates a lot
|
||||
var batteryInterval = setInterval(refreshBattery, 60000);
|
||||
setInterval(refreshBattery, 60000);
|
||||
|
||||
Bangle.setUI("clock");
|
||||
Bangle.setLocked(false);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "2ofthemclk",
|
||||
"name": "two of them clock",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "You can now wear teh memez on your wrist.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"shortName":"3DClock",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "This is a simple 3D scalig demo based on Anton Clock",
|
||||
"description": "This is a simple 3D scaling demo based on Anton Clock",
|
||||
"screenshots" : [ { "url":"screenshot.png" }],
|
||||
"type":"clock",
|
||||
"tags": "clock",
|
||||
|
|
|
@ -282,7 +282,7 @@ function drawWidgeds() {
|
|||
//print(BluetoothDevice.connected);
|
||||
var x1Bt = 160;
|
||||
var y1Bt = 0;
|
||||
var x2Bt = x1Bt + 30;
|
||||
//var x2Bt = x1Bt + 30;
|
||||
var y2Bt = y2Bt;
|
||||
|
||||
if (NRF.getSecurityStatus().connected)
|
||||
|
@ -391,4 +391,4 @@ Bangle.on('lock', function(on) {
|
|||
|
||||
SetFull(Bangle.isLocked());
|
||||
|
||||
var secondInterval = setInterval(draw, 1000);
|
||||
/*var secondInterval =*/ setInterval(draw, 1000);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Initial version for upload
|
||||
0.02: Better theme support, configurable colors, small improvements
|
||||
0.03: Use `messages` library to check for new messages
|
||||
0.04: Minor code improvements
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "7x7dotsclock",
|
||||
"name": "7x7 Dots Clock",
|
||||
"shortName":"7x7 Dots Clock",
|
||||
"version":"0.03",
|
||||
"version": "0.04",
|
||||
"description": "A clock with a big 7x7 dots Font",
|
||||
"icon": "dotsfontclock.png",
|
||||
"tags": "clock",
|
||||
|
|
After Width: | Height: | Size: 983 B |
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1 @@
|
|||
atob("MDCBAAAAAAAAAAAAAAAAAAAAH/AAAAAAf/4AAAAB4AeAAAAHgAHgAAAOAABwAAAcAAA4AAA4AMAcAABwAMAOAABgA/AGAADAAeADAAHAAMADgAGAAAABgAGAAAABgAMAAAAAwAMBAAAAwAMDAAAAwAMHwAAAwAMHwAAAwAMDAACAwAMBAADAwAMAAAPgwAMAAAPgwAMAAADAwAGAAACBgAGAAAABgAHAAAADgADAAAADAADgAAAHAAB////+AAB////+AABgAAAGAABgAAAGAABgAAAGAADAAAADAADAAAADAADAAAADAAGAAAABgAGAAAABgAH/////gAP/////wAYAAAAAYAYAAAAAYAf/////4AP/////wAAAAAAAAAAAAAAAAA==")
|
|
@ -0,0 +1,92 @@
|
|||
var keyboard = "textinput";
|
||||
var Name = "";
|
||||
Bangle.setLCDTimeout(0);
|
||||
var menuOpen = 1;
|
||||
var answers = new Array("no", "yes","WHAT????","What do you think", "That was a bad question", "YES!!!", "NOOOOO!!", "nope","100%","yup","why should I answer that?","think for yourself","ask again later, I'm busy", "what Was that horrible question","how dare you?","you wanted to hear yes? okay, yes", "Don't get angry when I say no","you are 100% wrong","totally, for sure","hmmm... I'll ponder it and get back to you later","wow, you really have a lot of questions", "NOPE","is the sky blue, hmmm...","I don't have time to answer","How many more questions before you change my name?","theres this thing called wikipedia","hmm... I don't seem to be able to reach the internet right now","if you phrase it like that, yes","Huh, never thought so hard in my life","The winds of time say no");
|
||||
var consonants = new Array("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z");
|
||||
var vowels = new Array("a","e","i","o","u");
|
||||
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
||||
function generateName()
|
||||
{
|
||||
Name = "";
|
||||
var nameLength = Math.round(Math.random()*5);
|
||||
for(var i = 0; i < nameLength; i++){
|
||||
var cosonant = consonants[Math.round(Math.random()*consonants.length/2)];
|
||||
var vowel = vowels[Math.round(Math.random()*vowels.length/2)];
|
||||
Name = Name + cosonant + vowel;
|
||||
if(Name == "")
|
||||
{
|
||||
generateName();
|
||||
}
|
||||
}
|
||||
}
|
||||
generateName();
|
||||
function menu()
|
||||
{
|
||||
g.clear();
|
||||
E.showMenu();
|
||||
menuOpen = 1;
|
||||
E.showMenu({
|
||||
"" : { title : Name },
|
||||
"< Back" : () => menu(),
|
||||
"Start" : () => {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
menuOpen = 0;
|
||||
Drawtext("ask " + Name + " a yes or no question");
|
||||
},
|
||||
"regenerate name" : () => {
|
||||
menu();
|
||||
generateName();
|
||||
},
|
||||
"show answers" : () => {
|
||||
var menu = new Array([]);
|
||||
for(var i = 0; i < answers.length; i++){
|
||||
menu.push({title : answers[i]});
|
||||
}
|
||||
E.showMenu(menu);
|
||||
|
||||
|
||||
},
|
||||
|
||||
"Add answer" : () => {
|
||||
E.showMenu();
|
||||
keyboard.input({}).then(result => {if(result != ""){answers.push(result);} menu();});
|
||||
},
|
||||
"Edit name" : () => {
|
||||
E.showMenu();
|
||||
keyboard.input({}).then(result => {if(result != ""){Name = result;} menu();});
|
||||
|
||||
},
|
||||
"Exit" : () => load(),
|
||||
});
|
||||
}
|
||||
menu();
|
||||
|
||||
var answer;
|
||||
function Drawtext(text)
|
||||
{
|
||||
g.clear();
|
||||
g.setFont("Vector", 20);
|
||||
g.drawString(g.wrapString(text, g.getWidth(), -20).join("\n"));
|
||||
}
|
||||
function WriteAnswer()
|
||||
{
|
||||
if (menuOpen == 0)
|
||||
{
|
||||
var randomnumber = Math.round(Math.random()*answers.length);
|
||||
answer = answers[randomnumber];
|
||||
Drawtext(answer);
|
||||
setTimeout(function() {
|
||||
Drawtext("ask " + Name + " a yes or no question");
|
||||
}, 3000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
setWatch(function() {
|
||||
menu();
|
||||
|
||||
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true, edge:"falling"});
|
||||
|
||||
Bangle.on('touch', function(button, xy) { WriteAnswer(); });
|
|
@ -0,0 +1,19 @@
|
|||
{ "id": "8ball",
|
||||
"name": "Magic 8 ball",
|
||||
"shortName":"8ball",
|
||||
"icon": "8ball.png",
|
||||
"version":"0.01",
|
||||
"screenshots": [
|
||||
{"url":"screenshot.png"},
|
||||
{"url":"screenshot-1.png"},
|
||||
{"url":"screenshot-2.png"}
|
||||
],
|
||||
"allow_emulator": true,
|
||||
"description": "A very sarcastic magic 8ball",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"8ball.app.js","url":"app.js"},
|
||||
{"name":"8ball.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.7 KiB |
|
@ -21,7 +21,6 @@
|
|||
'< Back': back,
|
||||
'Full Screen': {
|
||||
value: settings.fullscreen,
|
||||
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.fullscreen = !settings.fullscreen;
|
||||
save();
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
0.04: Set 00:00 to 12:00 for 12 hour time
|
||||
0.05: Display time, even on Thursday
|
||||
0.06: Fix light theme issue, where widgets would end up on a light strip
|
||||
0.07: Minor code improvements
|
||||
|
|
|
@ -8,7 +8,7 @@ var imgBg = require("heatshrink").decompress(atob("2GwgJC/AH4A/AH4A/AH4A/AH4A/AC
|
|||
// reg number first char 48 28 by 41
|
||||
var fontNum = atob("AAAAAAAAAAAAAA//8D//g//8P/+I//8//44//w//j4//A/+P4/8A/4/4AAAAD/4AAAAP/wAAAAf/gAAAA//AAAAB/+AAAAD/8AAAAH/4AAAAP/wAAAAf/gAAAA//AAAAB/+AAAAD/8AAAAH/wAAAAH/H/gH/H8f/gf/Hx//h//HH//n//Ef/+H//B//4H//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wB/4AP/4H/4A//4f/4D//5//4P//h//4//+B//4AAAAAAAAAAAAAAAAAf/+AAAB//4gAAD//jgAAD/+PgABj/4/gAHj/j/gAfgAP/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/AA/AAf8f88AAfx/8wAAfH/8AAAcf/8AAAR//4AAAH//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA4AAAAAD4AAYAAP4AD8AA/4AH4AD/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/wAHgAH/H/GH/H8f/gf/Hx//h//HH//n//Ef/+H//B//4H//AAAAAAAAAAAAAAP//AAAAP//AAAAP//AAAAP/8AAAAP/2AAAAP/eAAAAAB+AAAAAD8AAAAAH4AAAAAPwAAAAAfgAAAAA/AAAAAB+AAAAAD8AAAAAH4AAAAAPwAAAAAfgAAAAA/AAAAAB+AAAAAD8AAAB/7x/4AH/7H/4Af/4f/4B//5//4H//h//4f/+B//4AAAAAAAAAAAAAD//wAAAD//wAAAj//gAADj/+AAAPj/5gAA/j/ngAD/gAfgAP/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/AA/AAf8AA8f8fwAAx/8fAAAH/8cAAAf/8QAAA//8AAAA//8AAAAAAAAAAAAAA//8D//g//8P/+I//8//44//0//j4//Y/+P4/94/4/4AH4AD/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/wAPwAH/AAPH/H8AAMf/HwAAB//HAAAH//EAAAH//AAAAH//AAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAGAAAAAAOAAAAAAeAAAAAA+AAAAAB+AAAAAD8AAAAAH4AAAAAPwAAAAAfgAAAAA/AAAAAB+AAAAAD8AAAAAH4AAAAAPwAAAAAfgAAAAA/AAAAAB8AAAAADx/4B/4HH/4H/4Mf/4f/4R//5//4H//h//4f/+B//4AAAAAAAAAAAAAD//wP/+D//w//4j//z//jj//T/+Pj/9j/4/j/3j/j/gAfgAP/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/AA/AAf8f+8f8fx/+x/8fH/+H/8cf/+f/8R//4f/8H//gf/8AAAAAAAAAAAAAA//8AAAA//8AAAI//8AAA4//0AAD4//YAAP4/94AA/4AH4AD/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/4APwAP/wAfgAf/gA/AA//AB+AB/+AD8AD/8AH4AH/wAPwAH/H/vH/H8f/sf/Hx//h//HH//n//Ef/+H//B//4H//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
// tiny font for percentage first char 48 6 by 8
|
||||
var fontTiny = atob("AH6BgYF+ACFB/wEBAGGDhYlxAEKBkZFuAAx0hP8EAPqRkZGOAH6RkZFOAICHmKDAAG6RkZFuAHKJiYl+AAAAAAAAAAAAAAAA");
|
||||
//var fontTiny = atob("AH6BgYF+ACFB/wEBAGGDhYlxAEKBkZFuAAx0hP8EAPqRkZGOAH6RkZFOAICHmKDAAG6RkZFuAHKJiYl+AAAAAAAAAAAAAAAA");
|
||||
// date font first char 48 12 by 15
|
||||
var fontDate = atob("AAAAAfv149wAeADwAeADwAeADvHr9+AAAAAAAAAAAAAAAAAAAAAAAAAPHn9/AAAAAAP0A9wweGDwweGDwweGDvAL8AAAAAAAAAAAgwOGDwweGDwweGDvHp98AAAAA/gB6AAwAGAAwAGAAwAGAPHj9/AAAAAfgF6BwweGDwweGDwweGDgHoB+AAAAAfv169wweGDwweGDwweGDgHoB+AAAAAAAAAAgAGAAwAGAAwAGAAvHh9/AAAAAfv169wweGDwweGDwweGDvHr9+AAAAAfgF6BwweGDwweGDwweGDvHr9+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
|
||||
|
@ -25,7 +25,7 @@ var imgSun = E.toArrayBuffer(atob("Ig8BwHf7D7Ac/MHD/z8wMP/PzMQ/8/M/D/z8z8QPf7f6A
|
|||
|
||||
// define icons
|
||||
var imgSep = E.toArrayBuffer(atob("BhsBAAAAAA///////////////AAAAAAA"));
|
||||
var imgPercent = E.toArrayBuffer(atob("BwcBuq7ffbqugA=="));
|
||||
//var imgPercent = E.toArrayBuffer(atob("BwcBuq7ffbqugA=="));
|
||||
var img24hr = E.toArrayBuffer(atob("EwgBj7vO53na73tcDtu9uDev7vA93g=="));
|
||||
var imgPM = E.toArrayBuffer(atob("EwgB+HOfdnPu1X3ar4dV9+q+/bfftg=="));
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"shortName":"93 Dub",
|
||||
"icon": "93dub.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"version":"0.06",
|
||||
"version": "0.07",
|
||||
"description": "Fan recreation of orviwan's 91 Dub app for the Pebble smartwatch. Uses assets from his 91-Dub-v2.0 repo",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Minor code improvements
|
||||
|
|
|
@ -25,7 +25,7 @@ declare global variables for the toggle button
|
|||
statuses; if you add an additional toggle button
|
||||
you should declare it and initiase it here */
|
||||
|
||||
var status_spk = {value: true};
|
||||
//var status_spk = {value: true};
|
||||
var status_face = {value: true};
|
||||
var status_iris_light = {value: false};
|
||||
var status_iris = {value: false};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "BLEcontroller",
|
||||
"name": "BLE Customisable Controller with Joystick",
|
||||
"shortName": "BLE Controller",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A configurable controller for BLE devices and robots, with a basic four direction joystick. Designed to be easy to customise so you can add your own menus.",
|
||||
"icon": "BLEcontroller.png",
|
||||
"tags": "tool,bluetooth",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Change log created
|
|
@ -0,0 +1,18 @@
|
|||
Tyreid
|
||||
|
||||
Tyreid is a Bluetooth war-driving app for the Bangle.js 2.
|
||||
|
||||
Menu options:
|
||||
- Start: This turns on the Bluetooth and starts logging Bluetooth packets with time, latitude, and longitude information to a CSV file.
|
||||
- Pause/Continue: These functions pause the capture and then allow it to resume.
|
||||
- Devices: When paused this menu option will display the MAC addresses of discovered Bluetooth devices. Selecting a device will then display the MAC, Manufacturer code, the time it was first seen, and the RSSI of the first sighting.
|
||||
- Marker: This command adds a 'marker' to the CSV log, which consists of the time and location information, but the Bluetooth packet information is replaced with the word MARKER. Markers can also be added by pressing the watch's button.
|
||||
- Exit: This exits the app.
|
||||
|
||||
The current number of discovered devices is displayed in the top left corner.
|
||||
This value is displayed in green when the GPS has a fix, or red otherwise.
|
||||
|
||||
To retrieve the CSV file, connect to the watch through the Espruino web IDE (https://www.espruino.com/ide/). From there the files stored on the watch can be downloaded by clicking the storage icon in the IDE's central column.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
E.toArrayBuffer(atob("MDACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAtAAAAAAAAAAAAAAB/QAAAAAAAAAAAAAD/0AAAAAAAAAAAAAH+9AAAAAAAAAAAAAPtfQAAAAAAAAAAAAudH0AAAAAAAAAAAB8tB9AAAAAAAAAAAD0tAfQAAAAAAAAAAHgtAH0AAAAAAAAAAPAtAB9AAAAAAAAAAtAtAAfQAAAAAAAAB8AtAAH0AAAAAAAAD0AtAAB9AAAAAAAALgAtAAAfQAAAAAAAfAAtAAAH0AAAAAAA9AAtAAAB9AAAAAAC4AAtAAAAfQAAAAADwAAtAAAALwAAAAAAQAAtAAAAvgAAAAAAAAAsAAAC9AAAAAAAAAAsAAAP0AAAAAAAAAAsAAB/AAAAAAAAAAAsAAH4AAAAAAAAAAAsAAvQAAAAAAAAAAAsAD9AAAAAAAAAAAAsAD0AAAAAAAAAAAAsAB8AAAAAAAAAAAAsAAtAAAAAAAAAAAAsAAPQAAAAAAAAAAAsAAHwAAAAAAAAAAAsAAC4AAAAAAAAAAAsAAA9AAAAAAAAAAAsAAAPAAAAAAAAAAAsAAAHgAAAAAAAAAA8AAAC0AAAAAAAAAA8AAAA8AAAAAAAAAA8AAAAEAAAAAAAAAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
|
@ -0,0 +1,14 @@
|
|||
{ "id": "Tyreid",
|
||||
"name": "Tyreid",
|
||||
"shortName":"Tyreid",
|
||||
"version":"0.01",
|
||||
"description": "Bluetooth war-driving app for Bangle.js 2",
|
||||
"icon": "small_logo.png",
|
||||
"tags": "",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"Tyreid.app.js","url":"app.js"},
|
||||
{"name":"Tyreid.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 8.2 KiB |
|
@ -1 +1,2 @@
|
|||
0.01: 1st ver, defining a common UI/UX
|
||||
0.02: Minor code improvements
|
||||
|
|
|
@ -5,7 +5,7 @@ identify device and dimensions
|
|||
max printable position max_x-1 i.e 239
|
||||
*/
|
||||
|
||||
var colbackg='#111111';//black
|
||||
//var colbackg='#111111';//black
|
||||
var colorange='#e56e06'; //RGB format rrggbb
|
||||
var v_color_lines=0xFFFF; //White hex format
|
||||
var v_color_b_area='#111111';
|
||||
|
@ -13,13 +13,13 @@ max printable position max_x-1 i.e 239
|
|||
var v_font1size=10; //out of quotes
|
||||
var v_font2size=12;
|
||||
var v_font_banner_size=30;
|
||||
var v_clicks=0;
|
||||
//var v_clicks=0;
|
||||
//pend identify widget area dinamically
|
||||
var v_model=process.env.BOARD;
|
||||
console.log("device="+v_model);
|
||||
|
||||
var x_max_screen=g.getWidth();//240;
|
||||
var y_max_screen=g.getHeight(); //240;
|
||||
//var y_max_screen=g.getHeight(); //240;
|
||||
var y_wg_bottom=g.getHeight()-25;
|
||||
var y_wg_top=25;
|
||||
if (v_model=='BANGLEJS') {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "UI4swatch",
|
||||
"name": "UI 4 swatch",
|
||||
"shortName": "UI 4 swatch",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A UI/UX for espruino smartwatches, displays dinamically calc. x,y coordinates.",
|
||||
"icon": "app.png",
|
||||
"tags": "Color,input,buttons,touch,UI",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Increased Legibility, GUI rework
|
||||
0.03: 13 new chords
|
||||
0.04: Minor code improvements
|
||||
|
|
|
@ -176,8 +176,6 @@ const b7 = [
|
|||
|
||||
|
||||
|
||||
var index = 0;
|
||||
var chords = [];
|
||||
var menu = {
|
||||
"" : { "title" : "Uke Chords" },
|
||||
"C" : function() { draw(cc); },
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "Uke",
|
||||
"name": "Uke Chords",
|
||||
"shortName":"Uke",
|
||||
"version":"0.03",
|
||||
"version": "0.04",
|
||||
"description": "Wrist mounted ukulele chords",
|
||||
"icon": "app.png",
|
||||
"tags": "uke, chords",
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.01: New Widget!
|
||||
0.01: New Clock Info!
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New Clock!
|
|
@ -0,0 +1,25 @@
|
|||
# Clock Name
|
||||
|
||||
More info on making Clock Faces: https://www.espruino.com/Bangle.js+Clock
|
||||
|
||||
Describe the Clock...
|
||||
|
||||
## Usage
|
||||
|
||||
Describe how to use it
|
||||
|
||||
## Features
|
||||
|
||||
Name the function
|
||||
|
||||
## Controls
|
||||
|
||||
Name the buttons and what they are used for
|
||||
|
||||
## Requests
|
||||
|
||||
Name who should be contacted for support/update requests
|
||||
|
||||
## Creator
|
||||
|
||||
Your name
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
// timeout used to update every minute
|
||||
let drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
let queueDraw = function() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
};
|
||||
|
||||
let draw = function() {
|
||||
// queue next draw in one minute
|
||||
queueDraw();
|
||||
// Work out where to draw...
|
||||
var x = g.getWidth()/2;
|
||||
var y = g.getHeight()/2;
|
||||
g.reset();
|
||||
// work out locale-friendly date/time
|
||||
var date = new Date();
|
||||
var timeStr = require("locale").time(date,1);
|
||||
var dateStr = require("locale").date(date);
|
||||
// draw time
|
||||
g.setFontAlign(0,0).setFont("Vector",48);
|
||||
g.clearRect(0,y-15,g.getWidth(),y+25); // clear the background
|
||||
g.drawString(timeStr,x,y);
|
||||
// draw date
|
||||
y += 35;
|
||||
g.setFontAlign(0,0).setFont("6x8");
|
||||
g.clearRect(0,y-4,g.getWidth(),y+4); // clear the background
|
||||
g.drawString(dateStr,x,y);
|
||||
};
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// draw immediately at first, queue update
|
||||
draw();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI({mode:"clock", remove:function() {
|
||||
// free any memory we allocated to allow fast loading
|
||||
}});
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
}
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "7chname",
|
||||
"name": "My clock human readable name",
|
||||
"shortName":"Short Name",
|
||||
"version":"0.01",
|
||||
"description": "A detailed description of my clock",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"7chname.app.js","url":"app.js"},
|
||||
{"name":"7chname.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -2,3 +2,4 @@
|
|||
0.02: Shows night time on the map (2022/12/28)
|
||||
0.03: Add 1 minute timer with upper taps (2023/01/05)
|
||||
1.00: Page to set up custom time zones (2023/01/06)
|
||||
1.01: Minor code improvements
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
function getImg() {
|
||||
return require("heatshrink").decompress(atob("2FRgP/ABnxBRP5BJH+gEfBZHghnAv4JFmA+Bj0PBIn3//4h3An4oDAQJWEEIf8AwMEuFOCofAh/QjAWEg4VEwEAnw2DDoKEHEYPwAoUBmgrDhgUHS4XgAwUD/gVC/g+FAAZgEwEf4YqC/EQFQ4NDFgV/4Z3C/EcCo1974VCLAV/V4d7Co9/Co0PCoX+vk4Ko/HCosCRYX5nwTFkEAr/nCokICoL+B/aCGCoMHCoq3EdoraGCosPz4HBcILEJCocBwEHOwQrIgQrHgoHCFYMEgwVJYoMBsEnCofAnkMNQJXH4D4EbQMPkF/xwrEj+/HIkAoAVDj8QueHCoorDCoUDLwd96J0BKwgrHh4VDv+9CosDx6QCCo4HB//8VwvvXgQVDJIYSBCo/sBwaZBgF/NoYVHgH8V4qYDAwUYlAVFEYbFDDgwAGConogf9Zg8DCpP4cIh0Dg0BGAgVE+gVIgUA+AVI+wVE/xAEh5HDEgn+CpEAbgJCCHQoVBn4VJ/ED4ANDAAQVJ4EPPQPAt4VF4BeDColgj/8h/gFYwJBCpF//k//ANDCAYVIcgP+CpH/54VHCAIVB/4VIwYECCocIAwIVBx4VG9+AMITbCYAYJB34VG/UAj4VI7/9Cgw9CJYXAmBtDMAQsIfYhvCCofyvywGB4QFFgYGC/d+agYVLSgf8+ArG/APBD4QVBgh0CAwNwv/fCo4PCCo94s7VDCohnDAoI7Enlv8BZECoRCDAggAB3/3/gzDMAIVFY4IVE4IPBOoZ9DCpXwCoMvCqKxB//3bYywD4BtFAAPfDooVFFYIVGw4VFB4KZFngNE/uPCovgFYgEBuK+Fg4zFCoIrFCovwgQVF+AVFgPxEYzFEbgQVD4EDCoozBYogVCgYVE8bpGCo4HDCoPzBgoVIL4fAg4MGgAIHCofgCszND8BOHK4x2BCofwXgv4h6vGCps/Co6uDAA/7RgIjDDwTaDABPA//9FaAtDCop0FC5YVDLwoAH8//94GD/wVNCYKNECpwPBQggVPNggVBNp4VFFZwAGCquHCqnzCB4"));
|
||||
}
|
||||
var IMAGEWIDTH = 176;
|
||||
//var IMAGEWIDTH = 176;
|
||||
var IMAGEHEIGHT = 81;
|
||||
|
||||
Graphics.prototype.setFontMichroma36 = function() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "a_clock_timer",
|
||||
"name": "A Clock with Timer",
|
||||
"version": "1.00",
|
||||
"version": "1.01",
|
||||
"description": "A Clock with Timer, Map and Time Zones",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-1.png"}],
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: Initial version
|
||||
0.02: Add settings page; Add line break to update message
|
|
@ -1,10 +1,13 @@
|
|||
# a_dndtoggle - Toggle Quiet Mode of the watch
|
||||
|
||||
When Quiet mode is off, just start this app to set quiet mode. Start it again to turn off quiet mode.
|
||||
|
||||
Use the app settings to choose which quiet mode you prefer ("Alarms" or "Silent"). Default is "Silent".
|
||||
|
||||
Work in progress.
|
||||
|
||||
#ToDo
|
||||
Settings page, current status indicator.
|
||||
Current status indicator
|
||||
|
||||
## Creator
|
||||
|
||||
|
|
|
@ -6,11 +6,14 @@ let current = 0|bSettings.quiet;
|
|||
//1 alarms
|
||||
//2 silent
|
||||
|
||||
const dndSettings =
|
||||
require('Storage').readJSON("a_dndtoggle.settings.json", true) || {};
|
||||
|
||||
console.log("old: " + current);
|
||||
|
||||
switch (current) {
|
||||
case 0:
|
||||
bSettings.quiet = 2;
|
||||
bSettings.quiet = dndSettings.mode || 2;
|
||||
Bangle.buzz();
|
||||
setTimeout('Bangle.buzz();',500);
|
||||
break;
|
||||
|
@ -29,7 +32,7 @@ switch (current) {
|
|||
|
||||
console.log("new: " + bSettings.quiet);
|
||||
|
||||
E.showMessage(modeNames[current] + " -> " + modeNames[bSettings.quiet]);
|
||||
E.showMessage(modeNames[current] + " -> \n" + modeNames[bSettings.quiet]);
|
||||
setTimeout('exitApp();', 2000);
|
||||
|
||||
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
"id": "a_dndtoggle",
|
||||
"name": "a_dndtoggle - Toggle Quiet Mode of the watch",
|
||||
"shortName": "A_DND Toggle",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Toggle Quiet Mode of the watch just by starting this app.",
|
||||
"icon": "a_dndtoggle.png",
|
||||
"type": "app",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"data" : [
|
||||
{"name":"a_dndtoggle.settings.json"}
|
||||
],
|
||||
"storage": [
|
||||
{"name":"a_dndtoggle.app.js","url":"a_dndtoggle.app.js"},
|
||||
{"name":"a_dndtoggle.settings.js","url":"settings.js"},
|
||||
{"name":"a_dndtoggle.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"readme": "README.md"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
(function(back) {
|
||||
|
||||
const settings =
|
||||
require('Storage').readJSON("a_dndtoggle.settings.json", true) || {};
|
||||
|
||||
function updateSettings() {
|
||||
require('Storage').writeJSON("a_dndtoggle.settings.json", settings);
|
||||
}
|
||||
|
||||
function buildMainMenu(){
|
||||
// 0-Noisy is only a placeholder so that the other values map to the Bangle quiet mode options
|
||||
const modes = [/*LANG*/"Noisy",/*LANG*/"Alarms",/*LANG*/"Silent"];
|
||||
let mainmenu = {
|
||||
'': { 'title': 'A_DND Toggle' },
|
||||
'< Back': back,
|
||||
/*LANG*/"Quiet Mode": {
|
||||
value: settings.mode || 2,
|
||||
min: 1, // don't allow choosing 0-Noisy
|
||||
max: modes.length - 1,
|
||||
format: v => modes[v],
|
||||
onchange: v => {
|
||||
settings.mode = v;
|
||||
updateSettings();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return mainmenu;
|
||||
}
|
||||
|
||||
E.showMenu(buildMainMenu());
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
1.00: Release (2021/12/01)
|
||||
1.01: Grey font when timer is frozen (2021/12/04)
|
||||
1.02: Force light theme, since the app is not designed for dark theme (2022/12/28)
|
||||
1.03: Minor code improvements
|
||||
|
|
|
@ -124,7 +124,6 @@ Bangle.on('swipe',(swiperight, swipedown)=>{
|
|||
}
|
||||
});
|
||||
|
||||
var drawTimeout;
|
||||
var showInstructions = true;
|
||||
|
||||
function draw() {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id":"a_speech_timer",
|
||||
"name":"Speech Timer",
|
||||
"icon": "app.png",
|
||||
"version":"1.02",
|
||||
"version": "1.03",
|
||||
"description": "A timer designed to help keeping your speeches and presentations to time.",
|
||||
"tags": "tool,timer",
|
||||
"readme":"README.md",
|
||||
|
|
|
@ -12,3 +12,4 @@
|
|||
0.12: Actual pixels as of 29th Nov 2021
|
||||
0.13: Bangle.js 2: Use setUI to add software back button
|
||||
0.14: Add automatic translation of more strings
|
||||
0.15: Minor code improvements
|
||||
|
|
|
@ -20,11 +20,11 @@ function getVersion(name,file) {
|
|||
return v?(name+" "+(v?"v"+v:"Unknown")):"NO "+name;
|
||||
}
|
||||
|
||||
var versions = [
|
||||
/*var versions = [
|
||||
getVersion("Bootloader","boot.info"),
|
||||
getVersion("Launcher","launch.info"),
|
||||
getVersion("Settings","setting.info")
|
||||
];
|
||||
];*/
|
||||
var logo = E.toArrayBuffer(atob("PBwBAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAD/w+AAAAQAHA4hAAAAQAMAMhAAAAQAYBmhAAAAQAYBGiAAAAQAQCD/H74+R4wGDhoKJCSEwEDgoKJCT8wFDgoKJCSAwHDhoKJCSEQHj/H6I+R4YHmAAAACAAYEGAAABCAAMEMAAAA8AAHA4AAAAAAAD/wAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAB/gAAAAAAAB/g"));
|
||||
|
||||
var imageTop = 24;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "about",
|
||||
"name": "About",
|
||||
"version": "0.14",
|
||||
"version": "0.15",
|
||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system",
|
||||
|
|
|
@ -3,3 +3,5 @@
|
|||
Calculate the time moving in graph display
|
||||
Trigger on 1.04g now, and record 10 samples before trigger
|
||||
0.03: Bangle.js 2 compatibility
|
||||
0.04: Minor code improvements
|
||||
0.05: Can record 100hz, z-axis color changed to yellow, autosave to file, no need select, delete old records
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
var acc;
|
||||
//var acc;
|
||||
var HZ = 100;
|
||||
var SAMPLES = 5*HZ; // 5 seconds
|
||||
var SCALE = 5000;
|
||||
var THRESH = 1.04;
|
||||
var SAMPLES = 6 * HZ; // 6 seconds
|
||||
var SCALE = 2000;
|
||||
var THRESH = 1.4;
|
||||
var accelx = new Int16Array(SAMPLES);
|
||||
var accely = new Int16Array(SAMPLES); // North
|
||||
var accelz = new Int16Array(SAMPLES); // Into clock face
|
||||
var timestep = new Int16Array(SAMPLES); // Into clock face
|
||||
var accelIdx = 0;
|
||||
var lastAccel;
|
||||
function accelHandlerTrigger(a) {"ram"
|
||||
var timestep_start = 0;
|
||||
|
||||
function accelHandlerTrigger(a) {
|
||||
"ram"
|
||||
if (a.mag * 2 > THRESH) { // *2 because 8g mode
|
||||
tStart = getTime();
|
||||
timestep_start = getTime();
|
||||
g.drawString("Recording", g.getWidth() / 2, g.getHeight() / 2, 1);
|
||||
Bangle.removeListener('accel', accelHandlerTrigger);
|
||||
Bangle.on('accel', accelHandlerRecord);
|
||||
|
@ -21,14 +25,19 @@ function accelHandlerTrigger(a) {"ram"
|
|||
lastAccel.push(a);
|
||||
}
|
||||
}
|
||||
function accelHandlerRecord(a) {"ram"
|
||||
|
||||
function accelHandlerRecord(a) {
|
||||
"ram"
|
||||
var i = accelIdx++;
|
||||
accelx[i] = a.x*SCALE*2;
|
||||
accelx[i] = a.x * SCALE * 2; // *2 because of 8g mode
|
||||
accely[i] = -a.y * SCALE * 2;
|
||||
accelz[i] = a.z * SCALE * 2;
|
||||
timestep[i] = (getTime() - timestep_start) * 1000;
|
||||
if (accelIdx >= SAMPLES) recordStop();
|
||||
}
|
||||
function recordStart() {"ram"
|
||||
|
||||
function recordStart() {
|
||||
"ram"
|
||||
Bangle.setLCDTimeout(0); // force LCD on
|
||||
accelIdx = 0;
|
||||
lastAccel = [];
|
||||
|
@ -44,7 +53,8 @@ function recordStart() {"ram"
|
|||
}
|
||||
|
||||
|
||||
function recordStop() {"ram"
|
||||
function recordStop() {
|
||||
"ram"
|
||||
//console.log("Length:",getTime()-tStart);
|
||||
Bangle.setPollInterval(80); // default poll interval
|
||||
Bangle.accelWr(0x18, 0b01101100); // off, +-4g
|
||||
|
@ -52,12 +62,15 @@ function recordStop() {"ram"
|
|||
Bangle.accelWr(0x18, 0b11101100); // +-4g
|
||||
Bangle.removeListener('accel', accelHandlerRecord);
|
||||
E.showMessage("Finished");
|
||||
showData();
|
||||
showData(true);
|
||||
}
|
||||
|
||||
|
||||
function showData() {
|
||||
function showData(save_file) {
|
||||
g.clear(1);
|
||||
let csv_files_N = require("Storage").list(/^acc.*\.csv$/).length;
|
||||
let w_full = g.getWidth();
|
||||
let h = g.getHeight();
|
||||
var w = g.getWidth() - 20; // width
|
||||
var m = g.getHeight() / 2; // middle
|
||||
var s = 12; // how many pixels per G
|
||||
|
@ -71,7 +84,7 @@ function showData() {
|
|||
for (var i = 0; i < SAMPLES; i++)
|
||||
g.lineTo(10 + i * w / SAMPLES, m - a[i] * s / SCALE);
|
||||
}
|
||||
g.setColor("#0000ff");
|
||||
g.setColor("#FFFA5F");
|
||||
plot(accelz);
|
||||
g.setColor("#ff0000");
|
||||
plot(accelx);
|
||||
|
@ -80,28 +93,31 @@ function showData() {
|
|||
|
||||
// work out stats
|
||||
var maxAccel = 0;
|
||||
var tStart = SAMPLES, tEnd = 0;
|
||||
var vel = 0, maxVel = 0;
|
||||
var tStart = SAMPLES,
|
||||
tEnd = 0;
|
||||
var max_YZ = 0;
|
||||
for (var i = 0; i < SAMPLES; i++) {
|
||||
var a = accely[i]/SCALE;
|
||||
var a = Math.abs(accely[i] / SCALE);
|
||||
let a_yz = Math.sqrt(Math.pow(accely[i] / SCALE, 2) + Math.pow(accelz[i] / SCALE, 2));
|
||||
if (a > 0.1) {
|
||||
if (i < tStart) tStart = i;
|
||||
if (i > tEnd) tEnd = i;
|
||||
}
|
||||
if (a > maxAccel) maxAccel = a;
|
||||
vel += a/HZ;
|
||||
if (vel>maxVel) maxVel=vel;
|
||||
if (a_yz > max_YZ) max_YZ = a_yz;
|
||||
}
|
||||
g.reset();
|
||||
g.setFont("6x8").setFontAlign(1, 0);
|
||||
g.drawString("Max Y Accel: "+maxAccel.toFixed(2)+" g",g.getWidth()-14,g.getHeight()-50);
|
||||
g.drawString("Max Y Vel: "+maxVel.toFixed(2)+" m/s",g.getWidth()-14,g.getHeight()-40);
|
||||
g.drawString("Max X Accel: " + maxAccel.toFixed(2) + " g", g.getWidth() - 14, g.getHeight() - 50);
|
||||
g.drawString("Max YZ Accel: " + max_YZ.toFixed(2) + " g", g.getWidth() - 14, g.getHeight() - 40);
|
||||
g.drawString("Time moving: " + (tEnd - tStart) / HZ + " s", g.getWidth() - 14, g.getHeight() - 30);
|
||||
//console.log("End Velocity "+vel);
|
||||
g.setFont("6x8", 2).setFontAlign(0, 0);
|
||||
g.drawString("File num: " + (csv_files_N + 1), w_full / 2, h - 20);
|
||||
g.setFont("6x8").setFontAlign(0, 0, 1);
|
||||
g.drawString("FINISH", g.getWidth() - 4, g.getHeight() / 2);
|
||||
setWatch(function() {
|
||||
showMenu();
|
||||
if (save_file) showSaveMenu(); // when select only plot, don't ask for save option
|
||||
else showMenu();
|
||||
}, global.BTN2 ? BTN2 : BTN);
|
||||
}
|
||||
|
||||
|
@ -133,20 +149,23 @@ function showMenu() {
|
|||
E.showMenu();
|
||||
if (accelIdx == 0) countDown();
|
||||
else E.showPrompt("Overwrite Recording?").then(ok => {
|
||||
if (ok) countDown(); else showMenu();
|
||||
if (ok) countDown();
|
||||
else showMenu();
|
||||
});
|
||||
},
|
||||
"Plot": function() {
|
||||
E.showMenu();
|
||||
if (accelIdx) showData();
|
||||
if (accelIdx) showData(false);
|
||||
else E.showAlert("No Data").then(() => {
|
||||
showMenu();
|
||||
});
|
||||
},
|
||||
"Save" : function() {
|
||||
"Storage": function() {
|
||||
E.showMenu();
|
||||
if (accelIdx) showSaveMenu();
|
||||
else E.showAlert("No Data").then(()=>{
|
||||
if (require("Storage").list(/^acc.*\.csv$/).length)
|
||||
StorageMenu();
|
||||
else
|
||||
E.showAlert("No Data").then(() => {
|
||||
showMenu();
|
||||
});
|
||||
},
|
||||
|
@ -158,22 +177,77 @@ function showMenu() {
|
|||
}
|
||||
|
||||
function showSaveMenu() {
|
||||
var menu = {
|
||||
"" : { title : "Save" }
|
||||
};
|
||||
[1,2,3,4,5,6].forEach(i=>{
|
||||
var fn = "accelrec."+i+".csv";
|
||||
var exists = require("Storage").read(fn)!==undefined;
|
||||
menu["Recording "+i+(exists?" *":"")] = function() {
|
||||
var csv = "";
|
||||
E.showPrompt("Save recording?").then(ok => {
|
||||
if (ok)
|
||||
SaveFile();
|
||||
else
|
||||
showMenu();
|
||||
});
|
||||
}
|
||||
|
||||
function SaveFile() {
|
||||
let csv_files_N = require("Storage").list(/^acc.*\.csv$/).length;
|
||||
//if (csv_files_N > 20)
|
||||
// E.showMessage("Storage is full");
|
||||
// showMenu();
|
||||
let csv = "";
|
||||
let date = new Date();
|
||||
let fn = "accelrec_" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "_" + (csv_files_N + 1) + ".csv";
|
||||
E.showMessage("Saveing to file \n" + fn);
|
||||
for (var i = 0; i < SAMPLES; i++)
|
||||
csv += `${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`;
|
||||
csv += `${timestep[i]},${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`;
|
||||
require("Storage").write(fn, csv);
|
||||
showMenu();
|
||||
}
|
||||
|
||||
//Show saved csv files
|
||||
function StorageMenu() {
|
||||
var menu = {
|
||||
"": {
|
||||
title: "Storage"
|
||||
}
|
||||
};
|
||||
let csv_files = require("Storage").list(/^acc.*\.csv$/);
|
||||
var inx = 0;
|
||||
csv_files.forEach(fn => {
|
||||
inx++;
|
||||
menu[inx + ". " + fn] = function() {
|
||||
StorageOptions(fn);
|
||||
};
|
||||
});
|
||||
menu["< Back"] = function() {showMenu();};
|
||||
menu["< Back"] = function() {
|
||||
showMenu();
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function StorageOptions(file) {
|
||||
let menu = {
|
||||
"": {
|
||||
title: "Options"
|
||||
},
|
||||
"Plot": function() {
|
||||
showMenu();
|
||||
},
|
||||
"Delete": function() {
|
||||
E.showMenu();
|
||||
E.showPrompt("Delete recording?").then(ok => {
|
||||
if (ok)
|
||||
DeleteRecord(file);
|
||||
else
|
||||
StorageMenu();
|
||||
});
|
||||
},
|
||||
"< Back": function() {
|
||||
StorageMenu();
|
||||
},
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function DeleteRecord(file) {
|
||||
E.showMessage("Deleteing file \n" + file);
|
||||
require("Storage").erase(file);
|
||||
StorageMenu();
|
||||
}
|
||||
showMenu();
|
|
@ -37,7 +37,7 @@ function getData() {
|
|||
</div>`;
|
||||
promise = promise.then(function() {
|
||||
document.querySelector(`.btn[fn='${fn}'][act='save']`).addEventListener("click", function() {
|
||||
Util.saveCSV(fn.slice(0,-4), "X,Y,Z\n"+fileData[fn]);
|
||||
Util.saveCSV(fn.slice(0,-4), "Time,X,Y,Z\n"+fileData[fn]);
|
||||
});
|
||||
document.querySelector(`.btn[fn='${fn}'][act='delete']`).addEventListener("click", function() {
|
||||
Util.showModal("Deleting...");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "accelrec",
|
||||
"name": "Acceleration Recorder",
|
||||
"shortName": "Accel Rec",
|
||||
"version": "0.03",
|
||||
"version": "0.05",
|
||||
"description": "This app puts the Bangle's accelerometer into 100Hz mode and reads 2 seconds worth of data after movement starts. The data can then be exported back to the PC.",
|
||||
"icon": "app.png",
|
||||
"tags": "",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial release.
|
|
@ -0,0 +1,19 @@
|
|||
# Accerleration Data Provider
|
||||
|
||||
This app provides acceleration data via Bluetooth, which can be used in Gadgetbridge.
|
||||
|
||||
## Usage
|
||||
|
||||
This boot code runs in the background and has no user interface.
|
||||
Currently this app is used to enable Sleep as Android tracking for your Banglejs using Gadgetbridge.
|
||||
|
||||
**Please Note**: This app only listens to "accel" events and sends them to your phone using Bluetooth.
|
||||
|
||||
## Creator
|
||||
|
||||
[Another Stranger](https://github.com/anotherstranger)
|
||||
|
||||
## Aknowledgements
|
||||
|
||||
Special thanks to [José Rebelo](https://github.com/joserebelo) and [Rob Pilling](https://github.com/bobrippling)
|
||||
for their Code Reviews and guidance.
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,55 @@
|
|||
(() => {
|
||||
/**
|
||||
* Sends a message to the gadgetbridge via Bluetooth.
|
||||
* @param {Object} message - The message to be sent.
|
||||
*/
|
||||
function gbSend(message) {
|
||||
try {
|
||||
Bluetooth.println("");
|
||||
Bluetooth.println(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
console.error("Failed to send message via Bluetooth:", error);
|
||||
}
|
||||
}
|
||||
|
||||
var max_acceleration = { x: 0, y: 0, z: 0, diff: 0, td: 0, mag: 0 };
|
||||
var hasData = false;
|
||||
|
||||
/**
|
||||
* Updates the maximum acceleration if the current acceleration is greater.
|
||||
* @param {Object} accel - The current acceleration object with x, y, z, and mag properties.
|
||||
*/
|
||||
function updateAcceleration(accel) {
|
||||
hasData = true;
|
||||
var current_max_raw = accel.mag;
|
||||
var max_raw = max_acceleration.mag;
|
||||
|
||||
if (current_max_raw > max_raw) {
|
||||
max_acceleration = accel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the acceleration data and sends it to gadgetbridge.
|
||||
* Resets the maximum acceleration.
|
||||
* Note: If your interval setting is too short, the last value gets sent again.
|
||||
*/
|
||||
function sendAccelerationData() {
|
||||
var accel = hasData ? max_acceleration : Bangle.getAccel();
|
||||
|
||||
var update_data = {
|
||||
t: "accel", accel: accel
|
||||
};
|
||||
gbSend(update_data);
|
||||
|
||||
max_acceleration = { x: 0, y: 0, z: 0, mag: 0, diff: 0, td: 0 };
|
||||
hasData = false;
|
||||
}
|
||||
|
||||
var config = require("Storage").readJSON("accelsender.json") || {};
|
||||
if (config.enabled) { // Gadgetbridge needs to enable and disable tracking by writing {enabled: true} to "accelsender.json" and reloading
|
||||
setInterval(sendAccelerationData, config.interval);
|
||||
Bangle.on("accel", updateAcceleration); // Log all acceleration events
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1 @@
|
|||
(()=>{function e(a){c=!0;a.mag>b.mag&&(b=a)}function f(){var a={t:"accel",accel:c?b:Bangle.getAccel()};try{Bluetooth.println(""),Bluetooth.println(JSON.stringify(a))}catch(g){console.error("Failed to send message via Bluetooth:",g)}b={x:0,y:0,z:0,mag:0,diff:0,td:0};c=!1}var b={x:0,y:0,z:0,diff:0,td:0,mag:0},c=!1,d=require("Storage").readJSON("accelsender.json")||{};d.enabled&&(setInterval(f,d.interval),Bangle.on("accel",e))})();
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enabled": false,
|
||||
"interval": 10000
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"id": "accelsender",
|
||||
"name": "Acceleration Data Provider",
|
||||
"shortName": "Accel Data Provider",
|
||||
"version": "0.01",
|
||||
"description": "This app sends accelerometer and heart rate data from your Bangle.js via Bluetooth.",
|
||||
"icon": "bluetooth.png",
|
||||
"type": "bootloader",
|
||||
"tags": "accel",
|
||||
"supports": [
|
||||
"BANGLEJS",
|
||||
"BANGLEJS2"
|
||||
],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{
|
||||
"name": "accelsender.boot.js",
|
||||
"url": "boot.min.js"
|
||||
}
|
||||
],
|
||||
"data": [
|
||||
{
|
||||
"name": "accelsender.json",
|
||||
"url": "config.json"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
0.01: New App!
|
||||
0.02: Faster maze generation
|
||||
0.03: Avoid clearing bottom widgets
|
||||
0.04: Minor code improvements
|
||||
0.05: Minor code improvements
|
||||
|
|
|
@ -54,7 +54,7 @@ function Maze(n) {
|
|||
// Abort if BTN1 pressed [grace period for menu]
|
||||
// (for some reason setWatch() fails inside constructor)
|
||||
if (ngroups<n*n-16 && digitalRead(BTN1)) {
|
||||
aborting = true;
|
||||
//aborting = true;
|
||||
return;
|
||||
}
|
||||
from_group = to_group = -1;
|
||||
|
@ -206,7 +206,7 @@ function Maze(n) {
|
|||
return false;
|
||||
};
|
||||
this.tick = function() {
|
||||
accel = Bangle.getAccel();
|
||||
let accel = Bangle.getAccel();
|
||||
if (this.ball_x%this.wall_length) {
|
||||
this.try_move_horizontally(accel.x);
|
||||
} else if (this.ball_y%this.wall_length) {
|
||||
|
@ -243,7 +243,7 @@ function timeToText(t) { // Courtesy of stopwatch app
|
|||
return text;
|
||||
}
|
||||
|
||||
let aborting = false;
|
||||
//let aborting = false;
|
||||
let start_time = 0;
|
||||
let duration = 0;
|
||||
let maze=null;
|
||||
|
@ -261,13 +261,13 @@ Bangle.drawWidgets();
|
|||
Bangle.setLocked(false);
|
||||
Bangle.setLCDTimeout(0);
|
||||
E.showMenu(mazeMenu);
|
||||
let maze_interval = setInterval(
|
||||
/*let maze_interval =*/ setInterval(
|
||||
function() {
|
||||
if (maze) {
|
||||
if (digitalRead(BTN1) || maze.status==STATUS_ABORTED) {
|
||||
maze = null;
|
||||
start_time = duration = 0;
|
||||
aborting = false;
|
||||
//aborting = false;
|
||||
setTimeout(function() {E.showMenu(mazeMenu); }, 100);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "acmaze",
|
||||
"name": "AccelaMaze",
|
||||
"shortName":"AccelaMaze",
|
||||
"version":"0.03",
|
||||
"version": "0.05",
|
||||
"description": "Tilt the watch to roll a ball through a maze.",
|
||||
"icon": "app.png",
|
||||
"tags": "game",
|
||||
|
|
|
@ -7,3 +7,5 @@
|
|||
0.07: Added settings to be able to hide line1 and line2 so there is no visible widget
|
||||
0.08: Fixed zero steps issue caused by 0.07
|
||||
0.09: Prettied up user interface, decluttered graphs
|
||||
0.10: Minor code improvements
|
||||
0.11: Minor code improvements
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
function drawMenu() {
|
||||
var x = 100;
|
||||
var y = 24;
|
||||
var stps ="-";
|
||||
//var stps ="-";
|
||||
var y_inc = 25;
|
||||
|
||||
g.clear();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "activepedom",
|
||||
"name": "Active Pedometer",
|
||||
"shortName": "Active Pedometer",
|
||||
"version": "0.09",
|
||||
"version": "0.11",
|
||||
"description": "(NOT RECOMMENDED) Pedometer that filters out arm movement and displays a step goal progress. Steps are saved to a daily file and can be viewed as graph. The `Health` app now provides step logging and graphs.",
|
||||
"icon": "app.png",
|
||||
"tags": "outdoors,widget",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
var startTimeStep = new Date(); //set start time
|
||||
var stopTimeStep = 0; //Time after one step
|
||||
var timerResetActive = 0; //timer to reset active
|
||||
var timerStoreData = 0; //timer to store data
|
||||
//var timerStoreData = 0; //timer to store data
|
||||
var steps = 0; //steps taken
|
||||
var stepsCounted = 0; //active steps counted
|
||||
var active = 0; //x steps in y seconds achieved
|
||||
|
@ -32,10 +32,10 @@
|
|||
}
|
||||
|
||||
function storeData() {
|
||||
now = new Date();
|
||||
month = now.getMonth() + 1; //month is 0-based
|
||||
let now = new Date();
|
||||
let month = now.getMonth() + 1; //month is 0-based
|
||||
if (month < 10) month = "0" + month; //leading 0
|
||||
filename = filename = "activepedom" + now.getFullYear() + month + now.getDate() + ".data"; //new file for each day
|
||||
let filename = "activepedom" + now.getFullYear() + month + now.getDate() + ".data"; //new file for each day
|
||||
dataFile = s.open(filename,"a");
|
||||
if (dataFile) { //check if filen already exists
|
||||
if (dataFile.getLength() == 0) {
|
||||
|
@ -223,6 +223,7 @@
|
|||
});
|
||||
|
||||
// Read data from file and set variables
|
||||
{ // new scope ensures pedomData gets freed
|
||||
let pedomData = s.readJSON(PEDOMFILE,1);
|
||||
if (pedomData) {
|
||||
if (pedomData.lastUpdate) lastUpdate = new Date(pedomData.lastUpdate);
|
||||
|
@ -231,10 +232,10 @@
|
|||
stepsTooLong = pedomData.stepsTooLong;
|
||||
stepsOutsideTime = pedomData.stepsOutsideTime;
|
||||
}
|
||||
pedomdata = 0; //reset pedomdata to save memory
|
||||
}
|
||||
|
||||
setStepSensitivity(setting('stepSensitivity')); //set step sensitivity (80 is standard, 400 is muss less sensitive)
|
||||
timerStoreData = setInterval(storeData, storeDataInterval); //store data regularly
|
||||
/*timerStoreData =*/ setInterval(storeData, storeDataInterval); //store data regularly
|
||||
//Add widget, use: WIDGETS.activepedom.getSteps() inside another App to return todays step count
|
||||
WIDGETS["activepedom"]={area:"tl",width:width,draw:draw, getSteps:()=>stepsCounted};
|
||||
})();
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
0.09: New app screen (instead of showing settings or the alert) and some optimisations
|
||||
0.10: Add software back button via setUI
|
||||
0.11: Add setting to unlock screen
|
||||
0.12: Fix handling that dates can be given as ms since epoch.
|
||||
|
|
|
@ -29,16 +29,13 @@ exports.loadData = function () {
|
|||
dismissDate: new Date(1970),
|
||||
pauseDate: new Date(1970),
|
||||
},
|
||||
|
||||
require("Storage").readJSON("activityreminder.data.json") || {});
|
||||
|
||||
if (typeof (data.stepsDate) == "string")
|
||||
data.stepsDate = new Date(data.stepsDate);
|
||||
if (typeof (data.okDate) == "string")
|
||||
data.okDate = new Date(data.okDate);
|
||||
if (typeof (data.dismissDate) == "string")
|
||||
data.dismissDate = new Date(data.dismissDate);
|
||||
if (typeof (data.pauseDate) == "string")
|
||||
data.pauseDate = new Date(data.pauseDate);
|
||||
data.stepsDate = new Date(typeof data.stepsDate === 'string' ? data.stepsDate : data.stepsDate.ms);
|
||||
data.okDate = new Date(typeof data.okDate === 'string' ? data.okDate : data.okDate.ms);
|
||||
data.dismissDate = new Date(typeof data.dismissDate === 'string' ? data.dismissDate : data.dismissDate.ms);
|
||||
data.pauseDate = new Date(typeof data.pauseDate === 'string' ? data.pauseDate : data.pauseDate.ms);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
"name": "Activity Reminder",
|
||||
"shortName":"Activity Reminder",
|
||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||
"version":"0.11",
|
||||
"version":"0.12",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,activity",
|
||||
"tags": "tool,activity,health",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Tell clock widgets to hide.
|
||||
0.04: Swipe down to see widgets, step counter now just uses getHealthStatus
|
||||
0.05: Report latest HRM rather than HRM 10 minutes ago (fix #2395)
|
||||
0.06: Use watch temperature
|
|
@ -3,8 +3,7 @@
|
|||
<img src="https://user-images.githubusercontent.com/2981891/175355586-1dfc0d66-6555-4385-b124-1605fdb71a11.jpg" width="250" />
|
||||
|
||||
An over-engineered clock inspired by Casio watches.<br/>
|
||||
It has a dedicated timer, a scratchpad and can display the weather condition 4 days ahead.<br/>
|
||||
It uses a <a target="_blank" href="https://dotgreg.github.io/advCasioBangleClock/">custom web app</a> to update its content.<br/>
|
||||
It has a dedicated timer, a scratchpad and displays the current temperature.<br/>
|
||||
Forked from the awesome Cassio Watch.<br/>
|
||||
|
||||
## Todo
|
||||
|
@ -21,7 +20,7 @@ Forked from the awesome Cassio Watch.<br/>
|
|||
- Footsteps
|
||||
- Battery
|
||||
- Simple Timer embedded
|
||||
- Weather forecast (7 days)
|
||||
- Current temperature
|
||||
- Scratchpad
|
||||
|
||||
## Screenshots
|
||||
|
@ -36,14 +35,6 @@ Web interface to update weather & scratchpad <br/>
|
|||
<img src="https://user-images.githubusercontent.com/2981891/175519121-851bb209-7192-40db-a014-490c344f7597.jpg" width="250" />
|
||||
|
||||
## Usage
|
||||
### How to update the tasks list / weather
|
||||
- you will need a <a target="_blank" href="https://openweathermap.org/price#weather">free openweathermap.org api key</a>.
|
||||
- go to https://dotgreg.github.io/advCasioBangleClock/
|
||||
- Alternatively you can install it on your own server/heroku/service/github pages, the web-app code is <a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock/tree/master/web-app">here</a>
|
||||
- fill the location and the api key (it will be saved on your browser, no need to do it each time)
|
||||
- edit the scratchpad with what you want
|
||||
- click on sync
|
||||
- reload your clock!
|
||||
|
||||
### How to start/stop the timer
|
||||
- swipe up : add time (+5min)
|
||||
|
|
|
@ -88,9 +88,9 @@ function drawRocket() {
|
|||
|
||||
function getTemperature(){
|
||||
try {
|
||||
var weatherJson = storage.readJSON('weather.json');
|
||||
var weather = weatherJson.weather;
|
||||
return Math.round(weather.temp-273.15);
|
||||
var temperature = E.getTemperature()
|
||||
var formatted = require("locale").temp(temperature).replace(/[^\d-]/g, '');
|
||||
return formatted;
|
||||
|
||||
} catch(ex) {
|
||||
print(ex)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{ "id": "advcasio",
|
||||
"name": "Advanced Casio Clock",
|
||||
"shortName":"advcasio",
|
||||
"version":"0.05",
|
||||
"description": "An over-engineered clock inspired by Casio watches. It has a 4 days weather, a timer using swipe and a scratchpad. Can be updated using a dedicated webapp.",
|
||||
"version":"0.06",
|
||||
"description": "An over-engineered clock inspired by Casio watches. It has current temperature, a timer using swipe and a scratchpad. Can be updated using a dedicated webapp.",
|
||||
"icon": "app.png",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
|
@ -18,8 +18,5 @@
|
|||
"storage": [
|
||||
{"name":"advcasio.app.js","url":"app.js"},
|
||||
{"name":"advcasio.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{ "name": "advcasio.data.json", "url": "data.json", "storageFile": true }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -14,3 +14,4 @@
|
|||
0.12: Added color field and updating clkinfo periodically (running events)
|
||||
0.13: Show day of the week in date
|
||||
0.14: Fixed "Today" and "Yesterday" wrongly displayed for allDay events on some time zones
|
||||
0.15: Minor code improvements
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
var FILE = "android.calendar.json";
|
||||
//var FILE = "android.calendar.json";
|
||||
|
||||
var Locale = require("locale");
|
||||
|
||||
var fontSmall = "6x8";
|
||||
//var fontSmall = "6x8";
|
||||
var fontMedium = g.getFonts().includes("6x15")?"6x15":"6x8:2";
|
||||
var fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2";
|
||||
var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
||||
//var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
||||
|
||||
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
|
||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "agenda",
|
||||
"name": "Agenda",
|
||||
"version": "0.14",
|
||||
"version": "0.15",
|
||||
"description": "Simple agenda",
|
||||
"icon": "agenda.png",
|
||||
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
|
||||
|
|
|
@ -45,3 +45,9 @@
|
|||
0.40: Use substring of message when it's longer than fits the designated menu entry.
|
||||
0.41: Fix a menu bug affecting alarms with empty messages.
|
||||
0.42: Fix date not getting saved in event edit menu when tapping Confirm
|
||||
0.43: New settings: Show confirm, Show Overflow, Show Group.
|
||||
0.44: Add "delete timer after expiration" setting to events.
|
||||
0.45: Fix new alarm when selectedAlarm is undefined
|
||||
0.46: Show alarm groups if the Show Group setting is ON. Scroll alarms menu back to previous position when getting back to it.
|
||||
0.47: Fix wrap around when snoozed through midnight
|
||||
0.48: Use datetimeinput for Events, if available. Scroll back when getting out of group. Menu date format setting for shorter dates on current year.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This app allows you to add/modify any alarms, timers and events.
|
||||
|
||||
Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered.
|
||||
Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered. If a datetime input app (e.g. datetime_picker) is detected, it will be used for the selection of the date+time of events.
|
||||
|
||||
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
||||
|
||||
|
@ -13,7 +13,7 @@ It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master
|
|||
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
||||
- `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds)
|
||||
- `New Event` → Configure a new event (triggered based on time and date)
|
||||
- `Repeat` → Alarm can be be fired only once or repeated (every X number of _days_, _weeks_, _months_ or _years_)
|
||||
- `Repeat` → Alarm can be fired only once or repeated (every X number of _days_, _weeks_, _months_ or _years_)
|
||||
- `Advanced`
|
||||
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
||||
- `Enable All` → Enable _all_ disabled alarms & timers
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
const settings = Object.assign({
|
||||
showConfirm : true,
|
||||
showAutoSnooze : true,
|
||||
showHidden : true
|
||||
}, require('Storage').readJSON('alarm.json',1)||{});
|
||||
// 0 = Sunday (default), 1 = Monday
|
||||
const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
|
||||
const WORKDAYS = 62;
|
||||
|
@ -23,6 +28,8 @@ const iconTimerOff = "\0" + (g.theme.dark
|
|||
|
||||
// An array of alarm objects (see sched/README.md)
|
||||
var alarms = require("sched").getAlarms();
|
||||
// Fix possible wrap around in existing alarms #3281, broken alarms still needs to be saved to get fixed
|
||||
alarms.forEach(e => e.t %= 86400000); // This can probably be removed in the future when we are sure there are no more broken alarms
|
||||
|
||||
function handleFirstDayOfWeek(dow) {
|
||||
if (firstDayOfWeek == 1) {
|
||||
|
@ -43,20 +50,26 @@ function handleFirstDayOfWeek(dow) {
|
|||
alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow));
|
||||
|
||||
function getLabel(e) {
|
||||
const dateStr = e.date && require("locale").date(new Date(e.date), 1);
|
||||
const dateStr = getDateText(e.date);
|
||||
return (e.timer
|
||||
? require("time_utils").formatDuration(e.timer)
|
||||
: (dateStr ? `${dateStr}${e.rp?"*":""} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeRepeat(e)}` : ""))
|
||||
) + (e.msg ? ` ${e.msg}` : "");
|
||||
}
|
||||
|
||||
function getDateText(d) {
|
||||
return d && (settings.menuDateFormat === "mmdd" ? d.substring(d.startsWith(new Date().getFullYear()) ? 5 : 0) : require("locale").date(new Date(d), 1));
|
||||
}
|
||||
|
||||
function trimLabel(label, maxLength) {
|
||||
if(settings.showOverflow) return label;
|
||||
return (label.length > maxLength
|
||||
? label.substring(0,maxLength-3) + "..."
|
||||
: label.substring(0,maxLength));
|
||||
}
|
||||
|
||||
function formatAlarmMessage(msg) {
|
||||
function formatAlarmProperty(msg) {
|
||||
if(settings.showOverflow) return msg;
|
||||
if (msg == null) {
|
||||
return msg;
|
||||
} else if (msg.length > 7) {
|
||||
|
@ -66,39 +79,57 @@ function formatAlarmMessage(msg) {
|
|||
}
|
||||
}
|
||||
|
||||
function showMainMenu() {
|
||||
function showMainMenu(scroll, group, scrollback) {
|
||||
const menu = {
|
||||
"": { "title": /*LANG*/"Alarms & Timers" },
|
||||
"< Back": () => load(),
|
||||
/*LANG*/"New...": () => showNewMenu()
|
||||
"": { "title": group || /*LANG*/"Alarms & Timers", scroll: scroll },
|
||||
"< Back": () => group ? showMainMenu(scrollback) : load(),
|
||||
/*LANG*/"New...": () => showNewMenu(group)
|
||||
};
|
||||
const getGroups = settings.showGroup && !group;
|
||||
const groups = getGroups ? {} : undefined;
|
||||
var showAlarm;
|
||||
|
||||
alarms.forEach((e, index) => {
|
||||
showAlarm = !settings.showGroup || (group ? e.group === group : !e.group);
|
||||
if(showAlarm) {
|
||||
menu[trimLabel(getLabel(e),40)] = {
|
||||
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
||||
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
||||
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index, undefined, scroller.scroll, group)
|
||||
};
|
||||
} else if (getGroups) {
|
||||
groups[e.group] = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
if (!group) {
|
||||
Object.keys(groups).sort().forEach(g => menu[g] = () => showMainMenu(null, g, scroller.scroll));
|
||||
menu[/*LANG*/"Advanced"] = () => showAdvancedMenu();
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showNewMenu() {
|
||||
E.showMenu({
|
||||
var scroller = E.showMenu(menu).scroller;
|
||||
}
|
||||
|
||||
function showNewMenu(group) {
|
||||
const newMenu = {
|
||||
"": { "title": /*LANG*/"New..." },
|
||||
"< Back": () => showMainMenu(),
|
||||
/*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined),
|
||||
"< Back": () => showMainMenu(group),
|
||||
/*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined, false, null, group),
|
||||
/*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined),
|
||||
/*LANG*/"Event": () => showEditAlarmMenu(undefined, undefined, true)
|
||||
});
|
||||
/*LANG*/"Event": () => showEditAlarmMenu(undefined, undefined, true, null, group)
|
||||
};
|
||||
|
||||
if (group) delete newMenu[/*LANG*/"Timer"];
|
||||
E.showMenu(newMenu);
|
||||
}
|
||||
|
||||
function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
||||
function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) {
|
||||
var isNew = alarmIndex === undefined;
|
||||
|
||||
var alarm = require("sched").newDefaultAlarm();
|
||||
if (isNew && group) alarm.group = group;
|
||||
if (withDate || (selectedAlarm && selectedAlarm.date)) {
|
||||
alarm.del = require("sched").getSettings().defaultDeleteExpiredTimers;
|
||||
}
|
||||
alarm.dow = handleFirstDayOfWeek(alarm.dow);
|
||||
|
||||
if (selectedAlarm) {
|
||||
|
@ -111,14 +142,37 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
|||
var title = date ? (isNew ? /*LANG*/"New Event" : /*LANG*/"Edit Event") : (isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm");
|
||||
var keyboard = "textinput";
|
||||
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
||||
var datetimeinput;
|
||||
try {datetimeinput = require("datetimeinput");} catch(e) {datetimeinput = null;}
|
||||
|
||||
const menu = {
|
||||
"": { "title": title },
|
||||
"< Back": () => {
|
||||
prepareAlarmForSave(alarm, alarmIndex, time, date);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
},
|
||||
showMainMenu(scroll, group);
|
||||
}
|
||||
};
|
||||
|
||||
if (alarm.date && datetimeinput) {
|
||||
menu[`${getDateText(date.toLocalISOString().slice(0,10))} ${require("time_utils").formatTime(time)}`] = {
|
||||
value: date,
|
||||
format: v => "",
|
||||
onchange: v => {
|
||||
setTimeout(() => {
|
||||
var datetime = new Date(v.getTime());
|
||||
datetime.setHours(time.h, time.m);
|
||||
datetimeinput.input({datetime}).then(result => {
|
||||
time.h = result.getHours();
|
||||
time.m = result.getMinutes();
|
||||
prepareAlarmForSave(alarm, alarmIndex, time, result, true);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group);
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
Object.assign(menu, {
|
||||
/*LANG*/"Hour": {
|
||||
value: time.h,
|
||||
format: v => ("0" + v).substr(-2),
|
||||
|
@ -152,16 +206,33 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
|||
min: new Date().getFullYear(),
|
||||
max: 2100,
|
||||
onchange: v => date.setFullYear(v)
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(menu, {
|
||||
/*LANG*/"Message": {
|
||||
value: alarm.msg,
|
||||
format: formatAlarmMessage,
|
||||
format: formatAlarmProperty,
|
||||
onchange: () => {
|
||||
setTimeout(() => {
|
||||
keyboard.input({text:alarm.msg}).then(result => {
|
||||
alarm.msg = result;
|
||||
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group);
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
/*LANG*/"Group": {
|
||||
value: alarm.group,
|
||||
format: formatAlarmProperty,
|
||||
onchange: () => {
|
||||
setTimeout(() => {
|
||||
keyboard.input({text:alarm.group}).then(result => {
|
||||
alarm.group = result;
|
||||
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group);
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
@ -173,10 +244,13 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
|||
/*LANG*/"Repeat": {
|
||||
value: decodeRepeat(alarm),
|
||||
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, date || alarm.dow, (repeat, dow) => {
|
||||
if (repeat) {
|
||||
alarm.del = false; // do not auto delete a repeated alarm
|
||||
}
|
||||
alarm.rp = repeat;
|
||||
alarm.dow = dow;
|
||||
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group);
|
||||
})
|
||||
},
|
||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v),
|
||||
|
@ -184,23 +258,32 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
|||
value: alarm.as,
|
||||
onchange: v => alarm.as = v
|
||||
},
|
||||
/*LANG*/"Delete After Expiration": {
|
||||
value: alarm.del,
|
||||
onchange: v => alarm.del = v
|
||||
},
|
||||
/*LANG*/"Hidden": {
|
||||
value: alarm.hidden || false,
|
||||
onchange: v => alarm.hidden = v
|
||||
},
|
||||
/*LANG*/"Cancel": () => showMainMenu(),
|
||||
/*LANG*/"Cancel": () => showMainMenu(scroll, group),
|
||||
/*LANG*/"Confirm": () => {
|
||||
prepareAlarmForSave(alarm, alarmIndex, time, date);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
showMainMenu(scroll, group);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (!keyboard) delete menu[/*LANG*/"Message"];
|
||||
if (!keyboard || !settings.showGroup) delete menu[/*LANG*/"Group"];
|
||||
if (!settings.showConfirm) delete menu[/*LANG*/"Confirm"];
|
||||
if (!settings.showAutoSnooze) delete menu[/*LANG*/"Auto Snooze"];
|
||||
if (!settings.showHidden) delete menu[/*LANG*/"Hidden"];
|
||||
if (!alarm.date) {
|
||||
delete menu[/*LANG*/"Day"];
|
||||
delete menu[/*LANG*/"Month"];
|
||||
delete menu[/*LANG*/"Year"];
|
||||
delete menu[/*LANG*/"Delete After Expiration"];
|
||||
}
|
||||
|
||||
if (!isNew) {
|
||||
|
@ -209,10 +292,10 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
|||
if (confirm) {
|
||||
alarms.splice(alarmIndex, 1);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
showMainMenu(scroll, group);
|
||||
} else {
|
||||
alarm.t = require("time_utils").encodeTime(time);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -259,7 +342,6 @@ function decodeRepeat(alarm) {
|
|||
}
|
||||
|
||||
function showEditRepeatMenu(repeat, day, dowChangeCallback) {
|
||||
var originalRepeat = repeat;
|
||||
var dow;
|
||||
|
||||
const menu = {
|
||||
|
@ -292,26 +374,32 @@ function showEditRepeatMenu(repeat, day, dowChangeCallback) {
|
|||
},
|
||||
/*LANG*/"Custom": {
|
||||
value: isCustom ? decodeRepeat({ rp: true, dow: dow }) : false,
|
||||
onchange: () => setTimeout(showCustomDaysMenu, 10, dow, dowChangeCallback, originalRepeat, originalDow)
|
||||
onchange: () => setTimeout(showCustomDaysMenu, 10, dow, dowChangeCallback, repeat, originalDow)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// var date = day; // eventually: detect day of date and configure a repeat e.g. 3rd Monday of Month
|
||||
dow = EVERY_DAY;
|
||||
repeat = repeat || {interval: "month", num: 1};
|
||||
const repeatObj = repeat || {interval: "month", num: 1};
|
||||
|
||||
restOfMenu = {
|
||||
/*LANG*/"Every": {
|
||||
value: repeat.num,
|
||||
value: repeatObj.num,
|
||||
min: 1,
|
||||
onchange: v => repeat.num = v
|
||||
onchange: v => {
|
||||
repeat = repeatObj;
|
||||
repeat.num = v;
|
||||
}
|
||||
},
|
||||
/*LANG*/"Interval": {
|
||||
value: INTERVALS.indexOf(repeat.interval),
|
||||
value: INTERVALS.indexOf(repeatObj.interval),
|
||||
format: v => INTERVAL_LABELS[v],
|
||||
min: 0,
|
||||
max: INTERVALS.length - 1,
|
||||
onchange: v => repeat.interval = INTERVALS[v]
|
||||
onchange: v => {
|
||||
repeat = repeatObj;
|
||||
repeat.interval = INTERVALS[v];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -387,7 +475,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
|||
},
|
||||
/*LANG*/"Message": {
|
||||
value: timer.msg,
|
||||
format: formatAlarmMessage,
|
||||
format: formatAlarmProperty,
|
||||
onchange: () => {
|
||||
setTimeout(() => {
|
||||
keyboard.input({text:timer.msg}).then(result => {
|
||||
|
@ -420,6 +508,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
|||
};
|
||||
|
||||
if (!keyboard) delete menu[/*LANG*/"Message"];
|
||||
if (!settings.showConfirm) delete menu[/*LANG*/"Confirm"];
|
||||
if (!settings.showHidden) delete menu[/*LANG*/"Hidden"];
|
||||
if (!isNew) {
|
||||
menu[/*LANG*/"Delete"] = () => {
|
||||
E.showPrompt(getLabel(timer) + "\n" + /*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => {
|
||||
|
@ -440,7 +530,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
|||
|
||||
function prepareTimerForSave(timer, timerIndex, time, temp) {
|
||||
timer.timer = require("time_utils").encodeTime(time);
|
||||
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
|
||||
timer.t = (require("time_utils").getCurrentTimeMillis() + timer.timer) % 86400000;
|
||||
timer.last = 0;
|
||||
|
||||
if (!temp) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "alarm",
|
||||
"name": "Alarms & Timers",
|
||||
"shortName": "Alarms",
|
||||
"version": "0.42",
|
||||
"version": "0.48",
|
||||
"description": "Set alarms and timers on your Bangle",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm",
|
||||
|
@ -11,7 +11,8 @@
|
|||
"dependencies": { "scheduler":"type", "alarm":"widget" },
|
||||
"storage": [
|
||||
{ "name": "alarm.app.js", "url": "app.js" },
|
||||
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true }
|
||||
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true },
|
||||
{ "name": "alarm.settings.js", "url":"settings.js" }
|
||||
],
|
||||
"screenshots": [
|
||||
{ "url": "screenshot-1.png" },
|
||||
|
@ -25,5 +26,6 @@
|
|||
{ "url": "screenshot-9.png" },
|
||||
{ "url": "screenshot-10.png" },
|
||||
{ "url": "screenshot-11.png" }
|
||||
]
|
||||
],
|
||||
"data":[ {"name":"alarm.settings.json"} ]
|
||||
}
|
||||
|
|