Merge branch 'master' into icon-notifs

master
Lubomir (Mac) 2022-06-18 23:42:36 +10:00
commit 16255198a2
No known key found for this signature in database
GPG Key ID: 2283D4C5BAAD1570
139 changed files with 5773 additions and 720 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: espruino
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['http://www.espruino.com/Donate']# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -1,4 +1,4 @@
name: Node CI name: build
on: [push, pull_request] on: [push, pull_request]
@ -6,29 +6,25 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps: steps:
- name: Checkout repository and submodules - name: Checkout repository and submodules
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js 16.x
uses: actions/setup-node@v1 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: 16.x
- name: install testing dependencies - name: Install testing dependencies
run: npm i run: npm ci
- name: test all apps and widgets - name: Test all apps and widgets
run: npm run test run: npm test
- name: install typescript dependencies - name: Install typescript dependencies
working-directory: ./typescript working-directory: ./typescript
run: npm ci run: npm ci
- name: build types - name: Build types
working-directory: ./typescript working-directory: ./typescript
run: npm run build:types run: npm run build:types
- name: build all TS apps and widgets - name: Build all TS apps and widgets
working-directory: ./typescript working-directory: ./typescript
run: npm run build run: npm run build

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
.htaccess .htaccess
node_modules node_modules
package-lock.json
.DS_Store .DS_Store
*.js.bak *.js.bak
appdates.csv appdates.csv

View File

@ -1,7 +1,7 @@
Bangle.js App Loader (and Apps) Bangle.js App Loader (and Apps)
================================ ================================
[![Build Status](https://app.travis-ci.com/espruino/BangleApps.svg?branch=master)](https://app.travis-ci.com/github/espruino/BangleApps) [![Build Status](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml/badge.svg)](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml)
* Try the **release version** at [banglejs.com/apps](https://banglejs.com/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/) * Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/)
@ -191,7 +191,7 @@ widget bar at the top of the screen they can add themselves to the global
``` ```
WIDGETS["mywidget"]={ WIDGETS["mywidget"]={
area:"tl", // tl (top left), tr (top right) area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
sortorder:0, // (Optional) determines order of widgets in the same corner sortorder:0, // (Optional) determines order of widgets in the same corner
width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget draw:draw // called to draw the widget

View File

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

11
apps/2ofthemclk/README.md Normal file
View File

@ -0,0 +1,11 @@
# two of them clock
You can now wear teh memez on your wrist.
![](screenshot.png)
Also serves as an example of displaying seconds only when unlocked or charging and only refreshing on the minute otherwise.
Widgets not supported
## Creator
- [Kilrah](https://github.com/kilrah)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgZC/AH4ADkAPOgVJkgEBAQQAJiQRByEJgmQCJWSpMEAQMkyQJCpASHhAOBpAmBJJgjBCIUJCRg4CCIJxFMQ2SoARCkmACI0EBAJHCCIMLj4RFiUBskAgIXBEAU5A4P34CtCiEJsEJ/AHBCgOBAoQAEi0H////HciQsBwywICIXWzkG4A+BEY0gif46dt6/cgnIgkWnHfLIP/MoUWwHbpvC/kAjEEj0HNYQCCkEfGgP/64RB2EAifHLwMAjg1CCIMD/0H/0B8EAh+HgeAkARCE4IjC/4jBYIMPLIcIAYUPB4OBCIQABhu/AoShCHYIRBx6QBDgUw2//8OHPwcJ39//ILBCIU9LgMBSQgsBJAYRBkE/CIIABgRHD3wRFkk/2zBDAYU//3b/oRB8ARBj6ABgEE7YREEYf+oMkSwINCyClCn//z//+4RBgMkgU3EgUcwFJgEeboOXCIP2EYJCDAAVJkkGWoIuBgf2EYQPDkECCIOGd4ffyEJkgFBAAcSoEkwQCBhw+BwQaByVAkGAKwIFBBANLkEQgAyBCIVIkBpBgmSBYOQoApBgcgiQRCAQIyCCgsSjIFBCIcgRgJNCCgQyBpAgDAQT2BCgIOBBAQUCCIpfBCIwCKP4QRNpCSDCLyJBCIbjBTwYRLboJ0BCI4QD"))

90
apps/2ofthemclk/app.js Normal file

File diff suppressed because one or more lines are too long

BIN
apps/2ofthemclk/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
apps/2ofthemclk/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,17 @@
{
"id": "2ofthemclk",
"name": "two of them clock",
"version": "0.01",
"description": "You can now wear teh memez on your wrist.",
"readme": "README.md",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"storage": [
{"name":"2ofthemclk.app.js","url":"app.js"},
{"name":"2ofthemclk.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -9,7 +9,7 @@ currently-running apps */
// add your widget // add your widget
WIDGETS["mywidget"]={ WIDGETS["mywidget"]={
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right) area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen
width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget draw:draw // called to draw the widget
}; };

View File

@ -4,3 +4,4 @@
0.04: Obey system quiet mode 0.04: Obey system quiet mode
0.05: Battery optimisation, add the pause option, bug fixes 0.05: Battery optimisation, add the pause option, bug fixes
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night 0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
0.07: Fix bug on the cutting edge firmware

View File

@ -1,42 +1,46 @@
function drawAlert() { (function () {
E.showPrompt("Inactivity detected", { // load variable before defining functions cause it can trigger a ReferenceError
title: "Activity reminder", const activityreminder = require("activityreminder");
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } const storage = require("Storage");
}).then(function (v) { const activityreminder_settings = activityreminder.loadSettings();
if (v == 1) { let activityreminder_data = activityreminder.loadData();
activityreminder_data.okDate = new Date();
function drawAlert() {
E.showPrompt("Inactivity detected", {
title: "Activity reminder",
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 }
}).then(function (v) {
if (v == 1) {
activityreminder_data.okDate = new Date();
}
if (v == 2) {
activityreminder_data.dismissDate = new Date();
}
if (v == 3) {
activityreminder_data.pauseDate = new Date();
}
activityreminder.saveData(activityreminder_data);
load();
});
// Obey system quiet mode:
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
Bangle.buzz(400);
}
setTimeout(load, 20000);
} }
if (v == 2) {
activityreminder_data.dismissDate = new Date(); function run() {
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
drawAlert();
} else {
eval(storage.read("activityreminder.settings.js"))(() => load());
}
} }
if (v == 3) {
activityreminder_data.pauseDate = new Date();
}
activityreminder.saveData(activityreminder_data);
load();
});
// Obey system quiet mode: g.clear();
if (!(storage.readJSON('setting.json', 1) || {}).quiet) { Bangle.loadWidgets();
Bangle.buzz(400); Bangle.drawWidgets();
} run();
setTimeout(load, 20000);
}
function run() { })();
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
drawAlert();
} else {
eval(storage.read("activityreminder.settings.js"))(() => load());
}
}
const activityreminder = require("activityreminder");
const storage = require("Storage");
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
const activityreminder_settings = activityreminder.loadSettings();
const activityreminder_data = activityreminder.loadData();
run();

View File

@ -1,65 +1,70 @@
function run() { (function () {
if (isNotWorn()) return; // load variable before defining functions cause it can trigger a ReferenceError
let now = new Date(); const activityreminder = require("activityreminder");
let h = now.getHours(); const activityreminder_settings = activityreminder.loadSettings();
let activityreminder_data = activityreminder.loadData();
if (isDuringAlertHours(h)) { if (activityreminder_data.firstLoad) {
let health = Bangle.getHealthStatus("day");
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
activityreminder_data.stepsOnDate = health.steps;
activityreminder_data.stepsDate = now;
activityreminder.saveData(activityreminder_data);
/* todo in a futur release
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
(pass some argument to run() to handle long walks and not triggering so often)
*/
}
if(activityreminder.mustAlert(activityreminder_data, activityreminder_settings)){
load('activityreminder.app.js');
}
}
}
function isNotWorn() {
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
}
function isDuringAlertHours(h) {
if(activityreminder_settings.startHour < activityreminder_settings.endHour){ // not passing through midnight
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour)
} else{ // passing through midnight
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour)
}
}
Bangle.on('midnight', function() {
/*
Usefull trick to have the app working smothly for people using it at night
*/
let now = new Date();
let h = now.getHours();
if (activityreminder_settings.enabled && isDuringAlertHours(h)){
// updating only the steps and keeping the original stepsDate on purpose
activityreminder_data.stepsOnDate = 0;
activityreminder.saveData(activityreminder_data);
}
});
const activityreminder = require("activityreminder");
const activityreminder_settings = activityreminder.loadSettings();
if (activityreminder_settings.enabled) {
const activityreminder_data = activityreminder.loadData();
if(activityreminder_data.firstLoad){
activityreminder_data.firstLoad = false; activityreminder_data.firstLoad = false;
activityreminder.saveData(activityreminder_data); activityreminder.saveData(activityreminder_data);
} }
setInterval(run, 60000);
/* todo in a futur release
increase setInterval time to something that is still sensible (5 mins ?)
when we added a settimer
*/
}
function run() {
if (isNotWorn()) return;
let now = new Date();
let h = now.getHours();
if (isDuringAlertHours(h)) {
let health = Bangle.getHealthStatus("day");
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
activityreminder_data.stepsOnDate = health.steps;
activityreminder_data.stepsDate = now;
activityreminder.saveData(activityreminder_data);
/* todo in a futur release
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
(pass some argument to run() to handle long walks and not triggering so often)
*/
}
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
load('activityreminder.app.js');
}
}
}
function isNotWorn() {
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
}
function isDuringAlertHours(h) {
if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour);
} else { // passing through midnight
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour);
}
}
Bangle.on('midnight', function () {
/*
Usefull trick to have the app working smothly for people using it at night
*/
let now = new Date();
let h = now.getHours();
if (activityreminder_settings.enabled && isDuringAlertHours(h)) {
// updating only the steps and keeping the original stepsDate on purpose
activityreminder_data.stepsOnDate = 0;
activityreminder.saveData(activityreminder_data);
}
});
if (activityreminder_settings.enabled) {
setInterval(run, 60000);
/* todo in a futur release
increase setInterval time to something that is still sensible (5 mins ?)
when we added a settimer
*/
}
})();

View File

@ -1,5 +1,3 @@
const storage = require("Storage");
exports.loadSettings = function () { exports.loadSettings = function () {
return Object.assign({ return Object.assign({
enabled: true, enabled: true,
@ -10,20 +8,20 @@ exports.loadSettings = function () {
pauseDelayMin: 120, pauseDelayMin: 120,
minSteps: 50, minSteps: 50,
tempThreshold: 27 tempThreshold: 27
}, storage.readJSON("activityreminder.s.json", true) || {}); }, require("Storage").readJSON("activityreminder.s.json", true) || {});
}; };
exports.writeSettings = function (settings) { exports.writeSettings = function (settings) {
storage.writeJSON("activityreminder.s.json", settings); require("Storage").writeJSON("activityreminder.s.json", settings);
}; };
exports.saveData = function (data) { exports.saveData = function (data) {
storage.writeJSON("activityreminder.data.json", data); require("Storage").writeJSON("activityreminder.data.json", data);
}; };
exports.loadData = function () { exports.loadData = function () {
let health = Bangle.getHealthStatus("day"); let health = Bangle.getHealthStatus("day");
const data = Object.assign({ let data = Object.assign({
firstLoad: true, firstLoad: true,
stepsDate: new Date(), stepsDate: new Date(),
stepsOnDate: health.steps, stepsOnDate: health.steps,
@ -31,7 +29,7 @@ exports.loadData = function () {
dismissDate: new Date(1970), dismissDate: new Date(1970),
pauseDate: new Date(1970), pauseDate: new Date(1970),
}, },
storage.readJSON("activityreminder.data.json") || {}); require("Storage").readJSON("activityreminder.data.json") || {});
if(typeof(data.stepsDate) == "string") if(typeof(data.stepsDate) == "string")
data.stepsDate = new Date(data.stepsDate); data.stepsDate = new Date(data.stepsDate);

View File

@ -3,7 +3,7 @@
"name": "Activity Reminder", "name": "Activity Reminder",
"shortName":"Activity Reminder", "shortName":"Activity Reminder",
"description": "A reminder to take short walks for the ones with a sedentary lifestyle", "description": "A reminder to take short walks for the ones with a sedentary lifestyle",
"version":"0.06", "version":"0.07",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",
"tags": "tool,activity", "tags": "tool,activity",

View File

@ -1,85 +1,85 @@
(function (back) { (function (back) {
// Load settings // Load settings
const activityreminder = require("activityreminder"); const activityreminder = require("activityreminder");
const settings = activityreminder.loadSettings(); let settings = activityreminder.loadSettings();
// Show the menu // Show the menu
E.showMenu({ E.showMenu({
"": { "title": "Activity Reminder" }, "": { "title": "Activity Reminder" },
"< Back": () => back(), "< Back": () => back(),
'Enable': { 'Enable': {
value: settings.enabled, value: settings.enabled,
format: v => v ? "Yes" : "No", format: v => v ? "Yes" : "No",
onchange: v => { onchange: v => {
settings.enabled = v; settings.enabled = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
} }
}, },
'Start hour': { 'Start hour': {
value: settings.startHour, value: settings.startHour,
min: 0, max: 24, min: 0, max: 24,
onchange: v => { onchange: v => {
settings.startHour = v; settings.startHour = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
} }
}, },
'End hour': { 'End hour': {
value: settings.endHour, value: settings.endHour,
min: 0, max: 24, min: 0, max: 24,
onchange: v => { onchange: v => {
settings.endHour = v; settings.endHour = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
} }
}, },
'Max inactivity': { 'Max inactivity': {
value: settings.maxInnactivityMin, value: settings.maxInnactivityMin,
min: 15, max: 120, min: 15, max: 120,
onchange: v => { onchange: v => {
settings.maxInnactivityMin = v; settings.maxInnactivityMin = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
}, },
format: x => { format: x => {
return x + " min"; return x + " min";
} }
}, },
'Dismiss delay': { 'Dismiss delay': {
value: settings.dismissDelayMin, value: settings.dismissDelayMin,
min: 5, max: 60, min: 5, max: 60,
onchange: v => { onchange: v => {
settings.dismissDelayMin = v; settings.dismissDelayMin = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
}, },
format: x => { format: x => {
return x + " min"; return x + " min";
} }
}, },
'Pause delay': { 'Pause delay': {
value: settings.pauseDelayMin, value: settings.pauseDelayMin,
min: 30, max: 240, step: 5, min: 30, max: 240, step: 5,
onchange: v => { onchange: v => {
settings.pauseDelayMin = v; settings.pauseDelayMin = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
}, },
format: x => { format: x => {
return x + " min"; return x + " min";
} }
}, },
'Min steps': { 'Min steps': {
value: settings.minSteps, value: settings.minSteps,
min: 10, max: 500, step: 10, min: 10, max: 500, step: 10,
onchange: v => { onchange: v => {
settings.minSteps = v; settings.minSteps = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
} }
}, },
'Temp Threshold': { 'Temp Threshold': {
value: settings.tempThreshold, value: settings.tempThreshold,
min: 20, max: 40, step: 0.5, min: 20, max: 40, step: 0.5,
format: v => v + "°C", format: v => v + "°C",
onchange: v => { onchange: v => {
settings.tempThreshold = v; settings.tempThreshold = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
} }
} }
}); });
}) })

1
apps/agenda/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Basic agenda with events from GB

3
apps/agenda/README.md Normal file
View File

@ -0,0 +1,3 @@
# Agenda
Basic agenda reading the events synchronised from GadgetBridge

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwg1yhGIxAPMBwIPFhH//GAC5n/C4oHBC5/IGwoXBHQQAKC4OIFAWOxHv9GO9wAKI4XoC4foEIIWLC4IABC4gIBFxnuE4IqBC4gARC4ZzNAAwXaxe7ACO4C625C4m4xIJBzAeCxGbCAOIFgQOBC4pOBxe4AYIPBAYQKCAYYXE3GL/ADBx/oxb3BC4X+xG4xwOBC4uP/YDB54MBf4Po3eM/4XBx/+C4pTBGIIkBLgOYAYIvB9GJBwI6BL45zCL4aCCL4h3GU64ALdYS1CI55bBAAgXFO4mMO4QDBDIO/////YxBU53IxIVB/GfDAWYa5wtC/GPAYWIL4wXBL4oSBC4jcBC4m4QIWYSwWIIQIAG/CnMMAIAC/JLCMIIvMIwZHFJAJfLC5yPHAYIRDAoy/KCIi7BMon4d4+Od4IXBxAZBEQLtB/+YxIXDL4SLCL4WPzAXCNgRFBLIKnKLIrcEI4gXNAAp3CxGZAAzCBC5KnCKAIAICxBlBC4IAJxG/C4/4wAXLhBgD/IcD3AXMGAIqDDgRGNGAoXDFxxhEI4W4FxwwCaoYWBFx4YDAAQWRAEQ"))

127
apps/agenda/agenda.js Normal file
View File

@ -0,0 +1,127 @@
/* CALENDAR is a list of:
{id:int,
type,
timestamp,
durationInSeconds,
title,
description,
location,
allDay: bool,
}
*/
Bangle.loadWidgets();
Bangle.drawWidgets();
var FILE = "android.calendar.json";
var Locale = require("locale");
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";
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp)
function getDate(timestamp) {
return new Date(timestamp*1000);
}
function formatDateLong(date, includeDay) {
if(includeDay)
return Locale.date(date)+" "+Locale.time(date,1);
return Locale.time(date,1);
}
function formatDateShort(date) {
return Locale.date(date).replace(/\d\d\d\d/,"")+Locale.time(date,1);
}
var lines = [];
function showEvent(ev) {
var bodyFont = fontBig;
if(!ev) return;
g.setFont(bodyFont);
//var lines = [];
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10)
var titleCnt = lines.length;
var start = getDate(ev.timestamp);
var end = getDate((+ev.timestamp) + (+ev.durationInSeconds));
var includeDay = true;
if (titleCnt) lines.push(""); // add blank line after title
if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth())
includeDay = false;
if(includeDay) {
lines = lines.concat(
/*LANG*/"Start:",
g.wrapString(formatDateLong(start, includeDay), g.getWidth()-10),
/*LANG*/"End:",
g.wrapString(formatDateLong(end, includeDay), g.getWidth()-10));
} else {
lines = lines.concat(
g.wrapString(Locale.date(start), g.getWidth()-10),
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay), g.getWidth()-10),
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay), g.getWidth()-10));
}
if(ev.location)
lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
if(ev.description)
lines = lines.concat("",g.wrapString(ev.description, g.getWidth()-10));
lines = lines.concat(["",/*LANG*/"< Back"]);
E.showScroller({
h : g.getFontHeight(), // height of each menu item in pixels
c : lines.length, // number of menu items
// a function to draw a menu item
draw : function(idx, r) {
// FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12
g.setBgColor(idx<titleCnt ? g.theme.bg2 : g.theme.bg).
setColor(idx<titleCnt ? g.theme.fg2 : g.theme.fg).
clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
g.setFont(bodyFont).drawString(lines[idx], r.x, r.y);
}, select : function(idx) {
if (idx>=lines.length-2)
showList();
},
back : () => showList()
});
}
function showList() {
if(CALENDAR.length == 0) {
E.showMessage("No events");
return;
}
E.showScroller({
h : 52,
c : Math.max(CALENDAR.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
draw : function(idx, r) {"ram"
var ev = CALENDAR[idx];
g.setColor(g.theme.fg);
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
if (!ev) return;
var isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
var x = r.x+2, title = ev.title;
var body = formatDateShort(getDate(ev.timestamp))+"\n"+ev.location;
var m = ev.title+"\n"+ev.location, longBody=false;
if (title) g.setFontAlign(-1,-1).setFont(fontBig)
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x,r.y+2);
if (body) {
g.setFontAlign(-1,-1).setFont(fontMedium).setColor(isPast ? "#888" : g.theme.fg);
var l = g.wrapString(body, r.w-(x+14));
if (l.length>3) {
l = l.slice(0,3);
l[l.length-1]+="...";
}
longBody = l.length>2;
g.drawString(l.join("\n"), x+10,r.y+20);
}
//if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
},
select : idx => showEvent(CALENDAR[idx]),
back : () => load()
});
}
showList();

