1
0
Fork 0

Merge branch 'espruino:master' into master

master
pebl-hank 2022-06-20 07:44:36 +02:00 committed by GitHub
commit 6b3f49c0cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 3280 additions and 98 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

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

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

@ -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") {

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

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

View File

@ -1,7 +1,7 @@
{ {
"id": "qrcode", "id": "qrcode",
"name": "Custom QR Code", "name": "Custom QR Code",
"version": "0.05", "version": "0.06",
"description": "Use this to upload a customised QR code to Bangle.js", "description": "Use this to upload a customised QR code to Bangle.js",
"icon": "app.png", "icon": "app.png",
"tags": "qrcode", "tags": "qrcode",

View File

@ -4,11 +4,10 @@
E.showMenu({ E.showMenu({
"": { "title": /*LANG*/"Scheduler" }, "": { "title": /*LANG*/"Scheduler" },
/*LANG*/"< Back": () => back(), "< Back": () => back(),
/*LANG*/"Unlock at Buzz": { /*LANG*/"Unlock at Buzz": {
value: settings.unlockAtBuzz, value: settings.unlockAtBuzz,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => { onchange: v => {
settings.unlockAtBuzz = v; settings.unlockAtBuzz = v;
require("sched").setSettings(settings); require("sched").setSettings(settings);
@ -17,7 +16,6 @@
/*LANG*/"Default Auto Snooze": { /*LANG*/"Default Auto Snooze": {
value: settings.defaultAutoSnooze, value: settings.defaultAutoSnooze,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => { onchange: v => {
settings.defaultAutoSnooze = v; settings.defaultAutoSnooze = v;
require("sched").setSettings(settings); require("sched").setSettings(settings);
@ -38,7 +36,6 @@
/*LANG*/"Default Repeat": { /*LANG*/"Default Repeat": {
value: settings.defaultRepeat, value: settings.defaultRepeat,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => { onchange: v => {
settings.defaultRepeat = v; settings.defaultRepeat = v;
require("sched").setSettings(settings); require("sched").setSettings(settings);

View File

@ -7,3 +7,7 @@
use Layout library and display ETA use Layout library and display ETA
0.07: Add check for day of week 0.07: Add check for day of week
0.08: Update to new time_utils module 0.08: Update to new time_utils module
0.09: Vibrate with configured pattern
Add setting to defer start of algorithm
Add setting to disable scheduler alarm

View File

@ -4,10 +4,19 @@ The alarm must be in the next 24h.
The display shows: The display shows:
- the current time - The current time.
- time of the next alarm or timer - Time of the next alarm or timer.
- time difference between current time and alarm time (ETA) - Time difference between current time and alarm time (ETA).
- current state of the ESS algorithm, "Sleep" or "Awake", useful for debugging - Current state of the ESS algorithm, "Sleep" or "Awake", useful for debugging. State can also be "Deferred", see the "Run before alarm"-option.
## Settings
* **Keep alarm enabled**
- Yes: (default) Alert will stay enabled, e.g. for an alarm at 7:00 the clock will buzz at the calculated time from the ESS algorithm (for example 6:45) and again at 7:00.
- No: No action at configured alarm time from scheduler.
* **Run before alarm**
- disabled: (default) The ESS algorithm starts immediately when the application starts.
- 1..23: The ESS algorithm starts the configured time before the alarm. E.g. when set to 1h for an alarm at 7:00 the ESS algorithm will start at 6:00. This improves battery life.
## Logging ## Logging

View File

@ -1,9 +1,18 @@
const BANGLEJS2 = process.env.HWVERSION == 2; // check for bangle 2 const BANGLEJS2 = process.env.HWVERSION == 2; // check for bangle 2
const CONFIGFILE = "sleepphasealarm.json";
const Layout = require("Layout"); const Layout = require("Layout");
const locale = require('locale'); const locale = require('locale');
const alarms = require("Storage").readJSON("sched.json",1) || []; const alarms = require("Storage").readJSON("sched.json",1) || [];
const config = require("Storage").readJSON("sleepphasealarm.json",1) || {logs: []}; const config = Object.assign({
logs: [], // array of length 31 with one entry for each day of month
settings: {
startBeforeAlarm: 0, // 0 = start immediately, 1..23 = start 1h..23h before alarm time
disableAlarm: false,
}
}, require("Storage").readJSON(CONFIGFILE,1) || {});
const active = alarms.filter(a=>a.on); const active = alarms.filter(a=>a.on);
const schedSettings = require("sched").getSettings();
let buzzCount = schedSettings.buzzCount;
let logs = []; let logs = [];
// Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS): // Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS):
@ -43,7 +52,8 @@ function calc_ess(acc_magn) {
} }
// locate next alarm // locate next alarm
var nextAlarm; var nextAlarmDate;
var nextAlarmConfig;
active.forEach(alarm => { active.forEach(alarm => {
const now = new Date(); const now = new Date();
const time = require("time_utils").decodeTime(alarm.t); const time = require("time_utils").decodeTime(alarm.t);
@ -52,8 +62,9 @@ active.forEach(alarm => {
dateAlarm.setTime(dateAlarm.getTime() + (24*60*60*1000)); dateAlarm.setTime(dateAlarm.getTime() + (24*60*60*1000));
} }
if ((alarm.dow >> dateAlarm.getDay()) & 1) { // check valid day of week if ((alarm.dow >> dateAlarm.getDay()) & 1) { // check valid day of week
if (nextAlarm === undefined || dateAlarm < nextAlarm) { if (nextAlarmDate === undefined || dateAlarm < nextAlarmDate) {
nextAlarm = dateAlarm; nextAlarmDate = dateAlarm;
nextAlarmConfig = alarm;
} }
} }
}); });
@ -69,8 +80,8 @@ var layout = new Layout({
}, {lazy:true}); }, {lazy:true});
function drawApp() { function drawApp() {
var alarmHour = nextAlarm.getHours(); var alarmHour = nextAlarmDate.getHours();
var alarmMinute = nextAlarm.getMinutes(); var alarmMinute = nextAlarmDate.getMinutes();
if (alarmHour < 10) alarmHour = "0" + alarmHour; if (alarmHour < 10) alarmHour = "0" + alarmHour;
if (alarmMinute < 10) alarmMinute = "0" + alarmMinute; if (alarmMinute < 10) alarmMinute = "0" + alarmMinute;
layout.alarm_date.label = "Alarm at " + alarmHour + ":" + alarmMinute; layout.alarm_date.label = "Alarm at " + alarmHour + ":" + alarmMinute;
@ -80,51 +91,59 @@ function drawApp() {
if (Bangle.isLCDOn()) { if (Bangle.isLCDOn()) {
const now = new Date(); const now = new Date();
layout.date.label = locale.time(now, BANGLEJS2 && Bangle.isLocked() ? 1 : 0); // hide seconds on bangle 2 layout.date.label = locale.time(now, BANGLEJS2 && Bangle.isLocked() ? 1 : 0); // hide seconds on bangle 2
const diff = nextAlarm - now; const diff = nextAlarmDate - now;
const diffHour = Math.floor((diff % 86400000) / 3600000).toString(); const diffHour = Math.floor((diff % 86400000) / 3600000).toString();
const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString(); const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString();
layout.eta.label = "ETA: -"+ diffHour + ":" + diffMinutes.padStart(2, '0'); layout.eta.label = "ETA: -"+ diffHour + ":" + diffMinutes.padStart(2, '0');
layout.render(); layout.render();
} }
setTimeout(()=>{
drawTime();
}, 1000 - (Date.now() % 1000));
} }
drawTime(); drawTime();
setInterval(drawTime, 500); // 2Hz
} }
var buzzCount = 19;
function buzz() { function buzz() {
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
Bangle.setLCDPower(1); Bangle.setLCDPower(1);
Bangle.buzz().then(()=>{ require("buzz").pattern(nextAlarmConfig.vibrate || ";");
if (buzzCount--) { if (buzzCount--) {
setTimeout(buzz, 500); setTimeout(buzz, schedSettings.buzzIntervalMillis);
} else { } else {
// back to main after finish // back to main after finish
setTimeout(load, 1000); setTimeout(load, 1000);
} }
});
} }
function addLog(time, type) { function addLog(time, type) {
logs.push({time: time, type: type}); logs.push({time: time, type: type});
require("Storage").writeJSON("sleepphasealarm.json", config); if (logs.length > 1) { // Do not write if there is only one state
require("Storage").writeJSON(CONFIGFILE, config);
}
} }
// run // run
var minAlarm = new Date(); var minAlarm = new Date();
var measure = true; var measure = true;
if (nextAlarm !== undefined) { if (nextAlarmDate !== undefined) {
config.logs[nextAlarm.getDate()] = []; // overwrite log on each day of month config.logs[nextAlarmDate.getDate()] = []; // overwrite log on each day of month
logs = config.logs[nextAlarm.getDate()]; logs = config.logs[nextAlarmDate.getDate()];
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
let swest_last; let swest_last;
// minimum alert 30 minutes early // minimum alert 30 minutes early
minAlarm.setTime(nextAlarm.getTime() - (30*60*1000)); minAlarm.setTime(nextAlarmDate.getTime() - (30*60*1000));
Bangle.on('accel', (accelData) => { // 12.5Hz run = () => {
layout.state.label = "Start";
layout.render();
Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval
Bangle.setPollInterval(80); // 12.5Hz
Bangle.on('accel', (accelData) => {
const now = new Date(); const now = new Date();
const acc = accelData.mag; const acc = accelData.mag;
const swest = calc_ess(acc); const swest = calc_ess(acc);
@ -145,17 +164,35 @@ if (nextAlarm !== undefined) {
} }
} }
if (now >= nextAlarm) { if (now >= nextAlarmDate) {
// The alarm widget should handle this one // The alarm widget should handle this one
addLog(now, "alarm"); addLog(now, "alarm");
setTimeout(load, 1000); setTimeout(load, 1000);
} else if (measure && now >= minAlarm && swest === false) { } else if (measure && now >= minAlarm && swest_last === false) {
addLog(now, "alarm"); addLog(now, "alarm");
buzz(); buzz();
measure = false; measure = false;
if (config.settings.disableAlarm) {
// disable alarm for scheduler
nextAlarmConfig.last = now.getDate();
require("Storage").writeJSON("sched.json", alarms);
}
} }
}); });
};
drawApp(); drawApp();
if (config.settings.startBeforeAlarm === 0) {
// Start immediately
run();
} else {
// defer start
layout.state.label = "Deferred";
layout.render();
const diff = nextAlarmDate - Date.now();
let timeout = diff-config.settings.startBeforeAlarm*60*60*1000;
if (timeout < 0) timeout = 0;
setTimeout(run, timeout);
}
} else { } else {
E.showMessage('No Alarm'); E.showMessage('No Alarm');
setTimeout(load, 1000); setTimeout(load, 1000);

View File

@ -1,7 +1,6 @@
<html> <html>
<head> <head>
<link rel="stylesheet" href="../../css/spectre.min.css"> <link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/chart.js@2.8.0/dist/Chart.min.css">
</head> </head>
<body> <body>
<p>Please select a wakeup day:</p> <p>Please select a wakeup day:</p>

View File

@ -2,7 +2,7 @@
"id": "sleepphasealarm", "id": "sleepphasealarm",
"name": "SleepPhaseAlarm", "name": "SleepPhaseAlarm",
"shortName": "SleepPhaseAlarm", "shortName": "SleepPhaseAlarm",
"version": "0.08", "version": "0.09",
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.", "description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
"icon": "app.png", "icon": "app.png",
"tags": "alarm", "tags": "alarm",
@ -11,6 +11,7 @@
"dependencies": {"scheduler":"type"}, "dependencies": {"scheduler":"type"},
"storage": [ "storage": [
{"name":"sleepphasealarm.app.js","url":"app.js"}, {"name":"sleepphasealarm.app.js","url":"app.js"},
{"name":"sleepphasealarm.settings.js","url":"settings.js"},
{"name":"sleepphasealarm.img","url":"app-icon.js","evaluate":true} {"name":"sleepphasealarm.img","url":"app-icon.js","evaluate":true}
], ],
"data": [{"name":"sleepphasealarm.json","storageFile":true}], "data": [{"name":"sleepphasealarm.json","storageFile":true}],

View File

@ -0,0 +1,37 @@
(function(back) {
const CONFIGFILE = "sleepphasealarm.json";
// Load settings
const config = Object.assign({
logs: [], // array of length 31 with one entry for each day of month
settings: {
startBeforeAlarm: 0, // 0 = start immediately, 1..23 = start 1h..23h before alarm time
disableAlarm: false,
}
}, require("Storage").readJSON(CONFIGFILE,1) || {});
function writeSettings() {
require('Storage').writeJSON(CONFIGFILE, config);
}
// Show the menu
E.showMenu({
"" : { "title" : "SleepPhaseAlarm" },
'Keep alarm enabled': {
value: !!config.settings.disableAlarm,
format: v => v?"No":"Yes",
onchange: v => {
config.settings.disableAlarm = v;
writeSettings();
}
}, "< Back" : () => back(),
'Run before alarm': {
format: v => v === 0 ? 'disabled' : v+'h',
value: config.settings.startBeforeAlarm,
min: 0, max: 23,
onchange: v => {
config.settings.startBeforeAlarm = v;
writeSettings();
}
},
});
})

2
core

@ -1 +1 @@
Subproject commit cdab12fcf85bc140e1f5c3f7b15218108c3f7d32 Subproject commit c46b4edd2052d0df37fea41f8839af8175a78ec9

2168
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -176,8 +176,9 @@ declare type GraphicsApi = {
declare const Graphics: GraphicsApi; declare const Graphics: GraphicsApi;
declare const g: GraphicsApi; declare const g: GraphicsApi;
type WidgetArea = 'tl' | 'tr' | 'bl' | 'br';
declare type Widget = { declare type Widget = {
area: 'tr' | 'tl'; area: WidgetArea;
width: number; width: number;
draw: (this: { x: number; y: number }) => void; draw: (this: { x: number; y: number }) => void;
}; };