BIN
apps/agenda/agenda.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

17
apps/agenda/metadata.json Normal file
View File

@ -0,0 +1,17 @@
{
"id": "agenda",
"name": "Agenda",
"version": "0.02",
"description": "Simple agenda",
"icon": "agenda.png",
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
"tags": "agenda",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"agenda.app.js","url":"agenda.js"},
{"name":"agenda.settings.js","url":"settings.js"},
{"name":"agenda.img","url":"agenda-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

37
apps/agenda/settings.js Normal file
View File

@ -0,0 +1,37 @@
(function(back) {
function gbSend(message) {
Bluetooth.println("");
Bluetooth.println(JSON.stringify(message));
}
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
var mainmenu = {
"" : { "title" : "Agenda" },
"< Back" : back,
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?/*LANG*/"Yes":/*LANG*/"No" },
/*LANG*/"Force calendar sync" : () => {
if(NRF.getSecurityStatus().connected) {
E.showPrompt(/*LANG*/"Do you want to also clear the internal database first?", {
buttons: {/*LANG*/"Yes": 1, /*LANG*/"No": 2, /*LANG*/"Cancel": 3}
}).then((v)=>{
switch(v) {
case 1:
require("Storage").writeJSON("android.calendar.json",[]);
CALENDAR = [];
/* falls through */
case 2:
gbSend({t:"force_calendar_sync", ids: CALENDAR.map(e=>e.id)});
E.showAlert(/*LANG*/"Request sent to the phone").then(()=>E.showMenu(mainmenu));
break;
case 3:
default:
E.showMenu(mainmenu);
return;
}
});
} else {
E.showAlert(/*LANG*/"You are not connected").then(()=>E.showMenu(mainmenu));
}
},
};
E.showMenu(mainmenu);
})

View File

@ -30,3 +30,4 @@
0.28: Fix bug with alarms not firing when configured to fire only once 0.28: Fix bug with alarms not firing when configured to fire only once
0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday 0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
0.30: Fix "Enable All" 0.30: Fix "Enable All"
0.31: Add seconds to timers

View File

@ -268,11 +268,20 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
wrap: true, wrap: true,
onchange: v => time.m = v onchange: v => time.m = v
}, },
/*LANG*/"Seconds": {
value: time.s,
min: 0,
max: 59,
step: 1,
wrap: true,
onchange: v => time.s = v
},
/*LANG*/"Enabled": { /*LANG*/"Enabled": {
value: timer.on, value: timer.on,
onchange: v => timer.on = v onchange: v => timer.on = v
}, },
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v), /*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
/*LANG*/"Cancel": () => showMainMenu()
}; };
if (!isNew) { if (!isNew) {

View File

@ -2,7 +2,7 @@
"id": "alarm", "id": "alarm",
"name": "Alarms & Timers", "name": "Alarms & Timers",
"shortName": "Alarms", "shortName": "Alarms",
"version": "0.30", "version": "0.31",
"description": "Set alarms and timers on your Bangle", "description": "Set alarms and timers on your Bangle",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm,widget", "tags": "tool,alarm,widget",

View File

@ -90,6 +90,35 @@
sched.setAlarms(alarms); sched.setAlarms(alarms);
sched.reload(); sched.reload();
}, },
//TODO perhaps move those in a library (like messages), used also for viewing events?
//simple package with events all together
"calendarevents" : function() {
require("Storage").writeJSON("android.calendar.json", event.events);
},
//add and remove events based on activity on phone (pebble-like)
"calendar" : function() {
var cal = require("Storage").readJSON("android.calendar.json",true);
if (!cal || !Array.isArray(cal)) cal = [];
var i = cal.findIndex(e=>e.id==event.id);
if(i<0)
cal.push(event);
else
cal[i] = event;
require("Storage").writeJSON("android.calendar.json", cal);
},
"calendar-" : function() {
var cal = require("Storage").readJSON("android.calendar.json",true);
//if any of those happen we are out of sync!
if (!cal || !Array.isArray(cal)) return;
cal = cal.filter(e=>e.id!=event.id);
require("Storage").writeJSON("android.calendar.json", cal);
},
//triggered by GB, send all ids
"force_calendar_sync_start" : function() {
var cal = require("Storage").readJSON("android.calendar.json",true);
if (!cal || !Array.isArray(cal)) cal = [];
gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)});
}
}; };
var h = HANDLERS[event.t]; var h = HANDLERS[event.t];
if (h) h(); else console.log("GB Unknown",event); if (h) h(); else console.log("GB Unknown",event);

View File

@ -2,7 +2,7 @@
"id": "android", "id": "android",
"name": "Android Integration", "name": "Android Integration",
"shortName": "Android", "shortName": "Android",
"version": "0.10", "version": "0.11",
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,messages,notifications,gadgetbridge", "tags": "tool,system,messages,notifications,gadgetbridge",
@ -15,6 +15,6 @@
{"name":"android.img","url":"app-icon.js","evaluate":true}, {"name":"android.img","url":"app-icon.js","evaluate":true},
{"name":"android.boot.js","url":"boot.js"} {"name":"android.boot.js","url":"boot.js"}
], ],
"data": [{"name":"android.settings.json"}], "data": [{"name":"android.settings.json"}, {"name":"android.calendar.json"}],
"sortorder": -8 "sortorder": -8
} }

View File

@ -11,3 +11,4 @@
0.11: Use ClockFace.is12Hour 0.11: Use ClockFace.is12Hour
0.12: Add settings to hide date,widgets 0.12: Add settings to hide date,widgets
0.13: Add font setting 0.13: Add font setting
0.14: Use ClockFace_menu.addItems

View File

@ -1,7 +1,7 @@
{ {
"id": "barclock", "id": "barclock",
"name": "Bar Clock", "name": "Bar Clock",
"version": "0.13", "version": "0.14",
"description": "A simple digital clock showing seconds as a bar", "description": "A simple digital clock showing seconds as a bar",
"icon": "clock-bar.png", "icon": "clock-bar.png",
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],

View File

@ -1,26 +1,26 @@
(function(back) { (function(back) {
let s = require('Storage').readJSON("barclock.settings.json", true) || {}; let s = require("Storage").readJSON("barclock.settings.json", true) || {};
function saver(key) { function save(key, value) {
return value => { s[key] = value;
s[key] = value; require("Storage").writeJSON("barclock.settings.json", s);
require('Storage').writeJSON("barclock.settings.json", s);
}
} }
const fonts = [/*LANG*/"Bitmap",/*LANG*/"Vector"]; const fonts = [/*LANG*/"Bitmap",/*LANG*/"Vector"];
const menu = { let menu = {
"": {"title": /*LANG*/"Bar Clock"}, "": {"title": /*LANG*/"Bar Clock"},
/*LANG*/"< Back": back, /*LANG*/"< Back": back,
/*LANG*/"Show date": require("ClockFace_menu").showDate(s.showDate, saver('showDate')),
/*LANG*/"Load widgets": require("ClockFace_menu").loadWidgets(s.loadWidgets, saver('loadWidgets')),
/*LANG*/"Font": { /*LANG*/"Font": {
value: s.font|0, value: s.font|0,
min:0,max:1,wrap:true, min: 0, max: 1, wrap: true,
format:v=>fonts[v], format: v => fonts[v],
onchange:saver('font'), onchange: v => save("font", v),
}, },
}; };
require("ClockFace_menu").addItems(menu, save, {
showDate: s.showDate,
loadWidgets: s.loadWidgets,
});
E.showMenu(menu); E.showMenu(menu);
}); });

View File

@ -1,3 +1,5 @@
0.01: Initial version 0.01: Initial version
0.02: setTimeout bug fix; no leading zero on date; lightmode; 12 hour format; cleanup 0.02: setTimeout bug fix; no leading zero on date; lightmode; 12 hour format; cleanup
0.03: Internationalisation; bug fix - battery icon responds promptly to charging state 0.03: Internationalisation; bug fix - battery icon responds promptly to charging state
0.04: bug fix
0.05: proper fix for the race condition in queueDraw()

View File

@ -12,12 +12,12 @@ Graphics.prototype.setFontOpenSans = function(scale) {
var drawTimeout; var drawTimeout;
// schedule a draw for the next minute function queueDraw(millis_now) {
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function () { drawTimeout = setTimeout(function () {
drawTimeout = undefined;
draw(); draw();
}, 60300 - (Date.now() % 60000)); // We aim for 300ms into the next minute to ensure we make it! }, 60000 - (millis_now % 60000));
} }
function draw() { function draw() {
@ -69,7 +69,7 @@ function draw() {
// widget redraw // widget redraw
Bangle.drawWidgets(); Bangle.drawWidgets();
queueDraw(); queueDraw(date.getTime());
} }
Bangle.on('lcdPower', on => { Bangle.on('lcdPower', on => {

View File

@ -1,7 +1,7 @@
{ "id": "bigdclock", { "id": "bigdclock",
"name": "Big digit clock containing just the essentials", "name": "Big digit clock containing just the essentials",
"shortName":"Big digit clk", "shortName":"Big digit clk",
"version":"0.03", "version":"0.05",
"description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.", "description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.",
"icon": "bigdclock.png", "icon": "bigdclock.png",
"type": "clock", "type": "clock",

View File

@ -8,3 +8,4 @@
0.7: Update Rocket Sequences Scope to not use memory all time 0.7: Update Rocket Sequences Scope to not use memory all time
0.8: Update Some Variable Scopes to not use memory until need 0.8: Update Some Variable Scopes to not use memory until need
0.9: Remove ESLint spaces 0.9: Remove ESLint spaces
0.10: Show daily steps, heartrate and the temperature if weather information is available.

View File

@ -6,5 +6,6 @@ Clock with Space Cassio Watch Style.
It displays current temperature,day,steps,battery.heartbeat and weather. It displays current temperature,day,steps,battery.heartbeat and weather.
**To-do**: **To-do**:
Integrate heartbeat and Weather, Align and change size of some elements. Align and change size of some elements.

View File

@ -1,11 +1,9 @@
const storage = require('Storage');
require("Font6x12").add(Graphics); require("Font6x12").add(Graphics);
require("Font8x12").add(Graphics); require("Font8x12").add(Graphics);
require("Font7x11Numeric7Seg").add(Graphics); require("Font7x11Numeric7Seg").add(Graphics);
let ClockInterval;
let RocketInterval;
let BatteryInterval;
function bigThenSmall(big, small, x, y) { function bigThenSmall(big, small, x, y) {
g.setFont("7x11Numeric7Seg", 2); g.setFont("7x11Numeric7Seg", 2);
g.drawString(big, x, y); g.drawString(big, x, y);
@ -14,16 +12,6 @@ function bigThenSmall(big, small, x, y) {
g.drawString(small, x, y); g.drawString(small, x, y);
} }
function ClearIntervals(inoreclock) {
if (RocketInterval) clearInterval(RocketInterval);
if (BatteryInterval) clearInterval(BatteryInterval);
RocketInterval = undefined;
BatteryInterval = undefined;
if (inoreclock) return;
if (ClockInterval) clearInterval(ClockInterval);
ClockInterval = undefined;
}
function getBackgroundImage() { function getBackgroundImage() {
return require("heatshrink").decompress(atob("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")); return require("heatshrink").decompress(atob("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"));
} }
@ -41,15 +29,31 @@ function getRocketSequences() {
}; };
} }
let rocket_sequence = 1; let rocketSequence = 1;
let settings = storage.readJSON("cassioWatch.settings.json", true) || {};
let settings = require('Storage').readJSON("cassioWatch.settings.json", true) || {};
let rocketSpeed = settings.rocketSpeed || 700; let rocketSpeed = settings.rocketSpeed || 700;
delete settings; delete settings;
g.clear(); // schedule a draw for the next minute
let rocketInterval;
var drawTimeout;
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function DrawClock() {
function clearIntervals() {
if (rocketInterval) clearInterval(rocketInterval);
rocketInterval = undefined;
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
function drawClock() {
g.setFont("7x11Numeric7Seg", 3); g.setFont("7x11Numeric7Seg", 3);
g.clearRect(80, 57, 170, 96); g.clearRect(80, 57, 170, 96);
g.setColor(0, 255, 255); g.setColor(0, 255, 255);
@ -66,23 +70,57 @@ function DrawClock() {
g.drawString(time < 10 ? "0" + time : time, 78, 137); g.drawString(time < 10 ? "0" + time : time, 78, 137);
} }
function DrawBattery() { function drawBattery() {
bigThenSmall(E.getBattery(), "%", 135, 21); bigThenSmall(E.getBattery(), "%", 135, 21);
} }
function DrawRocket() { function drawRocket() {
let Rocket = getRocketSequences(); let Rocket = getRocketSequences();
g.clearRect(5, 62, 63, 115); g.clearRect(5, 62, 63, 115);
g.setColor(0, 255, 255); g.setColor(0, 255, 255);
g.drawRect(5, 62, 63, 115); g.drawRect(5, 62, 63, 115);
g.fillRect(5, 62, 63, 115); g.fillRect(5, 62, 63, 115);
g.drawImage(Rocket[rocket_sequence], 5, 65, { scale: 0.7 }); g.drawImage(Rocket[rocketSequence], 5, 65, { scale: 0.7 });
g.setColor(0, 0, 0); g.setColor(0, 0, 0);
rocket_sequence = rocket_sequence + 1; rocketSequence = rocketSequence + 1;
if (rocket_sequence > 8) rocket_sequence = 1; if(rocketSequence > 8) rocketSequence = 1;
} }
function DrawScene() { function getTemperature(){
try {
var weatherJson = storage.readJSON('weather.json');
var weather = weatherJson.weather;
return Math.round(weather.temp-273.15);
} catch(ex) {
print(ex)
return "?"
}
}
function getSteps() {
var steps = 0;
try{
if (WIDGETS.wpedom !== undefined) {
steps = WIDGETS.wpedom.getSteps();
} else if (WIDGETS.activepedom !== undefined) {
steps = WIDGETS.activepedom.getSteps();
} else {
steps = Bangle.getHealthStatus("day").steps;
}
} catch(ex) {
// In case we failed, we can only show 0 steps.
return "? k";
}
steps = Math.round(steps/1000);
return steps + "k";
}
function draw() {
queueDraw();
g.reset(); g.reset();
g.clear(); g.clear();
g.setColor(0, 255, 255); g.setColor(0, 255, 255);
@ -94,40 +132,44 @@ function DrawScene() {
g.drawString("Launching Process", 30, 20); g.drawString("Launching Process", 30, 20);
g.setFont("8x12"); g.setFont("8x12");
g.drawString("ACTIVATE", 40, 35); g.drawString("ACTIVATE", 40, 35);
g.setFontAlign(0,-1);
g.setFont("8x12", 2); g.setFont("8x12", 2);
g.drawString("30", 142, 132); g.drawString(getTemperature(), 155, 132);
g.drawString("55", 95, 98); g.drawString(Math.round(Bangle.getHealthStatus("last").bpm), 109, 98);
g.setFont("8x12", 1); g.drawString(getSteps(), 158, 98);
g.drawString(Bangle.getStepCount(), 143, 104);
ClockInterval = setInterval(DrawClock, 30000); g.setFontAlign(-1,-1);
DrawClock(); drawClock();
RocketInterval = setInterval(DrawRocket, rocketSpeed); drawRocket();
DrawRocket(); drawBattery();
BatteryInterval = setInterval(DrawBattery, 5 * 60000);
DrawBattery(); // Hide widgets
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
} }
Bangle.on("lcdPower", (on) => { Bangle.on("lcdPower", (on) => {
if (!on) { if (on) {
g.clear(); draw();
ClearIntervals(true); } else {
clearIntervals();
} }
}); });
Bangle.on("lock", (locked) => { Bangle.on("lock", (locked) => {
if (locked) { clearIntervals();
ClearIntervals(true); draw();
} else { if (!locked) {
ClearIntervals(); rocketInterval = setInterval(drawRocket, rocketSpeed);
DrawScene();
} }
}); });
// Load widgets, but don't show them
Bangle.loadWidgets();
Bangle.setUI("clock");
g.reset(); g.reset();
g.clear(); g.clear();
Bangle.setUI("clock"); draw();
DrawScene();
if (Bangle.isLocked()) {
ClearIntervals(true);
}

View File

@ -4,7 +4,7 @@
"description": "Animated Clock with Space Cassio Watch Style", "description": "Animated Clock with Space Cassio Watch Style",
"screenshots": [{ "url": "screens/screen_night.png" },{ "url": "screens/screen_day.png" }], "screenshots": [{ "url": "screens/screen_night.png" },{ "url": "screens/screen_day.png" }],
"icon": "app.png", "icon": "app.png",
"version": "0.9", "version": "0.10",
"type": "clock", "type": "clock",
"tags": "clock, weather, cassio, retro", "tags": "clock, weather, cassio, retro",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],

View File

@ -1,2 +1,3 @@
0.01: New clock 0.01: New clock
0.02: Use ClockFace library, add settings 0.02: Use ClockFace library, add settings
0.03: Use ClockFace_menu.addSettingsFile

View File

@ -1,7 +1,7 @@
{ {
"id": "cogclock", "id": "cogclock",
"name": "Cog Clock", "name": "Cog Clock",
"version": "0.02", "version": "0.03",
"description": "A cross-shaped clock inside a cog", "description": "A cross-shaped clock inside a cog",
"icon": "icon.png", "icon": "icon.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],

View File

@ -1,19 +1,10 @@
(function(back) { (function(back) {
let s = require('Storage').readJSON("cogclock.settings.json", true) || {}; let menu = {
function saver(key) {
return value => {
s[key] = value;
require('Storage').writeJSON("cogclock.settings.json", s);
}
}
const menu = {
"": {"title": /*LANG*/"Cog Clock"}, "": {"title": /*LANG*/"Cog Clock"},
/*LANG*/"< Back": back, /*LANG*/"< Back": back,
/*LANG*/"Show date": require("ClockFace_menu").showDate(s.showDate, saver('showDate')),
/*LANG*/"Load widgets": require("ClockFace_menu").loadWidgets(s.loadWidgets, saver('loadWidgets')),
}; };
require("ClockFace_menu").addSettingsFile(menu, "cogclock.settings.json", [
"showDate", "loadWidgets"
]);
E.showMenu(menu); E.showMenu(menu);
}); });

View File

@ -11,4 +11,5 @@
0.11: Fix bangle.js 1 white icons not displaying 0.11: Fix bangle.js 1 white icons not displaying
0.12: On Bangle 2 change to swiping up/down to move between pages as to match page indicator. Swiping from left to right now loads the clock. 0.12: On Bangle 2 change to swiping up/down to move between pages as to match page indicator. Swiping from left to right now loads the clock.
0.13: Added swipeExit setting so that left-right to exit is an option 0.13: Added swipeExit setting so that left-right to exit is an option
0.14: Don't move pages when doing exit swipe. 0.14: Don't move pages when doing exit swipe - Bangle 2.
0.15: 'Swipe to exit'-code is slightly altered to be more reliable - Bangle 2.

View File

@ -27,7 +27,7 @@ Bangle 2:
## Controls- Bangle 2 ## Controls- Bangle 2
**Touch** - icon to select, scond touch launches app **Touch** - icon to select, second touch launches app
**Swipe Left/Up** - move to next page of app icons **Swipe Left/Up** - move to next page of app icons

View File

@ -89,7 +89,7 @@ function drawPage(p){
Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{ Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{
selected = 0; selected = 0;
oldselected=-1; oldselected=-1;
if(settings.swipeExit && dirLeftRight==1) showClock(); if(settings.swipeExit && dirLeftRight==1) load();
if (dirUpDown==-1||dirLeftRight==-1){ if (dirUpDown==-1||dirLeftRight==-1){
++page; if (page>maxPage) page=0; ++page; if (page>maxPage) page=0;
drawPage(page); drawPage(page);
@ -99,12 +99,6 @@ Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{
} }
}); });
function showClock(){
var app = require("Storage").readJSON('setting.json', 1).clock;
if (app) load(app);
else E.showMessage("clock\nnot found");
}
function isTouched(p,n){ function isTouched(p,n){
if (n<0 || n>3) return false; if (n<0 || n>3) return false;
var x1 = (n%2)*72+XOFF; var y1 = n>1?72+YOFF:YOFF; var x1 = (n%2)*72+XOFF; var y1 = n>1?72+YOFF:YOFF;

View File

@ -1,7 +1,7 @@
{ {
"id": "dtlaunch", "id": "dtlaunch",
"name": "Desktop Launcher", "name": "Desktop Launcher",
"version": "0.14", "version": "0.15",
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.", "description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}], "screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
"icon": "icon.png", "icon": "icon.png",

View File

@ -6,7 +6,7 @@
"description": "Send commands to other Espruino devices via the Bluetooth UART interface. Customisable commands!", "description": "Send commands to other Espruino devices via the Bluetooth UART interface. Customisable commands!",
"icon": "app.png", "icon": "app.png",
"tags": "", "tags": "",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"custom": "custom.html", "custom": "custom.html",
"storage": [ "storage": [

View File

@ -2,3 +2,5 @@
0.02: Shows the current week number (ISO8601), can be disabled via settings 0.02: Shows the current week number (ISO8601), can be disabled via settings
0.03: Call setUI before loading widgets 0.03: Call setUI before loading widgets
Improve settings page Improve settings page
0.04: Use ClockFace library

View File

@ -1,22 +1,3 @@
const locale = require("locale");
const is12Hour = Object.assign({ "12hour": false }, require("Storage").readJSON("setting.json", true))["12hour"];
const showWeekNum = Object.assign({ showWeekNum: true }, require('Storage').readJSON("ffcniftya.json", true))["showWeekNum"];
/* Clock *********************************************/
const scale = g.getWidth() / 176;
const widget = 24;
const viewport = {
width: g.getWidth(),
height: g.getHeight(),
}
const center = {
x: viewport.width / 2,
y: Math.round(((viewport.height - widget) / 2) + widget),
}
// copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480 // copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
function ISO8601_week_no(date) { function ISO8601_week_no(date) {
var tdt = new Date(date.valueOf()); var tdt = new Date(date.valueOf());
@ -30,77 +11,49 @@ function ISO8601_week_no(date) {
return 1 + Math.ceil((firstThursday - tdt) / 604800000); return 1 + Math.ceil((firstThursday - tdt) / 604800000);
} }
function d02(value) { function format(value) {
return ('0' + value).substr(-2); return ("0" + value).substr(-2);
} }
function draw() { const ClockFace = require("ClockFace");
g.reset(); const clock = new ClockFace({
g.clearRect(0, widget, viewport.width, viewport.height); init: function () {
const now = new Date(); const appRect = Bangle.appRect;
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0)); this.viewport = {
const minutes = d02(now.getMinutes()); width: appRect.w,
const day = d02(now.getDate()); height: appRect.h
const month = d02(now.getMonth() + 1); };
const year = now.getFullYear(now);
const weekNum = d02(ISO8601_week_no(now));
const monthName = locale.month(now, 3);
const dayName = locale.dow(now, 3);
const centerTimeScaleX = center.x + 32 * scale; this.center = {
g.setFontAlign(1, 0).setFont("Vector", 90 * scale); x: this.viewport.width / 2,
g.drawString(hour, centerTimeScaleX, center.y - 31 * scale); y: Math.round((this.viewport.height / 2) + appRect.y)
g.drawString(minutes, centerTimeScaleX, center.y + 46 * scale); };
g.fillRect(center.x + 30 * scale, center.y - 72 * scale, center.x + 32 * scale, center.y + 74 * scale); this.scale = g.getWidth() / this.viewport.width;
this.centerTimeScaleX = this.center.x + 32 * this.scale;
this.centerDatesScaleX = this.center.x + 40 * this.scale;
},
draw: function (date) {
const hour = date.getHours() - (this.is12Hour && date.getHours() > 12 ? 12 : 0);
const month = date.getMonth() + 1;
const monthName = require("date_utils").month(month, 1);
const dayName = require("date_utils").dow(date.getDay(), 1);
const centerDatesScaleX = center.x + 40 * scale; g.setFontAlign(1, 0).setFont("Vector", 90 * this.scale);
g.setFontAlign(-1, 0).setFont("Vector", 16 * scale); g.drawString(format(hour), this.centerTimeScaleX, this.center.y - 31 * this.scale);
g.drawString(year, centerDatesScaleX, center.y - 62 * scale); g.drawString(format(date.getMinutes()), this.centerTimeScaleX, this.center.y + 46 * this.scale);
g.drawString(month, centerDatesScaleX, center.y - 44 * scale);
g.drawString(day, centerDatesScaleX, center.y - 26 * scale);
if (showWeekNum) g.drawString(weekNum, centerDatesScaleX, center.y + 15 * scale);
g.drawString(monthName, centerDatesScaleX, center.y + 48 * scale);
g.drawString(dayName, centerDatesScaleX, center.y + 66 * scale);
}
g.fillRect(this.center.x + 30 * this.scale, this.center.y - 72 * this.scale, this.center.x + 32 * this.scale, this.center.y + 74 * this.scale);
/* Minute Ticker *************************************/ g.setFontAlign(-1, 0).setFont("Vector", 16 * this.scale);
g.drawString(date.getFullYear(date), this.centerDatesScaleX, this.center.y - 62 * this.scale);
let tickTimer; g.drawString(format(month), this.centerDatesScaleX, this.center.y - 44 * this.scale);
g.drawString(format(date.getDate()), this.centerDatesScaleX, this.center.y - 26 * this.scale);
function clearTickTimer() { if (this.showWeekNum) g.drawString(format(ISO8601_week_no(date)), this.centerDatesScaleX, this.center.y + 15 * this.scale);
if (tickTimer) { g.drawString(monthName, this.centerDatesScaleX, this.center.y + 48 * this.scale);
clearTimeout(tickTimer); g.drawString(dayName, this.centerDatesScaleX, this.center.y + 66 * this.scale);
tickTimer = undefined; },
} settingsFile: "ffcniftya.json"
}
function queueNextTick() {
clearTickTimer();
tickTimer = setTimeout(tick, 60000 - (Date.now() % 60000));
}
function tick() {
draw();
queueNextTick();
}
/* Init **********************************************/
// Clear the screen once, at startup
g.clear();
tick();
Bangle.on('lcdPower', (on) => {
if (on) {
tick();
} else {
clearTickTimer();
}
}); });
clock.start();
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -1,7 +1,7 @@
{ {
"id": "ffcniftya", "id": "ffcniftya",
"name": "Nifty-A Clock", "name": "Nifty-A Clock",
"version": "0.03", "version": "0.04",
"description": "A nifty clock with time and date", "description": "A nifty clock with time and date",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot_nifty.png"}], "screenshots": [{"url":"screenshot_nifty.png"}],

View File

@ -3,3 +3,4 @@
0.03: Call setUI before loading widgets 0.03: Call setUI before loading widgets
Fix bug with black being unselectable Fix bug with black being unselectable
Improve settings page Improve settings page
0.04: Use ClockFace library

View File

@ -1,20 +1,10 @@
const is12Hour = Object.assign({ "12hour": false }, require("Storage").readJSON("setting.json", true))["12hour"]; var scale;
const color = Object.assign({ color: 63488 }, require("Storage").readJSON("ffcniftyb.json", true)).color; // Default to RED var screen;
var center;
var buf;
var img;
/* Clock *********************************************/ function format(value) {
const scale = g.getWidth() / 176;
const screen = {
width: g.getWidth(),
height: g.getHeight() - 24,
};
const center = {
x: screen.width / 2,
y: screen.height / 2,
};
function d02(value) {
return ("0" + value).substr(-2); return ("0" + value).substr(-2);
} }
@ -22,91 +12,69 @@ function renderEllipse(g) {
g.fillEllipse(center.x - 5 * scale, center.y - 70 * scale, center.x + 160 * scale, center.y + 90 * scale); g.fillEllipse(center.x - 5 * scale, center.y - 70 * scale, center.x + 160 * scale, center.y + 90 * scale);
} }
function renderText(g) { function renderText(g, date) {
const now = new Date(); const hour = date.getHours() - (this.is12Hour && date.getHours() > 12 ? 12 : 0);
const month = date.getMonth() + 1;
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0)); const monthName = require("date_utils").month(month, 1);
const minutes = d02(now.getMinutes()); const dayName = require("date_utils").dow(date.getDay(), 1);
const day = d02(now.getDate());
const month = d02(now.getMonth() + 1);
const year = now.getFullYear();
const month2 = require("locale").month(now, 3);
const day2 = require("locale").dow(now, 3);
g.setFontAlign(1, 0).setFont("Vector", 90 * scale); g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
g.drawString(hour, center.x + 32 * scale, center.y - 31 * scale); g.drawString(format(hour), center.x + 32 * scale, center.y - 31 * scale);
g.drawString(minutes, center.x + 32 * scale, center.y + 46 * scale); g.drawString(format(date.getMinutes()), center.x + 32 * scale, center.y + 46 * scale);
g.setFontAlign(1, 0).setFont("Vector", 16 * scale); g.setFontAlign(1, 0).setFont("Vector", 16 * scale);
g.drawString(year, center.x + 80 * scale, center.y - 42 * scale); g.drawString(date.getFullYear(), center.x + 80 * scale, center.y - 42 * scale);
g.drawString(month, center.x + 80 * scale, center.y - 26 * scale); g.drawString(format(month), center.x + 80 * scale, center.y - 26 * scale);
g.drawString(day, center.x + 80 * scale, center.y - 10 * scale); g.drawString(format(date.getDate()), center.x + 80 * scale, center.y - 10 * scale);
g.drawString(month2, center.x + 80 * scale, center.y + 44 * scale); g.drawString(monthName, center.x + 80 * scale, center.y + 44 * scale);
g.drawString(day2, center.x + 80 * scale, center.y + 60 * scale); g.drawString(dayName, center.x + 80 * scale, center.y + 60 * scale);
} }
const buf = Graphics.createArrayBuffer(screen.width, screen.height, 1, { const ClockFace = require("ClockFace");
msb: true const clock = new ClockFace({
init: function () {
const appRect = Bangle.appRect;
screen = {
width: appRect.w,
height: appRect.h
};
center = {
x: screen.width / 2,
y: screen.height / 2
};
buf = Graphics.createArrayBuffer(screen.width, screen.height, 1, { msb: true });
scale = g.getWidth() / screen.width;
img = {
width: screen.width,
height: screen.height,
transparent: 0,
bpp: 1,
buffer: buf.buffer
};
// default to RED (see settings.js)
// don't use || to default because 0 is a valid color
this.color = this.color === undefined ? 63488 : this.color;
},
draw: function (date) {
// render outside text with ellipse
buf.clear();
renderText(buf.setColor(1), date);
renderEllipse(buf.setColor(0));
g.setColor(this.color).drawImage(img, 0, 24);
// render ellipse with inside text
buf.clear();
renderEllipse(buf.setColor(1));
renderText(buf.setColor(0), date);
g.setColor(this.color).drawImage(img, 0, 24);
},
settingsFile: "ffcniftyb.json"
}); });
clock.start();
function draw() {
const img = {
width: screen.width,
height: screen.height,
transparent: 0,
bpp: 1,
buffer: buf.buffer
};
// cleat screen area
g.clearRect(0, 24, g.getWidth(), g.getHeight());
// render outside text with ellipse
buf.clear();
renderText(buf.setColor(1));
renderEllipse(buf.setColor(0));
g.setColor(color).drawImage(img, 0, 24);
// render ellipse with inside text
buf.clear();
renderEllipse(buf.setColor(1));
renderText(buf.setColor(0));
g.setColor(color).drawImage(img, 0, 24);
}
/* Minute Ticker *************************************/
let ticker;
function stopTick() {
if (ticker) {
clearTimeout(ticker);
ticker = undefined;
}
}
function startTick(run) {
stopTick();
run();
ticker = setTimeout(() => startTick(run), 60000 - (Date.now() % 60000));
}
/* Init **********************************************/
g.clear();
startTick(draw);
Bangle.on("lcdPower", (on) => {
if (on) {
startTick(draw);
} else {
stopTick();
}
});
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -1,7 +1,7 @@
{ {
"id": "ffcniftyb", "id": "ffcniftyb",
"name": "Nifty-B Clock", "name": "Nifty-B Clock",
"version": "0.03", "version": "0.04",
"description": "A nifty clock (series B) with time, date and colour configuration", "description": "A nifty clock (series B) with time, date and colour configuration",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],

View File

@ -1 +1,2 @@
1.00: Initial implementation 1.00: Initial implementation
1.01: Bug fixes and performance and visual improvements

View File

@ -1,4 +1,19 @@
// globals. TODO: move into an object
const digit = []; const digit = [];
let part = 0;
let endInc = 0;
var endGame = false;
let goalFrame = 0;
var stopped = true;
let score0 = 0;
let score1 = 0;
let inc = 0;
let msinc = 0;
let seq0 = 0;
let seq1 = 0;
let goaler = -1;
const w = g.getWidth();
let owner = -1;
const dash = { const dash = {
width: 75, width: 75,
@ -6,6 +21,7 @@ const dash = {
bpp: 1, bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4A/AH4A/AH4A/AH4A/AB0D/4AB+AJEBAX/BAk/CQ8PCQ4kDCQoIDCQgkDCQgkECQgIE4ASHFxH8JRgSEEgYSEPJAkEAH4A/AH4A/AH4A/AH4A/ACg=')) buffer: require('heatshrink').decompress(atob('AH4A/AH4A/AH4A/AH4A/AB0D/4AB+AJEBAX/BAk/CQ8PCQ4kDCQoIDCQgkDCQgkECQgIE4ASHFxH8JRgSEEgYSEPJAkEAH4A/AH4A/AH4A/AH4A/ACg='))
}; };
function loadDigits () { function loadDigits () {
digit[0] = { digit[0] = {
width: 80, width: 80,
@ -58,8 +74,8 @@ function loadDigits () {
digit[7] = { digit[7] = {
width: 80, width: 80,
height: 128, height: 128,
bpp: 1, bpp: 4,
buffer: require('heatshrink').decompress(atob('AGUH/4AE/wJBgYJF/gJBgIJF+AeCBJN/BIngsAJBn4JE4HgBIMfBImBBIUPBIkDBIRQE/0HBIRQE/kPBIRQE/EfBIRQE+E/BIZQD8AJEKAfABYIJCKAYsBBIYADIAIJHKgIJHNAIJ/BP4J/BP4Jzg//4AJGgf/wAJGgP/BAwAB/wJIvgJInAJIiAJIAH5PPMZJ3JRZCfJWZLHJfM4J/BP4J/BP4JNg4JIgYJIgIJIgAJJv4JIn4JIj4JIh4JIeg4JIgYJIgIJIgAJJsAJIkAJIAH4AQA=')) buffer : require("heatshrink").decompress(atob("AH4A/AEtVADdQE5Nf/4AayAnJgoma/J4LKDR2KKDZODUMadChKhiJwefE5RQXJwbxLKCxOEE5hQVJwgnNKCZOFE5pQTJwonOKCJOGE5xQRD4Q8EE5xQPJw4nPgFZzIAMqCdFE6IARJwgnhJwonhJwonhe5In/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4nlr4mE/NQE78FE4n1Ez5QGE0JQEJ0RQETsBQFJ0gABrJOkAH4A/AH4A/ADNZqAmkgv/yAnkr///JQjJwIABypOkAAP5J0oABUMJODKAShgEwhQiE/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/AA+fE80JE8xQGE8JQFE8JQFE8RQEE8RQEE8ZQDE8ZQDE8hQCE8hQCE8pQBE8pQBE80JE80AE84A/AH4A/AH4A/AAQA=="))
}; };
digit[8] = { digit[8] = {
@ -170,9 +186,6 @@ const gol11 = {
loadDigits(); loadDigits();
let goalFrame = 0;
let score0 = 0;
let score1 = 0;
function printNumber (n, x, y, options) { function printNumber (n, x, y, options) {
if (n > 9 || n < -1) { if (n > 9 || n < -1) {
@ -197,13 +210,7 @@ function printNumber (n, x, y, options) {
g.drawImage(img, x, y, options); g.drawImage(img, x, y, options);
} }
} }
let inc = 0;
let msinc = 0;
let seq0 = 0;
let seq1 = 0;
let goaler = -1;
const w = g.getWidth();
let owner = -1;
g.setBgColor(0, 0, 0); g.setBgColor(0, 0, 0);
g.clear(); g.clear();
g.setColor(1, 1, 1); g.setColor(1, 1, 1);
@ -247,43 +254,63 @@ function onStop () {
refresh(); refresh();
refresh_ms(); refresh_ms();
} }
var stopped = true;
Bangle.on('tap', function (pos) { function onButtonPress() {
console.log('touch', pos); console.log('on.tap');
setWatch(() => {
onButtonPress();
}, BTN1);
Bangle.beep();
if (endGame) { if (endGame) {
Bangle.beep();
score0 = 0; score0 = 0;
score1 = 0; score1 = 0;
seq0 = 0; seq0 = 0;
seq1 = 0; seq1 = 0;
part = 0;
inc = 0; inc = 0;
msinc = 0; msinc = 0;
stopped = true; stopped = true;
endGame = false; endGame = false;
} else { } else {
if (inc == 0) { if (inc == 0) {
autogame(); // autogame();
stopped = false;
} else { } else {
onStop(); onStop();
} }
} }
}
setWatch(() => {
onButtonPress();
}, BTN1);
/*Bangle.on('tap', function () {
onButtonPress();
}); });
*/
g.setFont12x20(3); g.setFont12x20(3);
let part = 0;
let endInc = 0;
var endGame = false;
function refresh () { function refresh () {
g.clear(); g.clear();
if (inc > 59) { if (inc > 59) {
inc = 0; inc = 0;
part++; part++;
} }
if (part >= 2 && inc > 30) {
part = 2;
Bangle.buzz();
endGame = true;
endInc = inc;
inc = 0;
}
if (inc > 44) { if (inc > 44) {
inc = 0;
if (part < 2) { if (part < 2) {
part++; part++;
} }
if (part >= 2) { if (part >= 2) {
if (score0 != score1) { if (score0 != score1) {
Bangle.buzz();
endGame = true; endGame = true;
endInc = inc; endInc = inc;
inc = 0; inc = 0;
@ -342,6 +369,12 @@ function refresh_pixels () {
let bx = (owner == 0) ? w / 3 : w / 2; let bx = (owner == 0) ? w / 3 : w / 2;
bx += 2; bx += 2;
g.drawImage(frame4 ? ball0 : ball1, bx, 10, { scale: 5 }); g.drawImage(frame4 ? ball0 : ball1, bx, 10, { scale: 5 });
const liney = 60;
if (owner) {
g.drawLine(w-8, liney, 2*(w/3), liney);
} else {
g.drawLine(8, liney, w/3, liney);
}
} }
let dots = 0; let dots = 0;
function refresh_dots () { function refresh_dots () {
@ -437,4 +470,5 @@ function autogame () {
} }
Bangle.setOptions({ lockTimeout: 0, backlightTimeout: 0 }); Bangle.setOptions({ lockTimeout: 0, backlightTimeout: 0 });
autogame(); // autogame();

View File

@ -82,6 +82,7 @@ function onInit(device) {
if (crc==46757280) version = "2v11.58"; if (crc==46757280) version = "2v11.58";
if (crc==3508163280 || crc==1418074094) version = "2v12"; if (crc==3508163280 || crc==1418074094) version = "2v12";
if (crc==4056371285) version = "2v13"; if (crc==4056371285) version = "2v13";
if (crc==1038322422) version = "2v14";
if (!ok) { if (!ok) {
version += `(&#9888; update required)`; version += `(&#9888; update required)`;
} }

View File

@ -113,7 +113,7 @@ function getMonthList() {
Util.showModal("Deleting..."); Util.showModal("Deleting...");
Util.eraseStorage(filename,()=>{ Util.eraseStorage(filename,()=>{
Util.hideModal(); Util.hideModal();
getTrackList(); getMonthList();
}); });
} }
if (task=="downloadcsv") { if (task=="downloadcsv") {

View File

@ -0,0 +1,7 @@
0.01: Release for Bangle 2 (2021/11/18)
0.02: Internal id update to wid_* as per Gordon's request (2021/11/21)
0.03: Support dark themes
0.04: Increase screen update rate when charging
0.05: Deleting Background - making Font larger
0.06: Fixing refresh issues
0.07

View File

@ -0,0 +1,15 @@
# A Battery Widget (with percentage)
Show the current battery level and charging status in the top right of the clock, with charge percentage
* Works with Bangle 2
* Simple design, no settings
* Red when the batterly level is below 30%
* Blue when charging
* 40 pixels wide
![](a_battery_widget-pic.jpg)
## Creator
[@alainsaas](https://github.com/alainsaas)
Mod by Hank

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -0,0 +1,15 @@
{
"id": "hwid_a_battery_widget",
"name": "A Battery Widget (with percentage) - Hanks Mod",
"shortName":"H Battery Widget",
"icon": "widget.png",
"version":"0.07",
"type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"description": "Simple and slim battery widget with charge status and percentage",
"tags": "widget,battery",
"storage": [
{"name":"hwid_a_battery_widget.wid.js","url":"widget.js"}
]
}

View File

@ -0,0 +1,54 @@
(function(){
const intervalLow = 60000; // update time when not charging
const intervalHigh = 2000; // update time when charging
var old_l;
let COLORS = {
'white': g.theme.dark ? "#000" : "#fff",
'black': g.theme.dark ? "#fff" : "#000",
'charging': "#08f",
'high': g.theme.dark ? "#fff" : "#000",
'low': "#f00",
};
const levelColor = (l) => {
if (Bangle.isCharging()) return COLORS.charging;
if (l >= 30) return COLORS.high;
return COLORS.low;
};
function draw() {
var s = 29;
var x = this.x, y = this.y;
const l = E.getBattery();
let xl = x+4+l*(s-12)/100;
if (l != old_l){ // Delete the old value from screen
old_l = l;
let xl_old = x+4+old_l*(s-12)/100;
g.setColor(COLORS.white);
// g.fillRect(x+2,y+5,x+s-6,y+18);
g.fillRect(x,y,xl+4,y+16+3); //Clear
g.setFontAlign(0,0);
g.setFont('Vector',16);
g.drawString(old_l, x + 14, y + 10);
g.fillRect(x+4,y+14+3,xl_old,y+16+3); // charging bar
}
g.setColor(levelColor(l));
g.fillRect(x+4,y+14+3,xl,y+16+3); // charging bar
g.fillRect((x+4+100*(s-12)/100)-1,y+14+3,x+4+100*(s-12)/100,y+16+3); // charging bar "full mark"
// Show percentage
g.setColor(COLORS.black);
g.setFontAlign(0,0);
g.setFont('Vector',16);
g.drawString(l, x + 14, y + 10);
if (Bangle.isCharging()) changeInterval(id, intervalHigh);
else changeInterval(id, intervalLow);
}
Bangle.on('charging',function(charging) { draw(); });
var id = setInterval(()=>WIDGETS["wid_a_battery_widget"].draw(), intervalLow);
WIDGETS["wid_a_battery_widget"]={area:"tr",width:30,draw:draw};
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

View File

@ -0,0 +1,7 @@
0.15: Initial release - be patient as this is the first try :)
0.16: Fix timing
0.17: Fix hours
0.18: Code cleanup and major changes with seconds timing. New feature: if watch is locked, seconds get refreshed every 10 seconds.
0.19: Fix PM Hours
0.20: Add theme support
0.21: Add Settings

View File

@ -0,0 +1,31 @@
# Hanks World Clock - See the time in four locations
In addition to the main clock and date in your current location, you can add up to three other locations. Great for travel or remote working.
Additionally we show the sunset/sunrise and seconds for the current location and the day name is shown in your locale.
If watch is locked, seconds get refreshed every 10 seconds.
![](hworldclock.png)
## Usage
Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India).
The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the app store and update. Currently the clock only supports 24 hour time format for the additional time zones.
## Requests
Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if you have feature requests or notice bugs.
## Creator
Created by Hank.
Based on the great work of
=================
World Clock - 4 time zones
Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock).
===== a n d =====
Sun Clock
[Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock)
=================

389
apps/hworldclock/app.js Normal file
View File

@ -0,0 +1,389 @@
// ------- Settings file
const SETTINGSFILE = "hworldclock.json";
var secondsMode;
var showSunInfo;
var colorWhenDark;
// ------- Settings file
const big = g.getWidth()>200;
// Font for primary time and date
const primaryTimeFontSize = big?6:5;
const primaryDateFontSize = big?3:2;
require("Font5x9Numeric7Seg").add(Graphics);
require("FontTeletext10x18Ascii").add(Graphics);
// Font for single secondary time
const secondaryTimeFontSize = 4;
const secondaryTimeZoneFontSize = 2;
// Font / columns for multiple secondary times
const secondaryRowColFontSize = 2;
const xcol1 = 10;
const xcol2 = g.getWidth() - xcol1;
const font = "6x8";
/* TODO: we could totally use 'Layout' here and
avoid a whole bunch of hard-coded offsets */
const xyCenter = g.getWidth() / 2;
const xyCenterSeconds = xyCenter + (big ? 85 : 68);
const yAmPm = xyCenter - (big ? 70 : 48);
const yposTime = big ? 70 : 55;
const yposTime2 = yposTime + (big ? 100 : 60);
const yposDate = big ? 135 : 95;
const yposWorld = big ? 170 : 120;
const OFFSET_TIME_ZONE = 0;
const OFFSET_HOURS = 1;
var PosInterval = 0;
var offsets = require("Storage").readJSON("hworldclock.settings.json") || [];
//=======Sun
setting = require("Storage").readJSON("setting.json",1);
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
SunCalc = require("hsuncalc.js");
const LOCATION_FILE = "mylocation.json";
var rise = "07:00";
var set = "20:00";
var pos = {altitude: 20, azimuth: 135};
var noonpos = {altitude: 37, azimuth: 180};
//=======Sun
var ampm = "AM";
// TESTING CODE
// Used to test offset array values during development.
// Uncomment to override secondary offsets value
/*
const mockOffsets = {
zeroOffsets: [],
oneOffset: [["UTC", 0]],
twoOffsets: [
["Tokyo", 9],
["UTC", 0],
],
fourOffsets: [
["Tokyo", 9],
["UTC", 0],
["Denver", -7],
["Miami", -5],
],
};*/
// Example hworldclock.settings.json
// [["London","0"],["NY","-5"],["Denver","-6"]]
// Uncomment one at a time to test various offsets array scenarios
//offsets = mockOffsets.zeroOffsets; // should render nothing below primary time
//offsets = mockOffsets.oneOffset; // should render larger in two rows
//offsets = mockOffsets.twoOffsets; // should render two in columns
//offsets = mockOffsets.fourOffsets; // should render in columns
// END TESTING CODE
// Load settings
function loadMySettings() {
// Helper function default setting
function def (value, def) {return value !== undefined ? value : def;}
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
secondsMode = def(settings.secondsMode, "when unlocked");
showSunInfo = def(settings.showSunInfo, true);
colorWhenDark = def(settings.colorWhenDark, "green");
}
// Check settings for what type our clock should be
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
// timeout used to update every minute
var drawTimeout;
var drawTimeoutSeconds;
var secondsTimeout;
g.setBgColor(g.theme.bg);
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
// schedule a draw for the next second
function queueDrawSeconds() {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = setTimeout(function() {
drawTimeoutSeconds = undefined;
drawSeconds();
//console.log("TO: " + secondsTimeout);
}, secondsTimeout - (Date.now() % secondsTimeout));
}
function doublenum(x) {
return x < 10 ? "0" + x : "" + x;
}
function getCurrentTimeFromOffset(dt, offset) {
return new Date(dt.getTime() + offset * 60 * 60 * 1000);
}
function updatePos() {
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"};
pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
rise = times.sunrise.toString().split(" ")[4].substr(0,5);
set = times.sunset.toString().split(" ")[4].substr(0,5);
noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
}
function drawSeconds() {
// get date
var d = new Date();
var da = d.toString().split(" ");
// default draw styles
g.reset();
g.setBgColor(g.theme.bg);
// drawSting centered
g.setFontAlign(0, 0);
// draw time
var time = da[4].split(":");
var seconds = time[2];
g.setFont("5x9Numeric7Seg",primaryTimeFontSize - 3);
if (g.theme.dark) {
if (colorWhenDark == "green") {
g.setColor("#22ff05");
} else {
g.setColor(g.theme.fg);
}
} else {
g.setColor(g.theme.fg);
}
//console.log("---");
//console.log(seconds);
if (Bangle.isLocked() && secondsMode != "always") seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x
//console.log(seconds);
g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true);
queueDrawSeconds();
}
function draw() {
// get date
var d = new Date();
var da = d.toString().split(" ");
// default draw styles
g.reset();
g.setBgColor(g.theme.bg);
// drawSting centered
g.setFontAlign(0, 0);
// draw time
var time = da[4].split(":");
var hours = time[0],
minutes = time[1];
if (_12hour){
//do 12 hour stuff
if (hours > 12) {
ampm = "PM";
hours = hours - 12;
if (hours < 10) hours = doublenum(hours);
} else {
ampm = "AM";
}
}
//g.setFont(font, primaryTimeFontSize);
g.setFont("5x9Numeric7Seg",primaryTimeFontSize);
if (g.theme.dark) {
if (colorWhenDark == "green") {
g.setColor("#22ff05");
} else {
g.setColor(g.theme.fg);
}
} else {
g.setColor(g.theme.fg);
}
g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true);
// am / PM ?
if (_12hour){
//do 12 hour stuff
//var ampm = require("locale").medidian(new Date()); Not working
g.setFont("Vector", 17);
g.drawString(ampm, xyCenterSeconds, yAmPm, true);
}
if (secondsMode != "none") drawSeconds(); // To make sure...
// draw Day, name of month, Date
//DATE
var localDate = require("locale").date(new Date(), 1);
localDate = localDate.substring(0, localDate.length - 5);
g.setFont("Vector", 17);
g.drawString(require("locale").dow(new Date(), 1).toUpperCase() + ", " + localDate, xyCenter, yposDate, true);
g.setFont(font, primaryDateFontSize);
// set gmt to UTC+0
var gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
// Loop through offset(s) and render
offsets.forEach((offset, index) => {
dx = getCurrentTimeFromOffset(gmt, offset[OFFSET_HOURS]);
hours = doublenum(dx.getHours());
minutes = doublenum(dx.getMinutes());
if (offsets.length === 1) {
var date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
// For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30;
g.setFont(font, secondaryTimeFontSize);
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true);
// draw Day, name of month, Date
g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(date, xyCenter, yposDate, true);
} else if (index < 3) {
// For > 1 extra timezones, render as columns / rows
g.setFont(font, secondaryRowColFontSize);
g.setFontAlign(-1, 0);
g.drawString(
offset[OFFSET_TIME_ZONE],
xcol1,
yposWorld + index * 15,
true
);
g.setFontAlign(1, 0);
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
}
});
if (showSunInfo) {
g.setFontAlign(-1, 0);
g.setFont("Vector",12);
g.drawString(`^${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw riseset
g.setFontAlign(1, 0);
g.drawString(`v${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw riseset
}
//debug settings
//g.setFontAlign(1, 0);
//g.drawString(secondsMode, xcol2, 3 + yposWorld + 3 * 15, true);
//g.drawString(showSunInfo, xcol2, 3 + yposWorld + 3 * 15, true);
//g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true);
queueDraw();
if (secondsMode != "none") queueDrawSeconds();
}
// clean app screen
g.clear();
// Init the settings of the app
loadMySettings();
// Show launcher when button pressed
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
// draw immediately at first, queue update
draw();
if (!Bangle.isLocked()) { // Initial state
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
}
secondsTimeout = 1000;
if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined;
}
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}else{
if (secondsMode == "always") secondsTimeout = 1000;
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined;
}
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
}
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}
Bangle.on('lock',on=>{
if (!on) { // UNlocked
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
}
secondsTimeout = 1000;
if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined;
}
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}else{ // locked
if (secondsMode == "always") secondsTimeout = 1000;
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined;
}
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}
});

BIN
apps/hworldclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,76 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<p>You can add up to 3 timezones. Please give a name and UTC offset in hours.
If you want less than 3, clear the checkbox to the left.</p>
<table id="hworldclock-offsets">
<tr>
<th>Enabled?</th>
<th>Name</th>
<th>UTC Offset</th>
</tr>
</table>
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
<script src="../../core/lib/customize.js"></script>
<script>
var offsets=[];
try{
var stored = localStorage.getItem('hworldclock-offset-list')
if(stored) offsets = JSON.parse(stored);
if (!offsets || offsets.length!=3) {
throw "Offsets invalid";
}
} catch(e){
offsets=[
[true,"London",0],
[true,"NY",-5],
[true, "Denver",-6],
];
}
console.log(offsets);
var tbl=document.getElementById("hworldclock-offsets");
for (var i=0; i<3; i++) {
var $offset = document.createElement('tr')
$offset.innerHTML = `
<td><input type="checkbox" id="enabled_${i}" ${offsets[i][0]? "checked" : ""}></td>
<td><input type="text" id="name_${i}" value="${offsets[i][1]}"></td>
<td><input type="number" id="offset_${i}" value="${offsets[i][2]}"></td>`
tbl.append($offset);
}
// When the 'upload' button is clicked...
document.getElementById("upload").addEventListener("click", function() {
var storage_offsets=[];
var app_offsets=[];
for (var i=0; i<3; i++) {
var checked=document.getElementById("enabled_"+i).checked;
var name=document.getElementById("name_"+i).value;
var offset=document.getElementById("offset_"+i).value;
if (checked) {
app_offsets.push([name,offset]);
}
storage_offsets.push([checked,name,offset]);
}
console.log(storage_offsets);
console.log(app_offsets);
localStorage.setItem('worldclock-offset-list',JSON.stringify(storage_offsets));
// send finished app (in addition to contents of app.json)
sendCustomizedApp({
storage:[
{name:"hworldclock.settings.json", content:JSON.stringify(app_offsets)},
]
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,298 @@
/* Module suncalc.js
(c) 2011-2015, Vladimir Agafonkin
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
https://github.com/mourner/suncalc
PB: Usage:
E.setTimeZone(2); // 1 = MEZ, 2 = MESZ
SunCalc = require("suncalc.js");
pos = SunCalc.getPosition(Date.now(), 53.3, 10.1);
times = SunCalc.getTimes(Date.now(), 53.3, 10.1);
rise = times.sunrise; // Date object
rise_str = rise.getHours() + ':' + rise.getMinutes(); //hh:mm
*/
var exports={};
// shortcuts for easier to read formulas
var PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
tan = Math.tan,
asin = Math.asin,
atan = Math.atan2,
acos = Math.acos,
rad = PI / 180;
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
// date/time constants and conversions
var dayMs = 1000 * 60 * 60 * 24,
J1970 = 2440588,
J2000 = 2451545;
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } // PB: onece removed + 0.5; included it again 4 Jan 2021
function toDays(date) { return toJulian(date) - J2000; }
// general calculations for position
var e = rad * 23.4397; // obliquity of the Earth
function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
function astroRefraction(h) {
if (h < 0) // the following formula works for positive altitudes only.
h = 0; // if h = -0.08901179 a div/0 would occur.
// formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
// 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
}
// general sun calculations
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
function eclipticLongitude(M) {
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
P = rad * 102.9372; // perihelion of the Earth
return M + C + P + PI;
}
function sunCoords(d) {
var M = solarMeanAnomaly(d),
L = eclipticLongitude(M);
return {
dec: declination(L, 0),
ra: rightAscension(L, 0)
};
}
// calculates sun position for a given date and latitude/longitude
exports.getPosition = function (date, lat, lng) {
var lw = rad * -lng,
phi = rad * lat,
d = toDays(date),
c = sunCoords(d),
H = siderealTime(d, lw) - c.ra;
return {
azimuth: Math.round((azimuth(H, phi, c.dec) / rad + 180) % 360), // PB: converted to deg
altitude: Math.round( altitude(H, phi, c.dec) / rad) // PB: converted to deg
};
};
// sun times configuration (angle, morning name, evening name)
var times = [
[-0.833, 'sunrise', 'sunset' ]
];
// calculations for sun times
var J0 = 0.0009;
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
// returns set time for the given sun altitude
function getSetJ(h, lw, phi, dec, n, M, L) {
var w = hourAngle(h, phi, dec),
a = approxTransit(w, lw, n);
return solarTransitJ(a, M, L);
}
// calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon
exports.getTimes = function (date, lat, lng, height) {
height = height || 0;
var lw = rad * -lng,
phi = rad * lat,
dh = observerAngle(height),
d = toDays(date),
n = julianCycle(d, lw),
ds = approxTransit(0, lw, n),
M = solarMeanAnomaly(ds),
L = eclipticLongitude(M),
dec = declination(L, 0),
Jnoon = solarTransitJ(ds, M, L),
i, len, time, h0, Jset, Jrise;
var result = {
solarNoon: fromJulian(Jnoon),
nadir: fromJulian(Jnoon - 0.5)
};
for (i = 0, len = times.length; i < len; i += 1) {
time = times[i];
h0 = (time[0] + dh) * rad;
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
Jrise = Jnoon - (Jset - Jnoon);
result[time[1]] = fromJulian(Jrise);
result[time[2]] = fromJulian(Jset);
}
return result;
};
// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
function moonCoords(d) { // geocentric ecliptic coordinates of the moon
var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
M = rad * (134.963 + 13.064993 * d), // mean anomaly
F = rad * (93.272 + 13.229350 * d), // mean distance
l = L + rad * 6.289 * sin(M), // longitude
b = rad * 5.128 * sin(F), // latitude
dt = 385001 - 20905 * cos(M); // distance to the moon in km
return {
ra: rightAscension(l, b),
dec: declination(l, b),
dist: dt
};
}
getMoonPosition = function (date, lat, lng) {
var lw = rad * -lng,
phi = rad * lat,
d = toDays(date),
c = moonCoords(d),
H = siderealTime(d, lw) - c.ra,
h = altitude(H, phi, c.dec),
// formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
h = h + astroRefraction(h); // altitude correction for refraction
return {
azimuth: azimuth(H, phi, c.dec),
altitude: h,
distance: c.dist,
parallacticAngle: pa
};
};
// calculations for illumination parameters of the moon,
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
getMoonIllumination = function (date) {
var d = toDays(date || new Date()),
s = sunCoords(d),
m = moonCoords(d),
sdist = 149598000, // distance from Earth to Sun in km
phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
return {
fraction: (1 + cos(inc)) / 2,
phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
angle: angle
};
};
function hoursLater(date, h) {
return new Date(date.valueOf() + h * dayMs / 24);
}
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
getMoonTimes = function (date, lat, lng, inUTC) {
var t = new Date(date);
if (inUTC) t.setUTCHours(0, 0, 0, 0);
else t.setHours(0, 0, 0, 0);
var hc = 0.133 * rad,
h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
for (var i = 1; i <= 24; i += 2) {
h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
a = (h0 + h2) / 2 - h1;
b = (h2 - h0) / 2;
xe = -b / (2 * a);
ye = (a * xe + b) * xe + h1;
d = b * b - 4 * a * h1;
roots = 0;
if (d >= 0) {
dx = Math.sqrt(d) / (Math.abs(a) * 2);
x1 = xe - dx;
x2 = xe + dx;
if (Math.abs(x1) <= 1) roots++;
if (Math.abs(x2) <= 1) roots++;
if (x1 < -1) x1 = x2;
}
if (roots === 1) {
if (h0 < 0) rise = i + x1;
else set = i + x1;
} else if (roots === 2) {
rise = i + (ye < 0 ? x2 : x1);
set = i + (ye < 0 ? x1 : x2);
}
if (rise && set) break;
h0 = h2;
}
var result = {};
if (rise) result.rise = hoursLater(t, rise);
if (set) result.set = hoursLater(t, set);
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
return result;
};

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgJC/ABEE+EA4EAj9E8HF//gn/gwP///wt/MgF//8gh/8gYLBwEP+EHAofghgFD4EOj//gEPA4ILBGgIxB/wFBgwFB/lsgCKBj/4oxHBvAFBJoV8gP4TQX+gJUBAAN/Aok+AoVgAoXogAfBjkA8AfBAoXAAoUYY4cAiCDEAooA/ABg"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,25 @@
{
"id": "hworldclock",
"name": "Hanks World Clock",
"shortName": "Hanks World Clock",
"version": "0.21",
"description": "Current time zone plus up to three others",
"allow_emulator":true,
"icon": "app.png",
"screenshots": [{"url":"screenshot_hworld.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"custom": "custom.html",
"storage": [
{"name":"hworldclock.app.js","url":"app.js"},
{"name":"hworldclock.img","url":"hworldclock-icon.js","evaluate":true},
{"name":"hworldclock.settings.js","url":"settings.js"},
{"name":"hsuncalc.js","url":"hsuncalc.js"}
],
"data": [
{"name":"hworldclock.settings.json"},
{"name":"hworldclock.json"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,59 @@
// Settings menu for the enhanced Anton clock
(function(back) {
var FILE = "hworldclock.json";
// Load settings
var settings = Object.assign({
secondsOnUnlock: false,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
// Helper method which uses int-based menu item for set of string values
function stringItems(startvalue, writer, values) {
return {
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
format: v => values[v],
min: 0,
max: values.length - 1,
wrap: true,
step: 1,
onchange: v => {
writer(values[v]);
writeSettings();
}
};
}
// Helper method which breaks string set settings down to local settings object
function stringInSettings(name, values) {
return stringItems(settings[name], v => settings[name] = v, values);
}
var mainmenu = {
"": {
"title": "Hanks World Clock"
},
"< Back": () => back(),
"Seconds": stringInSettings("secondsMode", ["always", "when unlocked", "none"]),
"Color w. dark": stringInSettings("colorWhenDark", ["green", "default"]),
"Show SunInfo": {
value: (settings.showSunInfo !== undefined ? settings.showSunInfo : true),
format: v => v ? "On" : "Off",
onchange: v => {
settings.showSunInfo = v;
writeSettings();
}
}
};
// Actually display the menu
E.showMenu(mainmenu);
});
// end of file

1
apps/kanawatch/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: First release

12
apps/kanawatch/README.md Normal file
View File

@ -0,0 +1,12 @@
# kanawatch
A simple watchface design with hiragana and katakana
cards for learning.
## Author
Written by pancake in 2022, powered by insomnia
## Screenshots
![hiragana and katakana](screenshot.jpg)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxEBAH4A/AEn/AAgrrAA4ttGL4hF9fGsU1pMNmti43rGLwcD/3MxEAud413p6uuvFzgGI5n+GDQaD6F8i2p8KKH8Opi186AwYC4Xv08A0fnXhfn0cA0/vGCoVC7+ItHNE4vQ+oxH5toxHfGCYTC8t/xaKH5VY+CUIxd/8owSCIPxymB8wkH8UA2yTI82Byn4F6AXCwNH7YjI7UATAwAD7dHHgYuP4sAc5XLgHrBpXAjngGBwOCrmJ/whJ1syBgXw7v6Bov+xObF5rWDgHWKJWEt3l4mQjkAoHzBwvWgHhGBgMC1WIDQuw1/L427z8ygAABp+R3vqH4+I1QvO/1R5YZF+t1FINWuMAy/W+BuKZ4NRT4ReL7kc+waG/fy/n/9kA74tLAAP2jncAgPBF5W5yIeLZgPxEgf3CJOR3JTCF5WU3wvL6sA/YFC7e0CJO+ygDB94vKt3aF5fHoQDB+/dzdL4nb+YRG7VuAYP5F5VF9ovL3dP3t8pOKgFw0+CjmT84RE9tFAYP+F6/uwMm1Hd/vCk3oQYWGl3XF6aPK/e0oVwrohCmu9Bof5sVF+yPSd5PtuWA9m7o///uCwH9B4m9gHKd6W5yIuG9NV3v+//Gjn/2VA9wQF6UA2AFCyO5AYPcF5Xcjh1DAAPnp/SEYnJiy2EAAXTgGvAgP2jncAgPBF44wC/1R5a7EsZHCAAPegEA3afH4sA4wEB5dROgP/FxBgD1WIPgky/QGD5MAxYfCAAuGjnvAgNHuBLCF5nhgHWAoWvuwEC9mWLwN+Fw6aB1wEB60A44EB6ovJGAebxJSC1lF4/AyMNoXBzUN/IuF5kmyP8VgOJrgKCFxUB8QOB8Ec4CnCLIMAmWr+v/9Vy/otD+WWmu7BAXAjnFF5xgD21H7f//u+0vN/CKH9Ojse4+QHC7dH2wuPgPVCAP4yk98wqHAAf734OF82ByhCDF5pgD/9/xfhGBYAF8OLv/lFyIABU4XfxFo5ouP5toxHfFyZhE9+ngGj84tL8+jgGn94uVSQvQvkW1KUI8Opi186AIDFygwF/3MxEAuew6fp9PT2FzgGI5n+FzQwFAAPr42fu9JpN3z/G9YPFFzAxIABYtbGKItfGZYrlAH4A+A"))

825
apps/kanawatch/app.js Normal file
View File

@ -0,0 +1,825 @@
const stripe_width = 32;
const stripe_pos = 40;
const stripe2_pos = 110;
const h = g.getHeight();
const w = g.getWidth();
/// /////////////////////////////////////////
const katakana = {};
const hiragana = {};
katakana.A = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAjAEBfv4B/+yeAXwAOgBAAPAAAEHAAABzAAAAPgAAADgAAAAwAAAAMAAAAGAAAABgAAAAYAAAAMAAAADAAAABgAAAAYAAAAMAAAAGAAAADAAAABgAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.A = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAACAAAAAwAAAAIAAAACAAAABgAAAAZ4AAGf4AAA/gAAAAQAAAAEAAAABBAAAAQwAAAN/wAADiGAADxAwABswEAAhYBgAQUAYAMHAEACBgDABh4AwAZ2AYAD4gcAAQAcAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.I = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAwAAAAGAAAADwAAAA0AAAAYAAAAUgAAAGAAAAFAAAADgAAAA8AAAA2AAAAZgAAAYYAAAMGAAAMFgAAGAYAAGAGAACABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.I = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAwAAgAEAAEABAAAgAQAAMAGAABAAgAAYAIAAGACAAAwAQAAMAEAADABiAAQAIgAAADQAAAAcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.U = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAQAAAAHAAAAAwAAAAICAAACIAAAAgIABQa3AAP7q4ADQANAAwADAAMABgADAAYAAwAGAAMADAADAAwAAwAYAAMAGAABADAAAABgAAAAwAAAAMAAAAGAAAACQAAADAAAABgAAAAwAAAAoAAAAAAAAAAAAAAAgAAA=')
};
hiragana.U = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAIAAAABwAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAA4YAAA4CAAAAAgAAAAIAAAACAAAAAgAAAAYAAAAGAAAABAAAAAQAAAAIAAAACAAAABAAAAAQAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.E = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAJXAAe+20ADRQAAAAOAAAABgAAAAQAAAAMAAAABAAAAAwAAAAEAACABAEAgJbvgP9qSsByAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.E = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAADgAAAAOAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAdwAAAcYAAB8MAAAIGAAAADAAAABgAAAAwAAAAYAAAAMAAAAGAAAADIAAAB4gAAA4EAAAMAgAACAOGAAAB/wAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.O = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAADwAAAAOAAAADAAAAAwAAAAMAAAAjAABAAydAbff/wH/XAUAwDwAAAB0AAAA7AAAAMwAAAHMAAADjAAABkwAAA4MAAAZDAAAMEwAAGEMAAGQzAADAHwABAA8AAAAHAAAABAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.O = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAgAAAAMAAAADACAAAwAYAAMADAADIAQAA/AGAF+AAAAyAAAAAgAAAAIAAAACAAAAAg/gAAJwOAADgBgABgAMAAoADAAyAAwAIgAMAEIAGABCADAAJgBgAD4AgAAMAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.HA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAcGAAADgwAAB4HAAA4A4AAMgHAAHAA4ADAAXAAwAA4AYAAHAMAABwGAAAMGAAACDAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.HA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAABAACAAYAAwAGAAMABgACAAYAAgAHwAIAD4AGAfYABAAGAAQABgAEAAYABAAGAAQABgAEAAYABAAGAAQABgAEAAYABAOGAAQEfgAFCA8ABggPwAYG+GAGAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.HI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAXAAAABwAAAAYAgAAGAMAABgDgABYD0AAWF4gABvwAAAfAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAgAAal8AAD//gAAJQAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.HI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEAAA8BAAB2AYAABACAAAwAQAAIAEAAGAJgABACIAAwAjAAIAIYACACGABABAwAQAQEAEAEAABADAAAAAgAAEAYAABAEAAAYDAAADDgAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.HU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAALwAYt/vAD/0DwAcABwAACAcAAAAGAAAADIAAAAwAAAAYAAAAOAAAAGAAAADgAAAAwAAAA0AAAAaAAAAOAAAAHAAAAHAAAAHAAAAGgAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.HU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAGAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAACAAAAAQAAAACAgAAAgEAAAMBgAABAYAgAwDAMAMAgBgCAAAYHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.HE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAANwAAAGHAAADA4AABwDgAIwBOADcABwQeAAHgDAAA8AIAADwAAAAeAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.HE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAIMAAAMAgAAGAGAADAAwAAAADAAAAAYAAAADgAAAAMAAAABwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.HO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAEAAAADQAAAAYAAAASAAAABgAAAAIAACAGK4A273dAHoYAAAAGAAAAAgAAAIIQAAECGAABAgwAAgYGAAIGAwAGAgGADAIBwBiCAaAYRgDAMDIAgAAeAAAADgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.HO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAQB+AAEAgAABAAQAAQAGAAIABgACAAQAAgAHwAIAD4ACAfQABAAEAAQABAAEAAQABAAGAAQABgAEAAYABAAGAAQBdgAHAg4ABwAHgAIB+OACAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.KA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAUAAAADwAAAAcAAAAGAAAABgAAAAYFABAOvwAfv9eAD6wHAAQMBwAADAYAABwGAAAYBgAAGAYAADAOAAAwDAAAYgwAAMgcAADEmAAFgzgAAwHwAA4B4ABYAcAAMABAAEAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.KA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAwAAAAIAAAACAAAABAAAAAQAgAAMAEAAD8AwAHggGAHQIBgAECAMADAgDAAgIAQAIGAEAEBAAABAwAAAwIAAAYGAAAGBgAAADwAAAAcAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.KI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAACwAAAAeAAAADgAAAAYAAAATBgAAAz8AAAP5AAxfQAAH8YAAA4GAAAABgHAAAYf4AAD+pAAF8AAMPsAAC/hgAAPAYAABAGAAAABwAAAAYAAAAHAAAAAwAAAAOAAAADAAAAAYAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.KI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAQAAAAGAAAAAgAAAAIAAAADDAAAAfwAAAeAAAA4gAAAwIAAAABAAAAAZwAAADwAAAHwAAAOGAAAAAgAAAAMAAAADAAAAAQAAAAAAAAAAAAAAAAAAEAAAABgAAAAPmAAAAfwAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.KU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAQAAAAHAAAAA4AAAAMAAAAHBwAAB/+AAA0XAAAaBkAAGA4AADAOAABgHAAAwBwAAYA4AAMAMAAGAHAAAADgAAABwAAAA0AAAAaAAAAOAAAAHAAAADIAAADgAAACgAAABgAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.KU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAADAAAABgAAAAQAAAAIAAAAGAAAABAAAAAgAAAAQAAAAEAAAACAAAAAQAAAAEAAAAAgAAAAEAAAABgAAAAIAAAADAAAAAYAAAAGAAAAAwAAAAMAAAABgAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.KE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAABwAAAAOAAAADgAAABwAAAAYAQAAGAAgABgF8AA79/gAb7gAAGQcQADAHgABgBgAAYAwAAZAMAAMAHAADABgAAgAwAAAAMAAAAGAAAALAAAABwAAAAYAAAAYAAAAMgAAAGAAAACAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.KE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAABAAAAAYAAIAGAAGABgABgAYAAYAGAAEAB+ABAB/gAQHmAAEABgADAAYAAgAGAAIABgACAAYAAgAGAAIABAACAAQAAgAEAAKABAADgAwAAYAIAAGACAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.KO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAwCtwAH//8AA+oGAAEABgAAAAYAAAAGAAAABgAAAAQAAAAsAAAADAAIAFwADv//AAf1CQACAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.KO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/8AAAADwAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAYAAAAD8EAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.MA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAFcAIG/3ga/0h4H6gA4AcAAcACAAOAAAAHAAAYDAAAFjgAAAPgAAAB4AAAAOAAAABwAAAAMAAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.MA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABBAAAAf8AAD+AAAOBAAAAAQAAAAGAAAABgAAAAZwAAAHwAAB/gAAAAYAAAAGAAAABgAAAAYAAAAGAAAARgAAAR4AAAIHgAACDPAAARg4AABAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.MI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAegAAAC+gAAAB8AAAAHgAAAAYAAAAQAAAAgAAAegAAAB+AAAAD4AAAAPAAAABwAAAAMAAAAAAAAAAAAAAAAAAAUAAAAF8AAAAHwAAAAPgAAAA8AAAAHwAAAAeAAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.MI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAA+YAAAEMAAAADAAAABgAAAAQAAAAMAAAACAAAABgAAAAQAAAAIAIAAGAGAADgBgAO/wQAEIH8ACEAH4AiABnAJgAQQBgAIAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.MU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAACgAAAAcAAAADgAAAA4AAAAcAAAAHAAAABkAAAAYAAAAMQAAADEAAABwwAAAYGAAAGAwAADAHAAAwA4AAIAOAAWBfwBBX9OAf/oDgH+gAYA6AAGAAEAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.MU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAYAAAACAAAAAgAwAAIAGAACIAwAA/gEAB+ABAB2AAAAAgAAAAYAAAAGAAAABgAAAAYAAAAGAEAABgBAAGQAQAA0AEAAFABAAAwAQAAEAMAARgCAAGWHgAA8fgAAGAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.ME = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAcAAAABgAAAAcAAAAHAAAADgAAAAwAAABcAABgGAAAfDgAAAewAAAB8AAAAPAAAAD8AAABzgAAA44AAAcHAAAGAwAADAAAACgAAABwAAAAoAAAAcAAAAMAAAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.ME = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABgAAAAYAABAGAAAIBAAACAwAAAgP4AAMeDgABZgMAAYQBgAOMAYAGiACADJgAgAjQAIAQcACAEGABgBBgAQARsAIAHwAEAAQAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.MO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAABAAUAASXfgAD7EQAAQwAAAAOAAAABAAAAAwAAAAEAUBADd/wNfaRID1EAAAIDAAAAAwAAAAEAAAADAAAAAQAAAAMAAAABiQAAAf+AAABKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.MO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAIAAAACAAAAB+AAAA/wAAB0AAAABAAAAAQAAAAEAAAABAAAAAQAAAAEYAAAf+AAABwAAAAMAAAACAIAAAgCAAAIAgAACAIAAAgCAAAEBAAABgwAAAP4AAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.NA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAPAAAAA4AAAAOAAAADAAAAAwBAAAMQAAADAaBAT9/wf/vbcD6DAAAQAxAAAAMAAAADAAAAAwAAAAMAAAAGAAAABgAAAAYAAAAMAAAAGAAAABgAAAAwAAAAYAAAAMAAAAEAAAABAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.NA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAgAAAAJgAAAH4AAA/gAAAMQAIAAIABAACAAYABgACAAQAAAAMAAAACAIAAAgCAAAAAgAAAAIAAAACAAAAAgAAAPIAAAEOAAABB4AAAQTgAAD4MAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.NI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgALAANb/8AB/6pAAMAAAAAACAAAAAAAAAAAAAAAAAAAAAAABAAAIAAAJvAKN//4D/1EGAdAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.NI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAGAAAABgA/AAYBwAAEAAAABAAAAAQAAAAMAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAEAAAIAgAADAH/gAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.NU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFgAML38AB/9OAAOgHAAAQBgABAA4AAAAMAADoHAAAPRgAAAfYAAAB4gAAAPQAAADeAAABjwAABwcAAI4DgAA4AcAAcADAAcAAQAaAAAAWAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.NU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAABAwAAAIMAAACDf4AAg4DAAIYAYACeACAAZAAgAMQAIAFMACACSAAgBDgAIAwwOGAIMEbACHBDgAjYPsAPCABgBggAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.NE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAGAAAAA4AAAAHAAAAA4AAAAGAAAABgAAAAJYABAv/AAf+nwAD4DoAAQB4AAAA8AAAB8AAAAeAAAAPQAAAHzgAAHMeAAHDB4ADgwOAHgMBwHADAMKgAwAgAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAEAAAAAAAAAAAAA=')
};
hiragana.NE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAgAAAAMAAAADAAAAAwAAAAIAAAADAHgAA8GIAA+CCAAzBAwAAhAMAAIgDAAGQAwABoAMAAsADAASAAwAFgAMAC4ACAAyAugAcgIYAGYCHABGAecABgABgAYAAIAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.NO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAGAAAABwAAAAeAAAAOAAAADgAAAAwAAAAcAAAANAAAADAAAABwAAAAYAAAAOAAAAHAAAABgAAAAwAAAAYAAAAMAAAAGAAAAGQAAADAAAADgAAAAkAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.NO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAD44AADEBAABBAIABgQBAAwMAYAICACAEBgAgAAQAIAgMACAICAAgCBgAYAwwAGAEIADABmAAgAPAAQADgAYAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.RA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAEAAQAIAANTvgAD/u0AAOAAAAAACAAAAAAABAACAAgAt4APf/vAB/UDQAIAJwAAAA4AAAAOAAAAHAAAABgAAAA4AAAAcAAAAOAAAAGgAAADQAAABoAAAA4AAAAcAAAAaAAAAMgAAAEIAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.RA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAQAAAACAAAAAwAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAYAAAAEAAAABAAAAAQAAAAEA+AADBwQAA3gCAAPgAgADAAIAAAAGAAAADAAAABgAAAAwAAAAgAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.RI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAQBwAAHgOAAAsDgAAGAwAABgMAAAYDAAAGAwAABgMAAAYDAAAGAwAABgMAAAYDAAAGAwAABgMAAAYDAAACAwAAAAYAAAAGAAAADAAAAEwAAAA0AAAAcAAAAOAAAAOAAAAOAAAAGAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.RI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAABAAAAAYAAAAGAAAABAQAAAQGAAAEAgAABAIAAAwCAAAIAgAACAIAAAoCAAAOAgAADAIAAAQCAAAEAgAAAAYAAAAGAAAABAAAAAQAAAAEAAAACAAAAAgAAAAAAAAAEAAAACAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.RU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAABAAAAAeAAAADgAAAA4AAAcGAAADhgAAA4YAAAMGABAGDAAwBkYAYAYGAMAMDAOADAYHAAwGDgAYBjgAMgbwADAHyABgD4AAwA4AAYAEAAMAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.RU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAO4AAA8MAAAAGAAAADAAAAAgAAAAQAAAAIAAAAGAAAABAAAAAgAAAAQAAAAIYMAAE4BgAB4AIAA4ACAAMAAgAAAAYAAAAEAAATCAAAEZAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.RE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAACgAAAAeAAAADgAAAAwAAAAMAAAADAAAAAwAAYAMAAMADAAGAAwAGAAsADgADABgAAwBwAAMBwAADA4AAAw8AAAM8AAAD8AAAA+AAAAGAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.RE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAGAAAYDgAAfBIAANgiAAMQwgAAMYIAAHMCAAB2AgAAnAIAAJgCAAEwAgAAcAIAAvACAAewAggHMAIwBDADwAAwAAAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.RO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAYABk3/AAP/a4ADQAYAAwAGAAMABgABAAwAAwAMAAMADAABAAwAAQEMAAEASAADEt4AA/++QAGgAAADAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.RO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAF8wAAAwYAAAAMAAAACAAAABAAAAAwAAAAYAAAAEAAAACAAAABAAAAAg/gAARgGAAPgAgAHgAMADgADAAQAAwAAAAYAAAAOAAAAGAAAAGAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.SA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAFAAAAA4AABAHAAAdBgAABwYAAAYGAAAGBgAABgYABAYGrAYu//4H/aomA4YGAAAGBgAABgYAAAYGAAAGBgAABgwAAAYMAAACGAAAABgAAAAwAAAAYAAAAOAAAAGAAAADgAAABgAAAAgAAAAAAAAAAAAAAAAAAA=')
};
hiragana.SA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAGAAAABgAAAAIAAAADAAAAAQgAAAG8AAAB4AAAB8AAAPhgAAAAIAAAABAAAAAYAAAADAAAABwAAAAGAAAAAgAAAAAAAAAAAAAAAAAAAAAAAEAAAAAwAAAAH/AAAAHwAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.SI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAA0AAAAHgAAAAcAAAAjgAAAAYABAAAAAwEAAAYB4AAMAHgAGAA4ADAAWABgAAgAyAAAAYAAAAcAAAAOAAAAOAAAIHAAAUHgAABnwAAAPwAAAB4AAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.SI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAQAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAIAQAADA4AAAf4AAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.SU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAEAAGC/gAE/9cAAOgOAAAgHAAAABwAAAA4AAAAcAAAAGAAAAHgAAAB2AAAA44AAAYHAAAcA4AAOAHAALAA4AHAAOAGgABgDAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.SU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAADAAAAAQAAAAEAAAABAAAAAQAAAAE/gAAf/4AH4QAAHAEAAAABAAAAAQAAAGkAAABFAAAARQAAAEcAAABDAAAAYwAAAB8AAAAGAAAABgAAAAQAAAAMAAAAGAAAABAAAABgAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.SE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAA4AAAAHgAAAA4AAAAMAAAADABAAAwG4CAN/vAw36DgH+wDgA6MBwACDA4AAAwZAAAMUAAADMAAAAyAAAAMAAAADAAAAAwAAAAMAAAADAGAAA//gAAL94AAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.SE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAMAAAgCAAAMAgAADAIAAAwCAAAMAgAADAf8AAx+AAAPhgAAPAQAA8wEAAMMBAAADAwAAAwcAAAEGAAABAAAAAQAAAACAAAAA8OAAAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.SO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAQAAAALAAIAA4ADAAeAAcAHAADIBwAAYA4AAHAOAAAwDAAAIBwAAAAYAAAAMgAAAHAAAADAAAABwAAAAYAAAAMAAAAOAAAAHAAAADgAAADgAAADgAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.SO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAYAAAAzAAAHxgAAAwwAAAAYAAAAEAAAACAAAABAAAAAgAAAAQDwAAIDwAAEGQAACOIAABeEAAAMCAAAAAAAAAAQAAAAEAAAABAAAAAYAAAADAAAAAYAAAADwAAAAMAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.TA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAOAAAABwAAAAcAAAAGBYAAB/fAAA1DgAAMA4AAGAcAABgHAAA4DAABdwwAAMPcAAGA+AADADkABgB8AAQAzAAAAMAAAAGAAAADAAAADgAAABwAAAA4AAAAYAAAAcAAAAMAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.TA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAACAAAABgAAAAYAAAAEAAAADHgAAA/gAAH8AAAAmAAAABAAAAAQAAAAMAAAACAfgABg4AAAQAAAAEAAAADAAAAAgAAAAYAAAAEAAAADAAAAAwDjgAIAP8ACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.TI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAOAAAAH4AAAPwAAAvgAADe4AAL4OAABADAAABAwAQAAMV4ECv//B7/0IwPQmAAAgDAAAAQwAAAAMAAAADAAAABgAAAAYAAAAMAAAAGAAAACgAAAAwAAAAwAAAAsAAAAMAAAACAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.TI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAABAAAAAIAAAAGAAAABgAAAAQAAAAEAAAABHAAAB/AAAH4AAAACAAAAAgAAAAQAAAAEAAAABAAAAAQAAAAIPcAACMBgAAsAIAAcACAAGAAgAAAAIAAAAGAAAADAAAABgAAABgAAABgAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.TU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAACAAAAAyBgAgHAOAGA4DwAwOA8AGBgcABwYHAAcADgADAA4AAQAcAAAAGAAAALgAAABwAAAAYAAAAMAAAAOAAAADAAAADgAAABwAAABwAAABwAAADRAAAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.TU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/HAAB4AYADwACAPwAAwBgAAMAAAADAAAAAgAAAAYAAAAEAAAADAAAADAAAADgAAADAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.TE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAACAACAn4AC3/vAAHoAAACQAAAAAAIAAAIAAAgAFfQG3+/8B/YwBAGAOAAAADgAAABgAAAAcAAAAGAAAADAAAABQAAAAYAAAAOAAAADAAAABgAAAAwAAAAwAAAAYAAAAKAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.TE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAPAAAAbgAAA5gAABwgAADwYAAHgEAAAgCAAAABAAAAAQAAAAAAAAACAAAAAgAAAAIAAAACAAAAAQAAAAEAAAAAgAAAAOAAAABwAAAAHAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.TO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAA4AAAAHgAAAA4AAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAD6AAAAzwAAAMPAAADA4AAAwHAAAMAwAADAEAAAwAAAAMAAAADAAAAAwAAAAMAAAAGAAAABwAAAAMAAAAAAAAAAAAAAAIAAAAAAAA=')
};
hiragana.TO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAGAAAAAgAAAAMAYAABAHAAAQHAAAGDAAAAhgAAAIwAAABwAAAAYAAAAMAAAAEAAAACAAAABAAAAAQAAAAAAAAACAAAAAAAAAAGAAAAAf/wAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.WA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAACAAANACsAB7//gAPtI4AJgAOAAYADAAMABwABgAYACYAGAAOABgABgA4AAwAcAAGAHAABADgAAAAwAAAAcAAAAOAAAAHAAAADgAAADgAAADgAAAFwAAACgAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.WA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAgAAAAMAAAADAAAAAwAAAAMAAAADAAAAA8AAAAfAAAAfgAAAIwAAAAIDnAAGCAYACiACAArAAwATAAMAJgADAD4AAgByAAYARgAEAAYACAAGACAABgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.WI = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAQAAAAPAAAAA4AAAAMAAAADAAAAAwAAAAsEABhLvgAP//cAB5MAAAGDAAADAwAAAQMAAAMDAAADAwcBg19/wf/7UsD1AwAAIAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.WI = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAbAAAB4wAAAMIAAAACAAAABgAAAAQAAAAEAAAADAAAAA3+AAAeAwAAeAGAAZAAgAMQAMAEMADACCAAwBBgAMAwQACAIMDxgBCBGwARAQYADgCcAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.WE = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAFBK4AB//+AAPUHgABADgAAARwAAAOwAAABwAAAAMAAAADAAAAAwAAAAMAAAALAAAgAyVgPv//+B/qIrgIAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.WE = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAA8AAAB3AAAHhgAAAAwAAAAYAAAAMAAAAGAAAADAAAABhwAAAzDAAAaAYAAOAGAADADAAACRgAAANgAAAGAAAADAAAABgAAAAgAAAAwAcAAYAxwAfggGAOGwAwDA4AAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.WO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAABAAAgAAwAO37/AB//bwAMgAwAAABYAAgAHAAAABgAGFb4AA//uAAHQDAAAAQwAAAAYAAAAOAAAADAAAABgAAAAwAAAAsAAAAGAAAADAAAABgAAABoAAAAwAAAA4AAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.WO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAMAAAACOAAAB/AAAPwAAAAMAAAACAAAABAAAAAwAgAAIAcAAHMMAADBOAAAAeAAAAGAAAADgAAADIAAABCAAAAgAAAAIAAAACAAAAAgAAAAGBwAAAP8AAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.YA = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAEAAAAD4AAAAOAAAADAAAAAwAIAAGADAABgX4AAa/fAAX6OAZfwHAD9MDgAcDBgAEAwwAAAmYAAABogAAAYAAAAGAAAABgAAAAcAAAADAAAAAwAAAAOAAAADgAAAA4AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.YA = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAgAAAAGAAAAAgAAAAMAAAQAAAAEAAAABAHGAAQOAwAGcAEAA4ADAA4AAwA7AAYA4QA4AAEAAAAAgAAAAIAAAADAAAAAQAAAAGAAAAAgAAAAMAAAADAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.YU = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAC4AAd//AAH2hoAAQAyAAAAMAAAALAAAAAwAAAAMAAAADAAABAwQEABe+Bt//9wP+kAIBwAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.YU = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAgAAAAIAACADAAAwD3AAIDIIACBCDAAgggQAIwIGACICBgBkAgYASAIEAEACBABQBgwAcB4YAGAGcABgB8AAYAQAACAIAAAACAAAABAAAAAQAAAAIAAAAAAAAAAAAAAAAAAAAAAAA=')
};
katakana.YO = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAIAlgAD//8AAfUGAACABgAAAAYAAAAMAABABgABBLwAAf/8AAF0DAAAgAwAAAAMAAAADAAAQAwAAgAMAANN3AAD/3wAANAIAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.YO = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAEAAAABgAAAAMAAAACAAAAAgAAAAIAAAACDAAAA3wAAAOAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAPCAAAEPgAABA8AAAQHwAAADPAAA/g8AAAADgAAAAYAAAAAAAAAAAAAAAAA=')
};
katakana.N = {
width: 32,
height: 32,
bpp: 1,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAEAAAABgAAAAOAAAABwAAIAMgAGADgADAAwABgAAAAwAAAAwAAABYAAABOAAAAHAAAAHAAAADgAAADkAAABwAACB4AAAx4AAAP4QAAB8AAAAOAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
hiragana.N = {
width: 32,
height: 32,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAABgAAAAQAAAAMAAAACAAAABAAAAAQAAAAIAAAAGAAAABAAAAAkAAAALgAAAFIAIADiAAAAwwBAAYMAQAEBAIADAQGAAgGDAAYAzgAEAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
};
/// /////////////////////////////////////////
let kana = katakana.KI;
let scroll = 0;
function drawWheel () {
if (scroll > 20 || scroll < -20) {
scroll = 0;
next();
}
}
let hiramode = false;
let curkana = 'KA';
function next () {
let found = false;
for (const k of Object.keys(katakana).sort()) {
if (found) {
kana = hiramode ? hiragana[k] : katakana[k];
curkana = k;
return;
}
if (curkana === k) {
found = true;
}
}
curkana = 'KA';
kana = hiramode ? hiragana[curkana] : katakana[curkana];
}
function prev () {
let oldk = '';
let count = 0;
for (const k of Object.keys(katakana).sort()) {
if (curkana === k) {
if (count > 0) {
curkana = oldk;
kana = katakana[curkana];
return;
} else {
}
}
oldk = k;
count++;
}
curkana = oldk;
kana = katakana[curkana];
}
const kanacolors = {
A: []
};
const clocktop = false;
function updateWatch (hhmm) {
if (!hhmm) {
hhmm = ohhmm;
}
g.setBgColor(0, 0, 0);
g.setColor(0, 0, 0);
if (false) {
g.fillRect(0, 0, g.getWidth(), g.getHeight());
g.setColor(0.3, 0.3, 0.3);
g.setColor(1, 0, 0);
g.fillRect(stripe_pos, 0, stripe_pos + stripe_width, h);
g.fillRect(stripe2_pos, 0, stripe2_pos + stripe_width, h);
for (i = 0; i < h; i += 8) {
g.setColor(0.15, 0.15, 0.15);
g.fillRect(0, i, g.getWidth(), i + 3);
g.setColor(0.4, 0.4, 0.4);
g.fillRect(stripe_pos, i, stripe_pos + stripe_width, i + 3);
g.fillRect(stripe2_pos, i, stripe2_pos + stripe_width, i + 3);
}
} else {
var whitecolor = false;
if (curkana.indexOf('A') != -1) {
g.setColor(1, 0, 0);
whitecolor = true;
} else if (curkana.indexOf('I') != -1) {
g.setColor(0, 1, 0);
} else if (curkana.indexOf('U') != -1) {
g.setColor(0, 0, 1);
whitecolor = true;
} else if (curkana.indexOf('E') != -1) {
g.setColor(1, 1, 0);
} else {
g.setColor(0, 1, 1);
}
g.fillRect(0, 0, w, h);
}
// GOOD FONT SIZE g.setFont("Vector", 62);
g.setFont('Vector', 50);
const bignumbers = false;
if (bignumbers) {
g.setColor(1, 1, 1);
g.drawString(hhmm, 12, 12);
g.setColor(0, 0, 0);
g.drawString(hhmm, 10, 10);
} else {
if (whitecolor) {
g.setColor(0, 0, 0);
} else {
g.setColor(0.5, 0.5, 0.5);
}
if (clocktop) {
x = 26; y = 26;
} else {
x = 26; y = h - 42;
}
g.drawString(hhmm, x - 3, y - 3);
if (whitecolor) {
g.setColor(1, 1, 1);
} else {
g.setColor(0, 0, 0);
}
g.drawString(hhmm, x, y - 1);
}
// drawKana(hira_a, 0, 60);
drawKana(hiragana.KA, g.getWidth() / 6, 60);
Bangle.drawWidgets();
}
function drawKana (img, x, y) {
g.setColor(0, 0, 0);
// g.fillRect(0,0,g.getWidth(), h);
if (clocktop) {
g.fillRect(0, h / 2.5, g.getWidth(), h);
} else {
g.fillRect(0, 0, g.getWidth(), 6 * (h / 8) + 1);
}
if (false) {
g.drawImage(hira_a, x, y);
g.setColor(1, 1, 1);
g.setFont('Vector', 30);
g.drawString(curkana, x + 32, y + 4);
} else {
if (clocktop) {
g.setColor(1, 1, 1);
g.drawImage(kana, x + 8, y + 12, { scale: 3.4 });
g.setColor(1, 1, 1);
g.setFont('Vector', 30);
g.drawString(curkana, 0, y + 16);
g.drawString(hiramode ? 'H' : 'K', w - 20, y + 16);
} else {
g.setColor(1, 1, 1);
g.drawImage(kana, x + 8, 26, { scale: 3.4 });
g.setColor(1, 1, 1);
g.setFont('Vector', 30);
g.drawString(curkana, 4, 32);
g.drawString(hiramode ? 'H' : 'K', w - 20, 32);
}
}
}
var ohhmm = '';
function tickWatch () {
const now = Date();
function zpad (n) {
return (n < 10) ? '0' + n : n;
}
const hhmm = zpad(now.getHours()) + ':' + zpad(now.getMinutes());
if (hhmm !== ohhmm) {
updateWatch(hhmm);
}
}
Bangle.on('touch', function (tap, top) {
if (top.y < h / 3) {
// clocktop = !clocktop;
return;
}
if (top.x < w / 4) {
prev();
} else if (top.x > (w - (w / 4))) {
next();
} else {
hiramode = !hiramode;
}
kana = hiramode ? hiragana[curkana] : katakana[curkana];
tickWatch();
});
Bangle.loadWidgets();
tickWatch();
setInterval(tickWatch, 1000);

BIN
apps/kanawatch/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,31 @@
{
"id": "kanawatch",
"name": "Kanawatch",
"shortName": "Kanawatch",
"version": "0.01",
"type": "clock",
"description": "Learn Hiragana and Katakana",
"icon": "app.png",
"allow_emulator": true,
"tags": "clock",
"supports": [
"BANGLEJS2"
],
"readme": "README.md",
"storage": [
{
"name": "kanawatch.app.js",
"url": "app.js"
},
{
"name": "kanawatch.img",
"url": "app-icon.js",
"evaluate": true
}
],
"screenshots": [
{
"url": "screenshot.jpg"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -111,11 +111,12 @@ function getNotificationImage() {
function getFBIcon() { function getFBIcon() {
return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA=="); return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA==");
} }
/*
* icons should be 24x24px with 1bpp colors and 'Transparency to Color' exports.getMessageImage = function(msg) {
* http://www.espruino.com/Image+Converter /*
*/ * icons should be 24x24px with 1bpp colors and 'Transparency to Color'
exports.getMessageImage = function (msg) { * http://www.espruino.com/Image+Converter
*/
if (msg.img) return atob(msg.img); if (msg.img) return atob(msg.img);
var s = (msg.src||"").toLowerCase(); var s = (msg.src||"").toLowerCase();
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA="); if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
@ -123,16 +124,13 @@ exports.getMessageImage = function (msg) {
if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA=="); if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA"); if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA");
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA"); if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA");
if (s=="facebook") return getFBIcon(); if (s=="facebook" || s=="messenger") return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA==");
if (s=="gmail") return getNotificationImage();
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA=="); if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA="); if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA="); if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA=="); if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA==");
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA"); if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44"); if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44");
if (s=="mail") return getNotificationImage();
if (s=="messenger") return getFBIcon();
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA"); if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA="); if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA=");
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA="); if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
@ -140,7 +138,6 @@ exports.getMessageImage = function (msg) {
if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA"); if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA");
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA=="); if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA=="); if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
if (s=="sms message") return getNotificationImage();
if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA"); if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA");
if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA"); if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA");
if (s=="telegram" || s=="telegram foss") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA=="); if (s=="telegram" || s=="telegram foss") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
@ -152,10 +149,11 @@ exports.getMessageImage = function (msg) {
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql"); if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA"); if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A="); if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
return getNotificationImage(); // if (s=="sms message" || s=="mail" || s=="gmail") // .. default icon (below)
} return atob("HBKBAD///8H///iP//8cf//j4//8f5//j/x/8//j/H//H4//4PB//EYj/44HH/Hw+P4//8fH//44///xH///g////A==");
};
exports.getMessageImageCol = function (msg,def) { exports.getMessageImageCol = function(msg,def) {
return { return {
// generic colors, using B2-safe colors // generic colors, using B2-safe colors
"alarm": "#fff", "alarm": "#fff",
@ -184,6 +182,7 @@ exports.getMessageImageCol = function (msg,def) {
"snapchat": "#ff0", "snapchat": "#ff0",
"teams": "#464eb8", "teams": "#464eb8",
"telegram": "#0088cc", "telegram": "#0088cc",
"telegram foss": "#0088cc",
"threema": "#000", "threema": "#000",
"to do": "#3999e5", "to do": "#3999e5",
"twitch": "#6441A4", "twitch": "#6441A4",
@ -192,4 +191,4 @@ exports.getMessageImageCol = function (msg,def) {
"wordfeud": "#e7d3c7", "wordfeud": "#e7d3c7",
"youtube": "#f00", "youtube": "#f00",
}[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg); }[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg);
} };

View File

@ -1,7 +1,7 @@
(function(back) { (function(back) {
function settings() { function settings() {
let settings = require('Storage').readJSON("messages.settings.json", true) || {}; let settings = require('Storage').readJSON("messages.settings.json", true) || {};
if (settings.vibrate===undefined) settings.vibrate="."; if (settings.vibrate===undefined) settings.vibrate=":";
if (settings.repeat===undefined) settings.repeat=4; if (settings.repeat===undefined) settings.repeat=4;
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60; if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
if (settings.maxMessages===undefined) settings.maxMessages=3; if (settings.maxMessages===undefined) settings.maxMessages=3;

View File

@ -60,10 +60,9 @@ draw:function(recall) {
WIDGETS["messages"].width=this.iconwidth * E.clip(msgs.length, 0, settings.maxMessages); WIDGETS["messages"].width=this.iconwidth * E.clip(msgs.length, 0, settings.maxMessages);
WIDGETS["messages"].msgs = msgs; WIDGETS["messages"].msgs = msgs;
Bangle.drawWidgets(); Bangle.drawWidgets();
if (msgs.length !== 0) Bangle.setLCDPower(1);// turns screen on
},buzz:function() { },buzz:function() {
if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode
require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || "."); require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ":");
},touch:function(b,c) { },touch:function(b,c) {
var w=WIDGETS["messages"]; var w=WIDGETS["messages"];
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+w.iconwidth) return; if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+w.iconwidth) return;

View File

@ -120,7 +120,7 @@ function showAlarm(alarm) {
Bangle.setLocked(false); Bangle.setLocked(false);
} }
require("buzz").pattern(alarm.vibrate === undefined ? ".." : alarm.vibrate).then(() => { require("buzz").pattern(alarm.vibrate === undefined ? "::" : alarm.vibrate).then(() => {
if (buzzCount--) { if (buzzCount--) {
setTimeout(buzz, settings.buzzIntervalMillis); setTimeout(buzz, settings.buzzIntervalMillis);
} else if (alarm.as) { // auto-snooze } else if (alarm.as) { // auto-snooze

View File

@ -187,7 +187,7 @@ function editAlarm(alarmIndex, alarm) {
as : false, as : false,
dow : 0b1111111, dow : 0b1111111,
last : 0, last : 0,
vibrate : ".." vibrate : "::"
}; };
if (msg != "") a["msg"] = msg; if (msg != "") a["msg"] = msg;
if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (!newAlarm) Object.assign(a, alarms[alarmIndex]);

9
apps/novaclock/README.md Normal file
View File

@ -0,0 +1,9 @@
# Nova Clock
A simple clock app that uses a clockwork star, from Kirby.
*Note: This clock draws slightly into the widget area, but since it's in the middle, it shouln't matter that much (nobody has that many widgets... right?)*
## Credits
Pixel art by me, [dronesflier](https://github.com/dronesflier)
The Kirby series belongs to Nintendo/HAL Labs

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AE3N5ouuGFovuFwYwrF9wuFGFIqC5nMF9guBF9ReD43GGFJeDF9ReFGFImCFwYvBGAReqMEAdCAAwvKAA4pWFxYwNGhQoJAAouHYQYAEGBwsIFBIANGRBgLFzIwDYhoweFx4xGGC4uSGDYuFFpqTIF1BhXFzBhVXSZhNF6QuWGAgvSFzAvS4wved6KOsSDovJ5gACF9IsCBIQxFF8ItEAAYxEF7qHDFowxGBwZeaFxgPEAwYvYABAONF74PVF64RbF6IThDZYVnDIoXtAH4A/AH4AkA=="))

274
apps/novaclock/app.js Normal file
View File

@ -0,0 +1,274 @@
function nova() {
var nova = require("heatshrink").decompress(atob("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"));
return nova;
}
function novaEyesStage1() {
var novaEyesStage1 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5gAFH58rDIXN5movwZRKs+oAIg/OgEr5mi1Go0V+AYIFCLN7oGABIZiK0fXAAkOh0Ph0IBQo+HDI0PhEHhEPDJpWoKQJVCh5ZHHwoZFCgcPg50BLIpYpHohVF1hFChxXODAQACL4hXsHgwABgAACBQg+HDIhuChAZEOYJYtJYkIKwo+EBoJXKJYIABDIyxGK8yUFSYpXKHwTIFKw4NFWFKUGHg0AKwaWGDIrHGWGA9QLIUIK5LJBDJAaDK9o8BhA9JHwSvLK5QQBhEOK9qVLgAMBK5bJJAAMHK9yuBJIRXWh78BDJBWBDIpXnWAIvB1hWNK4xYCOJK7BDIxYoGAZYGBQMOdg6wFBgZWFg7IGK9JYCGQI8FdYpXILAMIDI7IILFZOBAIYCBIoI8KOQ4VDYwRWtHorwCAAQ/DBohXKLAZZCLYQZKLFRZFBQo8HDJLLBDJpYlAB4ZiLE3M5moAQIAFHhgZD5oaCDofNK14/E1Go0QADHaBZDKwQBBKuTyKDNgA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AD4A=="));
return novaEyesStage1;
}
function novaEyesStage0() {
var novaEyesStage0 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj64AFH58rDLBVrH6EAlYZXK0Y7KH4gZiK1MOh0Ph0IHxoZGh8Ig8Ih5YwHgpSBKoUPLI4+FDIoUDh8HOgJZFLFI9EKousIoUOK5wYCAARfEK9g8GAAMAAAQKEHw4ZENwUIDIhzBLFpLEhBWFHwgNBK5RLBAAIZGWIxXmSgqTFK5Q+CZApWHBoqwpSgw8GgBWDSwwZFY4ywwHqBZChBXJZIIZIDQZXtHgMIHpI+CV5ZXKCAMIhxXtSpcABgJXLZJIABg5XuVwJJCK60PfgIZIKwIZFK86wBF4OsKxpXGLARxJXYIZGLFAwDLAwKBhzsHWAoMDKwsHZAxXpLAQyBHgrrFK5BYBhAZHZBBYrJwIBDAQJFBHhRyHCobGCK1o9FeAQACH4YNEK5RYDLIRbCDJRYqLIoKFHg4ZJZYIZNLFYAIHhIZZLEo/LBgIZkLNw7QDLJZnAAgZsAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AfA"));
return novaEyesStage0;
}
function novaEyesStage2() {
var novaEyesStage2 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5nM1ABC5g/PlYQB1AYCvwZRKsxWE0RZPBgOiv2iAQNUAYIACLN6RBHYQAETQKcDHxMAvwACLIYaEOYRWtGQYAD1BAELBKsBBgRRCWIQBCDwZYqKwKoGLowFDHwpWCDIRRBAQPMAwd+vANBWNRXBJASoDeRKWGDIoEBOoQDDDAQeBK9CUBJQiTCAAq2CB4Q+DDITGFCAZ2DOohYngAAVDLhWj64ACh0PhA0G6+sBoMPCQY+BgAKCBYMOhwZHBoYZFK88PAAKMHKwYABK4oZEKw5YEDIxXpHpRZChBXJZIIZIDQZXtHgMIHpI+CV5ZXKCAMIhxXtSpcABgJXLZJIABg5XuVwJJCK60PfgIZIKwIZFK86wBF4OsgErKxZXGLARxJXYIZGLEoAVDLhXk1AAC5mo0QGDABJXEBIgaBDYfMAA4ZELEouEHgIALHgsABAN+5hUB0R1CDJywlRIvNAQYECMgZXGY4ZXBCAIDCKQTIILFAAISYo8IOQYUDAoQDCBQRWrHwQABcoxdFHhJyCC4i3DDoWiK1hYDRgSQCAAgMBDJ3NDQJWE5oZMLM6ZGHaBZDOgQBBKuQ/FAAgZsAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AfA"));
return novaEyesStage2;
}
function novaEyesStage3() {
var novaEyesStage3 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5nM1ABC5g/PlYQB1AYCvwZRKsxWE0RZPBgOiv2iAQNUAYIACLN6RBHYQAETQKcDHxMAvwACLIYaEOYRWtGQYAD1BAELBKsBBgRRCWIQBCBQV+LFRWBVAxdGAoY+FKwQZCJgICB5hZCvF+qgFBWNRXBJASoDeROiK4ytDCIOoC4R5DAYQIBK9A8BGIY2EABQ+DZARRBUoJVCDYzTELE49CVgo7KHosABoocFZYgBDK8yuGABJfGHwIZQLYQcDLEsAAC4ZaK8nXAAMPh8OGhHX1gPBhATCK4QZDh0PJ5IaCh4ZEK848BhCOKHwI9FOIpXKCAMIhxXpdrAZZK8mo1Gi0V+AIYABBAOiBoIAEBAJXCAwQYDDIwAFDYRXoJAozCAYRDCAAN4I4RXCJAoFELBIdBK8o+DGoyzDKQKcDHgpyBYwquECoYMCAQJWmWAgvCUYadHdgzKF5jMCAITTD5gIDK84+B5gABG4SZDd5A8FZQZLDLQIWGDJCwlLAQAC5oCDAgQABVwoZEKIQeCAYTGFDI5YmcogEEAAg8IOQYUDAoQDCBQRWrHwQABJoV+SQ48KOQRpEW4YdC0RWsLAaMCAIQAEBgIZO5oaCKwfNDJhZnTIw7QLIZ0EKuQ/FAAgZsAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AfA=="));
return novaEyesStage3;
}
function novaEyesStage4() {
var novaEyesStage4 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5nM1ABC5g/PlYQB1AYCvwZRKsxWE0RZPBgOiv2iAQNUAYIACLN6RBHYQAETQKcDHxMAvwACLIYaEOYRWtGQYAD1BAELBKsBBgRRCWIQBCBQV+LFRWBVAxdGAoY+FKwQZCJgICB5hZCvF+qgFBWNRXBJASoDeROiK4ytDCIOoC4R5DAYQIBK9EAJQg2EBAoFEHwauBUIN4p1UKoQbGaYhYnK4KvCKoQFBHYi6EHosrBQQaCYwRbBDQd4XYSwoKwIuB5oACIAJBBAYpMDAAI+BKwIKDDAJKBvC2BEgIUDEwawmdgIvDGIhVDLYYAEHoIZBVIQPBKwJeBZoLJEXgYHBK8wzBHISMF5xdFK45GCMwQDBNYonBBAN+XAV+K8wAPlYHGDKIAHV8ztCqiHBWo6gEdgOoK4SpEAgQODDwIjBAAIjBCIRXm5g1CHQQCDHgJWCAgIAD0RXDDIPM5oRC5oECDYR+CK9aVES4ZQFHQShEK4yvEAAbJGAARXlHwI9BJYIAEAwLsEql4vwBBHgZYCCQRrEEYgMDAQJWmWAgvCJQJaDUQQ8DVwYZFVAS9CAITED5gIDK84+BcIQ3CTIa5HKwjKFJYZaBCwwZIWEpYCAAXNAQYECAAJkBK4ysCYYR2DAQIICAAZXpd4zqFAAg8IOQYUDAoQDCBQRWrHwQABJoV+SQ48KOQRpEW4YdC0RWsLAaMCAIQAEBgIZO5oaCKwfNDJhZnTIw7QLIZ0EKuQ/FAAgZsAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AfA=="));
return novaEyesStage4;
}
function novaEyesWhiteStage0() {
var novaEyesWhiteStage0 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj64AFH58rDLBVrH6EAlYZXK0Y7KH4gZiK2Q+JDLJW0Hw4ZZK/5X6HiY+FDLJX/K/Q8VHwYZZK/5X/K/5X/K/5X/K/5X/K+Y+WHgYZZK/5X7Hyg8FDLJX/K/Y+SHg4ZZLGg8JDLJYlH5YMBDMhZuHaAZZLM4AEDNgA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AD4A="));
return novaEyesWhiteStage0;
}
function novaEyesTransStage1() {
var novaEyesTransStage1 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5gAFH58rDIXN5movwZRKs+oAIg/OgEr5mi1Go0V+AYIFCLN7oGABIZiK0YRYDLJW0CY4ZZK/5X6FCoVDDLJX/K/QmXC4IZZK/5X/K/5X/K/5X/K/5X/K+YmWCoYZZK/5X7FCgTFDLJX/K/YqSCI4ZZLEoAPDMRYm5nM1ACBAAo8MDIfNDQQdD5pWvH4mo1GiAAY7QLIZWCAIJVyeRQZsAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AfA"));
return novaEyesTransStage1;
}
function novaEyesTransStage2() {
var novaEyesTransStage2 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5nM1ABC5g/PlYQB1AYCvwZRKsxWE0RZPBgOiv2iAQNUAYIACLN6RBHYQAETQKcDHxMAvwACLIYaEOYRWtGQYAD1BAELBKsBBgRRCWIQBCDwZYqKwKoGLowFDHwpWCDIRRBAQPMAwd+vANBWNRXBJASoDeRKWGDIoEBOoQDDDAQeBK9CUBJQiTCAAq2CB4Q+DDITGFCAZ2DOohYngAAVDLhWjC7AZZK/5X/K/5X/K/5X/K/5X/K+YmBACoZcK8moAAXM1GiAwYAJK4gJEDQIbD5gAHDIhYlFwg8BABY8FgAIBv3MKgOiOoQZOWEqJF5oCDAgRkDK4zHDK4IQBAYRSCZBBYoABCTFHhByDCgYFCAYQKCK1Y+CAALlGLoo8JOQQXEW4YdC0RWsLAaMCSAQAEBgIZO5oaBKwnNDJhZnTIw7QLIZ0CAIJVyH4oAEDNgA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AD4A=="));
return novaEyesTransStage2;
}
function novaEyesTransStage3() {
var novaEyesTransStage3 = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ArgAAFDNhVj5nM1ABC5g/PlYQB1AYCvwZRKsxWE0RZPBgOiv2iAQNUAYIACLN6RBHYQAETQKcDHxMAvwACLIYaEOYRWtGQYAD1BAELBKsBBgRRCWIQBCBQV+LFRWBVAxdGAoY+FKwQZCJgICB5hZCvF+qgFBWNRXBJASoDeROiK4ytDCIOoC4R5DAYQIBK9A8BGIY2EABQ+DZARRBUoJVCDYzTELE49CVgo7KHosABoocFZYgBDK8yuGABJfGHwIZQLYQcDLEsAAC4ZaK8gXYDLJX/K/jtYDLJXk1Go0WivwBDAAIIB0QNBAAgIBK4QGCDAYZGAAobCK9BIFGYQDCIYQABvBHCK4RIFAohYJDoJXlHwY1GWYZSBTgY8FOQLGFVwgVDBgQCBK0ywEF4SjDTo7sGZQvMZgQBCaYfMBAZXnHwPMAAI3CTIbvIHgrKDJYZaBCwwZIWEpYCAAXNAQYECAAKuFDIhRCDwQDCYwoZHLEzlEAggAEHhByDCgYFCAYQKCK1Y+CAAJNCvySHHhRyCNIi3DDoWiK1hYDRgQBCAAgMBDJ3NDQRWD5oZMLM6ZGHaBZDOghVyH4oAEDNgA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AD4="));
return novaEyesTransStage3;
}
function novaTopRedraw() {
var novaTopRedraw = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AHsAABZM/K5fM5kAv2igGi1EAvFUK/6v/K8isDAQV+BAK4BJn5XL1BOB1GoLod4LQIRE0XM5vM1HN5pX/5hXBIwJdGKwgYGA45X3TgJRDLQZXFAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AB8AIH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AP"));
return novaTopRedraw;
}
function star() {
var backgroundstar = require("heatshrink").decompress(atob("rFYxH+AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A9qxA/K/5W/AH5XGLH6w/K34A/AA2BwJB/K62sIP5XWIH4AW1iv/K/5X/AHFWqwCCq2BJ4ICB1mBAAJZBAQIQBlYUCAYMrAgJZ8KQICBU4QCBLIJVBK4QOCNAJSCOQQA/XpQA/K6q3CAH5XUwJB/ACqu/V7BA/AC2BwJB/LC5A/V/4AtqxX/AC2slZB/ACuBqxB/LC5A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4Ao"));
return backgroundstar;
}
function novaOpenEyes(speed, white, animation) {
if (!white) {
g.drawImage(novaEyesStage4(), -10, -10, {
scale: 2.2
});
setTimeout(function() {
g.drawImage(novaEyesStage3(), -10, -10, {
scale: 2.2
});
}, speed * 2);
setTimeout(function() {
g.drawImage(novaEyesStage2(), -10, -10, {
scale: 2.2
});
}, speed * 3);
setTimeout(function() {
g.drawImage(novaEyesStage1(), -10, -10, {
scale: 2.2
});
}, speed * 4);
if (animation) {
setTimeout(function() {
g.drawImage(novaEyesStage0(), -10, -10, {
scale: 2.2
});
}, speed * 5);
} else {}
} else {
g.drawImage(novaEyesStage4(), -10, -10, {
scale: 2.2
});
setTimeout(function() {
g.drawImage(novaEyesWhiteStage0(), -10, -10, {
scale: 2.2
});
timedraw(true);
g.drawImage(novaEyesTransStage3(), -10, -10, {
scale: 2.2
});
}, speed * 2);
setTimeout(function() {
g.drawImage(novaEyesWhiteStage0(), -10, -10, {
scale: 2.2
});
timedraw(true);
g.drawImage(novaEyesTransStage2(), -10, -10, {
scale: 2.2
});
}, speed * 3);
setTimeout(function() {
g.drawImage(novaEyesWhiteStage0(), -10, -10, {
scale: 2.2
});
timedraw(true);
g.drawImage(novaEyesTransStage1(), -10, -10, {
scale: 2.2
});
open = true;
}, speed * 4);
if (animation) {
setTimeout(function() {
g.drawImage(novaEyesWhiteStage0(), -10, -10, {
scale: 2.2
});
open = true;
}, speed * 5);
} else {}
}
}
function novaCloseEyes(speed, white, animation) {
if (!white) { // for other
if (animation) {
g.drawImage(novaEyesStage0(), -10, -10, {
scale: 2.2
});
} else {}
setTimeout(function() {
g.drawImage(novaEyesStage1(), -10, -10, {
scale: 2.2
});
}, speed * 2);
setTimeout(function() {
g.drawImage(novaEyesStage2(), -10, -10, {
scale: 2.2
});
}, speed * 3);
setTimeout(function() {
g.drawImage(novaEyesStage3(), -10, -10, {
scale: 2.2
});
}, speed * 4);
setTimeout(function() {
g.drawImage(novaEyesStage4(), -10, -10, {
scale: 2.2
});
}, speed * 5);
} else { // for time
if (animation) {
timedraw(true);
g.drawImage(novaEyesWhiteStage0(), -10, -10, {
scale: 2.2
});
} else {}
setTimeout(function() {
timedraw(true);
g.drawImage(novaEyesTransStage1(), -10, -10, {
scale: 2.2
});
}, speed * 2);
setTimeout(function() {
timedraw(true);
g.drawImage(novaEyesTransStage2(), -10, -10, {
scale: 2.2
});
}, speed * 3);
setTimeout(function() {
timedraw(true);
g.drawImage(novaEyesTransStage3(), -10, -10, {
scale: 2.2
});
}, speed * 4);
setTimeout(function() {
g.drawImage(novaEyesStage4(), -10, -10, {
scale: 2.2
});
}, speed * 5);
open = false;
}
}
function timedraw(animation) {
if (open && timemode || animation) {
g.setFont("6x8", 4);
g.setColor("#00F");
var d = new Date();
var h = d.getHours(),
m = d.getMinutes();
g.drawImage(novaEyesWhiteStage0(), -10, -10, {
scale: 2.2
});
g.drawImage(novaEyesTransStage1(), -10, -10, {
scale: 2.2
});
// Check if single digit
var hourDigits = h.toString();
if (hourDigits.length === 1) { // if hour digits only one, render in middle
g.drawString(h, 50, 66);
} else {
g.drawString(h, 38, 66);
}
var minutes = m.toString();
if (minutes.length === 1) { // same for mins
g.drawString(m, 107, 66);
} else {
g.drawString(m, 94, 66);
}
}
}
function main() {
Bangle.on("lock", function(lock) {
g.drawImage(novaTopRedraw(), -10, novaYPos, {
scale: 2.2
});
if (lock) {
novaCloseEyes(200, true, false);
setTimeout(function() {
novaOpenEyes(100, false, false);
timemode = false;
}, 1200);
}else{
novaCloseEyes(100, false, false);
setTimeout(function() {
timemode = true;
novaOpenEyes(200, true, false);
}, 600);
}
});
}
g.setFont("6x8", 4);
g.setColor("#FFF");
var open = false;
var timemode = true;
var clockmode;
var novaYPos = -7;
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
g.drawImage(nova(), -10, -10, {
scale: 2.2
});
Bangle.setUI("clock");
g.drawImage(star(), 5, -5, {scale:0.8});
g.drawImage(star(), -10, 120, {scale:0.8});
g.drawImage(star(), 120, -5, {scale:0.8});
var secondInterval = setInterval(function() {
timedraw();
g.drawImage(novaTopRedraw(), -10, novaYPos, {
scale: 2.2
});
}, 1000);
novaOpenEyes(300, true, false);
main();

BIN
apps/novaclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,15 @@
{ "id": "novaclock",
"name": "Nova Clock",
"shortName":"Nova Clock",
"icon": "app.png",
"type": "clock",
"version":"0.1",
"description": "A clock inspired by the Kirby series",
"tags": "clock",
"supports": ["BANGLEJS2"],
"readme":"README.md",
"storage": [
{"name":"novaclock.app.js","url":"app.js"},
{"name":"novaclock.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -1 +1,2 @@
0.01: First release 0.01: First release
0.02: added missing type i metadata

View File

@ -2,8 +2,9 @@
"name": "Pong Clock", "name": "Pong Clock",
"shortName":"Pong Clock", "shortName":"Pong Clock",
"icon": "pongclock.png", "icon": "pongclock.png",
"version":"0.01", "version":"0.02",
"description": "A Pong playing clock", "description": "A Pong playing clock",
"type": "clock",
"tags": "", "tags": "",
"allow_emulator":true, "allow_emulator":true,
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],

View File

@ -3,3 +3,4 @@
0.03: Forces integer scaling and adds more configuration (error correction, description, display) 0.03: Forces integer scaling and adds more configuration (error correction, description, display)
0.04: Allow scanning of QR codes from camera or file 0.04: Allow scanning of QR codes from camera or file
0.05: Change brightness on touch 0.05: Change brightness on touch
0.06: Add ability to generate contact info (MeCard format) QR code

View File

@ -8,6 +8,8 @@
<label for="useTEXT">Text</label></br> <label for="useTEXT">Text</label></br>
<input type="radio" id="useWIFI" name="mode"/> <input type="radio" id="useWIFI" name="mode"/>
<label for="useWIFI">Wifi Credentials</label></br> <label for="useWIFI">Wifi Credentials</label></br>
<input type="radio" id="useMECARD" name="mode"/>
<label for="useMECARD">Contact Info (<a href="https://en.wikipedia.org/wiki/MeCard_(QR_code)" target="_blank">MeCard</a>)</label></br>
<input type="radio" id="useFILE" name="mode"/> <input type="radio" id="useFILE" name="mode"/>
<label for="useFILE">QR image</label></br> <label for="useFILE">QR image</label></br>
<input type="radio" id="useCAM" name="mode"/> <input type="radio" id="useCAM" name="mode"/>
@ -64,6 +66,14 @@
</div> </div>
</div> </div>
<div id="srcMeCard">
<p>First Name: <input type="text" id="meNameFirst" class="form-input" value=""></p>
<p>Last Name: <input type="text" id="meNameLast" class="form-input" value=""></p>
<p>Phone Number: <input type="text" id="mePhoneNumber" class="form-input" value=""></p>
<p>Email: <input type="text" id="meEmail" class="form-input" value=""></p>
<p>Website: <input type="text" id="meWebsite" class="form-input" value=""></p>
</div>
<hr> <hr>
<p id="errors" style="color:Tomato;"></p> <p id="errors" style="color:Tomato;"></p>
<p>Try your QR Code: <div id="qrcode"></div></p> <p>Try your QR Code: <div id="qrcode"></div></p>
@ -156,7 +166,7 @@
function toggleVis(id){ function toggleVis(id){
console.info("Got id", id); console.info("Got id", id);
["srcScanFile", "srcText", "srcWifi", "srcScanCam"].forEach(function (item){ ["srcScanFile", "srcText", "srcWifi", "srcScanCam", "srcMeCard"].forEach(function (item){
document.getElementById(item).style.display = "none"; document.getElementById(item).style.display = "none";
}); });
if (id != undefined && id != null) document.getElementById(id).style.display = "block"; if (id != undefined && id != null) document.getElementById(id).style.display = "block";
@ -188,6 +198,37 @@
} }
return qrstring; return qrstring;
} }
function generateMeCardString(meNameFirst, meNameLast, mePhoneNumber, meEmail, meWebsite){
var meCardStringOutput = 'MECARD:';
//first & Last name part of string, can have one or both
if (meNameFirst.trim().length != 0 && meNameLast.trim().length != 0) {
meCardStringOutput += 'N:'+meNameLast.trim()+','+meNameFirst.trim()+';';
}
else if (meNameLast.trim().length != 0) {
meCardStringOutput += 'N:'+meNameLast.trim()+';';
}
else if (meNameFirst.trim().length != 0) {
meCardStringOutput += 'N:'+meNameFirst.trim()+';';
}
if (mePhoneNumber.trim().length != 0) {
meCardStringOutput += 'TEL:'+mePhoneNumber.trim()+';';
}
if (meEmail.trim().length != 0) {
meCardStringOutput += 'EMAIL:'+meEmail.trim()+';';
}
if (meWebsite.trim().length != 0) {
meCardStringOutput += 'URL:'+meWebsite.trim()+';';
}
meCardStringOutput += ';';
return meCardStringOutput;
}
function refreshQRCode(){ function refreshQRCode(){
if (qrcode == null){ if (qrcode == null){
qrcode = new QRCode("qrcode", { qrcode = new QRCode("qrcode", {
@ -206,6 +247,14 @@
const hidden = document.getElementById("hidden").checked; const hidden = document.getElementById("hidden").checked;
const wifiString = generateWifiString(ssid, password, hidden, encryption); const wifiString = generateWifiString(ssid, password, hidden, encryption);
qrText= wifiString; qrText= wifiString;
} else if (document.getElementById("useMECARD").checked) {
const meNameFirst = document.getElementById("meNameFirst").value;
const meNameLast = document.getElementById("meNameLast").value;
const mePhoneNumber = document.getElementById("mePhoneNumber").value;
const meEmail = document.getElementById("meEmail").value;
const meWebsite = document.getElementById("meWebsite").value;
const meCardString = generateMeCardString(meNameFirst, meNameLast, mePhoneNumber, meEmail, meWebsite);
qrText = meCardString;
} else if (document.getElementById("useCAM").checked) { } else if (document.getElementById("useCAM").checked) {
qrText= document.getElementById("camQrResult").innerText; qrText= document.getElementById("camQrResult").innerText;
} else if (document.getElementById("useFILE").checked) { } else if (document.getElementById("useFILE").checked) {
@ -258,6 +307,14 @@
} }
document.getElementById("useTEXT").addEventListener("change",function(){toggleVis("srcText");}); document.getElementById("useTEXT").addEventListener("change",function(){toggleVis("srcText");});
document.getElementById("useMECARD").addEventListener("change",function(){toggleVis("srcMeCard");});
document.getElementById("meNameFirst").addEventListener("change",refreshQRCode);
document.getElementById("meNameLast").addEventListener("change",refreshQRCode);
document.getElementById("mePhoneNumber").addEventListener("change",refreshQRCode);
document.getElementById("meEmail").addEventListener("change",refreshQRCode);
document.getElementById("meWebsite").addEventListener("change",refreshQRCode);
document.getElementById("useCAM").addEventListener("change",function(){ document.getElementById("useCAM").addEventListener("change",function(){
initQrScanner(); initQrScanner();
initQrCam(); initQrCam();
@ -314,7 +371,6 @@ g.setColor(1,1,1);
}); });
document.getElementById('camList').addEventListener('change', event => { document.getElementById('camList').addEventListener('change', event => {
scanner.setCamera(event.target.value).then(updateFlashAvailability); scanner.setCamera(event.target.value).then(updateFlashAvailability);
}); });

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